From 0624eca2c4c6b0878c3aca0482fda43f48879053 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 27 Aug 2025 10:48:13 -0600 Subject: [PATCH 1/5] MSC: In-room bot commands --- proposals/4332-in-room-bot-commands.md | 200 ++++++++++++++++++++++ proposals/images/4332-discord-example.png | Bin 0 -> 7654 bytes 2 files changed, 200 insertions(+) create mode 100644 proposals/4332-in-room-bot-commands.md create mode 100644 proposals/images/4332-discord-example.png diff --git a/proposals/4332-in-room-bot-commands.md b/proposals/4332-in-room-bot-commands.md new file mode 100644 index 00000000000..f1812552824 --- /dev/null +++ b/proposals/4332-in-room-bot-commands.md @@ -0,0 +1,200 @@ +# MSC4332: In-room bot commands + +Many bots on Matrix have a command interface consisting of `!botname `, and have a pretty +long help menus which can make it difficult to find the right command. Many clients already have a +concept of "slash commands" which are [desirable to reuse](https://github.com/matrix-org/matrix-spec/issues/93) +and [come up occasionally](https://github.com/matrix-org/matrix-spec/issues/2170) - finding a way to +populate this feature with bot-specific details is beneficial. + +This proposal suggests that bots maintain a state event in the rooms it joins to advertise available +commands, and their syntax. This does require that bots need power levels to maintain their state +event though, so bots without such power level (or are looking to maintain backwards compatibility +with clients which don't support this MSC) will need to rely on the existing `!botname help` convention. + +*Note*: There's a good chance that this MSC is over-engineered for what it *actually* needs to accomplish. +It may get broken down into smaller MSCs as use cases materialize around it. + + +## Proposal + +A new state event type is introduced: `m.bot.commands`. The state key is the bot's own user ID to +prevent other users/bots from changing it (this is a feature of rooms: see [`state_key`](https://spec.matrix.org/v1.15/client-server-api/#room-event-format)). + +When presenting command options to users, clients SHOULD use this event to suggest per-bot commands +too, unless the user ID implied by the `state_key` is not joined to the room. (Note that this means +"invalid" state keys get treated as unjoined users: an empty string, "not_a_user_id", etc can't +join rooms, but `@bot:example.org` can.) + +The `content` for such an event fits the following implied schema: + +```jsonc +{ + "sigil": "!", // Defaults to `!` if not specified. Clients can use this to show the user a consistent + // experience in the form of slash commands, but ultimately send the command as a + // sigil-prefixed string to the room. Eg: the UI might say `/botname`, but the command + // becomes `!botname` upon sending. + "commands": [ + { + "syntax": "botname example {roomId} {userId}", // `{words}` are variables. + "variables": { + // Variables use m.text from MSC1767 Extensible Events to later support MSC3554-style translations. + // See https://spec.matrix.org/v1.15/client-server-api/#mroomtopic_topiccontentblock + // See https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1767-extensible-events.md + // See https://github.com/matrix-org/matrix-spec-proposals/pull/3554 + "roomId": { + "m.text": [{"body": "The room ID"}] + }, + "userId": { + "m.text": [{"body": "The user ID"}] + } + }, + "description": { + // We also use m.text here for the same reason as the variables. + "m.text": [{"body": "An example command with variables"}] + } + } + ] +} +``` + +**Note**: It's not currently proposed that a command can include a literal `{` in its syntax. A future +iteration of this MSC may introduce an escape sequence, but for now the text between an opening curly +brace and closing curly brace is considered the variable name. This includes more curly braces: `{{var}}` +becomes the variable `{var}` with another `}` tacked on. `{var with spaces}` becomes `var with spaces`. + +**Reminder**: A convention among Matrix bots is to use their project name as the prefix for commands. +It's expected that this prefix goes into the `syntax` rather than `sigil` to avoid conflicts with +built-in client commands (discussed later in this proposal). + +A client may show the variables and commands similar to Discord: + +![](./images/4332-discord-example.png) + +When the user sends the command, the client creates either an `m.room.message` event with the following +`content` shape: + +```jsonc +{ + // These fields would be replaced by MSC1767 Extensible Events in future. + "body": "!botname example !room:example.org @alice:example.org", // note that the syntax template is populated + "msgtype": "m.text", + + // Mentions should always be added, to lower the chances of command conflicts. + // Bots SHOULD look for mentions where possible to avoid accidental activations. + "m.mentions": { + "user_ids": ["@bot:example.org"] // should be a single element array, containing the bot's user ID + // from the `m.bot.commands` state event's `state_key` (or `sender`). + }, + + // This is a new content block so bots don't *need* to do string unpacking when + // commands are sent this way. Bots may still need to unpack `body` when users + // send commands without client support/manually. + "m.bot.command": { + // The syntax is effectively used as a "command ID", so bots can identify which + // command the client is using without needing to track arbitrary strings. Whether + // the bot unpacks this string is an implementation detail for the bot. + "syntax": "botname example {roomId} {userId}", + "variables": { + // These are just the variables and their user-supplied values. + "roomId": "!room:example.org", + "userId": "@alice:example.org" + } + } +} +``` + +Bots can then respond however they normally would to the command input. + +Clients SHOULD be aware that some bots may attempt to create conflicts with built-in commands or other +bots. Where conflicts with built-in events exist, clients SHOULD NOT show the bot's option to the user. +Where conflicts with other bots exist, clients SHOULD show the bot's name/user ID in the autocomplete +text. For example, "@Giphy /gif {search}". Clients MAY wish to always disambiguate commands like +this to avoid future conflicts with built-in commands. From an implementation perspective, clients +might cause their built-in commands to always take precedence over any bot's commands to avoid users +becoming confused. + +**Tip**: Bots which don't use `m.bot.command` and need to support spaces in their variables can use +quotes in the command syntax to surround user input. For example, `"syntax": "gif \"{search}\""`. + +To aid consistency, clients SHOULD apply the described grammar validation to the following predefined +variables: + +* `userId` - Must be a valid [user ID](https://spec.matrix.org/v1.15/appendices/#user-identifiers) for + the room version. +* `roomId` - Must be a valid [room ID](https://spec.matrix.org/v1.15/appendices/#room-ids). +* `roomAlias` - Must be a valid [room alias](https://spec.matrix.org/v1.15/appendices/#room-aliases). +* `eventId` - Must be a valid [event ID](https://spec.matrix.org/v1.15/appendices/#event-ids). +* `serverName` - Must be a valid [server name](https://spec.matrix.org/v1.15/appendices/#server-name). +* `permalink` - Must be a valid [permalink URI](https://spec.matrix.org/v1.15/appendices/#uris) + (either `matrix.to` or `matrix:`). + +**Note**: For clarity, the above variables do not have to point at a room/user/server/etc that is known +to the client. They just need to *look* valid per the grammar. + + +## Extensions + +The following extensions/features are best considered by future MSCs: + +* Specifying a minimum power level required to send a command, to hint to users that a command may + be unavailable to them. This wouldn't be enforced by auth rules, but clients can stop a lot of the + accidental usage if they know the power level the caller must have. + +* Specifying a non-`m.room.message` event template to send instead. This could be useful if the bot + wants to minimize "visible" traffic in the room or has custom event types it wants to use. In future, + being able to specify extensible event content blocks which should be added to the resulting event + may be a better option. In either case, bots should not be able to cause users to send state events + to prevent bots from tricking users into changing power levels, join rules, etc. + + Such an event template could be used to quickly add features to clients ahead of mainline releases. + For example, a client which doesn't yet have support for polls may suggest adding a "poll bot" that + sets its command event template to [an `m.poll.start` event](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3381-polls.md). + +* Support for non-text variables like images/files, booleans, integers, etc. + +* Support for regex(-ish) validation on variables. + +* Support for repeated variables. For example: `{userId...}` to specify multiple user IDs. + +* Support for optional variables. For example: `[userId]`, if a user ID is not required. + + +## Potential issues + +Mentioned in the proposal, the lack of variable escaping isn't great. + +Using state events limits a bot's ability to advertise commands if it isn't given power to do so. + +The lack of formal "command IDs" isn't great - there's no clear reason to include them at this stage, +however. + +There are probably more potential issues this MSC needs to consider. + + +## Alternatives + +Not using state events would work, but can be tricky to manage. This proposal fills a gap until proposals +which solve the problem space more completely are written and proven by implementation. + + +## Security considerations + +Mentioned in the proposal, clients should be explicitly aware that bots may try to create confusion +for users and override built-in commands or another bot's commands. For example, a bot may advertise +a `myroomnick` command which leads to the client's functionality not working as expected. Clients +should be taking measures to minimize this confusion from happening. + + +## Unstable prefix + +While this proposal is not considered stable, implementations should use `org.matrix.msc4332.commands` +in place of `m.bot.commands` and `org.matrix.msc4332.command` in place of `m.bot.command`. + + +## Dependencies + +This proposal has no direct dependencies, but benefits more strongly from the following Extensible +Events MSCs: + +* [MSC1767 (accepted)](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1767-extensible-events.md) +* [MSC3554: Translatable Text](https://github.com/matrix-org/matrix-spec-proposals/pull/3554) diff --git a/proposals/images/4332-discord-example.png b/proposals/images/4332-discord-example.png new file mode 100644 index 0000000000000000000000000000000000000000..bc7074f95d3ad806aef6d2b9fcb5e75c6b4bbb56 GIT binary patch literal 7654 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 Okj`y=M3Kh*7yku(PJi71 literal 0 HcmV?d00001 From f9583b58bacc65c7deeaa04172662f19fdb4f002 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Aug 2025 13:39:43 -0600 Subject: [PATCH 2/5] Add types and variadic arguments Also, call variables "arguments" instead, given the higher semantic meaning. --- proposals/4332-in-room-bot-commands.md | 117 ++++++++++++++++--------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/proposals/4332-in-room-bot-commands.md b/proposals/4332-in-room-bot-commands.md index f1812552824..c7ddf489dd6 100644 --- a/proposals/4332-in-room-bot-commands.md +++ b/proposals/4332-in-room-bot-commands.md @@ -35,22 +35,49 @@ The `content` for such an event fits the following implied schema: // becomes `!botname` upon sending. "commands": [ { - "syntax": "botname example {roomId} {userId}", // `{words}` are variables. - "variables": { - // Variables use m.text from MSC1767 Extensible Events to later support MSC3554-style translations. - // See https://spec.matrix.org/v1.15/client-server-api/#mroomtopic_topiccontentblock - // See https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1767-extensible-events.md - // See https://github.com/matrix-org/matrix-spec-proposals/pull/3554 - "roomId": { - "m.text": [{"body": "The room ID"}] + "syntax": "botname {action} {roomId} {timeoutSeconds} {applyToPolicy} {userId...}", // `{words}` are positional arguments. + "arguments": [ + // First argument implied as `{action}` due to position. + { + "type": "enum", // can also be more types discussed later in this proposal + "description": { + // Descriptions use m.text from MSC1767 Extensible Events to later support MSC3554-style translations. + // See https://spec.matrix.org/v1.15/client-server-api/#mroomtopic_topiccontentblock + // See https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1767-extensible-events.md + // See https://github.com/matrix-org/matrix-spec-proposals/pull/3554 + "m.text": [{"body": "The room ID"}] + }, + "enum": [ // only required (and used) when type == enum. + "ban", + "ban_and_suspend" + ] }, - "userId": { - "m.text": [{"body": "The user ID"}] + + { + "type": "room_id", + "description": { "m.text": [{"body": "The room ID"}] } + }, + + { + "type": "integer", + "description": { "m.text": [{"body": "The timeout in seconds"}] }, + }, + + { + "type": "boolean", + "description": { "m.text": [{"body": "Whether to apply this to the policy"}] }, + }, + + // The final argument is variadic in this case, but doesn't need to be. + { + "type": "user_id", + "description": { "m.text": [{"body": "The user ID(s)"}] }, + "variadic": true // can only apply to the last argument. Default false when not supplied. } - }, + ], "description": { - // We also use m.text here for the same reason as the variables. - "m.text": [{"body": "An example command with variables"}] + // We also use m.text here for the same reason as the argument descriptions above. + "m.text": [{"body": "An example command with arguments"}] } } ] @@ -59,14 +86,15 @@ The `content` for such an event fits the following implied schema: **Note**: It's not currently proposed that a command can include a literal `{` in its syntax. A future iteration of this MSC may introduce an escape sequence, but for now the text between an opening curly -brace and closing curly brace is considered the variable name. This includes more curly braces: `{{var}}` -becomes the variable `{var}` with another `}` tacked on. `{var with spaces}` becomes `var with spaces`. +brace and closing curly brace is considered the argument name. This includes more curly braces: `{{var}}` +becomes the argument `{var}` with another `}` tacked on. `{var with spaces}` becomes `var with spaces`. **Reminder**: A convention among Matrix bots is to use their project name as the prefix for commands. It's expected that this prefix goes into the `syntax` rather than `sigil` to avoid conflicts with -built-in client commands (discussed later in this proposal). +built-in client commands (discussed later in this proposal). **TODO**: change this if clients ultimately +implement `/ban@botname` support instead of showing `/ban` as-is. -A client may show the variables and commands similar to Discord: +A client may show the arguments and commands similar to Discord: ![](./images/4332-discord-example.png) @@ -76,7 +104,7 @@ When the user sends the command, the client creates either an `m.room.message` e ```jsonc { // These fields would be replaced by MSC1767 Extensible Events in future. - "body": "!botname example !room:example.org @alice:example.org", // note that the syntax template is populated + "body": "!botname ban_and_suspend !room:example.org 42 true @alice:example.org @bob:example.org", // note that the syntax template is populated "msgtype": "m.text", // Mentions should always be added, to lower the chances of command conflicts. @@ -88,16 +116,24 @@ When the user sends the command, the client creates either an `m.room.message` e // This is a new content block so bots don't *need* to do string unpacking when // commands are sent this way. Bots may still need to unpack `body` when users - // send commands without client support/manually. + // send commands manually or without client support. "m.bot.command": { // The syntax is effectively used as a "command ID", so bots can identify which // command the client is using without needing to track arbitrary strings. Whether // the bot unpacks this string is an implementation detail for the bot. - "syntax": "botname example {roomId} {userId}", - "variables": { - // These are just the variables and their user-supplied values. - "roomId": "!room:example.org", - "userId": "@alice:example.org" + "syntax": "botname {action} {roomId} {timeoutSeconds} {applyToPolicy} {userId...}", + "arguments": { + // These are just the arguments and their user-supplied values. + "action": "ban_and_suspend", // enums have a value type of string + "roomId": { // Room IDs are special because they can carry routing information too. + "id": "!room:example.org", + "via": ["second.example.org"] // Optional, but recommended. + }, + "timeoutSeconds": 42, // integers and booleans use appropriate value types (converted from (probably) strings) + "applyToPolicy": true, // tip: clients can convert user input like "yes" to booleans + "userId": ["@alice:example.org", "@bob:example.org"] // variadic arguments have array value types + + // Note: all other types are represented as simple string values } } } @@ -113,22 +149,25 @@ this to avoid future conflicts with built-in commands. From an implementation pe might cause their built-in commands to always take precedence over any bot's commands to avoid users becoming confused. -**Tip**: Bots which don't use `m.bot.command` and need to support spaces in their variables can use +**Tip**: Bots which don't use `m.bot.command` and need to support spaces in their arguments can use quotes in the command syntax to surround user input. For example, `"syntax": "gif \"{search}\""`. -To aid consistency, clients SHOULD apply the described grammar validation to the following predefined -variables: +The following are the predefined `types` for an argument: -* `userId` - Must be a valid [user ID](https://spec.matrix.org/v1.15/appendices/#user-identifiers) for +* `string` - An arbitrary string. +* `integer` - An arbitrary whole number. May be negative or zero. +* `boolean` - `true` or `false` literal. +* `enum` - When paired with the `enum` options array, a string representing one of the options. +* `user_id` - Must be a valid [user ID](https://spec.matrix.org/v1.15/appendices/#user-identifiers) for the room version. -* `roomId` - Must be a valid [room ID](https://spec.matrix.org/v1.15/appendices/#room-ids). -* `roomAlias` - Must be a valid [room alias](https://spec.matrix.org/v1.15/appendices/#room-aliases). -* `eventId` - Must be a valid [event ID](https://spec.matrix.org/v1.15/appendices/#event-ids). -* `serverName` - Must be a valid [server name](https://spec.matrix.org/v1.15/appendices/#server-name). +* `room_id` - Must be a valid [room ID](https://spec.matrix.org/v1.15/appendices/#room-ids). +* `room_alias` - Must be a valid [room alias](https://spec.matrix.org/v1.15/appendices/#room-aliases). +* `event_id` - Must be a valid [event ID](https://spec.matrix.org/v1.15/appendices/#event-ids). +* `server_name` - Must be a valid [server name](https://spec.matrix.org/v1.15/appendices/#server-name). * `permalink` - Must be a valid [permalink URI](https://spec.matrix.org/v1.15/appendices/#uris) - (either `matrix.to` or `matrix:`). + (either `matrix.to` or `matrix:`) for an event ID. -**Note**: For clarity, the above variables do not have to point at a room/user/server/etc that is known +**Note**: For clarity, the above arguments do not have to point at a room/user/server/etc that is known to the client. They just need to *look* valid per the grammar. @@ -150,18 +189,16 @@ The following extensions/features are best considered by future MSCs: For example, a client which doesn't yet have support for polls may suggest adding a "poll bot" that sets its command event template to [an `m.poll.start` event](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3381-polls.md). -* Support for non-text variables like images/files, booleans, integers, etc. - -* Support for regex(-ish) validation on variables. +* Support for non-text-like arguments like images, files, etc. -* Support for repeated variables. For example: `{userId...}` to specify multiple user IDs. +* Some predefined validation on arguments, like a range for integers or maximum/minimum length of strings. -* Support for optional variables. For example: `[userId]`, if a user ID is not required. +* Support for optional arguments. For example: `[userId]`, if a user ID is not required. ## Potential issues -Mentioned in the proposal, the lack of variable escaping isn't great. +Mentioned in the proposal, the lack of argument escaping isn't great. Using state events limits a bot's ability to advertise commands if it isn't given power to do so. From ae933b6429df5ab512cae99ada817f09651cd9d6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Aug 2025 13:42:41 -0600 Subject: [PATCH 3/5] Fix argument references --- proposals/4332-in-room-bot-commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/4332-in-room-bot-commands.md b/proposals/4332-in-room-bot-commands.md index c7ddf489dd6..eab303d3e34 100644 --- a/proposals/4332-in-room-bot-commands.md +++ b/proposals/4332-in-room-bot-commands.md @@ -131,7 +131,7 @@ When the user sends the command, the client creates either an `m.room.message` e }, "timeoutSeconds": 42, // integers and booleans use appropriate value types (converted from (probably) strings) "applyToPolicy": true, // tip: clients can convert user input like "yes" to booleans - "userId": ["@alice:example.org", "@bob:example.org"] // variadic arguments have array value types + "userId...": ["@alice:example.org", "@bob:example.org"] // variadic arguments have array value types // Note: all other types are represented as simple string values } @@ -193,7 +193,7 @@ The following extensions/features are best considered by future MSCs: * Some predefined validation on arguments, like a range for integers or maximum/minimum length of strings. -* Support for optional arguments. For example: `[userId]`, if a user ID is not required. +* Support for optional arguments. For example: `[roomId]`, if a room ID is not required. ## Potential issues From 36db221fa75a0d224a644915740da4298bea99f8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Aug 2025 13:44:23 -0600 Subject: [PATCH 4/5] Mention that clients can accept broader inputs --- proposals/4332-in-room-bot-commands.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/4332-in-room-bot-commands.md b/proposals/4332-in-room-bot-commands.md index eab303d3e34..481c47ccdac 100644 --- a/proposals/4332-in-room-bot-commands.md +++ b/proposals/4332-in-room-bot-commands.md @@ -170,6 +170,10 @@ The following are the predefined `types` for an argument: **Note**: For clarity, the above arguments do not have to point at a room/user/server/etc that is known to the client. They just need to *look* valid per the grammar. +**Tip**: Clients can accept a wider variety of inputs for some types, provided they reduce them down +to the expected value types when sending the command. For example, accepting a room permalink for a +`room_id` type, or "yes" in place of `true` for a `boolean`. + ## Extensions From a730f65994d0b442194f8bc588fc5a6927747ac2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 29 Aug 2025 13:47:45 -0600 Subject: [PATCH 5/5] Clarify that `m.mentions` is for the bot --- proposals/4332-in-room-bot-commands.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/4332-in-room-bot-commands.md b/proposals/4332-in-room-bot-commands.md index 481c47ccdac..4c7088e71cb 100644 --- a/proposals/4332-in-room-bot-commands.md +++ b/proposals/4332-in-room-bot-commands.md @@ -112,6 +112,8 @@ When the user sends the command, the client creates either an `m.room.message` e "m.mentions": { "user_ids": ["@bot:example.org"] // should be a single element array, containing the bot's user ID // from the `m.bot.commands` state event's `state_key` (or `sender`). + // Note: doesn't include other users which may be referenced by the + // command being sent, such as via `user_id` arguments. }, // This is a new content block so bots don't *need* to do string unpacking when