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:] + } } }