From fd5e0fb2def1a5c1065ca5df228190212a021f2b Mon Sep 17 00:00:00 2001 From: "Gustavo \"Guz\" L. de Mello" Date: Mon, 19 Aug 2024 20:24:14 -0300 Subject: [PATCH] feat(commands): handling of sub commands --- internals/discord/bot/commands.go | 70 +++++++++++-- internals/discord/bot/commands/channels.go | 108 +++++++++++++++++++-- internals/discord/bot/commands/commands.go | 1 + 3 files changed, 165 insertions(+), 14 deletions(-) diff --git a/internals/discord/bot/commands.go b/internals/discord/bot/commands.go index 86f9398..5954da3 100644 --- a/internals/discord/bot/commands.go +++ b/internals/discord/bot/commands.go @@ -2,8 +2,11 @@ package bot import ( "dislate/internals/discord/bot/commands" + "encoding/json" + "errors" "fmt" "log/slog" + "slices" dgo "github.com/bwmarrin/discordgo" ) @@ -13,16 +16,72 @@ func (b *Bot) registerCommands() error { commands.NewManageChannel(b.db), } - rcs := make([]*dgo.ApplicationCommand, len(cs)) handlers := make(map[string]func(*dgo.Session, *dgo.InteractionCreate), len(cs)) - for i, v := range cs { - cmd, err := b.session.ApplicationCommandCreate(b.session.State.User.ID, "", v.Info()) - if err != nil { - return err + for _, v := range cs { + var cmd *dgo.ApplicationCommand + var err error + subCmds := make(map[string]commands.Command) + + sb := v.Subcommands() + + if len(sb) == 0 { + cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", v.Info()) + if err != nil { + return err + } + } else { + subCmdsOpts := make([]*dgo.ApplicationCommandOption, len(sb)) + for i, sb := range sb { + subCmds[sb.Info().Name] = sb + subCmdsOpts[i] = &dgo.ApplicationCommandOption{ + Type: dgo.ApplicationCommandOptionSubCommand, + Name: sb.Info().Name, + Description: sb.Info().Description, + Options: sb.Info().Options, + } + } + info := v.Info() + info.Options = subCmdsOpts + + cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", info) + if err != nil { + return err + } } handlers[cmd.Name] = func(s *dgo.Session, ic *dgo.InteractionCreate) { + b.logger.Debug("Handling command", + slog.String("id", ic.Interaction.ID), + slog.String("name", ic.Interaction.ApplicationCommandData().Name), + ) + + opts := ic.Interaction.ApplicationCommandData().Options + isSub := slices.IndexFunc(opts, func(o *dgo.ApplicationCommandInteractionDataOption) bool { + return o.Type == dgo.ApplicationCommandOptionSubCommand + }) + if isSub != -1 { + sc := opts[isSub] + + err := subCmds[sc.Name].Handle(s, ic) + + if err != nil { + _ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{ + Type: dgo.InteractionResponseDeferredChannelMessageWithSource, + Data: &dgo.InteractionResponseData{ + Content: fmt.Sprintf("Error while trying to handle sub command: %s", err.Error()), + Flags: dgo.MessageFlagsEphemeral, + }, + }) + b.logger.Error("Failed to handle sub command", + slog.String("name", sc.Name), + slog.String("err", err.Error()), + ) + } + + return + } + err := v.Handle(s, ic) if err != nil { _ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{ @@ -39,7 +98,6 @@ func (b *Bot) registerCommands() error { ) } } - rcs[i] = cmd b.logger.Info("Registered command", slog.String("name", cmd.Name), diff --git a/internals/discord/bot/commands/channels.go b/internals/discord/bot/commands/channels.go index eebc49e..8a73dbc 100644 --- a/internals/discord/bot/commands/channels.go +++ b/internals/discord/bot/commands/channels.go @@ -1,23 +1,51 @@ package commands import ( + "errors" + "strings" + "dislate/internals/guilddb" + gdb "dislate/internals/guilddb" + "dislate/internals/translator/lang" dgo "github.com/bwmarrin/discordgo" ) type ManageChannel struct { - db guilddb.GuildDB + db gdb.GuildDB } -func NewManageChannel(db guilddb.GuildDB) ManageChannel { +func NewManageChannel(db gdb.GuildDB) ManageChannel { return ManageChannel{db} } func (c ManageChannel) Info() *dgo.ApplicationCommand { + var permissions int64 = dgo.PermissionManageChannels + return &dgo.ApplicationCommand{ - Name: "channel", - Description: "Manages a channel options", + Name: "channel", + Description: "Manages a channel options", + DefaultMemberPermissions: &permissions, + } +} +func (c ManageChannel) Subcommands() []Command { + return []Command{ChannelsInfo(c)} +} +func (c ManageChannel) Handle(s *dgo.Session, i *dgo.InteractionCreate) error { + return nil +} + +type ChannelsInfo struct { + db gdb.GuildDB +} + +func (c ChannelsInfo) Info() *dgo.ApplicationCommand { + var permissions int64 = dgo.PermissionManageChannels + + return &dgo.ApplicationCommand{ + Name: "info", + Description: "Get information about a channel", + DefaultMemberPermissions: &permissions, Options: []*dgo.ApplicationCommandOption{{ Type: dgo.ApplicationCommandOptionChannel, Name: "channel", @@ -28,12 +56,36 @@ func (c ManageChannel) Info() *dgo.ApplicationCommand { }}, } } -func (c ManageChannel) Handle(s *dgo.Session, i *dgo.InteractionCreate) error { - err := s.InteractionRespond(i.Interaction, &dgo.InteractionResponse{ +func (c ChannelsInfo) Handle(s *dgo.Session, ic *dgo.InteractionCreate) error { + opts := getOptions(ic) + + var err error + + var dch *dgo.Channel + if c, ok := opts["channel"]; ok { + dch = c.ChannelValue(s) + } else { + dch, err = s.Channel(ic.ChannelID) + if err != nil { + return err + } + } + + ch, err := getChannel(c.db, dch.GuildID, dch.ID) + if err != nil { + return err + } + + info, err := getChannelInfo(c.db, ch) + if err != nil { + return err + } + + err = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{ Type: dgo.InteractionResponseChannelMessageWithSource, Data: &dgo.InteractionResponseData{ - Content: "Hello world!", - Flags: dgo.MessageFlagsEphemeral, + Embeds: []*dgo.MessageEmbed{info}, + Flags: dgo.MessageFlagsEphemeral, }, }) @@ -43,3 +95,43 @@ func (c ManageChannel) Handle(s *dgo.Session, i *dgo.InteractionCreate) error { return nil } +func (c ChannelsInfo) Subcommands() []Command { + return []Command{} +} + +func getChannel(db guilddb.GuildDB, guildID, channelID string) (gdb.Channel, error) { + ch, err := db.Channel(guildID, channelID) + if err != nil && errors.Is(err, gdb.ErrNotFound) { + if err := db.ChannelInsert(gdb.NewChannel(guildID, channelID, lang.EN)); err != nil { + return gdb.Channel{}, err + } + ch, err = db.Channel(guildID, channelID) + if err != nil { + return gdb.Channel{}, err + } + } else if err != nil { + return gdb.Channel{}, err + } + + return ch, nil +} + +func getChannelInfo(db guilddb.GuildDB, ch gdb.Channel) (*dgo.MessageEmbed, error) { + group, err := db.ChannelGroup(ch.GuildID, ch.ID) + if err != nil && !errors.Is(err, gdb.ErrNotFound) { + return nil, err + } + + g := make([]string, len(group)) + for i, gi := range group { + g[i] = "<#" + gi.ID + ">" + } + + return &dgo.MessageEmbed{Title: "Channel Information", + Fields: []*dgo.MessageEmbedField{ + {Name: "ID", Value: ch.ID, Inline: true}, + {Name: "Language", Value: string(ch.Language), Inline: true}, + {Name: "Linked Channels", Value: strings.Join(g, ", "), Inline: true}, + }, + }, nil +} diff --git a/internals/discord/bot/commands/commands.go b/internals/discord/bot/commands/commands.go index 81b0ace..240a6b6 100644 --- a/internals/discord/bot/commands/commands.go +++ b/internals/discord/bot/commands/commands.go @@ -7,6 +7,7 @@ import ( type Command interface { Info() *dgo.ApplicationCommand Handle(s *dgo.Session, i *dgo.InteractionCreate) error + Subcommands() []Command } func getOptions(i *dgo.InteractionCreate) map[string]*dgo.ApplicationCommandInteractionDataOption { opts := i.ApplicationCommandData().Options