-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommand.go
More file actions
208 lines (167 loc) · 4.99 KB
/
command.go
File metadata and controls
208 lines (167 loc) · 4.99 KB
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
200
201
202
203
204
205
206
207
208
package bcr
import (
"strings"
"sync"
"time"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/spf13/pflag"
"github.com/starshine-sys/snowflake/v2"
)
// CustomPerms is a custom permission checker
type CustomPerms interface {
// The string used for the permissions if the check fails
String(Contexter) string
// Returns true if the user has permission to run the command
Check(Contexter) (bool, error)
}
// Command is a single command, or a group
type Command struct {
Name string
Aliases []string
// Blacklistable commands use the router's blacklist function to check if they can be run
Blacklistable bool
// Summary is used in the command list
Summary string
// Description is used in the help command
Description string
// Usage is appended to the command name in help commands
Usage string
// Hidden commands are not shown in the help command
Hidden bool
Args *Args
CustomPermissions CustomPerms
// Flags is used to create a new flag set, which is then parsed before the command is run.
// These can then be retrieved with the (*FlagSet).Get*() methods.
Flags func(fs *pflag.FlagSet) *pflag.FlagSet
subCmds map[string]*Command
subMu sync.RWMutex
// GuildPermissions is the required *global* permissions
GuildPermissions discord.Permissions
// Permissions is the required permissions in the *context channel*
Permissions discord.Permissions
GuildOnly bool
OwnerOnly bool
Command func(*Context) error
Cooldown time.Duration
// id is a unique ID. This is automatically generated on startup and is (pretty much) guaranteed to be unique *per session*. This ID will *not* be consistent between restarts.
id snowflake.Snowflake
// Executed when a slash command is executed, with a *SlashContext being passed in.
// Also executed when Command is nil, with a *Context being passed in instead.
SlashCommand func(Contexter) error
// If this is set and SlashCommand is nil, AddCommand *will panic!*
// Even if the command has no options, this should be set to an empty slice rather than nil.
Options *[]discord.CommandOption
}
// AddSubcommand adds a subcommand to a command
func (c *Command) AddSubcommand(sub *Command) *Command {
if c.Options != nil && c.SlashCommand == nil {
panic("command.Options set without command.SlashCommand being set")
}
sub.id = sGen.Get()
c.subMu.Lock()
defer c.subMu.Unlock()
if c.subCmds == nil {
c.subCmds = make(map[string]*Command)
}
c.subCmds[strings.ToLower(sub.Name)] = sub
for _, a := range sub.Aliases {
c.subCmds[strings.ToLower(a)] = sub
}
return sub
}
// GetCommand gets a command by name
func (r *Router) GetCommand(name string) *Command {
r.cmdMu.RLock()
defer r.cmdMu.RUnlock()
if v, ok := r.cmds[strings.ToLower(name)]; ok {
return v
}
return nil
}
// GetCommand gets a command by name
func (c *Command) GetCommand(name string) *Command {
c.subMu.RLock()
defer c.subMu.RUnlock()
if v, ok := c.subCmds[strings.ToLower(name)]; ok {
return v
}
return nil
}
func (c *Command) Subcommands() []*Command {
// deduplicate commands
sf := make([]snowflake.Snowflake, 0)
cmds := make([]*Command, 0)
for _, c := range c.subCmds {
if !snowflakeInSlice(c.id, sf) {
sf = append(sf, c.id)
cmds = append(cmds, c)
}
}
return cmds
}
// Args is a minimum/maximum argument count.
// If either is -1, it's treated as "no minimum" or "no maximum".
// This replaces the Check* functions in Context.
type Args [2]int
// MinArgs returns an *Args with only a minimum number of arguments.
func MinArgs(i int) *Args {
return &Args{i, -1}
}
// MaxArgs returns an *Args with only a maximum number of arguments.
func MaxArgs(i int) *Args {
return &Args{-1, i}
}
// ArgRange returns an *Args with both a minimum and maximum number of arguments.
func ArgRange(i, j int) *Args {
return &Args{i, j}
}
// ExactArgs returns an *Args with an exact number of required arguments.
func ExactArgs(i int) *Args {
return &Args{i, i}
}
type requireRole struct {
name string
// owners override the role check
owners []discord.UserID
// any of these roles is required for the check to succeed
roles []discord.RoleID
}
var _ CustomPerms = (*requireRole)(nil)
func (r *requireRole) String(ctx Contexter) string {
return r.name
}
func (r *requireRole) Check(ctx Contexter) (bool, error) {
for _, u := range r.owners {
if u == ctx.User().ID {
return true, nil
}
}
if ctx.GetMember() == nil {
return false, nil
}
for _, r := range r.roles {
for _, mr := range ctx.GetMember().RoleIDs {
if r == mr {
return true, nil
}
}
}
return false, nil
}
// RequireRole returns a CustomPerms that requires the given roles.
// If any of r's owner IDs are not valid snowflakes, this function will panic!
func (r *Router) RequireRole(name string, roles ...discord.RoleID) CustomPerms {
owners := []discord.UserID{}
for _, u := range r.BotOwners {
id, err := discord.ParseSnowflake(u)
if err != nil {
panic(err)
}
owners = append(owners, discord.UserID(id))
}
return &requireRole{
name: name,
owners: owners,
roles: roles,
}
}