From 421ab5c773d522c730d2e2b4411a11a6c87f012e Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 9 Apr 2026 20:10:03 +0000 Subject: [PATCH] feat: permitir comandos de robots sin prefijo ! Cuando command_prefix es "" en el config, el parser trata el primer token del mensaje como nombre de comando sin requerir el prefijo !. Si el token empieza con !, se le quita igualmente para retrocompatibilidad. Cambios: - pkg/message/parse.go: modo sin prefijo en Parse() (puro, sin side effects) - agents/robot.go: mensaje "comando desconocido" y !help adaptados al prefijo - agents/handler.go: mensaje "comando desconocido" adaptado al prefijo - internal/config/schema.go: documentar command_prefix: "" en FiltersCfg - agents/_template_robot/config.yaml: ejemplo comentado de command_prefix: "" El comportamiento con command_prefix: "!" no cambia (retrocompatible). --- agents/_template_robot/config.yaml | 3 ++- agents/handler.go | 7 +++++-- agents/robot.go | 19 +++++++++++------- internal/config/schema.go | 4 ++++ pkg/message/parse.go | 31 +++++++++++++++++++++++++----- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/agents/_template_robot/config.yaml b/agents/_template_robot/config.yaml index ded4a43..b38ef3d 100644 --- a/agents/_template_robot/config.yaml +++ b/agents/_template_robot/config.yaml @@ -44,7 +44,8 @@ matrix: admin: [] filters: - command_prefix: "!" + command_prefix: "!" # usar "" para permitir comandos sin prefijo ! + # command_prefix: "" # sin prefijo: todo mensaje es un posible comando mention_respond: false # robots no responden a menciones (no hay LLM) dm_respond: false # robots no responden a DMs (no hay LLM) ignore_bots: true diff --git a/agents/handler.go b/agents/handler.go index c1f83da..6e83dd3 100644 --- a/agents/handler.go +++ b/agents/handler.go @@ -74,8 +74,11 @@ func (a *Agent) handleEvent(ctx context.Context, msgCtx decision.MessageContext, } else { // Unknown command — never falls through to rules or LLM a.logger.Info("command_unknown", "command", msgCtx.Command) - _ = a.sendReply(ctx, roomID, msgCtx.EventID, msgCtx.ThreadID, - fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command)) + unknownMsg := fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command) + if a.cfg.Matrix.Filters.CommandPrefix == "" { + unknownMsg = fmt.Sprintf("Comando desconocido: `%s`. Usa `help` para ver comandos disponibles.", msgCtx.Command) + } + _ = a.sendReply(ctx, roomID, msgCtx.EventID, msgCtx.ThreadID, unknownMsg) return } } diff --git a/agents/robot.go b/agents/robot.go index b2de743..bcf8dcf 100644 --- a/agents/robot.go +++ b/agents/robot.go @@ -206,8 +206,11 @@ func (r *Robot) handleEvent(ctx context.Context, msgCtx decision.MessageContext, // Unknown command r.logger.Info("command_unknown", "command", msgCtx.Command) - _ = r.sendReply(ctx, roomID, msgCtx.EventID, msgCtx.ThreadID, - fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command)) + unknownMsg := fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command) + if r.cfg.Matrix.Filters.CommandPrefix == "" { + unknownMsg = fmt.Sprintf("Comando desconocido: `%s`. Usa `help` para ver comandos disponibles.", msgCtx.Command) + } + _ = r.sendReply(ctx, roomID, msgCtx.EventID, msgCtx.ThreadID, unknownMsg) } // sendReply sends a markdown reply that respects thread context. @@ -224,13 +227,15 @@ func (r *Robot) cmdHelp(_ context.Context, _ decision.MessageContext) string { var b strings.Builder b.WriteString("**Comandos disponibles:**\n\n") + prefix := r.cfg.Matrix.Filters.CommandPrefix // "!" or "" + // Built-in commands appropriate for robots robotBuiltins := []command.Spec{ - {Name: "help", Aliases: []string{"h"}, Description: "Lista comandos disponibles", Usage: "!help"}, - {Name: "ping", Description: "Alive check", Usage: "!ping"}, - {Name: "status", Description: "Info del robot: uptime", Usage: "!status"}, - {Name: "info", Description: "Nombre, version y descripcion", Usage: "!info"}, - {Name: "version", Aliases: []string{"v"}, Description: "Version del robot", Usage: "!version"}, + {Name: "help", Aliases: []string{"h"}, Description: "Lista comandos disponibles", Usage: prefix + "help"}, + {Name: "ping", Description: "Alive check", Usage: prefix + "ping"}, + {Name: "status", Description: "Info del robot: uptime", Usage: prefix + "status"}, + {Name: "info", Description: "Nombre, version y descripcion", Usage: prefix + "info"}, + {Name: "version", Aliases: []string{"v"}, Description: "Version del robot", Usage: prefix + "version"}, } for _, spec := range robotBuiltins { writeSpec(&b, spec) diff --git a/internal/config/schema.go b/internal/config/schema.go index 8a0838a..a8f4b77 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -263,6 +263,10 @@ type RoomsCfg struct { } type FiltersCfg struct { + // CommandPrefix is the prefix required for commands (e.g. "!" means "!help"). + // Set to "" (empty) to allow commands without prefix — useful for robots where + // every message is a potential command. When empty, "!help" still works for + // backward compatibility (the leading "!" is stripped automatically). CommandPrefix string `yaml:"command_prefix"` MentionRespond bool `yaml:"mention_respond"` DMRespond bool `yaml:"dm_respond"` diff --git a/pkg/message/parse.go b/pkg/message/parse.go index 918eaa2..460310a 100644 --- a/pkg/message/parse.go +++ b/pkg/message/parse.go @@ -38,12 +38,33 @@ func Parse(body, senderID, roomID string, powerLevel int, isDM bool, opts ParseO } } - // Parse command - if opts.CommandPrefix != "" && strings.HasPrefix(body, opts.CommandPrefix) { - parts := strings.Fields(strings.TrimPrefix(body, opts.CommandPrefix)) + // Parse command. + // When CommandPrefix is non-empty (e.g. "!"), only messages starting with that + // prefix are treated as commands. + // When CommandPrefix is empty, every message is a potential command: the first + // token is the command name. If it starts with "!", the "!" is stripped for + // backward compatibility (so both "help" and "!help" work the same). + switch { + case opts.CommandPrefix != "": + // Standard mode: require prefix + if strings.HasPrefix(body, opts.CommandPrefix) { + parts := strings.Fields(strings.TrimPrefix(body, opts.CommandPrefix)) + if len(parts) > 0 { + ctx.Command = strings.ToLower(parts[0]) + ctx.Args = parts[1:] + } + } + case opts.CommandPrefix == "": + // No-prefix mode: treat first token as command + parts := strings.Fields(body) if len(parts) > 0 { - ctx.Command = strings.ToLower(parts[0]) - ctx.Args = parts[1:] + cmd := parts[0] + // Strip leading "!" for backward compatibility + cmd = strings.TrimPrefix(cmd, "!") + if cmd != "" { + ctx.Command = strings.ToLower(cmd) + ctx.Args = parts[1:] + } } }