mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:40:44 +00:00
fix(matrix): strip mention prefix before slash command matching
Fixes openclaw#68547 Root cause: When a user sends a message like `@bot:server /new` in a Matrix room, the slash command was not recognized because the mention prefix was not stripped before checking for slash commands via hasControlCommand(). Fix: Added stripMatrixMentionPrefixes() function that strips bot mention prefixes from the beginning of messages before passing text to hasControlCommand(). This follows the same pattern used by Feishu (normalizeMentions) and ensures that slash commands work correctly when the bot is explicitly mentioned. - Added stripMatrixMentionPrefixes() helper function in handler.ts - Applied stripping before hasControlCommand() call - Added comprehensive test coverage in handler.strip-mention.test.ts
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
efc19f0ddb
commit
f371453ec2
@@ -0,0 +1,105 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
/**
|
||||
* Test helper to strip mention prefixes from text for slash command detection.
|
||||
* This is the test for the fix of issue #68547.
|
||||
*/
|
||||
function stripMatrixMentionPrefixes(text: string, mentionRegexes: RegExp[]): string {
|
||||
if (!text || mentionRegexes.length === 0) {
|
||||
return text;
|
||||
}
|
||||
let result = text;
|
||||
for (const pattern of mentionRegexes) {
|
||||
// Match mention at the start of the text, followed by optional whitespace
|
||||
const match = result.match(new RegExp(`^(${pattern.source})\\s*`));
|
||||
if (match) {
|
||||
result = result.slice(match[0].length).trimStart();
|
||||
break; // Only strip the first mention prefix
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
describe("stripMatrixMentionPrefixes", () => {
|
||||
it("returns original text when mentionRegexes is empty", () => {
|
||||
const text = "@bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, []);
|
||||
expect(result).toBe("@bot:server /new");
|
||||
});
|
||||
|
||||
it("returns original text when text is empty", () => {
|
||||
const result = stripMatrixMentionPrefixes("", [/\s*@bot:server\s*/]);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
it("strips mention prefix before slash command (issue #68547)", () => {
|
||||
const mentionRegexes = [/@bot:server\b/];
|
||||
const text = "@bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("/new");
|
||||
});
|
||||
|
||||
it("strips mention prefix with extra whitespace", () => {
|
||||
const mentionRegexes = [/@bot:server\b/];
|
||||
const text = "@bot:server /help";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("/help");
|
||||
});
|
||||
|
||||
it("strips mention prefix with display name", () => {
|
||||
const mentionRegexes = [/@OpenClaw Bot\b/];
|
||||
const text = "@OpenClaw Bot /model";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("/model");
|
||||
});
|
||||
|
||||
it("does not strip mention from middle of text", () => {
|
||||
const mentionRegexes = [/@bot:server\b/];
|
||||
const text = "Hello @bot:server how are you";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("Hello @bot:server how are you");
|
||||
});
|
||||
|
||||
it("does not strip non-matching patterns", () => {
|
||||
const mentionRegexes = [/@otherbot:server\b/];
|
||||
const text = "@bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("@bot:server /new");
|
||||
});
|
||||
|
||||
it("strips only the first mention prefix", () => {
|
||||
const mentionRegexes = [/@bot:server\b/];
|
||||
const text = "@bot:server @bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("@bot:server /new");
|
||||
});
|
||||
|
||||
it("handles multiple regex patterns and strips first match", () => {
|
||||
const mentionRegexes = [/@otherbot:server\b/, /@bot:server\b/];
|
||||
const text = "@bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
// First pattern doesn't match, second does
|
||||
expect(result).toBe("/new");
|
||||
});
|
||||
|
||||
it("preserves original text when no patterns match", () => {
|
||||
const mentionRegexes = [/@otherbot:server\b/, /@anotherbot:server\b/];
|
||||
const text = "@bot:server /new";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("@bot:server /new");
|
||||
});
|
||||
|
||||
it("handles regex with special characters in mention", () => {
|
||||
const mentionRegexes = [/@bot\+123:server\.com\b/];
|
||||
const text = "@bot+123:server.com /status";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("/status");
|
||||
});
|
||||
|
||||
it("preserves regular message without slash command after stripping", () => {
|
||||
const mentionRegexes = [/@bot:server\b/];
|
||||
const text = "@bot:server hello world";
|
||||
const result = stripMatrixMentionPrefixes(text, mentionRegexes);
|
||||
expect(result).toBe("hello world");
|
||||
});
|
||||
});
|
||||
@@ -268,6 +268,27 @@ function resolveMatrixMentionPrecheckText(params: {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip mention prefixes from text for slash command detection.
|
||||
* This ensures that messages like "@bot:server /new" are recognized as slash commands.
|
||||
* Similar to Feishu's normalizeMentions and Mattermost's stripMentionPrefix.
|
||||
*/
|
||||
function stripMatrixMentionPrefixes(text: string, mentionRegexes: RegExp[]): string {
|
||||
if (!text || mentionRegexes.length === 0) {
|
||||
return text;
|
||||
}
|
||||
let result = text;
|
||||
for (const pattern of mentionRegexes) {
|
||||
// Match mention at the start of the text, followed by optional whitespace
|
||||
const match = result.match(new RegExp(`^(${pattern.source})\\s*`));
|
||||
if (match) {
|
||||
result = result.slice(match[0].length).trimStart();
|
||||
break; // Only strip the first mention prefix
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function hasBundledMatrixReplacementRelation(event: MatrixRawEvent) {
|
||||
const relations = event.unsigned?.["m.relations"];
|
||||
if (!relations || typeof relations !== "object") {
|
||||
@@ -935,8 +956,14 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
surface: "matrix",
|
||||
});
|
||||
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
|
||||
const hasControlCommandInMessage = core.channel.text.hasControlCommand(
|
||||
// Strip mention prefixes before checking for slash commands so that messages
|
||||
// like "@bot:server /new" are recognized as slash commands (#68547)
|
||||
const commandCheckText = stripMatrixMentionPrefixes(
|
||||
mentionPrecheckText,
|
||||
agentMentionRegexes,
|
||||
);
|
||||
const hasControlCommandInMessage = core.channel.text.hasControlCommand(
|
||||
commandCheckText,
|
||||
cfg,
|
||||
);
|
||||
const commandGate = resolveControlCommandGate({
|
||||
|
||||
Reference in New Issue
Block a user