/
router.go
187 lines (160 loc) · 4.32 KB
/
router.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
package main
import (
"database/sql"
"fmt"
"github.com/MattiasBerlin/outbot/commands"
"github.com/MattiasBerlin/outbot/handlers"
"github.com/bwmarrin/discordgo"
"sort"
"strings"
)
// Router for commands.
type Router struct {
commands map[string]*commands.Command
prefix string
guildID string
db *sql.DB
}
// NewRouter adds and initializes the commands.
func NewRouter(prefix string, guildID string, s *discordgo.Session, db *sql.DB) *Router {
r := &Router{
commands: make(map[string]*commands.Command),
prefix: prefix,
guildID: guildID,
db: db,
}
cmds := getCommands()
// Init commands
for _, cmd := range cmds {
if cmd.Init != nil {
fmt.Println("Initializing handler:", cmd.CallPhrase)
cmd.Init(s, db)
}
}
r.AddCommands(cmds)
return r
}
// AddCommand to the router.
// Aliases are also registered.
func (r *Router) AddCommand(cmd commands.Command) {
r.commands[cmd.CallPhrase] = &cmd
// Add aliases
for _, alias := range cmd.Aliases {
// TODO: For now the prefix (super's callphrase or whatever) is ignored for aliases but
// it would be nice to be able to use it if wanted
r.commands[alias] = &cmd
}
r.addSubCommands(cmd)
}
func (r *Router) addSubCommands(cmd commands.Command) {
for _, sub := range cmd.SubCommands {
for _, alias := range sub.Aliases {
r.commands[alias] = &sub
}
r.addSubCommands(sub)
}
}
// AddCommands to the router.
func (r *Router) AddCommands(cmds []commands.Command) {
for _, cmd := range cmds {
r.AddCommand(cmd)
}
}
// getCommand returns the command matching the message.
// The remaining text (after the command) is also returned.
func (r *Router) getCommand(msg string) (*commands.Command, string) {
split := strings.Split(msg, " ")
fmt.Println("Checking", split[0])
cmd := r.commands[split[0]]
// Check for subcommand matches if relevant
if cmd != nil && len(split) > 1 {
split = split[1:]
sub := r.getSubCommand(cmd, split) // TODO: Ugly, make this prettier..
for sub != nil {
cmd = sub
split = split[1:]
sub = r.getSubCommand(sub, split)
}
} else {
if len(split) > 1 {
split = split[1:]
} else {
split = []string{}
}
}
return cmd, strings.Join(split, " ")
}
func (r *Router) getSubCommand(cmd *commands.Command, trail []string) *commands.Command {
for _, sub := range cmd.SubCommands {
if sub.CallPhrase == trail[0] {
return &sub
}
}
return nil
}
func (r *Router) getAllCommands() []commands.Command {
cmds := make([]commands.Command, len(r.commands))
keys := make([]string, len(r.commands))
// Go randomizes the element order if you use range on the map directly,
// so the keys have to be sorted to get them in a consistent order
i := 0
for key := range r.commands {
keys[i] = key
i++
}
sort.Strings(keys)
i = 0
for _, key := range keys {
cmds[i] = *r.commands[key]
i++
}
return cmds
}
// OnMessageSent gets called when a message is sent and routes to the correct handler based on the message.
func (r *Router) OnMessageSent(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot || !strings.HasPrefix(m.Content, r.prefix) {
return
}
// Only get first word
//msg := strings.Split(m.Content, " ")[0]
// Strip prefix
msg := m.Content[len(r.prefix):]
command, msg := r.getCommand(msg)
if command == nil {
fmt.Println("Command not found")
return
}
if command.Handler == nil {
fmt.Println(command.CallPhrase, "does not have a handler")
return
}
user, err := s.GuildMember(r.guildID, m.Author.ID)
if err != nil {
fmt.Println("Failed to obtain guild member:", err.Error())
return
}
if user == nil {
// Not sent by a user?
// Probably a join message or something like that
return
}
if command.Permission.Authorized(*user) {
command.Handler(msg, s, m, r.db, guildID, r.getAllCommands()) // TODO: There's no need to get all the commands every call, just do it once and save it
} else {
fmt.Println(m.Author.Username, "tried to use", command.CallPhrase, "without the required authorization")
}
}
func getCommands() []commands.Command {
return []commands.Command{
handlers.HelpCommand(),
handlers.PingCommand(),
handlers.EventCommand(),
handlers.OptInCommand(),
handlers.OptOutCommand(),
handlers.SetOptInCommand(),
handlers.ListParticipantsCommand(),
handlers.ClearParticipantsCommand(),
handlers.StatusCommand(),
handlers.SetRolesCommand(),
}
}