github.com/starshine-sys/bcr@v0.21.0/bcr.go (about) 1 package bcr 2 3 import ( 4 "flag" 5 "strings" 6 "sync" 7 "time" 8 9 "github.com/diamondburned/arikawa/v3/api" 10 "github.com/diamondburned/arikawa/v3/discord" 11 "github.com/diamondburned/arikawa/v3/gateway" 12 "github.com/diamondburned/arikawa/v3/gateway/shard" 13 "github.com/diamondburned/arikawa/v3/state" 14 ) 15 16 // Version returns the current brc version 17 func Version() string { 18 return "0.18.0" 19 } 20 21 // RequiredIntents are the intents required for the command handler 22 const RequiredIntents = gateway.IntentGuildMessages | gateway.IntentGuildMessageReactions | gateway.IntentDirectMessages | gateway.IntentDirectMessageReactions | gateway.IntentGuilds 23 24 // Router is the command router 25 type Router struct { 26 BotOwners []string 27 28 Prefixes []string 29 Prefixer Prefixer 30 31 ShardManager *shard.Manager 32 Bot *discord.User 33 Logger *Logger 34 35 BlacklistFunc func(Contexter) bool 36 HelpCommand func(*Context) error 37 DefaultMentions *api.AllowedMentions 38 EmbedColor discord.Color 39 40 ReactTimeout time.Duration 41 42 cooldowns *CooldownCache 43 cmds map[string]*Command 44 cmdMu sync.RWMutex 45 46 slashGroups []*Group 47 48 // maps + mutexes 49 reactions map[reactionKey]reactionInfo 50 reactionMu sync.RWMutex 51 messages map[messageKey]messageInfo 52 messageMu sync.RWMutex 53 buttons map[buttonKey]buttonInfo 54 buttonMu sync.RWMutex 55 slashButtons map[buttonKey]slashButtonInfo 56 slashButtonMu sync.RWMutex 57 } 58 59 // New creates a new router object 60 func New(s *shard.Manager, owners, prefixes []string) *Router { 61 r := &Router{ 62 ShardManager: s, 63 BotOwners: owners, 64 Prefixes: prefixes, 65 EmbedColor: discord.DefaultEmbedColor, 66 67 Logger: NewStdlibLogger(false), 68 69 DefaultMentions: &api.AllowedMentions{ 70 Parse: []api.AllowedMentionType{api.AllowUserMention}, 71 }, 72 73 ReactTimeout: 15 * time.Minute, 74 75 cmds: make(map[string]*Command), 76 reactions: make(map[reactionKey]reactionInfo), 77 messages: make(map[messageKey]messageInfo), 78 buttons: make(map[buttonKey]buttonInfo), 79 slashButtons: make(map[buttonKey]slashButtonInfo), 80 cooldowns: newCooldownCache(), 81 } 82 83 // set prefixer 84 r.Prefixer = r.DefaultPrefixer 85 86 // add required handlers 87 r.AddHandler(r.ReactionAdd) 88 r.AddHandler(r.ReactionRemove) 89 r.AddHandler(r.ReactionMessageDelete) 90 r.AddHandler(r.MsgHandlerCreate) 91 r.AddHandler(r.ButtonHandler) 92 93 return r 94 } 95 96 // NewWithState creates a new router with a state. 97 // The token is automatically prefixed with `Bot `. 98 func NewWithState(token string, owners []discord.UserID, prefixes []string) (*Router, error) { 99 return NewWithIntents(token, owners, prefixes, RequiredIntents) 100 } 101 102 // NewWithIntents creates a new router with a state, with the specified intents. 103 // The token is automatically prefixed with `Bot `. 104 func NewWithIntents(token string, owners []discord.UserID, prefixes []string, intents gateway.Intents) (*Router, error) { 105 ownerStrings := make([]string, 0) 106 for _, o := range owners { 107 ownerStrings = append(ownerStrings, o.String()) 108 } 109 110 newShard := state.NewShardFunc(func(m *shard.Manager, s *state.State) { 111 s.AddIntents(intents) 112 }) 113 114 m, err := shard.NewManager("Bot "+token, newShard) 115 if err != nil { 116 return nil, err 117 } 118 119 r := New(m, ownerStrings, prefixes) 120 return r, nil 121 } 122 123 // AddCommand adds a command to the router 124 func (r *Router) AddCommand(c *Command) *Command { 125 if c.Options != nil && c.SlashCommand == nil { 126 panic("command.Options set without command.SlashCommand being set") 127 } 128 129 if c.Options != nil && c.Command == nil { 130 c.stdFlags = func(ctx *Context, fs *flag.FlagSet) (*Context, *flag.FlagSet) { 131 for _, o := range *c.Options { 132 if o.Required { 133 continue 134 } 135 136 name := strings.ToLower(o.Name) 137 138 switch o.Type { 139 case discord.StringOption, discord.ChannelOption, discord.UserOption, discord.RoleOption, discord.MentionableOption: 140 v := fs.String(name, "", o.Description) 141 ctx.FlagMap[name] = v 142 case discord.IntegerOption: 143 v := fs.Int64(name, 0, o.Description) 144 ctx.FlagMap[name] = v 145 case discord.BooleanOption: 146 v := fs.Bool(name, false, o.Description) 147 ctx.FlagMap[name] = v 148 case discord.NumberOption: 149 v := fs.Float64(name, 0, o.Description) 150 ctx.FlagMap[name] = v 151 default: 152 ctx.Router.Logger.Error("invalid CommandOptionType set in command %v, option %v: %v", c.Name, o.Name, o.Type) 153 } 154 } 155 156 return ctx, fs 157 } 158 } 159 160 c.id = sGen.Get() 161 r.cmdMu.Lock() 162 defer r.cmdMu.Unlock() 163 r.cmds[strings.ToLower(c.Name)] = c 164 165 for _, a := range c.Aliases { 166 r.cmds[strings.ToLower(a)] = c 167 } 168 169 return c 170 } 171 172 // AddHandler adds a handler to all States in this Router 173 func (r *Router) AddHandler(v interface{}) { 174 r.ShardManager.ForEach(func(s shard.Shard) { 175 state := s.(*state.State) 176 177 state.AddHandler(v) 178 }) 179 } 180 181 // StateFromGuildID returns the state.State for the given guild ID 182 func (r *Router) StateFromGuildID(guildID discord.GuildID) (st *state.State, id int) { 183 if guildID.IsValid() { 184 s, shardID := r.ShardManager.FromGuildID(guildID) 185 return s.(shard.ShardState).Shard.(*state.State), shardID 186 } 187 188 s := r.ShardManager.Shard(0) 189 return s.(shard.ShardState).Shard.(*state.State), 0 190 }