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  }