Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 90 additions & 14 deletions api/slash_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ var (
// as value and the name of the discordgo.ApplicationCommand as key.
componentCommandMap map[string]*Command

// messageActionMap is a map that holds the to-be-executed MessageAction
// as value and the CustomID of a message component as the key.
messageActionMap map[string]*MessageAction

// unregisterCommandHandler holds the function that can be used to unregister
// the command Handler registered by InitCommandHandling
unregisterCommandHandler func()
Expand All @@ -47,20 +51,31 @@ var (
// init slash command sub-system
func init() {
componentCommandMap = make(map[string]*Command)
messageActionMap = make(map[string]*MessageAction)
slashCommandManagerLogger = logger.New(slashCommandLogPrefix, nil)
}

// Command is a struct that acts as a container for
// discordgo.ApplicationCommand and the assigned command Handler.
//
// Create an instance of the struct and pass to Register a command
// Create an instance of the struct and pass to Register a Command
type Command struct {
Cmd *discordgo.ApplicationCommand
Category Category
Handler func(s *discordgo.Session, i *discordgo.InteractionCreate)
c *Component
}

// MessageAction is a struct that acts as a container for
// a string indicator and the assigned message interaction Handler.
//
// Create an instance of the struct and pass to Register a MessageAction
type MessageAction struct {
CustomID string
Handler func(s *discordgo.Session, i *discordgo.InteractionCreate)
c *Component
}

// SlashCommandManager is a type that is used to hold
// the owner that keeps the information about the
// component used by the slash Command manager methods.
Expand All @@ -72,12 +87,18 @@ type SlashCommandManager struct {
// how slash commands should be created and registered
// in the application
type CommonSlashCommandManager interface {
// Register allows to register a command
// Register allows to register a Command
//
// It requires a Command to be passed.
// The Command holds the common discordgo.ApplicationCommand
// and the function that should handle the command.
Register(cmd *Command) error
// RegisterMessageAction allows to register a MessageAction
//
// It requires a MessageAction to be passed.
// The MessageAction holds a string named CustomID,
// and it's assigned function it should handle when called.
RegisterMessageAction(msgAction *MessageAction) error
// SyncApplicationComponentCommands ensures that the available discordgo.ApplicationCommand
// are synced for the given component with the given guild.
//
Expand All @@ -102,8 +123,15 @@ func InitCommandHandling(session *discordgo.Session) error {
}

unregisterCommandHandler = session.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
if command, ok := componentCommandMap[i.ApplicationCommandData().Name]; ok {
command.Handler(s, i)
switch i.Type {
case discordgo.InteractionApplicationCommand:
if command, ok := componentCommandMap[i.ApplicationCommandData().Name]; ok {
command.Handler(s, i)
}
case discordgo.InteractionMessageComponent:
if command, ok := messageActionMap[i.MessageComponentData().CustomID]; ok {
command.Handler(s, i)
}
}
})

Expand Down Expand Up @@ -164,7 +192,6 @@ func (c *SlashCommandManager) Register(cmd *Command) error {
cmd.c = c.owner

err := c.validateCommand(cmd)

if nil != err {
return err
}
Expand All @@ -191,16 +218,46 @@ func (c *SlashCommandManager) Register(cmd *Command) error {
return nil
}

// validateCommand validates the passed command to ensure it is valid
// and can be registered properly.
// RegisterMessageAction allows to register a MessageAction
//
// It requires a MessageAction to be passed.
// The MessageAction holds a string named CustomID,
// and it's assigned function it should handle when called.
func (c *SlashCommandManager) RegisterMessageAction(msgAction *MessageAction) error {
msgAction.c = c.owner

err := c.validateMessageAction(msgAction)
if nil != err {
return err
}

if _, ok := messageActionMap[msgAction.CustomID]; ok {
err = errors.New("cannot register a CustomID twice")

c.owner.Logger().Err(
err,
"Failed to register the message action \"%v\" for component \"%v\": %v!",
msgAction.CustomID,
c.owner.Name,
err.Error())

return err
}

messageActionMap[msgAction.CustomID] = msgAction

return nil
}

// validateCommand validates the passed Command
// to ensure it is valid and can be registered properly.
func (c *SlashCommandManager) validateCommand(cmd *Command) error {
if nil == cmd.Cmd {
err := errors.New("the discordgo.ApplicationCommand of the passed command is nil")

c.owner.Logger().Err(
err,
"Failed to register the slash-Cmd \"%v\" for component \"%v\": %v!",
cmd.Cmd.Name,
"Failed to register a slash-Cmd for component \"%v\": %v!",
c.owner.Name,
err.Error())

Expand All @@ -220,14 +277,33 @@ func (c *SlashCommandManager) validateCommand(cmd *Command) error {
return err
}

if nil == cmd.Handler {
err := errors.New("the Handler of the passed command is nil")
return nil
}

// validateMessageAction validates the passed MessageAction
// to ensure it is valid and can be registered properly.
func (c *SlashCommandManager) validateMessageAction(msgAction *MessageAction) error {
if "" == msgAction.CustomID {
err := errors.New("the CustomID of the passed command is empty")

c.owner.Logger().Err(
err,
"Failed to register the slash-Cmd \"%v\" for component \"%v\"!",
cmd.Cmd.Name,
c.owner.Name)
"Failed to register the message action for component \"%v\": %v!",
c.owner.Name,
err.Error())

return err
}

if nil == msgAction.Handler {
err := errors.New("the Handler of the passed message action is nil")

c.owner.Logger().Err(
err,
"Failed to register the message action \"%v\" for component \"%v\": %v!",
msgAction.CustomID,
c.owner.Name,
err.Error())

return err
}
Expand Down
1 change: 1 addition & 0 deletions components/component_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package components

import (
_ "github.com/lazybytez/jojo-discord-bot/components/dice"
_ "github.com/lazybytez/jojo-discord-bot/components/events"
_ "github.com/lazybytez/jojo-discord-bot/components/pingpong"
_ "github.com/lazybytez/jojo-discord-bot/components/statistics"
)
135 changes: 135 additions & 0 deletions components/events/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* JOJO Discord Bot - An advanced multi-purpose discord bot
* Copyright (C) 2022 Lazy Bytez (Elias Knodel, Pascal Zarrad)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package events

import (
"github.com/bwmarrin/discordgo"
"github.com/lazybytez/jojo-discord-bot/api"
"github.com/lazybytez/jojo-discord-bot/api/slash_commands"
)

var eventsCommand = &api.Command{
Cmd: &discordgo.ApplicationCommand{
Name: "events",
Description: "Show and manage your custom Events!",
Options: []*discordgo.ApplicationCommandOption{
{
Name: "list",
Description: "List all the Events you signed up for!",
Type: discordgo.ApplicationCommandOptionSubCommand,
},
{
Name: "manage",
Description: "Manage (Create, Edit, Delete) the Events on Discords you have permission on!",
Type: discordgo.ApplicationCommandOptionSubCommand,
},
},
},
Handler: handleEventsCommand,
}

func handleEventsCommand(s *discordgo.Session, i *discordgo.InteractionCreate) {
subCommand := map[string]func(
s *discordgo.Session,
i *discordgo.InteractionCreate,
option *discordgo.ApplicationCommandInteractionDataOption,
){
"list": handleList,
"manage": handleManage,
}

api.ProcessSubCommands(s, i, nil, subCommand)
}

func handleList(s *discordgo.Session, i *discordgo.InteractionCreate, _ *discordgo.ApplicationCommandInteractionDataOption) {
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseDeferredMessageUpdate,
Data: buildListEmbed(s),
})
}

func handleManage(s *discordgo.Session, i *discordgo.InteractionCreate, _ *discordgo.ApplicationCommandInteractionDataOption) {
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: buildManageEmbed(s),
})
}

func buildListEmbed(s *discordgo.Session) *discordgo.InteractionResponseData {
resp := slash_commands.GenerateEphemeralInteractionResponseTemplate(
":calendar_spiral: Your upcoming events",
"Here you can see all the events you signed up for the next 14 days.\n")

eventsField := []*discordgo.MessageEmbedField{
{
Name: "Events",
Value: "- <#1025840920404435045>: <t:1664634300:F>\n" +
"- <#1025840920404435045>: <t:1664634300:F>\n" +
"- <#1025840920404435045>: <t:1664634300:F>\n" +
"- <#1025840920404435045>: <t:1664634300:F>\n",
},
}

resp.Embeds[0].Fields = eventsField
resp.Embeds[0].Color = embedColor

return resp
}

func buildManageEmbed(s *discordgo.Session) *discordgo.InteractionResponseData {
resp := slash_commands.GenerateEphemeralInteractionResponseTemplate(
":calendar_spiral: Event Manager",
"Do you want to Create a new Event or Edit / Delete an existing one?")

cudButtons := []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "📜",
},
Label: "Create",
Style: discordgo.PrimaryButton,
CustomID: "event_create",
},
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🔧",
},
Label: "Edit",
Style: discordgo.SecondaryButton,
CustomID: "event_edit",
},
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🗑",
},
Label: "Delete",
Style: discordgo.DangerButton,
CustomID: "event_delete",
},
},
},
}

resp.Embeds[0].Color = embedColor
resp.Components = cudButtons

return resp
}
59 changes: 59 additions & 0 deletions components/events/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* JOJO Discord Bot - An advanced multi-purpose discord bot
* Copyright (C) 2022 Lazy Bytez (Elias Knodel, Pascal Zarrad)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package events

import (
"github.com/bwmarrin/discordgo"
"github.com/lazybytez/jojo-discord-bot/api"
)

// All embeds sent by this component will have the specified embedColor
var embedColor = 0xF9E2AF

// C is the instance of the component.
// Can be used to register the component or get information about it.
var C = api.Component{
// Metadata
Code: "events",
Name: "Events",
Description: "This module manages events.",

State: &api.State{
DefaultEnabled: true,
},
}

// init initializes the component with its metadata
func init() {
api.RegisterComponent(&C, LoadComponent)
}

// LoadComponent loads the Component
func LoadComponent(_ *discordgo.Session) error {
_ = C.SlashCommandManager().Register(eventsCommand)

_ = C.SlashCommandManager().RegisterMessageAction(messageActionEventCreate)
_ = C.SlashCommandManager().RegisterMessageAction(messageActionEventCreateModal)
_ = C.SlashCommandManager().RegisterMessageAction(messageActionEventCreateModalAfter)

_ = C.SlashCommandManager().RegisterMessageAction(messageActionEventEdit)
_ = C.SlashCommandManager().RegisterMessageAction(messageActionEventDelete)

return nil
}
Loading