dcbot/internal/discord/bot.go
Dawid Wysokiński 09ffc7644a
All checks were successful
continuous-integration/drone/push Build is passing
feat: i18n (#108)
Reviewed-on: #108
2023-06-25 06:31:12 +00:00

181 lines
4.5 KiB
Go

package discord
import (
"context"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/discord/internal/discordi18n"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/bwmarrin/discordgo"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
"golang.org/x/text/language"
)
type GroupService interface {
Create(ctx context.Context, params domain.CreateGroupParams) (domain.GroupWithMonitors, error)
AddTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
RemoveTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
SetChannelGains(ctx context.Context, id, serverID, channel string) (domain.GroupWithMonitors, error)
SetChannelLosses(ctx context.Context, id, serverID, channel string) (domain.GroupWithMonitors, error)
SetInternals(ctx context.Context, id, serverID string, internals bool) (domain.GroupWithMonitors, error)
SetBarbarians(ctx context.Context, id, serverID string, barbarians bool) (domain.GroupWithMonitors, error)
Execute(ctx context.Context) ([]domain.EnnoblementNotification, error)
CleanUp(ctx context.Context) error
ListServer(ctx context.Context, serverID string) ([]domain.GroupWithMonitors, error)
GetWithTribes(ctx context.Context, id, serverID string) (domain.GroupWithMonitorsAndTribes, error)
Delete(ctx context.Context, id, serverID string) error
}
type ChoiceService interface {
Versions(ctx context.Context) ([]domain.Choice, error)
}
type Bot struct {
session *discordgo.Session
cron *cron.Cron
groupSvc GroupService
choiceSvc ChoiceService
logger *zap.Logger
localizer *discordi18n.Localizer
}
func NewBot(
token string,
groupSvc GroupService,
client ChoiceService,
logger *zap.Logger,
) (*Bot, error) {
localizer, err := discordi18n.NewLocalizer(language.English)
if err != nil {
return nil, err
}
s, err := discordgo.New("Bot " + token)
if err != nil {
return nil, fmt.Errorf("discordgo.New: %w", err)
}
s.Identify.Intents = discordgo.IntentsNone
b := &Bot{
session: s,
cron: cron.New(
cron.WithLocation(time.UTC),
cron.WithChain(
cron.SkipIfStillRunning(cron.DiscardLogger),
),
),
groupSvc: groupSvc,
choiceSvc: client,
logger: logger,
localizer: localizer,
}
b.session.AddHandler(b.handleSessionReady)
b.session.AddHandler(b.logCommands)
return b, nil
}
func (b *Bot) Run() error {
if err := b.registerCronJobs(); err != nil {
return fmt.Errorf("initCron: %w", err)
}
if err := b.session.Open(); err != nil {
return fmt.Errorf("s.Open: %w", err)
}
if err := b.registerCommands(); err != nil {
_ = b.session.Close()
return fmt.Errorf("couldn't register commands: %w", err)
}
b.cron.Run()
return nil
}
type command interface {
name() string
register(s *discordgo.Session) error
}
func (b *Bot) registerCommands() error {
commands := []command{
&groupCommand{localizer: b.localizer, logger: b.logger, groupSvc: b.groupSvc, choiceSvc: b.choiceSvc},
}
for _, cmd := range commands {
if err := cmd.register(b.session); err != nil {
return fmt.Errorf("couldn't register command '%s': %w", cmd.name(), err)
}
}
return nil
}
func (b *Bot) registerCronJobs() error {
jobs := []struct {
spec string
job cron.Job
}{
{
spec: "@every 1m",
job: &executeMonitorsJob{svc: b.groupSvc, s: b.session, logger: b.logger},
},
{
spec: "0 */8 * * *",
job: &cleanUpGroupsJob{svc: b.groupSvc, logger: b.logger},
},
}
for _, j := range jobs {
if _, err := b.cron.AddJob(j.spec, j.job); 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
for len(options) > 0 &&
(options[0].Type == discordgo.ApplicationCommandOptionSubCommand || options[0].Type == discordgo.ApplicationCommandOptionSubCommandGroup) {
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.cron.Stop().Done()
return b.session.Close()
}