dcbot/internal/discord/bot.go

173 lines
3.9 KiB
Go
Raw Normal View History

package discord
import (
"context"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/bwmarrin/discordgo"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
)
type GroupService interface {
Create(ctx context.Context, params domain.CreateGroupParams) (domain.Group, error)
SetChannelGains(ctx context.Context, id, serverID, channel string) (domain.Group, error)
SetChannelLosses(ctx context.Context, id, serverID, channel string) (domain.Group, error)
SetInternals(ctx context.Context, id, serverID string, internals bool) (domain.Group, error)
SetBarbarians(ctx context.Context, id, serverID string, barbarians bool) (domain.Group, error)
List(ctx context.Context, params domain.ListGroupsParams) ([]domain.Group, error)
Delete(ctx context.Context, id, serverID string) error
}
type MonitorService interface {
Create(ctx context.Context, groupID, serverID, tribeTag string) (domain.Monitor, error)
List(ctx context.Context, groupID, serverID string) ([]domain.MonitorWithTribe, error)
Execute(ctx context.Context) ([]domain.EnnoblementNotification, error)
Delete(ctx context.Context, id, serverID string) error
}
type ChoiceService interface {
Versions(ctx context.Context) ([]domain.Choice, error)
}
type Bot struct {
s *discordgo.Session
c *cron.Cron
groupSvc GroupService
monitorSvc MonitorService
choiceSvc ChoiceService
logger *zap.Logger
}
func NewBot(
token string,
groupSvc GroupService,
monitorSvc MonitorService,
client ChoiceService,
logger *zap.Logger,
) (*Bot, error) {
s, err := discordgo.New("Bot " + token)
if err != nil {
return nil, fmt.Errorf("discordgo.New: %w", err)
}
s.Identify.Intents = discordgo.IntentsNone
b := &Bot{
s: s,
c: cron.New(
cron.WithLocation(time.UTC),
cron.WithChain(
cron.SkipIfStillRunning(cron.DiscardLogger),
),
),
groupSvc: groupSvc,
monitorSvc: monitorSvc,
choiceSvc: client,
logger: logger,
}
b.s.AddHandler(b.handleSessionReady)
b.s.AddHandler(b.logCommands)
return b, nil
}
func (b *Bot) Run() error {
if err := b.initCron(); err != nil {
return fmt.Errorf("initCron: %w", err)
}
if err := b.s.Open(); err != nil {
return fmt.Errorf("s.Open: %w", err)
}
if err := b.registerCommands(); err != nil {
_ = b.s.Close()
return fmt.Errorf("couldn't register commands: %w", err)
}
b.c.Run()
return nil
}
type command interface {
name() string
register(s *discordgo.Session) error
}
func (b *Bot) registerCommands() error {
commands := []command{
&groupCommand{groupSvc: b.groupSvc, choiceSvc: b.choiceSvc},
&monitorCommand{svc: b.monitorSvc},
}
for _, c := range commands {
if err := b.registerCommand(c); err != nil {
return err
}
}
return nil
}
func (b *Bot) registerCommand(cmd command) error {
if err := cmd.register(b.s); err != nil {
return fmt.Errorf("couldn't register command '%s': %w", cmd.name(), err)
}
return nil
}
func (b *Bot) initCron() error {
_, err := b.c.AddJob("@every 1m", &executeMonitorsJob{
svc: b.monitorSvc,
s: b.s,
logger: b.logger,
})
if err != nil {
return err
}
return nil
}
func (b *Bot) handleSessionReady(s *discordgo.Session, _ *discordgo.Ready) {
_ = s.UpdateGameStatus(0, "Tribal Wars")
}
func (b *Bot) logCommands(_ *discordgo.Session, i *discordgo.InteractionCreate) {
if i.Type != discordgo.InteractionApplicationCommand {
return
}
cmdData := i.ApplicationCommandData()
cmd := cmdData.Name
options := cmdData.Options
2022-10-30 04:20:43 +00:00
for len(options) > 0 && options[0].Type == discordgo.ApplicationCommandOptionSubCommand {
cmd += " " + options[0].Name
options = options[0].Options
}
userID := ""
if i.User != nil {
userID = i.User.ID
} else if i.Member != nil {
userID = i.Member.User.ID
}
b.logger.Info(
"executing command",
zap.String("command", cmd),
zap.String("user", userID),
zap.String("serverID", i.GuildID),
zap.String("channelID", i.ChannelID),
)
}
func (b *Bot) Close() error {
<-b.c.Stop().Done()
return b.s.Close()
}