diff --git a/internal/activity/activity.go b/internal/activity/activity.go index 488de9d..9430085 100644 --- a/internal/activity/activity.go +++ b/internal/activity/activity.go @@ -2,13 +2,12 @@ package activity import ( "errors" - "fmt" "sync" "time" "gitlab.com/whom/bingobot/internal/config" - "gitlab.com/whom/bingobot/internal/logging" "gitlab.com/whom/bingobot/internal/state" + "gitlab.com/whom/bingobot/internal/logging" ) /* Activity module @@ -19,7 +18,7 @@ const ( ActivityModuleStartFail = "failed to start activity module" ) -var currentUserActivity = make(map[string][]state.UserActiveEvent) +var currentUserActivity map[string][]state.UserActiveEvent var userActivityLock sync.RWMutex func Start() error { @@ -39,10 +38,6 @@ func Start() error { user := emap[state.UserEventUserKey] etime := ev.Time() delta := time.Since(etime).Hours() / float64(24) - logging.Debug(fmt.Sprintf( - "processing UserActive event for %s", user, - )) - if delta <= float64(config.Get().UserEventLifespanDays) { new := []state.UserActiveEvent{ev.(state.UserActiveEvent)} @@ -51,7 +46,6 @@ func Start() error { if found { new = append(new, current...) } - currentUserActivity[user] = new userActivityLock.Unlock() } else { logging.Warn("recieved expired useractive event") diff --git a/internal/confession/confession.go b/internal/confession/confession.go deleted file mode 100644 index d76aa38..0000000 --- a/internal/confession/confession.go +++ /dev/null @@ -1,60 +0,0 @@ -package confession - -import ( - "errors" - "fmt" - "sync" - - "github.com/bwmarrin/discordgo" - "gitlab.com/whom/bingobot/internal/logging" - "gitlab.com/whom/bingobot/internal/state" -) - -/* Activity module - * This module posts anonymous confessions according to a linked channel map - */ - -const ( - ActivityModuleStartFail = "failed to start activity module" -) - -var ( - // guild ID to channel ID - linkLock sync.RWMutex - confessionChannelLinks = make(map[string]state.ConfessionsChannelLinkEvent) -) - -func Start() error { - ch, err := state.ConfessionsChannelLink.Subscribe() - if err != nil { - return errors.Join( - errors.New(ActivityModuleStartFail), - err, - ) - } - - // process incoming events loop - go func() { - for { - ev := <- ch - logging.Debug("recieved new confessional channel link") - e := ev.(state.ConfessionsChannelLinkEvent) - linkLock.Lock() - confessionChannelLinks[e.GuildID] = e - linkLock.Unlock() - } - }() - - return nil -} - -func MakeConfession(s *discordgo.Session, guildID string, content string) { - linkLock.RLock() - link, ok := confessionChannelLinks[guildID] - linkLock.RUnlock() - if !ok { - logging.Error(fmt.Sprintf("Failed to send confession in guild %s: no link exists in map", guildID)) - return - } - s.ChannelMessageSend(link.ChannelID, content) -} diff --git a/internal/discord/activity.go b/internal/discord/activity.go index 002f3b4..afa6c8d 100644 --- a/internal/discord/activity.go +++ b/internal/discord/activity.go @@ -42,7 +42,7 @@ func NewActivityTimer(uid string) *UserActivityTimer { Start() initializes the timer and calls run() */ func (t *UserActivityTimer) Start(ctx context.Context) { - logging.Debug("starting voiceActivityTimer", "uid", t.UID) + logging.Info("starting voiceActivityTimer", "uid", t.UID) t.sleepDuration = time.Millisecond * time.Duration(config.Get().VoiceActivityTimerSleepIntervalMillis) activityTimerDuration := time.Second * time.Duration(config.Get().VoiceActivityThresholdSeconds) @@ -89,7 +89,7 @@ func (t *UserActivityTimer) run() { } // the timer's context has been cancelled or deadline expired. - logging.Debug("voiceActivityTimer stopping", "uid", t.UID, "reason", context.Cause(t.ctx)) + logging.Info("voiceActivityTimer stopping", "uid", t.UID, "reason", context.Cause(t.ctx)) if context.Cause(t.ctx) == ErrTimerExpired { /* @@ -128,7 +128,7 @@ func stopActivityTimer(uid string) { timerMutex.Lock() defer timerMutex.Unlock() - if _, ok := activityTimers[uid]; ok { + if _, ok := activityTimers[uid]; !ok { activityTimers[uid].Cancel() delete(activityTimers, uid) } @@ -147,5 +147,5 @@ func emitUserActiveEvent(uid string) { return } - logging.Debug("published UserActiveEvent", "uid", uid) + logging.Info("published UserActiveEvent", "uid", uid) } diff --git a/internal/discord/commands.go b/internal/discord/commands.go deleted file mode 100644 index c774dc3..0000000 --- a/internal/discord/commands.go +++ /dev/null @@ -1,129 +0,0 @@ -package discord - -import ( - "fmt" - "time" - - "github.com/bwmarrin/discordgo" - - "gitlab.com/whom/bingobot/internal/confession" - "gitlab.com/whom/bingobot/internal/logging" - "gitlab.com/whom/bingobot/internal/state" -) - -var ( - // map of guildID to registeredCommands - registeredCommands = make(map[string][]*discordgo.ApplicationCommand) - - // all commands - commandList = []*discordgo.ApplicationCommand{ - // TODO: Limit usage somehow? - // maybe delete this and use the vote module instead - &discordgo.ApplicationCommand{ - Name: "confessional", - Description: "mark a channel as a designated confessional for a guild", - }, - - &discordgo.ApplicationCommand{ - Name: "confess", - Description: "anonymously post a confession in configured channel", - Options: []*discordgo.ApplicationCommandOption{ - &discordgo.ApplicationCommandOption{ - Type: discordgo.ApplicationCommandOptionString, - Name: "confession", - Description: "A confession to be posted anonymously", - Required: true, - }, - }, - }, - } - - commandHandlers = map[string]func( - s *discordgo.Session, - i *discordgo.InteractionCreate, - ) { - "confessional": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - if err := state.PublishEvent(state.ConfessionsChannelLinkEvent{ - GuildID: i.GuildID, - ChannelID: i.ChannelID, - Created: time.Now(), - }); err != nil { - logging.Error(fmt.Sprintf( - "failed to publish confession channel link: %s", - err.Error(), - )) - } else { - logging.Info("published confession channel link") - } - }, - - // handle a confession - "confess": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - for _, v := range i.ApplicationCommandData().Options { - if v.Name == "confession" { - confession.MakeConfession(s, i.GuildID, v.StringValue()) - } - } - }, - } -) - -func handleCommand(s *discordgo.Session, e *discordgo.InteractionCreate) { - name := e.ApplicationCommandData().Name - // TODO: audit log - if h, ok := commandHandlers[name]; ok { - h(s, e) - } else { - logging.Debug("no handler for command: %s", name) - } - - s.InteractionRespond(e.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Flags: discordgo.MessageFlagsEphemeral, - Content: "Your cauldron bubbles...", - }, - }) -} - -func registerCommands(s *discordgo.Session) { - for _, guild := range s.State.Guilds { - cmds, err := s.ApplicationCommandBulkOverwrite( - s.State.Application.ID, - guild.ID, - commandList, - ) - - if err != nil { - logging.Error(fmt.Sprintf( - "Failed to register commands for guild %s: %s", - guild.ID, err.Error(), - )) - } else { - logging.Info(fmt.Sprintf("Registered commands for guild %s", guild.ID)) - registeredCommands[guild.ID] = cmds - } - } -} - -func deregisterCommands(s *discordgo.Session) { - for guild, commands := range registeredCommands { - for _, cmd := range commands { - if err := s.ApplicationCommandDelete( - s.State.Application.ID, - guild, - cmd.ID, - ); err != nil { - logging.Error(fmt.Sprintf( - "Failed to delete %s command (id: %s) from guild %s", - cmd.Name, cmd.ID, guild, - )) - } else { - logging.Info(fmt.Sprintf( - "Deregistered command %s (id: %s) from guild %s", - cmd.Name, cmd.ID, guild, - )) - } - } - } -} diff --git a/internal/discord/discord.go b/internal/discord/discord.go index 6c81929..896ba1a 100644 --- a/internal/discord/discord.go +++ b/internal/discord/discord.go @@ -36,13 +36,10 @@ func Connect(token string) error { return fmt.Errorf("failed to open discord session: %s", err) } - registerCommands(session.s) - return nil } func Close() { - deregisterCommands(session.s) err := session.s.Close() if err != nil { diff --git a/internal/discord/handlers.go b/internal/discord/handlers.go index 03b5434..c01d441 100644 --- a/internal/discord/handlers.go +++ b/internal/discord/handlers.go @@ -9,7 +9,6 @@ func addHandlers() { session.s.AddHandler(handleConnect) session.s.AddHandler(handleDisconnect) session.s.AddHandler(handleVoiceStateUpdate) - session.s.AddHandler(handleCommand) // handles InteractionCreate } func handleConnect(s *discordgo.Session, e *discordgo.Connect) { diff --git a/internal/state/events.go b/internal/state/events.go index 44ad0f4..c14ee77 100644 --- a/internal/state/events.go +++ b/internal/state/events.go @@ -226,47 +226,3 @@ func (te TestEvent) Validate() error { func (te TestEvent) Disposable() bool { return te.Dispose } - -const ( - ConfessionsLinkEventGuildKey = "guild_id" - ConfessionsLinkEventChannelKey = "channel_id" - ConfessionsLinkEventCreatedKey = "created" - ConfessionsLinkEventObsoleteKey = "obsolete" - - BadConfessionsLinkEventError = "link event doesnt have required fields" -) - -type ConfessionsChannelLinkEvent struct { - GuildID string - ChannelID string - Created time.Time -} - -func (e ConfessionsChannelLinkEvent) Type() EventType { - return ConfessionsChannelLink -} - -func (e ConfessionsChannelLinkEvent) Time() time.Time { - return e.Time() -} - -func (e ConfessionsChannelLinkEvent) Data() map[string]string { - return map[string]string{ - ConfessionsLinkEventGuildKey: e.GuildID, - ConfessionsLinkEventChannelKey: e.ChannelID, - ConfessionsLinkEventCreatedKey: e.Created.Format(time.RFC3339), - } -} - -func (e ConfessionsChannelLinkEvent) Validate() error { - if len(e.ChannelID) <= 1 || len(e.GuildID) <= 1 { - return errors.New(BadConfessionsLinkEventError) - } - - return nil -} - -func (e ConfessionsChannelLinkEvent) Disposable() bool { - // TODO - return false -} diff --git a/internal/state/state.go b/internal/state/state.go index cc439f5..f4a83fd 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -159,7 +159,6 @@ func Start() error { // return no error. we are handling SIGINT func Teardown() { - logging.Warn("Tearing down state engine") // riskily ignore all locking.... // we are handling a termination signal after all i, e := eventStream.Close() @@ -168,7 +167,6 @@ func Teardown() { logging.Warn("will attempt to truncate event store anyways") } - logging.Warn("teardown: events flushed") e = os.Truncate(eventStorageFileName, i) if e != nil { logging.Error("FAILED TO TRUNCATE EVENT STORE!!") @@ -185,7 +183,6 @@ const ( Restoration UserActive Test - ConfessionsChannelLink // ... // leave this last @@ -205,8 +202,6 @@ func EventTypeFromString(doc string) EventType { return UserActive case "Test": return Test - case "ConfessionsChannelLink": - return ConfessionsChannelLink default: // error case return NumEventTypes @@ -220,7 +215,6 @@ func (et EventType) String() string { "Restoration", "UserActive", "Test", - "ConfessionsChannelLink", } if et < 0 || et >= NumEventTypes { @@ -272,28 +266,24 @@ func (et EventType) MakeEvent(data map[string]string) (Event, error) { switch et { case Vote: return VoteEvent(data), nil - case Challenge: e, err := MakeUserEvent(data) if err != nil { return nil, errors.Join(errors.New(BadChallengeEvent), err) } return ChallengeEvent{*e}, nil - case Restoration: e, err := MakeUserEvent(data) if err != nil { return nil, errors.Join(errors.New(BadRestorationEvent), err) } return RestorationEvent{*e}, nil - case UserActive: e, err := MakeUserEvent(data) if err != nil { return nil, errors.Join(errors.New(BadUserActiveEvent), err) } return UserActiveEvent{*e}, nil - case Test: disp := false if v, ok := data[TestEventDisposeKey]; ok && v == "t" { @@ -310,33 +300,6 @@ func (et EventType) MakeEvent(data map[string]string) (Event, error) { Dispose: disp, ID: id, }, nil - - case ConfessionsChannelLink: - gid, ok := data[ConfessionsLinkEventGuildKey] - if !ok { - return nil, errors.New(BadConfessionsLinkEventError) - } - - cid, ok := data[ConfessionsLinkEventChannelKey] - if !ok { - return nil, errors.New(BadConfessionsLinkEventError) - } - - ti, ok := data[ConfessionsLinkEventCreatedKey] - if !ok { - return nil, errors.New(BadConfessionsLinkEventError) - } - t, err := time.Parse(time.RFC3339, ti) - if err != nil { - return nil, errors.Join(errors.New(BadConfessionsLinkEventError), err) - } - - return ConfessionsChannelLinkEvent{ - GuildID: gid, - ChannelID: cid, - Created: t, - }, nil - default: return nil, errors.New(BadEventTypeError) } diff --git a/internal/web/web.go b/internal/web/web.go index fbe65eb..38051e6 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -14,7 +14,6 @@ func Start() error { http.HandleFunc("/", HandleHttpRequest) go logging.Error(http.ListenAndServe(frag, nil).Error()) - logging.Debug("Web handlers started") return nil } diff --git a/main.go b/main.go index 96e2e36..0528e1c 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,8 @@ package main import ( "flag" "log" - "os" - "os/signal" "gitlab.com/whom/bingobot/internal/activity" - "gitlab.com/whom/bingobot/internal/confession" "gitlab.com/whom/bingobot/internal/config" "gitlab.com/whom/bingobot/internal/discord" "gitlab.com/whom/bingobot/internal/logging" @@ -31,7 +28,6 @@ func main() { logging.Init() flag.Parse() - logging.Info("startup: initializing state engine") if err := state.Init( config.Get().InMemoryEventCacheSize, config.Get().PersistentCacheStore, @@ -39,43 +35,35 @@ func main() { log.Fatalf("couldn't initialize state engine: %s", err.Error()) } - logging.Info("startup: starting activity module") if err := activity.Start(); err != nil { + // TODO: handle gracefully and continue? log.Fatalf("failed to start activity module: %s", err.Error()) } - logging.Info("startup: starting confession module") - if err := confession.Start(); err != nil { - log.Fatalf("failed to start confession module: %s", err.Error()) + if err := web.Start(); err != nil { + log.Fatalf("failed to start local web server: %s", err.Error()) } - logging.Info("startup: starting web module") - go func() { - if err := web.Start(); err != nil { - log.Fatalf("failed to start local web server: %s", err.Error()) - } - }() - // start this LAST - logging.Info("startup: starting state engine") if err := state.Start(); err != nil { log.Fatalf("failed to start state machine: %s", err.Error()) } + defer state.Teardown() - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - go func(){ - for _ = range c { - state.Teardown() - discord.Close() - os.Exit(1) - } - }() - - logging.Info("startup: connecting to discord") - if err := discord.Connect(*token); err != nil { + if err := startBot(); err != nil { log.Fatal(err) } - - for {} +} + +func startBot() error { + err := discord.Connect(*token) + + if err != nil { + return err + } + + logging.Info("shutting down gracefully", "type", "shutdown") + discord.Close() + + return nil }