From 40d9eecb1ee73a56868b35d62bbfce9ebbbc67de Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 27 Aug 2025 10:48:13 -0600 Subject: [PATCH 01/20] MSC: In-room bot commands Start off by incorporating MSC4332. --- proposals/images/4332-discord-example.png | Bin 0 -> 7654 bytes .../xxxx-simplified-in-room-bot-commands.md | 271 ++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 proposals/images/4332-discord-example.png create mode 100644 proposals/xxxx-simplified-in-room-bot-commands.md 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 diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md new file mode 100644 index 00000000000..bcccd559bff --- /dev/null +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -0,0 +1,271 @@ +# MSC0000: Simplified 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 {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", + ], + }, + + { + "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 argument descriptions above. + "m.text": [{ "body": "An example command with arguments" }], + }, + }, + ], +} +``` + +**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 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). **TODO**: change this if clients ultimately implement +`/ban@botname` support instead of showing `/ban` as-is. + +A client may show the arguments 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 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. + // 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`). + // 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 + // commands are sent this way. Bots may still need to unpack `body` when users + // 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 {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 + }, + }, +} +``` + +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 arguments can use quotes in the command syntax to surround user input. For +example, `"syntax": "gif \"{search}\""`. + +The following are the predefined `types` for an argument: + +- `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. +- `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:`) for an event ID. + +**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 + +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-like arguments like images, files, etc. + +- Some predefined validation on arguments, like a range for integers or + maximum/minimum length of strings. + +- Support for optional arguments. For example: `[roomId]`, if a room ID is not + required. + +## Potential issues + +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. + +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) From e4fb942240f98185097433cb6228e075a1bc62a2 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 16:06:30 +0000 Subject: [PATCH 02/20] Writeup key semantics --- .../xxxx-simplified-in-room-bot-commands.md | 153 +++++++++--------- 1 file changed, 72 insertions(+), 81 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index bcccd559bff..22d8935dac9 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -1,5 +1,12 @@ # MSC0000: Simplified in-room bot commands +> [!NOTE] +> +> This proposal is a simplification of +> [MSC4332: In-room bot commands](https://github.com/matrix-org/matrix-spec-proposals/pull/4332) +> that specifies only an object format for bot commands, and keeps syntax and +> command interaction implementation dependant. + 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 @@ -8,18 +15,15 @@ command. Many clients already have a concept of "slash commands" which are 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. +advertise available commands. 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. ## Proposal +### Command description + 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 @@ -33,6 +37,15 @@ as unjoined users: an empty string, "not_a_user_id", etc can't join rooms, but The `content` for such an event fits the following implied schema: +TODO: A single event for all commands is bad and we will probably easily exhaust +that with Draupnir. + +TODO: Variadic arguments should be replaced with an array schema. + +TODO: Enum should be replaced with union and literal types. + +TODO: number instead of integer type + ```jsonc { "sigil": "!", // Defaults to `!` if not specified. Clients can use this to show the user a consistent @@ -41,11 +54,11 @@ The `content` for such an event fits the following implied schema: // becomes `!botname` upon sending. "commands": [ { - "syntax": "botname {action} {roomId} {timeoutSeconds} {applyToPolicy} {userId...}", // `{words}` are positional arguments. - "arguments": [ - // First argument implied as `{action}` due to position. + "designator": ["ban"], + "parameters": [ { - "type": "enum", // can also be more types discussed later in this proposal + "key": "target_room", + "type": "room_id", "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 @@ -53,32 +66,27 @@ The `content` for such an event fits the following implied schema: // 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", - ], - }, - - { - "type": "room_id", - "description": { "m.text": [{ "body": "The room ID" }] }, }, { + "key": "timeout_seconds", "type": "integer", "description": { "m.text": [{ "body": "The timeout in seconds" }] }, }, { + "key": "apply_to_policy", "type": "boolean", "description": { "m.text": [{ "body": "Whether to apply this to the policy" }], }, + // This argument is not required + "required": false, }, // The final argument is variadic in this case, but doesn't need to be. { + "key": "target_users", "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. @@ -93,30 +101,32 @@ 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 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). **TODO**: change this if clients ultimately implement -`/ban@botname` support instead of showing `/ban` as-is. - A client may show the arguments and commands similar to Discord: ![](./images/4332-discord-example.png) +#### Invariants of command descriptions + +- A command description with parameters that have duplicate keys is invalid and + the command SHOULD be hidden by clients. + +- The position of parameter descriptions in the _parameters_ property that are + _required_ is significant and clients SHOULD use the same order when prompting + for arguments. + +- The position of parameter descriptions that are not _required_ is not + significant. + +### Command invocation + When the user sends the command, the client creates either an `m.room.message` -event with the following `content` shape: +or an `m.room.bot.command` event with the following `content` shape: ```jsonc { - // These fields would be replaced by MSC1767 Extensible Events in future. - "body": "!botname ban_and_suspend !room:example.org 42 true @alice:example.org @bob:example.org", // note that the syntax template is populated + // body is client supplied and may not match at all with the bot's fallback syntax. + // body may be omitted entirely in an `m.room.bot.command`. + "body": "@bot:example.org ban !room:example.org 42 true @alice:example.org @bob:example.org", "msgtype": "m.text", // Mentions should always be added, to lower the chances of command conflicts. @@ -132,23 +142,19 @@ event with the following `content` shape: // commands are sent this way. Bots may still need to unpack `body` when users // 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 {action} {roomId} {timeoutSeconds} {applyToPolicy} {userId...}", + "designator": ["ban"], "arguments": { // These are just the arguments and their user-supplied values. - "action": "ban_and_suspend", // enums have a value type of string - "roomId": { + "room_id": { // Room IDs are special because they can carry routing information too. + // Object types have a type specifier. "id": "!room:example.org", "via": ["second.example.org"], // Optional, but recommended. + "type": "room_id", }, - "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 + "timeout_seconds": 42, // integers and booleans use appropriate value types (converted from (probably) strings) + "apply_to_policy": true, // tip: clients can convert user input like "yes" to booleans + "users": ["@alice:example.org", "@bob:example.org"], }, }, } @@ -156,6 +162,10 @@ event with the following `content` shape: Bots can then respond however they normally would to the command input. +TODO: I don't know if this text below about conflicting commands is even +relevant. Especially if we are going to make the descriptions go in one event +each and have a `state_key` of `hmac_sha256(mxid, ...designator_parts)` + 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 @@ -165,17 +175,17 @@ 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 arguments can use quotes in the command syntax to surround user input. For -example, `"syntax": "gif \"{search}\""`. +### Type schema + +TODO: distinction of room_id, alias, event AND permalink seems like a disaster, +each of these should be permalinks. And permalinks should be provided in object +format like the room_id was. The following are the predefined `types` for an argument: - `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. @@ -191,10 +201,6 @@ The following are the predefined `types` for an argument: [permalink URI](https://spec.matrix.org/v1.15/appendices/#uris) (either `matrix.to` or `matrix:`) for an event ID. -**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 @@ -217,36 +223,21 @@ The following extensions/features are best considered by future MSCs: 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-like arguments like images, files, etc. - Some predefined validation on arguments, like a range for integers or maximum/minimum length of strings. -- Support for optional arguments. For example: `[roomId]`, if a room ID is not - required. - ## Potential issues -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. - -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. +- Using state events limits a bot's ability to advertise commands if it isn't + given power to do so. ## 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. +- 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. Sticky events maybe? ## Security considerations @@ -259,8 +250,8 @@ 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`. +`org.matrix.msc0000.commands` in place of `m.bot.commands` and +`org.matrix.msc0000.command` in place of `m.bot.command`. ## Dependencies From a8e7f2a1b16e548e45289586a3c7c56dfee23455 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 16:24:14 +0000 Subject: [PATCH 03/20] JSON-RPC similarity acknowledgement --- .../xxxx-simplified-in-room-bot-commands.md | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index 22d8935dac9..abd79557243 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -4,8 +4,12 @@ > > This proposal is a simplification of > [MSC4332: In-room bot commands](https://github.com/matrix-org/matrix-spec-proposals/pull/4332) -> that specifies only an object format for bot commands, and keeps syntax and -> command interaction implementation dependant. +> that specifies only an object format for bot commands, to be embedded within +> message event content. The proposal does not make any impositions on the +> textual command syntax used by bots or clients. To make use of the MSC, +> clients and bots will have to implement a simple parser for the proposal's +> command invocation protocol, which is similar in nature to +> [JSON-RPC](https://www.jsonrpc.org/specification). 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 @@ -166,14 +170,15 @@ TODO: I don't know if this text below about conflicting commands is even relevant. Especially if we are going to make the descriptions go in one event each and have a `state_key` of `hmac_sha256(mxid, ...designator_parts)` -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. +Clients SHOULD be aware that some bots may attempt to create conflicts with the +client's built-in commands (such as `/myroomnick`) or the commands of 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. ### Type schema @@ -210,6 +215,9 @@ example, accepting a room permalink for a `room_id` type, or "yes" in place of The following extensions/features are best considered by future MSCs: +- A standardised command response similar to JSON-RPC response object. This + would also enable dynamic and arbitrary prompt flow. + - 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 From 3c5786932a15540c992093c75ebbacc664435a29 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 16:43:35 +0000 Subject: [PATCH 04/20] Use multiple state events for command descriptions. --- .../xxxx-simplified-in-room-bot-commands.md | 122 ++++++++---------- 1 file changed, 56 insertions(+), 66 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index abd79557243..cf3b9e1fecd 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -28,80 +28,70 @@ this MSC) will need to rely on the existing `!botname help` convention. ### Command description -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.) +A new state event type is introduced: `m.bot.command_description`. When +presenting command options to users, clients SHOULD use this event to suggest +commands, scoped by the `sender` of the description. The `content` for such an event fits the following implied schema: -TODO: A single event for all commands is bad and we will probably easily exhaust -that with Draupnir. - TODO: Variadic arguments should be replaced with an array schema. TODO: Enum should be replaced with union and literal types. TODO: number instead of integer type -```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": [ - { - "designator": ["ban"], - "parameters": [ - { - "key": "target_room", - "type": "room_id", - "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" }], - }, - }, +TODO: enabled_commands state event? - { - "key": "timeout_seconds", - "type": "integer", - "description": { "m.text": [{ "body": "The timeout in seconds" }] }, - }, +```json +{ + "type": "m.bot.command_description", + "sender": "@draupnir:draupnir.space", + // derived from `sha256(designator.join('') + mxid)` + "state_key": "JBDLR6YMe+72yqsEMi/MVdTmjN3ynPThMz+M7QLATZQ=", + "content": { + "designator": ["ban"], + "parameters": [ + { + "key": "target_room", + "type": "room_id", + "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" }] + } + }, - { - "key": "apply_to_policy", - "type": "boolean", - "description": { - "m.text": [{ "body": "Whether to apply this to the policy" }], - }, - // This argument is not required - "required": false, - }, + { + "key": "timeout_seconds", + "type": "integer", + "description": { "m.text": [{ "body": "The timeout in seconds" }] } + }, - // The final argument is variadic in this case, but doesn't need to be. - { - "key": "target_users", - "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. + { + "key": "apply_to_policy", + "type": "boolean", + "description": { + "m.text": [{ "body": "Whether to apply this to the policy" }] }, - ], - "description": { - // We also use m.text here for the same reason as the argument descriptions above. - "m.text": [{ "body": "An example command with arguments" }], + // This argument is not required + "required": false }, - }, - ], + + // The final argument is variadic in this case, but doesn't need to be. + { + "key": "target_users", + "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 argument descriptions above. + "m.text": [{ "body": "An example command with arguments" }] + } + } } ``` @@ -121,6 +111,10 @@ A client may show the arguments and commands similar to Discord: - The position of parameter descriptions that are not _required_ is not significant. +- Command descriptions only specify commands for the sender of the + `m.bot.command_description`. If the `sender` is not currently joined to the + room, the command should be hidden. + ### Command invocation When the user sends the command, the client creates either an `m.room.message` @@ -166,10 +160,6 @@ or an `m.room.bot.command` event with the following `content` shape: Bots can then respond however they normally would to the command input. -TODO: I don't know if this text below about conflicting commands is even -relevant. Especially if we are going to make the descriptions go in one event -each and have a `state_key` of `hmac_sha256(mxid, ...designator_parts)` - Clients SHOULD be aware that some bots may attempt to create conflicts with the client's built-in commands (such as `/myroomnick`) or the commands of other bots. Where conflicts with built-in events exist, clients SHOULD NOT show the @@ -258,8 +248,8 @@ measures to minimize this confusion from happening. ## Unstable prefix While this proposal is not considered stable, implementations should use -`org.matrix.msc0000.commands` in place of `m.bot.commands` and -`org.matrix.msc0000.command` in place of `m.bot.command`. +`org.matrix.msc0000.command_description` in place of `m.bot.command_description` +and `org.matrix.msc0000.command` in place of `m.bot.command`. ## Dependencies From 6229934a78d044474786493242c0de0eddd4066c Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 17:01:42 +0000 Subject: [PATCH 05/20] Union and array types. --- .../xxxx-simplified-in-room-bot-commands.md | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index cf3b9e1fecd..fc9ce47a21c 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -34,11 +34,7 @@ commands, scoped by the `sender` of the description. The `content` for such an event fits the following implied schema: -TODO: Variadic arguments should be replaced with an array schema. - -TODO: Enum should be replaced with union and literal types. - -TODO: number instead of integer type +TODO: Literal types. TODO: enabled_commands state event? @@ -79,12 +75,10 @@ TODO: enabled_commands state event? "required": false }, - // The final argument is variadic in this case, but doesn't need to be. { "key": "target_users", - "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. + "type": { "kind": "array", "items": "user_id" }, + "description": { "m.text": [{ "body": "The user ID(s)" }] } } ], "description": { @@ -143,7 +137,7 @@ or an `m.room.bot.command` event with the following `content` shape: "designator": ["ban"], "arguments": { // These are just the arguments and their user-supplied values. - "room_id": { + "target_room": { // Room IDs are special because they can carry routing information too. // Object types have a type specifier. "id": "!room:example.org", @@ -152,7 +146,7 @@ or an `m.room.bot.command` event with the following `content` shape: }, "timeout_seconds": 42, // integers and booleans use appropriate value types (converted from (probably) strings) "apply_to_policy": true, // tip: clients can convert user input like "yes" to booleans - "users": ["@alice:example.org", "@bob:example.org"], + "target_users": ["@alice:example.org", "@bob:example.org"], }, }, } @@ -172,6 +166,13 @@ bot's commands to avoid users becoming confused. ### Type schema +> [!NOTE] +> +> The reason for providing an _integer_ type instead of a _number_ type to match +> the [JSON value](https://datatracker.ietf.org/doc/html/rfc8259#section-6) is +> because matrix.org's canonical JSON +> [does not support encoding floats](https://spec.matrix.org/v1.15/appendices/#canonical-json). + TODO: distinction of room_id, alias, event AND permalink seems like a disaster, each of these should be permalinks. And permalinks should be provided in object format like the room_id was. @@ -195,6 +196,12 @@ The following are the predefined `types` for an argument: - `permalink` - Must be a valid [permalink URI](https://spec.matrix.org/v1.15/appendices/#uris) (either `matrix.to` or `matrix:`) for an event ID. +- When an object is provided, the type is a composite type described by the + property `kind`: + - The `array` composite type specifies the type of the items with the `items` + property. + - The `union` composite type specifies the types of the variants with the + `variants` property. Which is an array of type schema. **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 From d5603641041e02c16da6094b21b59438f5bd67be Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 17:04:47 +0000 Subject: [PATCH 06/20] Literal types. --- proposals/xxxx-simplified-in-room-bot-commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index fc9ce47a21c..54e5d4ecd5d 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -34,8 +34,6 @@ commands, scoped by the `sender` of the description. The `content` for such an event fits the following implied schema: -TODO: Literal types. - TODO: enabled_commands state event? ```json @@ -202,6 +200,8 @@ The following are the predefined `types` for an argument: property. - The `union` composite type specifies the types of the variants with the `variants` property. Which is an array of type schema. + - The `literal` composite type specifies a literal value with the `value` + property and the type of the literal value with the `literal_type` property. **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 From 24f3ab7f493b842cd14eb45ae043beaa6990430b Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 17:05:35 +0000 Subject: [PATCH 07/20] Resolve enabled commands TODO. Not really needed when the command description state event is scoped by sender. --- proposals/xxxx-simplified-in-room-bot-commands.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index 54e5d4ecd5d..e705827a381 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -34,8 +34,6 @@ commands, scoped by the `sender` of the description. The `content` for such an event fits the following implied schema: -TODO: enabled_commands state event? - ```json { "type": "m.bot.command_description", From 01defb81ee73d6069fa4a419c7bb6d2c96a600df Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 18:12:10 +0000 Subject: [PATCH 08/20] More schema types. --- .../xxxx-simplified-in-room-bot-commands.md | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index e705827a381..7089904b48e 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -73,7 +73,7 @@ The `content` for such an event fits the following implied schema: { "key": "target_users", - "type": { "kind": "array", "items": "user_id" }, + "type": { "schema_type": "array", "items": "user_id" }, "description": { "m.text": [{ "body": "The user ID(s)" }] } } ], @@ -169,37 +169,41 @@ bot's commands to avoid users becoming confused. > because matrix.org's canonical JSON > [does not support encoding floats](https://spec.matrix.org/v1.15/appendices/#canonical-json). -TODO: distinction of room_id, alias, event AND permalink seems like a disaster, -each of these should be permalinks. And permalinks should be provided in object -format like the room_id was. - -The following are the predefined `types` for an argument: +The following essential types are the predefined `types` for an argument: - `string` - An arbitrary string. - `integer` - An arbitrary whole number. May be negative or zero. - `boolean` - `true` or `false` literal. + +Constrained string types: + - `user_id` - Must be a valid [user ID](https://spec.matrix.org/v1.15/appendices/#user-identifiers) for the - room version. -- `room_id` - Must be a valid - [room ID](https://spec.matrix.org/v1.15/appendices/#room-ids). + room version encoded as a string. +- `server_name` - Must be a valid + [server name](https://spec.matrix.org/v1.15/appendices/#server-name). - `room_alias` - Must be a valid [room alias](https://spec.matrix.org/v1.15/appendices/#room-aliases). -- `event_id` - Must be a valid + +Room reference types: + +- `room_id` - An object with the property `room_id` which must be a valid + [room ID](https://spec.matrix.org/v1.15/appendices/#room-ids). The object + SHOULD also contain a `via` property that contains an array of server names + that the room can be joined via. +- `event_id` - An object with the same properties as `room_id` with the addition + of an `event_id` property that 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:`) for an event ID. -- When an object is provided, the type is a composite type described by the - property `kind`: - - The `array` composite type specifies the type of the items with the `items` - property. - - The `union` composite type specifies the types of the variants with the - `variants` property. Which is an array of type schema. - - The `literal` composite type specifies a literal value with the `value` - property and the type of the literal value with the `literal_type` property. + +When an object is provided as the type schema, the type is a derived from a +schema described by the property `schema_type`: + +- The `array` schema type specifies the type of the items with the `items` + property. +- The `union` schema type specifies the types of the variants with the + `variants` property. Which is an array of type schema. +- The `literal` schema type specifies a literal value with the `value` property + and the type of the literal value with the `literal_type` property. **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 From fa2ae365280360f79b7c5b1e98240538b31b252c Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 18:14:19 +0000 Subject: [PATCH 09/20] Profile of command description sender. --- proposals/xxxx-simplified-in-room-bot-commands.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index 7089904b48e..43284d861f0 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -252,7 +252,8 @@ 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. +measures to minimize this confusion from happening, and always showing the +profile of the sender that registered the command. ## Unstable prefix From 793c0ee6689bdbd9645fd3a02de2311872bddfa5 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 19:08:11 +0000 Subject: [PATCH 10/20] Write new background to the MSC and the problems we want to solve. Even though the MSC is simpler, we're more ambitious and able to do more with it. --- .../xxxx-simplified-in-room-bot-commands.md | 86 ++++++++++++++++--- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/xxxx-simplified-in-room-bot-commands.md index 43284d861f0..8da548cbb45 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/xxxx-simplified-in-room-bot-commands.md @@ -11,21 +11,72 @@ > command invocation protocol, which is similar in nature to > [JSON-RPC](https://www.jsonrpc.org/specification). -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. 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. +## Background + +Interaction with matrix bots today is done by sending shell-like command within +the body of a Matrix `m.room.message` e.g. +`!draupnir unban @example:example.com --invite --no-confirm`. + +This interface has the following problems: + +- Users unfamiliar with command line interfaces have to learn heavily by example + in order to start using bots, which may be providing essential functions + within a matrix room or community. + +- No feedback is provided within the clients if a user enters incorrect syntax, + an unexpected argument, or a value of an incorrect type. Additionally, bots + have to explain these failure modes to a range of users. + +- The `body` emitted in `m.room.message` content is treated as _plain text + fallback_ or markdown by most matrix clients, preferring to render + `org.matrix.custom.html`. This means there are a number of inconsistencies in + clients, particularly around the fallback for room and user pills, which can + complicate and limit argument parsing + https://github.com/the-draupnir-project/Draupnir/issues/131. + +- Long help menus that 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. + +- [_prompt_](https://github.com/the-draupnir-project/Draupnir?tab=readme-ov-file#prompt-ux) + and [_button_](https://core.telegram.org/api/bots/buttons) interfaces have to + be implemented with `m.reaction` events. + +- _partial commands_ have to be implemented with `m.reaction` + [prompts](https://github.com/the-draupnir-project/Draupnir?tab=readme-ov-file#prompt-ux) + and replies containing an arbitrary payload of suggestions or a default. This + also leads to noisy timeline views when there are multiple partial steps to + entering a complete command (e.g. `!draupnir ban @spam:example.com`: + [demo](https://github.com/the-draupnir-project/Draupnir/blob/main/docs/ban-command-prompt.gif)). ## Proposal +This proposal introduces the following in order to solve the above problems: + +- _command description_ state events that can be used by a bot to communicate to + clients the parameters of the command and the types of arguments that those + parameters can accept. + +- A JSON representation of a command and the user supplied arguments, which a + client can send to the bot within a matrix event. This is a similar idea to a + JSON-RPC request object. + +- While a response format is not specified in this MSC, the MSC is designed to + be forward compatible with a follow up MSC for _partial commands_ and + _prompts_. + +The proposal makes no attempt to specify the syntax of commands within an +`m.room.message` body or describe the syntax of those commands. This is +specifically because when +[MSC4332 tried to do this](https://github.com/matrix-org/matrix-spec-proposals/pull/4332/files#r2313755345) +it was not found to be easy to do in a backwards compatible way and would have +required some existing bots to make changes to their parsers. Additionally the +argument and overloading semantics in MSC4332 make _partial commands_ and +_prompts_ difficult to design. + ### Command description A new state event type is introduced: `m.bot.command_description`. When @@ -246,6 +297,17 @@ The following extensions/features are best considered by future MSCs: fills a gap until proposals which solve the problem space more completely are written and proven by implementation. Sticky events maybe? +- [MSC4332](https://github.com/matrix-org/matrix-spec-proposals/pull/4332). As + stated throughout this proposal, MSC4332 makes a number of complicated + considerations for command syntax that are not backwards compatible for all + bots. In addition the semantics for variadic and positional arguments as they + appear in the MSC make it difficult to implement _options_, _prompts_ and + _partial commands_ + https://github.com/matrix-org/matrix-spec-proposals/pull/4332#discussion_r2313755345. + This MSC uses a JSON object to supply arguments to a command and provides + union and array types that can be used to implement variadic arguments and + various styles of options. + ## Security considerations Mentioned in the proposal, clients should be explicitly aware that bots may try From 359359130a482e5763767c507455003a3c3bba9a Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 24 Dec 2025 19:19:18 +0000 Subject: [PATCH 11/20] MSC4391 --- ...discord-example.png => 4391-discord-example.png} | Bin ...s.md => 4391-simplified-in-room-bot-commands.md} | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename proposals/{images/4332-discord-example.png => 4391-discord-example.png} (100%) rename proposals/{xxxx-simplified-in-room-bot-commands.md => 4391-simplified-in-room-bot-commands.md} (98%) diff --git a/proposals/images/4332-discord-example.png b/proposals/4391-discord-example.png similarity index 100% rename from proposals/images/4332-discord-example.png rename to proposals/4391-discord-example.png diff --git a/proposals/xxxx-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md similarity index 98% rename from proposals/xxxx-simplified-in-room-bot-commands.md rename to proposals/4391-simplified-in-room-bot-commands.md index 8da548cbb45..ebb129ade7f 100644 --- a/proposals/xxxx-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -1,4 +1,4 @@ -# MSC0000: Simplified in-room bot commands +# MSC4391: Simplified in-room bot commands > [!NOTE] > @@ -320,8 +320,8 @@ profile of the sender that registered the command. ## Unstable prefix While this proposal is not considered stable, implementations should use -`org.matrix.msc0000.command_description` in place of `m.bot.command_description` -and `org.matrix.msc0000.command` in place of `m.bot.command`. +`org.matrix.msc4391.command_description` in place of `m.bot.command_description` +and `org.matrix.msc4391.command` in place of `m.bot.command`. ## Dependencies From b7bc11cd3eba6036d5922adcb865968ef2863aa0 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Mon, 5 Jan 2026 16:31:01 +0000 Subject: [PATCH 12/20] designator -> command. https://github.com/matrix-org/matrix-spec-proposals/pull/4391#discussion_r2646280698 Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index ebb129ade7f..d1572c431f2 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -89,10 +89,11 @@ The `content` for such an event fits the following implied schema: { "type": "m.bot.command_description", "sender": "@draupnir:draupnir.space", - // derived from `sha256(designator.join('') + mxid)` + // derived from `sha256(command + mxid)` "state_key": "JBDLR6YMe+72yqsEMi/MVdTmjN3ynPThMz+M7QLATZQ=", "content": { - "designator": ["ban"], + // space separated string for commands such as "rooms add" + "command": "ban", "parameters": [ { "key": "target_room", @@ -181,7 +182,7 @@ or an `m.room.bot.command` event with the following `content` shape: // commands are sent this way. Bots may still need to unpack `body` when users // send commands manually or without client support. "m.bot.command": { - "designator": ["ban"], + "command": "ban", "arguments": { // These are just the arguments and their user-supplied values. "target_room": { From 07e45e6407c69bda5e4edf861e78db13aab02778 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Mon, 5 Jan 2026 16:43:15 +0000 Subject: [PATCH 13/20] Sounder reasoning about fallback. https://github.com/matrix-org/matrix-spec-proposals/pull/4391#discussion_r2646197140 Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index d1572c431f2..7601e57a819 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -70,12 +70,15 @@ This proposal introduces the following in order to solve the above problems: The proposal makes no attempt to specify the syntax of commands within an `m.room.message` body or describe the syntax of those commands. This is -specifically because when -[MSC4332 tried to do this](https://github.com/matrix-org/matrix-spec-proposals/pull/4332/files#r2313755345) -it was not found to be easy to do in a backwards compatible way and would have -required some existing bots to make changes to their parsers. Additionally the -argument and overloading semantics in MSC4332 make _partial commands_ and -_prompts_ difficult to design. +specifically to avoid the fallback version of the command becoming the source of +truth over the JSON representation, which was previously a source of +[compatibility issues](https://github.com/matrix-org/matrix-spec-proposals/pull/4332/files#r2313755345) +in [MSC4332](https://github.com/matrix-org/matrix-spec-proposals/pull/4332). +Advanced bots would be required some to make changes to their parsers. +Additionally the argument and overloading semantics in MSC4332 make _partial +commands_ and _prompts_ difficult to design. However, this proposal may be +extended by another proposal to specify a fallback derived from the JSON +representation that may work for many simple bots. ### Command description From e0e4f21527453e16f0ba6013ed93a5f957b0423d Mon Sep 17 00:00:00 2001 From: gnuxie Date: Mon, 5 Jan 2026 16:49:41 +0000 Subject: [PATCH 14/20] Rename parameter description "type" prop to "schema". Enable ease of parsing https://github.com/matrix-org/matrix-spec-proposals/pull/4391#discussion_r2646287889 Co-authored-by: Tulir Asokan --- .../4391-simplified-in-room-bot-commands.md | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index 7601e57a819..68226d22816 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -88,7 +88,7 @@ commands, scoped by the `sender` of the description. The `content` for such an event fits the following implied schema: -```json +```jsonc { "type": "m.bot.command_description", "sender": "@draupnir:draupnir.space", @@ -100,43 +100,46 @@ The `content` for such an event fits the following implied schema: "parameters": [ { "key": "target_room", - "type": "room_id", + "schema": { "schema_type": "primitive", "type": "room_id" }, "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" }] - } + "m.text": [{ "body": "The room ID" }], + }, }, { "key": "timeout_seconds", - "type": "integer", - "description": { "m.text": [{ "body": "The timeout in seconds" }] } + "schema": { "schema_type": "primitive", "type": "integer" }, + "description": { "m.text": [{ "body": "The timeout in seconds" }] }, }, { "key": "apply_to_policy", - "type": "boolean", + "schema": { "schema_type": "primitive", "type": "boolean" }, "description": { - "m.text": [{ "body": "Whether to apply this to the policy" }] + "m.text": [{ "body": "Whether to apply this to the policy" }], }, // This argument is not required - "required": false + "required": false, }, { "key": "target_users", - "type": { "schema_type": "array", "items": "user_id" }, - "description": { "m.text": [{ "body": "The user ID(s)" }] } - } + "schema": { + "schema_type": "array", + "items": { "schema_type": "primitive", "type": "user_id" }, + }, + "description": { "m.text": [{ "body": "The user ID(s)" }] }, + }, ], "description": { // We also use m.text here for the same reason as the argument descriptions above. - "m.text": [{ "body": "An example command with arguments" }] - } - } + "m.text": [{ "body": "An example command with arguments" }], + }, + }, } ``` From 5c4b68e85566e5b7f71f5f612340400558a77eef Mon Sep 17 00:00:00 2001 From: gnuxie Date: Mon, 5 Jan 2026 16:55:02 +0000 Subject: [PATCH 15/20] Rename required -> optional. https://github.com/matrix-org/matrix-spec-proposals/pull/4391#discussion_r2646313869 Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index 68226d22816..f8c9050f883 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -123,7 +123,7 @@ The `content` for such an event fits the following implied schema: "m.text": [{ "body": "Whether to apply this to the policy" }], }, // This argument is not required - "required": false, + "optional": true, }, { From ee43dd829999deee85ce0a57532e18b5234948db Mon Sep 17 00:00:00 2001 From: gnuxie Date: Mon, 5 Jan 2026 17:39:05 +0000 Subject: [PATCH 16/20] Restrict unions and arrays. https://github.com/matrix-org/matrix-spec-proposals/pull/4391#discussion_r2646300409 Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index f8c9050f883..43d8c5599e3 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -257,9 +257,17 @@ When an object is provided as the type schema, the type is a derived from a schema described by the property `schema_type`: - The `array` schema type specifies the type of the items with the `items` - property. + property. The following additional restrictions apply to `array`: + - Arrays can only appear at the top level, `items` can only be a schema type + of `union`, `primitive`, and `literal`. - The `union` schema type specifies the types of the variants with the - `variants` property. Which is an array of type schema. + `variants` property. Which is an array of type schema. The following + restrictions apply to unions: + - Outside of the top-level, only an array of unions is permitted. + - Nested unions are not permitted as they are unnecessary and should be + flatted. + - A union of arrays is not permitted. + - Therefore `variants` can only be a schema type of `primitive` or `literal`. - The `literal` schema type specifies a literal value with the `value` property and the type of the literal value with the `literal_type` property. From 40486d0d6858cd1bca8ef2b1faca8120779a02ed Mon Sep 17 00:00:00 2001 From: gnuxie Date: Thu, 8 Jan 2026 14:55:37 +0000 Subject: [PATCH 17/20] Resolve array schema in the middle. Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index 43d8c5599e3..d0a61fb05ef 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -11,6 +11,13 @@ > command invocation protocol, which is similar in nature to > [JSON-RPC](https://www.jsonrpc.org/specification). +> [!NOTE] +> +> While this MSC expects and anticipates clients will start with text parsing of +> commands, for clients to provide their own syntax, and that TUI clients will +> likely always use text parsing. The MSC is designed specifically so that it is +> possible to support clients that will only provide a full command UI. + ## Background Interaction with matrix bots today is done by sending shell-like command within @@ -163,6 +170,12 @@ A client may show the arguments and commands similar to Discord: `m.bot.command_description`. If the `sender` is not currently joined to the room, the command should be hidden. +- Parameters with an _array_ schema may appear in any place, not just at the end + of the parameter list. This is to provide a ubiquitous way of providing + multiple arguments. Clients that parse command text may need to provide + special syntax for lists or multiple arguments in order to support _required_ + parameters that have an array schema. + ### Command invocation When the user sends the command, the client creates either an `m.room.message` From d529b165692d876a684d7e0ade5655e88271bbf1 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Thu, 8 Jan 2026 15:12:20 +0000 Subject: [PATCH 18/20] Deduplicating command descriptions explicitly. Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index d0a61fb05ef..511a4a31be9 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -176,6 +176,11 @@ A client may show the arguments and commands similar to Discord: special syntax for lists or multiple arguments in order to support _required_ parameters that have an array schema. +- The state_key MUST be a padded base64 encoded SHA256 digest of the `command` + property of the command description AND the mxid of the sender. This is in + order to ensure that command descriptions are deduplicated and conflict free + between different bots. + ### Command invocation When the user sends the command, the client creates either an `m.room.message` From bb6c471e016ada1b4c6725928ff3ba48ec71dde6 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Thu, 8 Jan 2026 17:06:23 +0000 Subject: [PATCH 19/20] Restrict literals. Co-authored-by: Tulir Asokan --- proposals/4391-simplified-in-room-bot-commands.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index 511a4a31be9..aa5b2c88acf 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -288,6 +288,8 @@ schema described by the property `schema_type`: - Therefore `variants` can only be a schema type of `primitive` or `literal`. - The `literal` schema type specifies a literal value with the `value` property and the type of the literal value with the `literal_type` property. + - For simplicity only `boolean`, `integer`, and `string` may be provided for + the `value` property. This may be extended in future if need is found. **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 From 8cefeb8dec0b81894ae9570c93fe41bc378ca0cc Mon Sep 17 00:00:00 2001 From: gnuxie Date: Thu, 8 Jan 2026 17:10:21 +0000 Subject: [PATCH 20/20] Note about schema validation being optional for clients. --- proposals/4391-simplified-in-room-bot-commands.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/proposals/4391-simplified-in-room-bot-commands.md b/proposals/4391-simplified-in-room-bot-commands.md index aa5b2c88acf..8ae74d1e646 100644 --- a/proposals/4391-simplified-in-room-bot-commands.md +++ b/proposals/4391-simplified-in-room-bot-commands.md @@ -245,6 +245,11 @@ bot's commands to avoid users becoming confused. > because matrix.org's canonical JSON > [does not support encoding floats](https://spec.matrix.org/v1.15/appendices/#canonical-json). +> [!NOTE] +> +> Bots SHOULD NOT rely on clients to validate or build input with respect to the +> input and should perform their own parsing of command arguments. + The following essential types are the predefined `types` for an argument: - `string` - An arbitrary string. @@ -291,10 +296,12 @@ schema described by the property `schema_type`: - For simplicity only `boolean`, `integer`, and `string` may be provided for the `value` property. This may be extended in future if need is found. -**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`. +> [!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