/
message.go
159 lines (143 loc) · 4.02 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package sdbot
import (
"regexp"
"strconv"
"strings"
"time"
)
// Message represents a message sent by a user to either a room the bot is
// currently in, or to the bot via private messages. A message also defines
// behaviour in its methods to reply to these messages.
type Message struct {
Bot *Bot
Time time.Time
Command string
Params []string
Timestamp int
Room *Room
User *User
Auth string
Target Target
Message string
Matches map[string]map[*regexp.Regexp][]string
}
// NewMessage creates a new message and parses the message.
func NewMessage(s string, bot *Bot) *Message {
m := &Message{
Bot: bot,
Time: time.Now(),
Matches: make(map[string]map[*regexp.Regexp][]string),
}
m.Command, m.Params, m.Timestamp, m.Room, m.User, m.Auth, m.Target, m.Message = parseMessage(s, bot)
return m
}
// Parse a raw message and return data in the following order:
// Command, Params, Timestamp, Room, User, Auth, Target, Message
// TODO Reduce cyclic complexity? Lots of ifs ands and switches.
func parseMessage(s string, b *Bot) (string, []string, int, *Room, *User, string, Target, string) {
newlineDelimited := strings.Split(s, "\n")
vertbarDelimited := strings.Split(s, "|")
var command string
var params []string
var timestamp int
var room *Room
var user *User
var auth string
var message string
// The command is always after the first vertical bar.
if len(vertbarDelimited) < 2 {
command = "none"
} else {
command = string(vertbarDelimited[1])
}
// Parse the parameters following a command.
if command == "" {
params = []string{}
} else {
if len(vertbarDelimited) > 2 {
params = vertbarDelimited[2:]
} else {
params = []string{}
}
}
// Parse the timestamp of a chat event.
var err error
if strings.Contains(command, ":") {
timestamp, err = strconv.Atoi(params[0])
CheckErr(err)
} else {
timestamp = 0
}
// If the message starts with a ">" then it comes from a room.
if newlineDelimited[0] == "" {
room = &Room{}
} else {
if string(newlineDelimited[0][0]) == ">" {
room = FindRoomEnsured(string(newlineDelimited[0][1:]), b)
} else {
room = &Room{}
}
}
// Parse the user sending a command, and their auth level.
switch strings.ToLower(command) {
case "c:":
auth = string(vertbarDelimited[3][0])
user = FindUserEnsured(string(vertbarDelimited[3][1:]), b)
case "c":
fallthrough
case "j":
fallthrough
case "l":
fallthrough
case "n":
fallthrough
case "pm":
auth = string(vertbarDelimited[2][0])
user = FindUserEnsured(string(vertbarDelimited[2][1:]), b)
}
// Parse the message
if command == "" {
message = ""
} else {
switch strings.ToLower(command) {
case "c:", "pm":
message = strings.Join(vertbarDelimited[4:], "|")
case "none":
message = newlineDelimited[len(newlineDelimited)-1]
}
}
// Decide the target
if strings.ToLower(command) == "pm" {
return command, params, timestamp, room, user, auth, user, message
}
return command, params, timestamp, room, user, auth, room, message
}
// Reply responds to a message and prepends the username of the user the bot
// is responding to.
func (m *Message) Reply(res string) {
m.Target.Reply(m, res)
}
// RawReply responds to a message without prepending anything to the message.
// Note that you should take care to not allow users to influence a raw
// reply message to do a client command. For this reason, prefer to use
// Reply unless you are responding with a static message. You may want to
// event freeze the string.
func (m *Message) RawReply(res string) {
m.Target.RawReply(m, res)
}
// Match adds matches to the message and return true if there was no previous
// match and if there was indeed a match.
func (m *Message) Match(r *regexp.Regexp, event string) bool {
if m.Matches[event][r] == nil {
matches := r.FindStringSubmatch(m.Message)
if matches != nil {
m.Matches[event][r] = matches
return true
}
}
return false
}
// Private returns true if the message was sent in a private message.
func (m *Message) Private() bool {
return m.User == m.Target
}