-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MSC3006: Bot Interactions #3006
base: old_master
Are you sure you want to change the base?
Changes from all commits
dc16f23
5120830
02ec11f
a317bf3
bb666bb
d4e67df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,322 @@ | ||
# MSC3006: Bot Interactions | ||
|
||
Bots are currently only possible in IRC style. They are entirely text based and not interactive like, for example, Telegram bots. | ||
This is trying to solve that issue by using a similar backwards compatible way like the one in Telegram. | ||
|
||
This is highly inspired by https://core.telegram.org/bots#keyboards and is trying to allow similar types | ||
of keyboards for bots to allow more "fancy" bots while keeping full backwards compatibility. | ||
|
||
## Proposal | ||
|
||
The proposal is to have multiple interaction events. Which tell the client what kind of interactions the | ||
bot has and the client replies with a specific format on that. | ||
|
||
_Note: a deeper explanation of the events will be below the interaction flow_ | ||
|
||
### Interaction Flow | ||
|
||
1. The bot joins a room and sends a state event containing information about possible interactions: | ||
|
||
```json | ||
{ | ||
"type": "m.bot.interactions", | ||
"state_key": "@faq_bot:example.matrix", | ||
"content": { | ||
"command_prefix": "!", | ||
"interactions": [ | ||
{ | ||
"type": "m.interaction.command", | ||
"description": "Shows the help of the FAQ Bot", | ||
"name": "help" | ||
}, | ||
{ | ||
"type": "m.interaction.button", | ||
"description": "Search question", | ||
"name": "Search" | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
2. The clients shows a button to open the commands list and/or a button to open the custom keyboard | ||
|
||
3. The user clicks on for example the help command one. | ||
|
||
4. The client sends an event of type `m.room.message` and msgtype `m.text`. This allows us to have fallback for free while | ||
having typed commands when client and bot support it. | ||
|
||
```json | ||
{ | ||
"content": { | ||
"body": "!help", | ||
"msgtype": "m.text", | ||
"m.interaction.command": { | ||
"command": "help", | ||
"target": "@faq_bot:example.matrix" | ||
} | ||
}, | ||
"type": "m.room.message" | ||
} | ||
``` | ||
|
||
5. The Bot responds with a request for the client to display a separate custom keyboard for the next stage of | ||
this command | ||
|
||
```json | ||
{ | ||
"content": { | ||
"event_id": "$random_id123", | ||
"interactions": [ | ||
{ | ||
"type": "m.interaction.button", | ||
"description": "The Search command", | ||
"name": "Search" | ||
} | ||
] | ||
}, | ||
"type": "m.interaction.stage.start" | ||
} | ||
``` | ||
|
||
6. The bot now directly shows the new custom keyboard, indicating that the user currently is in a stage of the previous command. | ||
Additionally a way of exiting that stage should be available. | ||
|
||
7a. The user selects the exit way and the client now sends a stage end event and returns to normal chat operation. | ||
|
||
```json | ||
{ | ||
"content": { | ||
"event_id": "$random_id123" | ||
}, | ||
"type": "m.interaction.stage.end" | ||
} | ||
``` | ||
|
||
7b. If the user clicks the new button the client sends the command as in step 4 with additionally mentioning the `stage_id` under the | ||
`m.interaction.command` object. The bot after that can react either with a new stage or with the same `m.interaction.stage.end` | ||
event to indicate the end of a interaction flow. | ||
|
||
#### Flow as a graphic | ||
|
||
``` | ||
+----------------------------+ | ||
| | | ||
| User sends a command | | ||
| | | ||
+---------------|------------+ | ||
| | ||
| | ||
| | ||
| | ||
| | ||
| | ||
| | ||
+----------------v--------------+ | ||
| | | ||
| Bot instructs client | | ||
| to show a submenu of | +-------------------------------------------+ | ||
+------------------------>+ buttons. | User exits the command flow | | | ||
| | +----------------------------------------->+ Client sends a stage.end event. | | ||
| | Starting a new "stage" | | | | ||
| | | | This indicates a flow end to the bot. | | ||
| +----------------|----|---------+ | | | ||
| | | +-------------------------------------------+ | ||
| | | | ||
| | | | ||
| | | | ||
| | | | ||
| | | | ||
| | | | ||
| | | | ||
| | | +---------------------------------------------+ | ||
| | | Bot doesn't have another submenu | | | ||
| | +----------------------------------------->+ Bot sends a stage.end event. | | ||
| | | | | ||
| | | This indicated a flow end to the client. | | ||
| | | | | ||
| | +---------------------------------------------+ | ||
| User selects an option and bot | | ||
| has a new submenu. | | ||
| Another stage.start is sent by the bot | | ||
+-------------------------------------<----+ | ||
``` | ||
|
||
|
||
### Event Specification | ||
|
||
#### m.bot.interactions | ||
|
||
A state event defining the possible entry actions a bot has. It does not list any stage specific actions. | ||
It is used to allow clients to display a custom keyboard and a list of command based on the defined data. | ||
|
||
The `state_key` should be the mxid of the bot adding that event. | ||
|
||
The `command_prefix` is used to support fallback for clients not supporting the MSC and is used to generate a irc style | ||
bot message as fallback. (See "Fallback" section for Details). | ||
|
||
The interactions array defines all supported entry actions of a bot, Those can be either of type: `m.interaction.command` | ||
or `m.interaction.button`, | ||
|
||
Example: | ||
|
||
```json | ||
{ | ||
"type": "m.bot.interactions", | ||
"state_key": "@faq_bot:example.matrix", | ||
"content": { | ||
"command_prefix": "!", | ||
"interactions": [ | ||
{ | ||
"type": "m.interaction.command", | ||
"description": "Shows the help of the FAQ Bot", | ||
"name": "help" | ||
}, | ||
{ | ||
"type": "m.interaction.button", | ||
"description": "Search question", | ||
"name": "Search" | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
#### Interaction event types | ||
|
||
Both `m.interaction.command` and `m.interaction.button` are required to be an object consisting of: | ||
|
||
* Its `type` | ||
* A useful `description` which might be used to be displayed to the user | ||
* A `name` which is used for displaying in the UI. Be aware this is also used as lowercase for the fallback! | ||
|
||
#### `m.room.message` extension with `m.interaction.command` | ||
|
||
Each message that was sent using selecting either a button or command must have an additional object with the key `m.interaction.command` | ||
besides the body of that event. | ||
|
||
The `target` field is required to distinguish between bots in a room. It is required at all time to contain the mxid of the bot that is meant. | ||
This is required because there might be multiple bots with the same command. | ||
|
||
Example: | ||
|
||
```json | ||
"m.interaction.command": { | ||
"command": "help", | ||
"target": "@faq_bot:example.matrix" | ||
} | ||
``` | ||
|
||
#### `m.interaction.stage.start` | ||
|
||
This event indicates a new stage being entered. | ||
|
||
The `event_id` is supposed to reference the event that was the start of the last event. So this might initially be the initial command message a user sent. | ||
|
||
The `interactions` array is the same format as in the state event. It indicates new action specific to the current stage. | ||
The client should indicate that this is currently a stage of the initially started command. It should show also show the interactions the bot now added | ||
as well as a way to abort the stage immediately. (See flow example for details) | ||
|
||
The bot which is currently starting a stage should be identified by the sender field of the event which contains the bot mxid. | ||
|
||
This event should be ignored when the user not started a command or the user has not previously received a stage start event. | ||
This can happen either because of abusive bots or because someone else started a command. Possible ways to check this are: | ||
|
||
* Track the commands locally in the client | ||
* Check for a `m.room.message` event with `m.interaction.command` and the `sender` matching the logged in user. | ||
|
||
Benefit of the second option is that this works across multiple used clients. | ||
|
||
Example: | ||
|
||
```json | ||
{ | ||
"content": { | ||
"event_id": "$random_id123", | ||
"interactions": [ | ||
{ | ||
"type": "m.interaction.button", | ||
"description": "The Search command", | ||
"name": "Search" | ||
} | ||
] | ||
}, | ||
"type": "m.interaction.stage.start" | ||
} | ||
``` | ||
|
||
#### `m.interaction.stage.end` | ||
|
||
This event indicates a flow end. | ||
|
||
The `event_id` is supposed to reference the event that was the start of the last event. It is not supposed to be the initial command. | ||
|
||
This event can be sent by both the client as well as the bot. | ||
If a client sends the event the bot should think of this as if the user aborted the current action entirely. | ||
If a bot sends the event the client should think of this as having completed a flow fully. | ||
|
||
This event should be ignored when the user has not started a command or the user has not previously received a stage start event. | ||
This can happen either because of abusive bots or because someone else started a command. Possible ways to check this are: | ||
|
||
* Track the commands locally in the client | ||
* Check for a `m.room.message` event with `m.interaction.command` and the `sender` matching the logged in user. | ||
|
||
Benefit of the second option is that this works across multiple used clients. | ||
|
||
Example: | ||
|
||
```json | ||
{ | ||
"content": { | ||
"event_id": "$random_id123" | ||
}, | ||
"type": "m.interaction.stage.end" | ||
} | ||
``` | ||
|
||
## What is `m.interaction.button`? | ||
|
||
This type is meant to be like Telegram's custom keyboard feature. It is used to display a button in the UI which behaves like a custom keyboard with fixed functions. | ||
It is however in this MSC not meant to support inline buttons on events but those in theory can be done by reusing this type. | ||
|
||
## How are stages meant to work | ||
|
||
Imagine stages as submenus. They are indicating the next step in a greater action. | ||
|
||
If a stage start is sent the user moves into the next decision tree of a menu. If a stage end happens the user gets back to the top of this decision tree. | ||
Stage end should not be treated as a back button. | ||
|
||
|
||
## Fallback | ||
|
||
The fallback is done by making this a transparent change to the message event. It only adds metadata to the event instead of proposing a new message event. | ||
This brings the benefit that the fallback works in any existing client without needing to change the clients code. This means that user without the | ||
MSC implemented in their client still can fully follow the conversation without missing out events. | ||
|
||
## Other neccesarry changes to make this even work | ||
|
||
To make this even work this needs an addition to the default room powerlevels. The `m.bot.interactions` state event type MUST be able to be set by | ||
the rooms default Powerlevel as bots usually are just regular users and not moderators of the room. | ||
|
||
## Potential issues | ||
|
||
* Priority of stages is not defined if 2 commands happen at the same time | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. define this as the timestamp order of the interaction targets' read receipts There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Timestamps are difficult across federated systems. However this most likely is already solved by the relations. (Would love to have some more input on this assumption from some that understand relations better than I do) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Read receipts only advance forwards, hence that order is always well defined and the problem boils down to retrieving the read receipts from the bot's server. And relations would not be useful across multiple unrelated commands from different users. |
||
* Multiple users using multiple bots in a group is currently not handled directly. The MSC only demands to ignore stages if the user not started the command itself. | ||
|
||
## Alternatives | ||
|
||
* MSC2929 - This seems to solve parts of this as well but doesn't seem to specify custom keyboards | ||
MTRNord marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* A full form based approach where a bot can define layouts - In a discussion with multiple client developers at #matrix-client-developers:matrix.org | ||
this was being decided difficult or even dangerous as bots or abusive users might do harm with that and in general it seems too hard to implement. | ||
|
||
## Security considerations | ||
|
||
* This wasn't thought with e2ee in mind yet. It would need to be tried if it still works with being fully encrypted. | ||
* It might leak metadata if it is not encrypted | ||
|
||
## Unstable prefix | ||
|
||
`dev.nordgedanken.msc3006.` shall be used as a prefix. | ||
|
||
e.g.: `m.bot.interactions` becomes `dev.nordgedanken.msc3006.bot.interactions` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving from #3006 (comment) (cc @JoshuaDietz ) to a thread. (Next time please use thread to make the PR more readable please)
The goal of this MSC is actually to not do inline buttons. That would be a separate MSC and I do agree it should exist. This one is meant to be non message bound commands :) Thats why buttons are not bound to messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I got that the goal of this MSC are not inline buttons. But as I described I don't really see how these keyboard buttons can work with matrix since every user can send them to every room as he wants (as long as there are no protections against that, i.e. a new permission). So if they are implemented in my opinion there needs to be a concept on how to avoid abuse of the feature (see the problems that I described in my previous comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well a) as "m.bot.interactions" is an own event you could restrict it by powerlevels b) thats more an overall "issue" in matrix if you want to call it like that. Because bots are not separate from users. They are the same thing. So solving this is imho out of scope of this MSC. This is only possible to solve by separating users and bots as otherwise it is impossible to make restrictions that work for bots but restrict users :/