diff --git a/cmd/pen-fun/main.go b/cmd/pen-fun/main.go index e2f1d28..77a6c70 100644 --- a/cmd/pen-fun/main.go +++ b/cmd/pen-fun/main.go @@ -6,12 +6,15 @@ import ( "os" "github.com/Neon-Genesis-Linux/pen-bot/internal/community" + "github.com/Neon-Genesis-Linux/pen-bot/internal/config" "github.com/Neon-Genesis-Linux/pen-bot/internal/core" + _ "github.com/Neon-Genesis-Linux/pen-bot/internal/logger" "github.com/disgoorg/disgo/events" ) func main() { community.Register() + config.Register() if err := core.Start(context.Background(), os.Getenv("BOT_TOKEN"), onMessageCreate); err != nil { slog.Error("failed to start pen-fun bot", slog.Any("error", err)) } diff --git a/internal/community/commands.go b/internal/community/commands.go index 9087130..a6ed170 100644 --- a/internal/community/commands.go +++ b/internal/community/commands.go @@ -2,7 +2,7 @@ package community import ( "github.com/Neon-Genesis-Linux/pen-bot/internal/core" - "github.com/disgoorg/disgo/discord" + "github.com/Neon-Genesis-Linux/pen-bot/internal/messaging" "github.com/disgoorg/disgo/events" ) @@ -13,9 +13,9 @@ func Register() { } func handlePing(event *events.MessageCreate) { - _, _ = event.Client().Rest.CreateMessage(event.ChannelID, discord.NewMessageCreate().WithContent("pong")) + _ = messaging.SendReply(event, "pong") } func handlePong(event *events.MessageCreate) { - _, _ = event.Client().Rest.CreateMessage(event.ChannelID, discord.NewMessageCreate().WithContent("ping")) + _ = messaging.SendReply(event, "ping") } diff --git a/internal/config/commands.go b/internal/config/commands.go new file mode 100644 index 0000000..835bac8 --- /dev/null +++ b/internal/config/commands.go @@ -0,0 +1,46 @@ +package config + +import ( + "os" + "strings" + + "github.com/Neon-Genesis-Linux/pen-bot/internal/core" + "github.com/Neon-Genesis-Linux/pen-bot/internal/messaging" + "github.com/disgoorg/disgo/events" +) + +const ownerEnv = "BOT_OWNER_ID" + +// Register registers configuration commands with the bot. +func Register() { + core.RegisterCommandPath([]string{"config", "prefix", "set"}, handlePrefixSet) +} + +func handlePrefixSet(event *events.MessageCreate, args []string) { + if !isOwner(event) { + _ = messaging.SendReply(event, "Unauthorized: only bot owner can run config commands.") + return + } + + if len(args) < 1 { + _ = messaging.SendReply(event, "Usage: "+core.GetBotPrefix()+"config prefix set ") + return + } + + newPrefix := strings.TrimSpace(args[0]) + if newPrefix == "" { + _ = messaging.SendReply(event, "Prefix cannot be empty.") + return + } + + core.SetBotPrefix(newPrefix) + _ = messaging.Send(event, "Command prefix set to `"+newPrefix+"`.") +} + +func isOwner(event *events.MessageCreate) bool { + ownerID := os.Getenv(ownerEnv) + if ownerID == "" { + return false + } + return event.Message.Author.ID.String() == ownerID +} diff --git a/internal/messaging/message.go b/internal/messaging/message.go new file mode 100644 index 0000000..08f02f6 --- /dev/null +++ b/internal/messaging/message.go @@ -0,0 +1,51 @@ +package messaging + +import ( + "log/slog" + + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/disgo/events" +) + +// SendOptions configures message sending behavior +type SendOptions struct { + Reply bool // If true, reply to the triggering message +} + +// SendMessage sends a message with optional reply. +// Errors are logged but not returned to user. +// Returns error for caller's information if needed. +func SendMessage(event *events.MessageCreate, content string, opts *SendOptions) error { + if opts == nil { + opts = &SendOptions{} + } + + builder := discord.NewMessageCreate().WithContent(content) + + if opts.Reply { + messageID := event.Message.ID + builder = builder.WithMessageReference(&discord.MessageReference{ + MessageID: &messageID, + }) + } + + _, err := event.Client().Rest.CreateMessage(event.ChannelID, builder) + if err != nil { + slog.Error("failed to send message", + slog.String("channel_id", event.ChannelID.String()), + slog.String("content", content), + slog.Any("error", err), + ) + } + return err +} + +// Send sends a simple message (no reply) +func Send(event *events.MessageCreate, content string) error { + return SendMessage(event, content, nil) +} + +// SendReply sends a message as a reply to the user's message +func SendReply(event *events.MessageCreate, content string) error { + return SendMessage(event, content, &SendOptions{Reply: true}) +}