diff --git a/server/src/chat/util.rs b/server/src/chat/util.rs index 1170c7d4..81b24a88 100644 --- a/server/src/chat/util.rs +++ b/server/src/chat/util.rs @@ -72,16 +72,19 @@ pub fn strip_bot_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str trimmed } -/// Strip an optional Element tab-completion separator (`:` or `,`) and -/// surrounding whitespace from the start of text that follows a bot mention. +/// Strip decoration between a bot mention and the command text. /// -/// Element's tab-completion inserts `DisplayName: ` (colon + space) after the -/// name. Without this strip the leading `:` would be treated as part of the -/// command name and no command would match. +/// After the bot name/ID is stripped, what remains may include whitespace, +/// emoji from display names (e.g. `Timmy ⚡️`), and Element tab-completion +/// separators (`:` or `,`). This function skips all of that and returns a +/// slice starting at the first ASCII alphanumeric character (the command). fn strip_mention_separator(rest: &str) -> &str { - let rest = rest.trim_start(); - let rest = rest.strip_prefix([',', ':']).unwrap_or(rest); - rest.trim_start() + let byte_skip = rest + .char_indices() + .find(|(_, c)| c.is_ascii_alphanumeric()) + .map(|(i, _)| i) + .unwrap_or(rest.len()); + &rest[byte_skip..] } /// Returns `true` when `text` ends while inside an open fenced code block. @@ -381,6 +384,15 @@ mod tests { assert_eq!(rest, "help"); } + #[test] + fn strip_mention_short_name_emoji_suffix_in_body() { + // bot_name is "Timmy" (no emoji) but Element mention pill puts + // "Timmy ⚡️ status" in the body — the emoji is part of the display + // name as set on the Matrix server, not in bot.toml. + let rest = strip_bot_mention("Timmy ⚡️ status", "Timmy", "@timmy:homeserver.local"); + assert_eq!(rest, "status"); + } + // -- drain_complete_paragraphs ------------------------------------------ #[test]