Add Command Functionality to Discord Package & Implement Feedback Webhooks (!3)
Co-authored-by: Layla <layla@layla.gg> Reviewed-on: https://gitea.sumulayla.synology.me/layla/birdbot/pulls/3
This commit is contained in:
112
discord/command.go
Normal file
112
discord/command.go
Normal file
@ -0,0 +1,112 @@
|
||||
package discord
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/yeslayla/birdbot/common"
|
||||
)
|
||||
|
||||
type CommandConfiguration struct {
|
||||
Description string
|
||||
EphemeralResponse bool
|
||||
Options map[string]CommandOption
|
||||
}
|
||||
|
||||
type CommandOption struct {
|
||||
Description string
|
||||
Type CommandOptionType
|
||||
Required bool
|
||||
}
|
||||
|
||||
type CommandOptionType uint64
|
||||
|
||||
const (
|
||||
CommandTypeString CommandOptionType = CommandOptionType(discordgo.ApplicationCommandOptionString)
|
||||
CommandTypeInt CommandOptionType = CommandOptionType(discordgo.ApplicationCommandOptionInteger)
|
||||
CommandTypeBool CommandOptionType = CommandOptionType(discordgo.ApplicationCommandOptionBoolean)
|
||||
CommandTypeFloat CommandOptionType = CommandOptionType(discordgo.ApplicationCommandOptionNumber)
|
||||
)
|
||||
|
||||
// RegisterCommand creates an new command that can be used to interact with bird bot
|
||||
func (discord *Discord) RegisterCommand(name string, config CommandConfiguration, handler func(common.User, map[string]any) string) {
|
||||
command := &discordgo.ApplicationCommand{
|
||||
Name: name,
|
||||
Description: config.Description,
|
||||
}
|
||||
|
||||
// Convert options to discordgo objects
|
||||
command.Options = make([]*discordgo.ApplicationCommandOption, len(config.Options))
|
||||
index := 0
|
||||
for name, option := range config.Options {
|
||||
command.Options[index] = &discordgo.ApplicationCommandOption{
|
||||
Name: name,
|
||||
Description: option.Description,
|
||||
Required: option.Required,
|
||||
Type: discordgo.ApplicationCommandOptionType(option.Type),
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
// Register handler
|
||||
discord.commandHandlers[name] = func(session *discordgo.Session, r *discordgo.InteractionCreate) {
|
||||
if r.Interaction.Type != discordgo.InteractionApplicationCommand {
|
||||
return
|
||||
}
|
||||
|
||||
cmdOptions := r.ApplicationCommandData().Options
|
||||
|
||||
// Parse option types
|
||||
optionsMap := make(map[string]any, len(cmdOptions))
|
||||
for _, opt := range cmdOptions {
|
||||
switch config.Options[opt.Name].Type {
|
||||
case CommandTypeString:
|
||||
optionsMap[opt.Name] = opt.StringValue()
|
||||
case CommandTypeInt:
|
||||
optionsMap[opt.Name] = opt.IntValue()
|
||||
case CommandTypeBool:
|
||||
optionsMap[opt.Name] = opt.BoolValue()
|
||||
case CommandTypeFloat:
|
||||
optionsMap[opt.Name] = opt.FloatValue()
|
||||
default:
|
||||
optionsMap[opt.Name] = opt.Value
|
||||
}
|
||||
}
|
||||
|
||||
result := handler(NewUser(r.Member.User), optionsMap)
|
||||
|
||||
if result != "" {
|
||||
// Handle response
|
||||
responseData := &discordgo.InteractionResponseData{
|
||||
Content: result,
|
||||
}
|
||||
|
||||
if config.EphemeralResponse {
|
||||
responseData.Flags = discordgo.MessageFlagsEphemeral
|
||||
}
|
||||
|
||||
session.InteractionRespond(r.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: responseData,
|
||||
})
|
||||
} else {
|
||||
log.Printf("Command '%s' did not return a response: %v", name, optionsMap)
|
||||
}
|
||||
}
|
||||
|
||||
cmd, err := discord.session.ApplicationCommandCreate(discord.applicationID, discord.guildID, command)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot create command '%s': %v", name, err)
|
||||
}
|
||||
discord.commands[name] = cmd
|
||||
}
|
||||
|
||||
// ClearCommands deregisters all commands from the discord API
|
||||
func (discord *Discord) ClearCommands() {
|
||||
for _, v := range discord.commands {
|
||||
err := discord.session.ApplicationCommandDelete(discord.session.State.User.ID, discord.guildID, v.ID)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot delete command '%s': %v", v.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,10 @@ func (discord *Discord) NewButton(id string, label string) *Button {
|
||||
// OnClick registers an event when the button is clicked
|
||||
func (button *Button) OnClick(action func(user common.User)) {
|
||||
button.discord.session.AddHandler(func(s *discordgo.Session, r *discordgo.InteractionCreate) {
|
||||
if r.Interaction.Type != discordgo.InteractionMessageComponent {
|
||||
return
|
||||
}
|
||||
|
||||
if r.MessageComponentData().CustomID == button.ID {
|
||||
|
||||
action(NewUser(r.Member.User))
|
||||
|
@ -14,25 +14,32 @@ import (
|
||||
type Discord struct {
|
||||
mock.Mock
|
||||
|
||||
guildID string
|
||||
session *discordgo.Session
|
||||
guildID string
|
||||
applicationID string
|
||||
session *discordgo.Session
|
||||
|
||||
commands map[string]*discordgo.ApplicationCommand
|
||||
commandHandlers map[string]func(session *discordgo.Session, i *discordgo.InteractionCreate)
|
||||
|
||||
// Signal for shutdown
|
||||
stop chan os.Signal
|
||||
}
|
||||
|
||||
// New creates a new Discord session
|
||||
func New(guildID string, token string) *Discord {
|
||||
func New(applicationID string, guildID string, token string) *Discord {
|
||||
|
||||
// Create Discord Session
|
||||
session, err := discordgo.New(fmt.Sprint("Bot ", token))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create Discord session: %v", err)
|
||||
}
|
||||
|
||||
session.ShouldReconnectOnError = true
|
||||
return &Discord{
|
||||
session: session,
|
||||
guildID: guildID,
|
||||
session: session,
|
||||
applicationID: applicationID,
|
||||
guildID: guildID,
|
||||
commands: make(map[string]*discordgo.ApplicationCommand),
|
||||
commandHandlers: make(map[string]func(*discordgo.Session, *discordgo.InteractionCreate)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,10 +51,24 @@ func (discord *Discord) Run() error {
|
||||
}
|
||||
defer discord.session.Close()
|
||||
|
||||
// Register command handler
|
||||
discord.session.AddHandler(func(session *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if i.GuildID != discord.guildID {
|
||||
return
|
||||
}
|
||||
|
||||
if handler, ok := discord.commandHandlers[i.ApplicationCommandData().Name]; ok {
|
||||
handler(session, i)
|
||||
}
|
||||
})
|
||||
|
||||
// Keep alive
|
||||
discord.stop = make(chan os.Signal, 1)
|
||||
signal.Notify(discord.stop, os.Interrupt)
|
||||
<-discord.stop
|
||||
|
||||
discord.ClearCommands()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,8 @@ func NewUser(user *discordgo.User) common.User {
|
||||
}
|
||||
|
||||
return common.User{
|
||||
ID: user.ID,
|
||||
DisplayName: user.Username,
|
||||
ID: user.ID,
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user