From 10be86587aafa9ecd3b169c1bd62ed82e6c25a12 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 31 Mar 2026 10:14:53 +0000 Subject: [PATCH] storkit: merge 447_bug_element_tab_completion_display_name_breaks_bot_command_matching --- Cargo.lock | 97 +++++++++-------------------------------- server/src/chat/util.rs | 54 ++++++++++++++++++++--- 2 files changed, 70 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e4b6bd4..e3000ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "generic-array", ] @@ -286,15 +286,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" -dependencies = [ - "hybrid-array", -] - [[package]] name = "block-padding" version = "0.3.3" @@ -436,7 +427,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "inout", "zeroize", ] @@ -501,12 +492,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - [[package]] name = "const_panic" version = "0.2.15" @@ -620,15 +605,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" -dependencies = [ - "hybrid-array", -] - [[package]] name = "ctr" version = "0.9.2" @@ -647,7 +623,7 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest 0.10.7", + "digest", "fiat-crypto", "rustc_version", "serde", @@ -773,7 +749,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid 0.9.6", + "const-oid", "zeroize", ] @@ -846,22 +822,11 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", + "block-buffer", + "crypto-common", "subtle", ] -[[package]] -name = "digest" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" -dependencies = [ - "block-buffer 0.12.0", - "const-oid 0.10.2", - "crypto-common 0.2.1", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -906,7 +871,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.9", + "sha2", "subtle", "zeroize", ] @@ -1415,7 +1380,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1495,15 +1460,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hybrid-array" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a79f2aff40c18ab8615ddc5caa9eb5b96314aef18fe5823090f204ad988e813" -dependencies = [ - "typenum", -] - [[package]] name = "hyper" version = "1.8.1" @@ -2211,7 +2167,7 @@ dependencies = [ "serde", "serde_html_form", "serde_json", - "sha2 0.10.9", + "sha2", "tempfile", "thiserror 2.0.18", "tokio", @@ -2304,7 +2260,7 @@ dependencies = [ "ruma", "serde", "serde_json", - "sha2 0.10.9", + "sha2", "subtle", "thiserror 2.0.18", "time", @@ -2339,7 +2295,7 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_json", - "sha2 0.10.9", + "sha2", "thiserror 2.0.18", "tokio", "tracing", @@ -2393,7 +2349,7 @@ dependencies = [ "rmp-serde", "serde", "serde_json", - "sha2 0.10.9", + "sha2", "thiserror 2.0.18", "zeroize", ] @@ -2652,7 +2608,7 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", - "sha2 0.10.9", + "sha2", "thiserror 1.0.69", "url", ] @@ -2710,7 +2666,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", + "digest", "hmac", ] @@ -3557,7 +3513,7 @@ dependencies = [ "rand 0.8.5", "ruma-common", "serde_json", - "sha2 0.10.9", + "sha2", "thiserror 2.0.18", ] @@ -3605,7 +3561,7 @@ version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" dependencies = [ - "sha2 0.10.9", + "sha2", "walkdir", ] @@ -3930,7 +3886,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.10.7", + "digest", ] [[package]] @@ -3941,18 +3897,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "digest 0.11.2", + "digest", ] [[package]] @@ -4110,7 +4055,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "serde_yaml", - "sha2 0.11.0", + "sha2", "strip-ansi-escapes", "tempfile", "tokio", @@ -4745,7 +4690,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "subtle", ] @@ -4845,7 +4790,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "sha2 0.10.9", + "sha2", "subtle", "thiserror 2.0.18", "x25519-dalek", diff --git a/server/src/chat/util.rs b/server/src/chat/util.rs index f0e944d5..1170c7d4 100644 --- a/server/src/chat/util.rs +++ b/server/src/chat/util.rs @@ -46,29 +46,44 @@ pub fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { /// - `@bot_localpart:server.com rest` → `rest` /// - `@bot_localpart rest` → `rest` /// - `DisplayName rest` → `rest` +/// - `DisplayName: rest` → `rest` (Element tab-completion inserts a colon) +/// - `DisplayName, rest` → `rest` (Element tab-completion may insert a comma) +/// - `DisplayName ⚡️: rest` → `rest` (display name with emoji) pub fn strip_bot_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { let trimmed = message.trim(); // Try full Matrix user ID (e.g. "@timmy:homeserver.local") if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; + return strip_mention_separator(rest); } // Try @localpart (e.g. "@timmy") if let Some(localpart) = bot_user_id.split(':').next() && let Some(rest) = strip_prefix_ci(trimmed, localpart) { - return rest; + return strip_mention_separator(rest); } - // Try display name (e.g. "Timmy") + // Try display name (e.g. "Timmy" or "timmy ⚡️") if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; + return strip_mention_separator(rest); } trimmed } +/// Strip an optional Element tab-completion separator (`:` or `,`) and +/// surrounding whitespace from the start of text that follows a bot mention. +/// +/// 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. +fn strip_mention_separator(rest: &str) -> &str { + let rest = rest.trim_start(); + let rest = rest.strip_prefix([',', ':']).unwrap_or(rest); + rest.trim_start() +} + /// Returns `true` when `text` ends while inside an open fenced code block. /// /// A fenced code block opens and closes on lines that start with ` ``` ` @@ -334,7 +349,36 @@ mod tests { #[test] fn strip_mention_comma_after_name() { let rest = strip_bot_mention("@timmy, help", "Timmy", "@timmy:homeserver.local"); - assert_eq!(rest.trim().trim_start_matches(',').trim(), "help"); + assert_eq!(rest.trim(), "help"); + } + + #[test] + fn strip_mention_colon_separator_element_tab_completion() { + // Element tab-completes display names with a trailing ": " + let rest = strip_bot_mention( + "timmy ⚡️: ambient on", + "timmy ⚡️", + "@timmy:homeserver.local", + ); + assert_eq!(rest, "ambient on"); + } + + #[test] + fn strip_mention_emoji_display_name_no_separator() { + // Display name with emoji, no separator + let rest = strip_bot_mention( + "timmy ⚡️ ambient on", + "timmy ⚡️", + "@timmy:homeserver.local", + ); + assert_eq!(rest, "ambient on"); + } + + #[test] + fn strip_mention_colon_after_localpart() { + // Element may also produce "@timmy: help" + let rest = strip_bot_mention("@timmy: help", "Timmy", "@timmy:homeserver.local"); + assert_eq!(rest, "help"); } // -- drain_complete_paragraphs ------------------------------------------