From b1c19872feb5a4ec9eff92feece6ad9be6f10c8c Mon Sep 17 00:00:00 2001 From: awfufu Date: Thu, 18 Dec 2025 11:07:46 +0800 Subject: [PATCH] refactor: convert commands to global variables; use log.Fatal in main --- cmd/hurobot/main.go | 4 ++++ internal/cmds/cmds.go | 42 ++++++++++++++++------------------ internal/cmds/crypto.go | 32 ++++++++------------------ internal/cmds/delete.go | 43 ++++++++++++----------------------- internal/cmds/draw.go | 32 ++++++++------------------ internal/cmds/echo.go | 32 ++++++++------------------ internal/cmds/essence.go | 32 ++++++++------------------ internal/cmds/fx.go | 32 ++++++++------------------ internal/cmds/group.go | 30 ++++++++---------------- internal/cmds/perm.go | 41 ++++++++++++--------------------- internal/cmds/sh.go | 28 +++++++---------------- internal/cmds/specialtitle.go | 32 ++++++++------------------ internal/cmds/which.go | 32 ++++++++------------------ 13 files changed, 139 insertions(+), 273 deletions(-) diff --git a/cmd/hurobot/main.go b/cmd/hurobot/main.go index 646f7aa..3840e56 100644 --- a/cmd/hurobot/main.go +++ b/cmd/hurobot/main.go @@ -1,6 +1,8 @@ package main import ( + "log" + "github.com/awfufu/go-hurobot/internal/cmds" "github.com/awfufu/go-hurobot/internal/config" "github.com/awfufu/go-hurobot/internal/db" @@ -28,6 +30,8 @@ func main() { cmds.HandleCommand(sender, msg) } }() + case err := <-receiver.Error(): + log.Fatal(err) } } } diff --git a/internal/cmds/cmds.go b/internal/cmds/cmds.go index 470a82e..172a83a 100644 --- a/internal/cmds/cmds.go +++ b/internal/cmds/cmds.go @@ -12,43 +12,39 @@ import ( "github.com/google/shlex" ) -type command interface { - Self() *cmdBase - Exec(b *qbot.Sender, msg *qbot.Message) -} - -type cmdBase struct { +type Command struct { Name string // Command name HelpMsg string // Help message Permission config.Permission // Permission requirement NeedRawMsg bool - MaxArgs int // Maximum number of arguments - MinArgs int // Minimum number of arguments + MaxArgs int // Maximum number of arguments + MinArgs int // Minimum number of arguments + Exec func(b *qbot.Sender, msg *qbot.Message) // Execute function } const commandPrefix = '/' -var cmdMap map[string]command +var cmdMap map[string]*Command func init() { - cmdMap = map[string]command{ - "crypto": NewCryptoCommand(), - "delete": NewDeleteCommand(), - "draw": NewDrawCommand(), - "echo": NewEchoCommand(), - "essence": NewEssenceCommand(), - "fx": NewErCommand(), - "group": NewGroupCommand(), - "perm": NewPermCommand(), - "sh": NewShCommand(), - "specialtitle": NewSpecialtitleCommand(), - "which": NewWhichCommand(), + cmdMap = map[string]*Command{ + "crypto": cryptoCommand, + "delete": deleteCommand, + "draw": drawCommand, + "echo": echoCommand, + "essence": essenceCommand, + "fx": erCommand, + "group": groupCommand, + "perm": permCommand, + "sh": shCommand, + "specialtitle": specialtitleCommand, + "which": whichCommand, } } func InitCommandPermissions() { for name, cmd := range cmdMap { - base := cmd.Self() + base := cmd perm := db.GetCommandPermission(name) if perm != nil { @@ -103,7 +99,7 @@ func HandleCommand(b *qbot.Sender, msg *qbot.Message) { return } - cmdBase := cmd.Self() + cmdBase := cmd // check permission if !checkCmdPermission(cmdBase.Name, msg.UserID, msg.GroupID) { diff --git a/internal/cmds/crypto.go b/internal/cmds/crypto.go index e2dbf19..8ca8297 100644 --- a/internal/cmds/crypto.go +++ b/internal/cmds/crypto.go @@ -75,29 +75,17 @@ Examples: /crypto BTC /crypto BTC USD` -type CryptoCommand struct { - cmdBase +var cryptoCommand *Command = &Command{ + Name: "crypto", + HelpMsg: cryptoHelpMsg, + Permission: getCmdPermLevel("crypto"), + NeedRawMsg: false, + MaxArgs: 3, + MinArgs: 2, + Exec: execCrypto, } -func NewCryptoCommand() *CryptoCommand { - return &CryptoCommand{ - cmdBase: cmdBase{ - Name: "crypto", - HelpMsg: cryptoHelpMsg, - Permission: getCmdPermLevel("crypto"), - - NeedRawMsg: false, - MaxArgs: 3, - MinArgs: 2, - }, - } -} - -func (cmd *CryptoCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *CryptoCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execCrypto(b *qbot.Sender, msg *qbot.Message) { if len(msg.Array) == 2 { if msg.Array[1].Type() == qbot.TextType { coin := strings.ToUpper(msg.Array[1].Text()) @@ -116,7 +104,7 @@ func (cmd *CryptoCommand) Exec(b *qbot.Sender, msg *qbot.Message) { handleCryptoCurrencyPair(b, msg, coin, currency) } } else { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, cryptoHelpMsg) } } diff --git a/internal/cmds/delete.go b/internal/cmds/delete.go index 76fb009..478e511 100644 --- a/internal/cmds/delete.go +++ b/internal/cmds/delete.go @@ -9,32 +9,19 @@ import ( const deleteHelpMsg = `Delete a message by replying to it. Usage: [Reply to a message] /delete` -type DeleteCommand struct { - cmdBase -} - -func NewDeleteCommand() *DeleteCommand { - return &DeleteCommand{ - cmdBase: cmdBase{ - Name: "delete", - HelpMsg: deleteHelpMsg, - Permission: getCmdPermLevel("delete"), - NeedRawMsg: false, - MaxArgs: 1, - MinArgs: 1, - }, - } -} - -func (cmd *DeleteCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *DeleteCommand) Exec(b *qbot.Sender, msg *qbot.Message) { - if msg.ReplyID != 0 { - b.DeleteMsg(msg.ReplyID) - log.Printf("delete message %d", msg.ReplyID) - } else { - b.SendGroupMsg(msg.GroupID, "Please reply to a message to delete it, and ensure the bot has permission to delete it") - } +var deleteCommand *Command = &Command{ + Name: "delete", + HelpMsg: deleteHelpMsg, + Permission: getCmdPermLevel("delete"), + NeedRawMsg: false, + MaxArgs: 1, + MinArgs: 1, + Exec: func(b *qbot.Sender, msg *qbot.Message) { + if msg.ReplyID != 0 { + b.DeleteMsg(msg.ReplyID) + log.Printf("delete message %d", msg.ReplyID) + } else { + b.SendGroupMsg(msg.GroupID, "Please reply to a message to delete it, and ensure the bot has permission to delete it") + } + }, } diff --git a/internal/cmds/draw.go b/internal/cmds/draw.go index 7402976..6720370 100644 --- a/internal/cmds/draw.go +++ b/internal/cmds/draw.go @@ -18,6 +18,15 @@ Usage: /draw [--size ] Supported sizes: 1328x1328, 1584x1056, 1140x1472, 1664x928, 928x1664 Example: /draw a cat --size 1328x1328` +var drawCommand *Command = &Command{ + Name: "draw", + HelpMsg: drawHelpMsg, + Permission: getCmdPermLevel("draw"), + NeedRawMsg: false, + MinArgs: 2, + Exec: execDraw, +} + type ImageGenerationRequest struct { Model string `json:"model"` Prompt string `json:"prompt"` @@ -36,28 +45,7 @@ type ImageGenerationResponse struct { Seed int64 `json:"seed"` } -type DrawCommand struct { - cmdBase -} - -func NewDrawCommand() *DrawCommand { - return &DrawCommand{ - cmdBase: cmdBase{ - Name: "draw", - HelpMsg: drawHelpMsg, - Permission: getCmdPermLevel("draw"), - - NeedRawMsg: false, - MinArgs: 2, - }, - } -} - -func (cmd *DrawCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *DrawCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execDraw(b *qbot.Sender, msg *qbot.Message) { if config.Cfg.ApiKeys.DrawApiKey == "" { b.SendGroupMsg(msg.GroupID, "No API key") return diff --git a/internal/cmds/echo.go b/internal/cmds/echo.go index c53adde..bd8354a 100644 --- a/internal/cmds/echo.go +++ b/internal/cmds/echo.go @@ -8,27 +8,13 @@ const echoHelpMsg string = `Echoes messages to a target destination. Usage: /echo Example: /echo helloworld` -type EchoCommand struct { - cmdBase -} - -func NewEchoCommand() *EchoCommand { - return &EchoCommand{ - cmdBase: cmdBase{ - Name: "echo", - HelpMsg: echoHelpMsg, - Permission: getCmdPermLevel("echo"), - - NeedRawMsg: false, - MinArgs: 2, - }, - } -} - -func (cmd *EchoCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *EchoCommand) Exec(b *qbot.Sender, msg *qbot.Message) { - b.SendGroupMsg(msg.GroupID, msg.Array[1:]) +var echoCommand *Command = &Command{ + Name: "echo", + HelpMsg: echoHelpMsg, + Permission: getCmdPermLevel("echo"), + NeedRawMsg: false, + MinArgs: 2, + Exec: func(b *qbot.Sender, msg *qbot.Message) { + b.SendGroupMsg(msg.GroupID, msg.Array[1:]) + }, } diff --git a/internal/cmds/essence.go b/internal/cmds/essence.go index 4c02cb3..3053652 100644 --- a/internal/cmds/essence.go +++ b/internal/cmds/essence.go @@ -7,29 +7,17 @@ import ( const essenceHelpMsg string = `Manage essence messages. Usage: [Reply to a message] /essence [add|rm]` -type EssenceCommand struct { - cmdBase +var essenceCommand *Command = &Command{ + Name: "essence", + HelpMsg: essenceHelpMsg, + Permission: getCmdPermLevel("essence"), + NeedRawMsg: false, + Exec: execEssence, } -func NewEssenceCommand() *EssenceCommand { - return &EssenceCommand{ - cmdBase: cmdBase{ - Name: "essence", - HelpMsg: essenceHelpMsg, - Permission: getCmdPermLevel("essence"), - - NeedRawMsg: false, - }, - } -} - -func (cmd *EssenceCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *EssenceCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execEssence(b *qbot.Sender, msg *qbot.Message) { if msg.ReplyID == 0 { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, essenceHelpMsg) return } @@ -41,10 +29,10 @@ func (cmd *EssenceCommand) Exec(b *qbot.Sender, msg *qbot.Message) { case "add": b.SetGroupEssence(msg.ReplyID) default: - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, essenceHelpMsg) } } else { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, essenceHelpMsg) } } else { b.SetGroupEssence(msg.ReplyID) diff --git a/internal/cmds/fx.go b/internal/cmds/fx.go index da93918..0a6a398 100644 --- a/internal/cmds/fx.go +++ b/internal/cmds/fx.go @@ -22,35 +22,23 @@ const erHelpMsg string = `Query foreign exchange rates. Usage: fx Example: fx CNY HKD` -type ErCommand struct { - cmdBase +var erCommand *Command = &Command{ + Name: "fx", + HelpMsg: erHelpMsg, + Permission: getCmdPermLevel("fx"), + NeedRawMsg: false, + MaxArgs: 3, + MinArgs: 3, + Exec: execEr, } -func NewErCommand() *ErCommand { - return &ErCommand{ - cmdBase: cmdBase{ - Name: "fx", - HelpMsg: erHelpMsg, - Permission: getCmdPermLevel("fx"), - - NeedRawMsg: false, - MaxArgs: 3, - MinArgs: 3, - }, - } -} - -func (cmd *ErCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *ErCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execEr(b *qbot.Sender, msg *qbot.Message) { if config.Cfg.ApiKeys.ExchangeRateAPIKey == "" { return } if len(msg.Array) < 3 { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, erHelpMsg) return } diff --git a/internal/cmds/group.go b/internal/cmds/group.go index f377fa5..19a9b17 100644 --- a/internal/cmds/group.go +++ b/internal/cmds/group.go @@ -15,28 +15,16 @@ Examples: /group rename awa /group op @user1 @user2 ...` -type GroupCommand struct { - cmdBase +var groupCommand *Command = &Command{ + Name: "group", + HelpMsg: groupHelpMsg, + Permission: getCmdPermLevel("group"), + NeedRawMsg: false, + MinArgs: 2, + Exec: execGroup, } -func NewGroupCommand() *GroupCommand { - return &GroupCommand{ - cmdBase: cmdBase{ - Name: "group", - HelpMsg: groupHelpMsg, - Permission: getCmdPermLevel("group"), - - NeedRawMsg: false, - MinArgs: 2, - }, - } -} - -func (cmd *GroupCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *GroupCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execGroup(b *qbot.Sender, msg *qbot.Message) { getText := func(i int) string { if i < len(msg.Array) { if msg.Array[i].Type() == qbot.TextType { @@ -47,7 +35,7 @@ func (cmd *GroupCommand) Exec(b *qbot.Sender, msg *qbot.Message) { } if len(msg.Array) < 2 { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, groupHelpMsg) return } diff --git a/internal/cmds/perm.go b/internal/cmds/perm.go index a3da8f3..bbddba2 100644 --- a/internal/cmds/perm.go +++ b/internal/cmds/perm.go @@ -25,29 +25,18 @@ Examples: /perm special draw user add @user /perm user @user admin` -type PermCommand struct { - cmdBase +var permCommand *Command = &Command{ + Name: "perm", + HelpMsg: permHelpMsg, + Permission: config.Master, + NeedRawMsg: false, + MinArgs: 2, + Exec: execPerm, } -func NewPermCommand() *PermCommand { - return &PermCommand{ - cmdBase: cmdBase{ - Name: "perm", - HelpMsg: permHelpMsg, - Permission: config.Master, // Only master can manage permissions - NeedRawMsg: false, - MinArgs: 2, - }, - } -} - -func (cmd *PermCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *PermCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execPerm(b *qbot.Sender, msg *qbot.Message) { if len(msg.Array) < 2 { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, permHelpMsg) return } @@ -63,17 +52,17 @@ func (cmd *PermCommand) Exec(b *qbot.Sender, msg *qbot.Message) { subCmd := getText(1) switch subCmd { case "set": - cmd.handleSet(b, msg) + handleSet(b, msg) case "special": - cmd.handleSpecial(b, msg) + handleSpecial(b, msg) case "user": - cmd.handleUserRole(b, msg) + handleUserRole(b, msg) default: b.SendGroupMsg(msg.GroupID, "Unknown subcommand: "+subCmd) } } -func (cmd *PermCommand) handleSet(b *qbot.Sender, msg *qbot.Message) { +func handleSet(b *qbot.Sender, msg *qbot.Message) { getText := func(i int) string { if i < len(msg.Array) { if msg.Array[i].Type() == qbot.TextType { @@ -144,7 +133,7 @@ func (cmd *PermCommand) handleSet(b *qbot.Sender, msg *qbot.Message) { } } -func (cmd *PermCommand) handleSpecial(b *qbot.Sender, msg *qbot.Message) { +func handleSpecial(b *qbot.Sender, msg *qbot.Message) { getText := func(i int) string { if i < len(msg.Array) { if msg.Array[i].Type() == qbot.TextType { @@ -288,7 +277,7 @@ func extractTargets(args []qbot.MsgItem, targetType string) []uint64 { return targets } -func (cmd *PermCommand) handleUserRole(b *qbot.Sender, msg *qbot.Message) { +func handleUserRole(b *qbot.Sender, msg *qbot.Message) { getText := func(i int) string { if i < len(msg.Array) { if msg.Array[i].Type() == qbot.TextType { diff --git a/internal/cmds/sh.go b/internal/cmds/sh.go index 9a1d15a..e560464 100644 --- a/internal/cmds/sh.go +++ b/internal/cmds/sh.go @@ -57,28 +57,16 @@ func truncateString(s string) string { return s } -type ShCommand struct { - cmdBase +var shCommand *Command = &Command{ + Name: "sh", + HelpMsg: shHelpMsg, + Permission: getCmdPermLevel("sh"), + NeedRawMsg: true, + MinArgs: 2, + Exec: execSh, } -func NewShCommand() *ShCommand { - return &ShCommand{ - cmdBase: cmdBase{ - Name: "sh", - HelpMsg: shHelpMsg, - Permission: getCmdPermLevel("sh"), - - NeedRawMsg: true, - MinArgs: 2, - }, - } -} - -func (cmd *ShCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *ShCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execSh(b *qbot.Sender, msg *qbot.Message) { // For NeedRawMsg, msg.Array[1] contains the raw arguments string. // But let's verify if we need parsing for --reset // The original code checked args[1] == "--reset". diff --git a/internal/cmds/specialtitle.go b/internal/cmds/specialtitle.go index 6e2a058..b6dfd37 100644 --- a/internal/cmds/specialtitle.go +++ b/internal/cmds/specialtitle.go @@ -11,29 +11,17 @@ const specialtitleHelpMsg string = `Set special title for group members. Usage: /specialtitle [@user] Example: /specialtitle @user qwq` -type SpecialtitleCommand struct { - cmdBase +var specialtitleCommand *Command = &Command{ + Name: "specialtitle", + HelpMsg: specialtitleHelpMsg, + Permission: getCmdPermLevel("specialtitle"), + NeedRawMsg: false, + MaxArgs: 3, + MinArgs: 2, + Exec: execSpecialTitle, } -func NewSpecialtitleCommand() *SpecialtitleCommand { - return &SpecialtitleCommand{ - cmdBase: cmdBase{ - Name: "specialtitle", - HelpMsg: specialtitleHelpMsg, - Permission: getCmdPermLevel("specialtitle"), - - NeedRawMsg: false, - MaxArgs: 3, - MinArgs: 2, - }, - } -} - -func (cmd *SpecialtitleCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *SpecialtitleCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execSpecialTitle(b *qbot.Sender, msg *qbot.Message) { var targetUserID qbot.UserID var title string @@ -81,7 +69,7 @@ func (cmd *SpecialtitleCommand) Exec(b *qbot.Sender, msg *qbot.Message) { } } else { // Should be handled by MaxArgs/MinArgs, but safe fallback - b.SendGroupReplyMsg(msg.GroupID, msg.MsgID, cmd.HelpMsg) + b.SendGroupReplyMsg(msg.GroupID, msg.MsgID, specialtitleHelpMsg) return } diff --git a/internal/cmds/which.go b/internal/cmds/which.go index d525d4d..ad234ee 100644 --- a/internal/cmds/which.go +++ b/internal/cmds/which.go @@ -26,29 +26,17 @@ const whichHelpMsg string = `Query abbreviation meanings. Usage: /which <text> Example: /which yyds` -type WhichCommand struct { - cmdBase +var whichCommand *Command = &Command{ + Name: "which", + HelpMsg: whichHelpMsg, + Permission: getCmdPermLevel("which"), + NeedRawMsg: false, + MaxArgs: 2, + MinArgs: 2, + Exec: execWhich, } -func NewWhichCommand() *WhichCommand { - return &WhichCommand{ - cmdBase: cmdBase{ - Name: "which", - HelpMsg: whichHelpMsg, - Permission: getCmdPermLevel("which"), - - NeedRawMsg: false, - MaxArgs: 2, - MinArgs: 2, - }, - } -} - -func (cmd *WhichCommand) Self() *cmdBase { - return &cmd.cmdBase -} - -func (cmd *WhichCommand) Exec(b *qbot.Sender, msg *qbot.Message) { +func execWhich(b *qbot.Sender, msg *qbot.Message) { // Check for non-text type parameters for i := 1; i < len(msg.Array); i++ { str := "" @@ -70,7 +58,7 @@ func (cmd *WhichCommand) Exec(b *qbot.Sender, msg *qbot.Message) { text := strings.Join(parts, " ") if text == "" { - b.SendGroupMsg(msg.GroupID, cmd.HelpMsg) + b.SendGroupMsg(msg.GroupID, whichHelpMsg) return }