Thisnthat il y a 1 an
Parent
commit
974632bf55
8 fichiers modifiés avec 260 ajouts et 155 suppressions
  1. 27 17
      bot.go
  2. 0 97
      bot/event/mux/slashRouter.go
  3. 3 3
      bot/event/mux/help.go
  4. 3 2
      bot/event/mux/message.go
  5. 11 11
      bot/event/mux/router.go
  6. 0 24
      members.go
  7. 1 1
      session.go
  8. 215 0
      slashRouter.go

+ 27 - 17
bot.go

@@ -1,13 +1,12 @@
-package discord
+package discordbot
 
 import (
 	"fmt"
 	"os"
-	"os/signal"
 	"sync"
 	"syscall"
+	"time"
 
-	"git.mgmcomp.net/thisnthat/discord-bot/bot/event/mux"
 	"github.com/bwmarrin/discordgo"
 	"github.com/sirupsen/logrus"
 )
@@ -17,9 +16,11 @@ type Bot struct {
 	session     *discordgo.Session
 	online      bool
 	running     chan os.Signal
-	slashRouter *mux.SlashRouter
+	slashRouter SlashRouter
 }
 
+var logHandler *logrus.Logger
+
 func (bot *Bot) AddHandler(handler interface{}) error {
 	// If the bot is online, we can not add a handler
 	if bot.online {
@@ -31,17 +32,15 @@ func (bot *Bot) AddHandler(handler interface{}) error {
 	return nil
 }
 
-func (bot *Bot) SetEventRouter(router *mux.Router) {
+func (bot *Bot) SetLegacyRouter(router *LegacyRouter) {
 	bot.AddHandler(router.OnMessageCreate)
 }
 
-func (bot *Bot) SetSlashEventRouter(slashRouter *mux.SlashRouter) {
-	bot.slashRouter = slashRouter
+func (bot *Bot) SetSlashEventRouter(slashRouter *SlashRouter) {
+	bot.slashRouter = *slashRouter
 }
 
 func (bot *Bot) Start(wg *sync.WaitGroup) error {
-	defer wg.Done()
-
 	err := bot.session.Open()
 	if err != nil {
 		return err
@@ -52,16 +51,25 @@ func (bot *Bot) Start(wg *sync.WaitGroup) error {
 		return err
 	}
 
-	go logrus.Info("The bot has started")
+	logHandler.Info("The bot has started")
 	bot.running = make(chan os.Signal, 1)
 	bot.online = true
-	signal.Notify(bot.running, syscall.SIGINT, syscall.SIGTERM)
-	<-bot.running
 
-	bot.slashRouter.CleanCommands(bot.session)
-	bot.session.Close()
-	go logrus.Info("The bot has gone offline")
-	bot.online = false
+	go func() {
+		for {
+			select {
+			case <-bot.running:
+				logHandler.Info("Shutting down the bot")
+				bot.slashRouter.CleanCommands(bot.session)
+				bot.session.Close()
+				logHandler.Info("The bot has gone offline")
+				bot.online = false
+				wg.Done()
+			default:
+				time.Sleep(time.Second * 1)
+			}
+		}
+	}()
 
 	return nil
 }
@@ -72,10 +80,12 @@ func (bot *Bot) Stop() {
 	}
 }
 
-func NewBot(token string) (*Bot, error) {
+func NewBot(token string, log *logrus.Logger) (*Bot, error) {
 	bot := &Bot{}
 	bot.token = token
 
+	logHandler = log
+
 	var err error
 	bot.session, err = discordgo.New("Bot " + bot.token)
 

+ 0 - 97
bot/event/mux/slashRouter.go

@@ -1,97 +0,0 @@
-package mux
-
-import (
-	"github.com/bwmarrin/discordgo"
-	"github.com/sirupsen/logrus"
-)
-
-type SlashRouter struct {
-	registeredCommands []*discordgo.ApplicationCommand
-	commands           []*discordgo.ApplicationCommand
-	commandHandlers    map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate)
-	guildIDs           []string
-}
-
-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)
-}
-
-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 {
-			logrus.Info(v)
-			cmd, err := s.ApplicationCommandCreate(s.State.User.ID, guildID, v)
-			if err != nil {
-				logrus.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 {
-				logrus.Errorf("Cannot delete '%v' command: %v", v.Name, err)
-				return err
-			}
-		}
-	}
-
-	return nil
-}

+ 3 - 3
bot/event/mux/help.go

@@ -1,4 +1,4 @@
-package mux
+package discordbot
 
 import (
 	"fmt"
@@ -8,7 +8,7 @@ import (
 	"github.com/bwmarrin/discordgo"
 )
 
-func (r *Router) helpCommandHandler(s *discordgo.Session, m *discordgo.Message, ctx *Context) {
+func (r *LegacyRouter) helpCommandHandler(s *discordgo.Session, m *discordgo.Message, ctx *Context) {
 	displayPrefix := ""
 
 	switch ctx.Method {
@@ -30,7 +30,7 @@ func (r *Router) helpCommandHandler(s *discordgo.Session, m *discordgo.Message,
 	for _, v := range r.categories {
 		keys := make([]string, 0, len(r.routes))
 		for _, route := range r.routes {
-			// If the route is hidde, do not add it to the help list
+			// If the route is hidden, do not add it to the help list
 			if route.Hidden {
 				continue
 			}

+ 3 - 2
bot/event/mux/message.go

@@ -1,4 +1,4 @@
-package mux
+package discordbot
 
 import (
 	"fmt"
@@ -8,11 +8,12 @@ import (
 	"github.com/bwmarrin/discordgo"
 )
 
+// This provides support for the legacy command handler
 // OnMessageCreate is a DiscordGo Event Handler function.  This must be
 // registered using the DiscordGo.Session.AddHandler function.  This function
 // will receive all Discord messages and parse them for matches to registered
 // routes.
-func (r *Router) OnMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
+func (r *LegacyRouter) OnMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 	var err error
 
 	// Ignore all messages created by the Bot account itself

+ 11 - 11
bot/event/mux/router.go

@@ -1,4 +1,4 @@
-package mux
+package discordbot
 
 import (
 	"errors"
@@ -70,7 +70,7 @@ const (
 )
 
 // Router is the main struct for all mux methods.
-type Router struct {
+type LegacyRouter struct {
 	routes            []*route
 	helpRoute         *route
 	prefix            string
@@ -80,8 +80,8 @@ type Router struct {
 }
 
 // New returns a new Discord message route mux
-func New() *Router {
-	r := &Router{}
+func NewLegacyRouter() *LegacyRouter {
+	r := new(LegacyRouter)
 
 	r.isAdminRoute = isAdminRouteDefault
 
@@ -97,15 +97,15 @@ func New() *Router {
 }
 
 // SetCommandPrefix will set the prefix used for message commands
-func (r *Router) SetCommandPrefix(cmdPrefix string) {
+func (r *LegacyRouter) SetCommandPrefix(cmdPrefix string) {
 	r.prefix = cmdPrefix + " "
 }
 
-func (r *Router) SetAdminRouteCallback(callback IsAdminRouteHandler) {
+func (r *LegacyRouter) SetAdminRouteCallback(callback IsAdminRouteHandler) {
 	r.isAdminRoute = callback
 }
 
-func (r *Router) ForcePrefixMethod(force bool) {
+func (r *LegacyRouter) ForcePrefixMethod(force bool) {
 	r.forcePrefixMethod = force
 }
 
@@ -119,8 +119,8 @@ var ErrRegisterInvalidRouteType = errors.New("invalid route type")
 var ErrRegisterCallbackRequired = errors.New("a valid callback must be provided")
 
 // Register allows you to register a route
-// func (r *Router) Register(routeType RouteType, name, desc string, callback Handler) (*Route, error) {
-func (r *Router) Register(name string, options RouteOptions) error {
+// func (r *LegacyRouter) Register(routeType RouteType, name, desc string, callback Handler) (*Route, error) {
+func (r *LegacyRouter) Register(name string, options RouteOptions) error {
 	if name == "" {
 		return ErrRegisterRouteNameRequired
 	}
@@ -160,7 +160,7 @@ func (r *Router) Register(name string, options RouteOptions) error {
 var ErrAddCategoryNameRequired = errors.New("all categories must have a name")
 
 // AddCategory allows you to a a router category
-func (r *Router) AddCategory(name, description string) error {
+func (r *LegacyRouter) AddCategory(name, description string) error {
 	if name == "" {
 		return ErrAddCategoryNameRequired
 	}
@@ -183,7 +183,7 @@ func isValidRouteType(routeType RouteType) bool {
 	return false
 }
 
-func (r *Router) findRoute(searchString string, routeType RouteType) (*route, []string, []string) {
+func (r *LegacyRouter) findRoute(searchString string, routeType RouteType) (*route, []string, []string) {
 	fields := strings.Fields(searchString)
 
 	if len(fields) == 0 {

+ 0 - 24
members.go

@@ -1,24 +0,0 @@
-package discord
-
-import (
-	"github.com/bwmarrin/discordgo"
-)
-
-// GetMember gets a discord member.
-// session  : An active discordgo session
-// guid		: The ID of the Guild
-// uid	    : The ID of a User.
-func GetMember(session *discordgo.Session, guid, uid string) (*discordgo.Member, error) {
-	return session.GuildMember(guid, uid)
-}
-
-// UpdateMember edits the roles of a member.
-// session  : An active discordgo session
-// guid		: The ID of the Guild
-// template : GuildMemberParams for Role and Nick
-// roles    : A list of role ID's to set on the member.
-func UpdateMember(session *discordgo.Session, guid, uid string, template *discordgo.GuildMemberParams) error {
-	_, err := session.GuildMemberEdit(guid, uid, template)
-
-	return err
-}

+ 1 - 1
session.go

@@ -1,4 +1,4 @@
-package discord
+package discordbot
 
 import "github.com/bwmarrin/discordgo"
 

+ 215 - 0
slashRouter.go

@@ -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
+}