This repository has been archived by the owner on Jan 27, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
cmd.go
199 lines (169 loc) · 5.21 KB
/
cmd.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package bot
import (
"fmt"
"log"
"sync"
)
// Cmd holds the parsed user's input for easier handling of commands
type Cmd struct {
Raw string // Raw is full string passed to the command
Channel string // Channel where the command was called
Nick string // User who sent the message
Message string // Full string without the prefix
Command string // Command is the first argument passed to the bot
FullArg string // Full argument as a single string
Args []string // Arguments as array
}
// PassiveCmd holds the information which will be passed to passive commands when receiving a message
type PassiveCmd struct {
Raw string // Raw message sent to the channel
Channel string // Channel which the message was sent to
Nick string // Nick of the user which sent the message
}
type customCommand struct {
Version int
Cmd string
CmdFuncV1 activeCmdFuncV1
CmdFuncV2 activeCmdFuncV2
Description string
ExampleArgs string
}
type incomingMessage struct {
Channel string
Text string
SenderNick string
BotCurrentNick string
}
// CmdResult is the result message of V2 commands
type CmdResult struct {
Channel string // The channel where the bot should send the message
Message string // The message to be sent
}
const (
v1 = iota
v2
)
const (
commandNotAvailable = "Command %v not available."
noCommandsAvailable = "No commands available."
errorExecutingCommand = "Error executing %s: %s"
)
type passiveCmdFunc func(cmd *PassiveCmd) (string, error)
type activeCmdFuncV1 func(cmd *Cmd) (string, error)
type activeCmdFuncV2 func(cmd *Cmd) (CmdResult, error)
var (
commands = make(map[string]*customCommand)
passiveCommands = make(map[string]passiveCmdFunc)
)
// RegisterCommand adds a new command to the bot.
// The command(s) should be registered in the Init() func of your package
// command: String which the user will use to execute the command, example: reverse
// decription: Description of the command to use in !help, example: Reverses a string
// exampleArgs: Example args to be displayed in !help <command>, example: string to be reversed
// cmdFunc: Function which will be executed. It will received a parsed command as a Cmd value
func RegisterCommand(command, description, exampleArgs string, cmdFunc activeCmdFuncV1) {
commands[command] = &customCommand{
Version: v1,
Cmd: command,
CmdFuncV1: cmdFunc,
Description: description,
ExampleArgs: exampleArgs,
}
}
// RegisterCommandV2 adds a new command to the bot.
// It is the same as RegisterCommand but the command can specify the channel to reply to
func RegisterCommandV2(command, description, exampleArgs string, cmdFunc activeCmdFuncV2) {
commands[command] = &customCommand{
Version: v2,
Cmd: command,
CmdFuncV2: cmdFunc,
Description: description,
ExampleArgs: exampleArgs,
}
}
// RegisterPassiveCommand adds a new passive command to the bot.
// The command should be registered in the Init() func of your package
// Passive commands receives all the text posted to a channel without any parsing
// command: String used to identify the command, for internal use only (ex: logs)
// cmdFunc: Function which will be executed. It will received the raw message, channel and nick
func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (string, error)) {
passiveCommands[command] = cmdFunc
}
func isPrivateMsg(channel, currentNick string) bool {
return channel == currentNick
}
func messageReceived(channel, text, senderNick string, conn connection) {
if isPrivateMsg(channel, conn.GetNick()) {
channel = senderNick // should reply in private
}
command := parse(text, channel, senderNick)
if command == nil {
executePassiveCommands(&PassiveCmd{
Raw: text,
Channel: channel,
Nick: senderNick,
}, conn)
return
}
switch command.Command {
case helpCommand:
help(command, channel, senderNick, conn)
case joinCommand:
join(command, channel, senderNick, conn)
case partCommand:
part(command, channel, senderNick, conn)
default:
handleCmd(command, conn)
}
}
func executePassiveCommands(cmd *PassiveCmd, conn connection) {
var wg sync.WaitGroup
for k, v := range passiveCommands {
cmdName := k
cmdFunc := v
wg.Add(1)
go func() {
defer wg.Done()
log.Println("Executing passive command: ", cmdName)
result, err := cmdFunc(cmd)
if err != nil {
log.Println(err)
} else {
conn.Privmsg(cmd.Channel, result)
}
}()
}
wg.Wait()
}
func handleCmd(c *Cmd, conn connection) {
cmd := commands[c.Command]
if cmd == nil {
log.Printf("Command not found %v", c.Command)
return
}
log.Printf("HandleCmd %v %v", c.Command, c.FullArg)
switch cmd.Version {
case v1:
message, err := cmd.CmdFuncV1(c)
checkCmdError(err, c, conn)
if message != "" {
conn.Privmsg(c.Channel, message)
}
case v2:
result, err := cmd.CmdFuncV2(c)
checkCmdError(err, c, conn)
if result.Channel == "" {
result.Channel = c.Channel
}
if result.Message != "" {
conn.Privmsg(result.Channel, result.Message)
}
}
}
func checkCmdError(err error, c *Cmd, conn connection) {
if err != nil {
errorMsg := fmt.Sprintf(errorExecutingCommand, c.Command, err.Error())
log.Printf(errorMsg)
conn.Privmsg(c.Channel, errorMsg)
}
}