diff --git a/common/common.go b/common/common.go index 6f33115..a56e1ff 100644 --- a/common/common.go +++ b/common/common.go @@ -11,6 +11,7 @@ import ( "net/http" "net/url" "strings" + "time" lm "github.com/hrfee/jfa-go/logmessages" ) @@ -155,3 +156,11 @@ func decodeResp(resp *http.Response) (string, error) { } return buf.String(), nil } + +// MustAuthenticateOptions is used to control the behaviour of the MustAuthenticate-like methods. +type MustAuthenticateOptions struct { + RetryCount int // Number of Retries before failure. + RetryGap time.Duration // Duration to wait between tries. + LogFailures bool // Whether or not to print failures to the log. + Counter int // The current retry count. +} diff --git a/discord.go b/discord.go index af0d52e..5bf60d3 100644 --- a/discord.go +++ b/discord.go @@ -8,6 +8,7 @@ import ( "time" dg "github.com/bwmarrin/discordgo" + "github.com/hrfee/jfa-go/common" lm "github.com/hrfee/jfa-go/logmessages" "github.com/timshannon/badgerhold/v4" ) @@ -28,6 +29,7 @@ type DiscordDaemon struct { commandHandlers map[string]func(s *dg.Session, i *dg.InteractionCreate, lang string) commandIDs []string commandDescriptions []*dg.ApplicationCommand + retryOpts *common.MustAuthenticateOptions } func newDiscordDaemon(app *appContext) (*DiscordDaemon, error) { @@ -59,6 +61,16 @@ func newDiscordDaemon(app *appContext) (*DiscordDaemon, error) { dd.users[user.ID] = user } + dd.retryOpts = &common.MustAuthenticateOptions{ + RetryCount: app.config.Section("advanced").Key("auth_retry_count").MustInt(6), + RetryGap: time.Duration(app.config.Section("advanced").Key("auth_retry_gap").MustInt(10)) * time.Second, + LogFailures: true, + } + + dd.bot.AddHandler(dd.commandHandler) + + dd.bot.Identify.Intents = dg.IntentsGuildMessages | dg.IntentsDirectMessages | dg.IntentsGuildMembers | dg.IntentsGuildInvites + return dd, nil } @@ -99,13 +111,27 @@ func (d *DiscordDaemon) MustGetUser(channelID, userID, discrim, username string) return d.NewUnknownUser(channelID, userID, discrim, username) } -func (d *DiscordDaemon) run() { - d.bot.AddHandler(d.commandHandler) +func (d *DiscordDaemon) Run() { + ro := common.MustAuthenticateOptions{} + ro = *d.retryOpts + ro.Counter = 0 + d.run(&ro) +} - d.bot.Identify.Intents = dg.IntentsGuildMessages | dg.IntentsDirectMessages | dg.IntentsGuildMembers | dg.IntentsGuildInvites +func (d *DiscordDaemon) run(retry *common.MustAuthenticateOptions) { if err := d.bot.Open(); err != nil { - d.app.err.Printf(lm.FailedStartDaemon, lm.Discord, err) - return + if retry == nil || retry.LogFailures { + d.app.err.Printf(lm.FailedStartDaemon, lm.Discord, err) + } + if retry != nil { + retry.Counter += 1 + if retry.Counter >= retry.RetryCount { + return + } + time.Sleep(retry.RetryGap) + d.run(retry) + return + } } // Wait for everything to populate, it's slow sometimes. for d.bot.State == nil { @@ -135,15 +161,18 @@ func (d *DiscordDaemon) run() { d.InviteChannel.Name = invChannel } } - err = d.bot.UpdateGameStatus(0, "/"+d.app.config.Section("discord").Key("start_command").MustString("start")) + d.bot.UpdateGameStatus(0, "/"+d.app.config.Section("discord").Key("start_command").MustString("start")) defer d.deregisterCommands() defer d.bot.Close() - go d.registerCommands() + ro := common.MustAuthenticateOptions{} + ro = *(d.retryOpts) + ro.Counter = 0 + + go d.registerCommands(&ro) <-d.ShutdownChannel d.ShutdownChannel <- "Down" - return } // ListRoles returns a list of available (excluding bot and @everyone) roles in a guild as a list of containing an array of the guild ID and its name. @@ -333,7 +362,7 @@ func (d *DiscordDaemon) Shutdown() { close(d.ShutdownChannel) } -func (d *DiscordDaemon) registerCommands() { +func (d *DiscordDaemon) registerCommands(retry *common.MustAuthenticateOptions) { d.commandDescriptions = []*dg.ApplicationCommand{ { Name: d.app.config.Section("discord").Key("start_command").MustString("start"), @@ -430,7 +459,27 @@ func (d *DiscordDaemon) registerCommands() { // if err != nil { // d.app.err.Printf("Discord: Cannot create commands: %v", err) // } - for i, cmd := range d.commandDescriptions { + + cCommands, err := d.bot.ApplicationCommandBulkOverwrite(d.bot.State.User.ID, d.guildID, d.commandDescriptions) + if err != nil { + if retry == nil || retry.LogFailures { + d.app.err.Printf(lm.FailedRegisterDiscordCommand, "*", err) + } + if retry != nil { + retry.Counter += 1 + if retry.Counter >= retry.RetryCount { + return + } + time.Sleep(retry.RetryGap) + d.registerCommands(retry) + } + } else { + for i := range len(d.commandDescriptions) { + d.commandIDs[i] = cCommands[i].ID + } + d.app.debug.Printf(lm.RegisterDiscordCommand, "*") + } + /* for i, cmd := range d.commandDescriptions { command, err := d.bot.ApplicationCommandCreate(d.bot.State.User.ID, d.guildID, cmd) if err != nil { d.app.err.Printf(lm.FailedRegisterDiscordCommand, cmd.Name, err) @@ -438,7 +487,7 @@ func (d *DiscordDaemon) registerCommands() { d.app.debug.Printf(lm.RegisterDiscordCommand, cmd.Name) d.commandIDs[i] = command.ID } - } + } */ } func (d *DiscordDaemon) deregisterCommands() { diff --git a/main.go b/main.go index 04ac95a..2993b3e 100644 --- a/main.go +++ b/main.go @@ -542,7 +542,7 @@ func start(asDaemon, firstCall bool) { discordEnabled = false } else { app.debug.Println(lm.InitDiscord) - go app.discord.run() + go app.discord.Run() defer app.discord.Shutdown() app.contactMethods = append(app.contactMethods, app.discord) }