butterrobot/internal/plugin/help/help.go

164 lines
4.7 KiB
Go

package help
import (
"fmt"
"sort"
"strings"
"git.nakama.town/fmartingr/butterrobot/internal/db"
"git.nakama.town/fmartingr/butterrobot/internal/model"
"git.nakama.town/fmartingr/butterrobot/internal/plugin"
"golang.org/x/exp/slog"
)
// ChannelPluginGetter is an interface for getting channel plugins
type ChannelPluginGetter interface {
GetChannelPlugins(channelID int64) ([]*model.ChannelPlugin, error)
GetChannelPluginsFromPlatformID(platform, platformChannelID string) ([]*model.ChannelPlugin, error)
}
// HelpPlugin provides help information about available commands
type HelpPlugin struct {
plugin.BasePlugin
db ChannelPluginGetter
}
// New creates a new HelpPlugin instance
func New(db ChannelPluginGetter) *HelpPlugin {
return &HelpPlugin{
BasePlugin: plugin.BasePlugin{
ID: "utility.help",
Name: "Help",
Help: "Shows available commands when you type '!help'",
},
db: db,
}
}
// OnMessage handles incoming messages
func (p *HelpPlugin) OnMessage(msg *model.Message, config map[string]interface{}, cache model.CacheInterface) []*model.MessageAction {
// Check if message is the help command
if !strings.EqualFold(strings.TrimSpace(msg.Text), "!help") {
return nil
}
// Get channel plugins from database using platform and platform channel ID
channelPlugins, err := p.db.GetChannelPluginsFromPlatformID(msg.Channel.Platform, msg.Channel.PlatformChannelID)
if err != nil && err != db.ErrNotFound {
slog.Error("Failed to get channel plugins", slog.Any("err", err))
return []*model.MessageAction{}
}
// If no plugins found, initialize empty slice
if err == db.ErrNotFound {
channelPlugins = []*model.ChannelPlugin{}
}
// Get all available plugins
availablePlugins := plugin.GetAvailablePlugins()
// Filter to only enabled plugins for this channel
enabledPlugins := make(map[string]model.Plugin)
for _, channelPlugin := range channelPlugins {
if channelPlugin.Enabled {
if availablePlugin, exists := availablePlugins[channelPlugin.PluginID]; exists {
enabledPlugins[channelPlugin.PluginID] = availablePlugin
}
}
}
// If no plugins are enabled, return a message
if len(enabledPlugins) == 0 {
response := &model.Message{
Text: "No plugins are currently enabled for this channel.",
Chat: msg.Chat,
ReplyTo: msg.ID,
Channel: msg.Channel,
}
return []*model.MessageAction{
{
Type: model.ActionSendMessage,
Message: response,
Chat: msg.Chat,
Channel: msg.Channel,
},
}
}
// Group plugins by category
categories := map[string][]model.Plugin{
"Development": {},
"Fun and Entertainment": {},
"Utility": {},
"Security": {},
"Social Media": {},
"Other": {},
}
// Categorize plugins based on their ID prefix
for _, p := range enabledPlugins {
category := p.GetID()
switch {
case strings.HasPrefix(category, "dev."):
categories["Development"] = append(categories["Development"], p)
case strings.HasPrefix(category, "fun."):
categories["Fun and Entertainment"] = append(categories["Fun and Entertainment"], p)
case strings.HasPrefix(category, "util.") || strings.HasPrefix(category, "reminder.") || strings.HasPrefix(category, "utility."):
categories["Utility"] = append(categories["Utility"], p)
case strings.HasPrefix(category, "security."):
categories["Security"] = append(categories["Security"], p)
case strings.HasPrefix(category, "social."):
categories["Social Media"] = append(categories["Social Media"], p)
default:
categories["Other"] = append(categories["Other"], p)
}
}
// Build the help message
var helpText strings.Builder
helpText.WriteString("🤖 **Available Commands**\n\n")
// Sort category names for consistent output
categoryOrder := []string{"Development", "Fun and Entertainment", "Utility", "Security", "Social Media", "Other"}
for _, categoryName := range categoryOrder {
pluginList := categories[categoryName]
if len(pluginList) == 0 {
continue
}
// Sort plugins within category by name
sort.Slice(pluginList, func(i, j int) bool {
return pluginList[i].GetName() < pluginList[j].GetName()
})
helpText.WriteString(fmt.Sprintf("**%s:**\n", categoryName))
for _, p := range pluginList {
if p.GetHelp() == "" {
continue
}
helpText.WriteString(fmt.Sprintf("• **%s** - %s\n", p.GetName(), p.GetHelp()))
}
helpText.WriteString("\n")
}
// Add footer
helpText.WriteString("_Use the specific commands or triggers mentioned above to interact with the bot._")
response := &model.Message{
Text: helpText.String(),
Chat: msg.Chat,
ReplyTo: msg.ID,
Channel: msg.Channel,
}
return []*model.MessageAction{
{
Type: model.ActionSendMessage,
Message: response,
Chat: msg.Chat,
Channel: msg.Channel,
},
}
}