|
@@ -0,0 +1,215 @@
|
|
|
+package discordbot
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/bwmarrin/discordgo"
|
|
|
+)
|
|
|
+
|
|
|
+type SlashRouter struct {
|
|
|
+ registeredCommands []*discordgo.ApplicationCommand
|
|
|
+ commands []*discordgo.ApplicationCommand
|
|
|
+ commandHandlers map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|
|
+ guildIDs []string
|
|
|
+ existingCommands []Command
|
|
|
+}
|
|
|
+
|
|
|
+type SlashRouteOptions struct {
|
|
|
+ Name string // match name that should trigger this route handler
|
|
|
+ Description string // short description of this route
|
|
|
+ Callback SlashHandler
|
|
|
+ Options []*discordgo.ApplicationCommandOption // Array of application command options for the command
|
|
|
+}
|
|
|
+
|
|
|
+// Handler is the function signature required for a message route handler.
|
|
|
+type SlashHandler func(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|
|
+
|
|
|
+func NewSlashRouter() *SlashRouter {
|
|
|
+ r := &SlashRouter{}
|
|
|
+
|
|
|
+ r.commandHandlers = make(map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate))
|
|
|
+
|
|
|
+ return r
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) RegisterGuild(guildID string) {
|
|
|
+ r.guildIDs = append(r.guildIDs, guildID)
|
|
|
+}
|
|
|
+
|
|
|
+type Command struct {
|
|
|
+ applicationID string
|
|
|
+ guildID string
|
|
|
+ commandID string
|
|
|
+ name string
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) GetExistingCommand(appID, guildID, name string) (string, bool) {
|
|
|
+ for _, cmd := range r.existingCommands {
|
|
|
+ if cmd.applicationID == appID && cmd.guildID == guildID && cmd.name == name {
|
|
|
+ return cmd.commandID, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return "", false
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) LoadExistingCommands(s *discordgo.Session) error {
|
|
|
+ logHandler.Info("Loading Existing Commands")
|
|
|
+ for _, guildID := range r.guildIDs {
|
|
|
+ logHandler.Infof("Getting applications for %s", guildID)
|
|
|
+ cmds, err := s.ApplicationCommands(s.State.User.ID, guildID)
|
|
|
+ if err != nil {
|
|
|
+ r.existingCommands = nil
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, cmd := range cmds {
|
|
|
+ newCommand := Command{
|
|
|
+ applicationID: cmd.ApplicationID,
|
|
|
+ guildID: cmd.GuildID,
|
|
|
+ commandID: cmd.ID,
|
|
|
+ name: cmd.Name,
|
|
|
+ }
|
|
|
+
|
|
|
+ r.existingCommands = append(r.existingCommands, newCommand)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logHandler.Infof("Loaded %d commands", len(r.existingCommands))
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) Register(name string, options SlashRouteOptions) error {
|
|
|
+ if name == "" {
|
|
|
+ return ErrRegisterRouteNameRequired
|
|
|
+ }
|
|
|
+
|
|
|
+ if options.Callback == nil {
|
|
|
+ return ErrRegisterCallbackRequired
|
|
|
+ }
|
|
|
+
|
|
|
+ command := discordgo.ApplicationCommand{
|
|
|
+ Name: name,
|
|
|
+ Description: options.Description,
|
|
|
+ Options: options.Options,
|
|
|
+ }
|
|
|
+
|
|
|
+ r.commands = append(r.commands, &command)
|
|
|
+
|
|
|
+ r.commandHandlers[name] = options.Callback
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) GenerateCommands(s *discordgo.Session) error {
|
|
|
+ r.registeredCommands = make([]*discordgo.ApplicationCommand, len(r.commands))
|
|
|
+
|
|
|
+ s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
|
+ if h, ok := r.commandHandlers[i.ApplicationCommandData().Name]; ok {
|
|
|
+ h(s, i)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ for _, guildID := range r.guildIDs {
|
|
|
+ for i, v := range r.commands {
|
|
|
+ var cmd *discordgo.ApplicationCommand
|
|
|
+ var err error
|
|
|
+ if cmdID, exists := r.GetExistingCommand(s.State.User.ID, guildID, v.Name); exists {
|
|
|
+ logHandler.Infof("Updating the existing command - %s", v.Name)
|
|
|
+ cmd, err = s.ApplicationCommandEdit(s.State.User.ID, guildID, cmdID, v)
|
|
|
+ } else {
|
|
|
+ logHandler.Infof("Creating a new command - %s", v.Name)
|
|
|
+ cmd, err = s.ApplicationCommandCreate(s.State.User.ID, guildID, v)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ logHandler.Errorf("Cannot create '%v' command: %v", v.Name, err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ r.registeredCommands[i] = cmd
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (r *SlashRouter) CleanCommands(s *discordgo.Session) error {
|
|
|
+
|
|
|
+ for _, guildID := range r.guildIDs {
|
|
|
+ for _, v := range r.registeredCommands {
|
|
|
+ err := s.ApplicationCommandDelete(s.State.User.ID, guildID, v.ID)
|
|
|
+ if err != nil {
|
|
|
+ logHandler.Errorf("Cannot delete '%v' command: %v", v.Name, err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Create a Router
|
|
|
+// Added commands the bot will support
|
|
|
+
|
|
|
+func (bot *Bot) AddSlashCommands(guildID string, commands *[]discordgo.ApplicationCommand) error {
|
|
|
+ err := bot.session.Open()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ defer bot.session.Close()
|
|
|
+
|
|
|
+ existingCommands, err := bot.session.ApplicationCommands(bot.session.State.User.ID, guildID)
|
|
|
+ if err != nil {
|
|
|
+ logHandler.WithError(err).Errorf("Failed to retrieve application commands from the server %s for user %s", guildID, bot.session.State.User.ID)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ var find = func(name string, commands []*discordgo.ApplicationCommand) bool {
|
|
|
+ for _, command := range commands {
|
|
|
+ if command.Name == name {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, newCommand := range *commands {
|
|
|
+ if exists := find(newCommand.Name, existingCommands); !exists {
|
|
|
+ cmd, err := bot.session.ApplicationCommandCreate(bot.session.State.User.ID, guildID, &newCommand)
|
|
|
+ if err != nil {
|
|
|
+ logHandler.WithError(err).Errorf("Failed to create command %s", newCommand.Name)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ logHandler.Infof("New Command for %s added, new command id is %s", newCommand.Name, cmd.ID)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (bot *Bot) RemoveSlashCommands(guildID string) error {
|
|
|
+ err := bot.session.Open()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ defer bot.session.Close()
|
|
|
+
|
|
|
+ existingCommands, err := bot.session.ApplicationCommands(bot.session.State.User.ID, guildID)
|
|
|
+ if err != nil {
|
|
|
+ logHandler.WithError(err).Errorf("Failed to retrieve application commands from the server %s for user %s", guildID, bot.session.State.User.ID)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, existingCommand := range existingCommands {
|
|
|
+ if err := bot.session.ApplicationCommandDelete(bot.session.State.User.ID, guildID, existingCommand.ID); err != nil {
|
|
|
+ logHandler.WithError(err).Errorf("Failed to delete command %s", existingCommand.Name)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|