Skip to content
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

feat: Document bots #10151

Merged
merged 5 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions appinfo/routes/routesBotController.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
['name' => 'Bot#react', 'url' => '/api/{apiVersion}/bot/{token}/reaction/{messageId}', 'verb' => 'POST', 'requirements' => $requirementsWithMessageId],
/** @see \OCA\Talk\Controller\BotController::deleteReaction() */
['name' => 'Bot#deleteReaction', 'url' => '/api/{apiVersion}/bot/{token}/reaction/{messageId}', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId],
/** @see \OCA\Talk\Controller\BotController::adminListBots() */
['name' => 'Bot#adminListBots', 'url' => '/api/{apiVersion}/bot/admin', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BotController::listBots() */
['name' => 'Bot#listBots', 'url' => '/api/{apiVersion}/bot/{token}', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\BotController::enableBot() */
Expand Down
31 changes: 31 additions & 0 deletions docs/bot-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# List of available bots

If you want to write your own bot or webhook, please see the
[Bots and Webhooks developer documentation](bots.md). Afterwards send a pull
request against the [docs/bot-list.md](https://github.com/nextcloud/spreed/blob/master/docs/bot-list.md)
to add your bot to the list, using the following template:

```markdown
## Name of the bot

Useful, but short description, best to keep it to 1-3 lines of text.

![Screenshot can be put into docs/botscreenshots/bot-name.png, max. width 700px, max. height 480px](botscreenshots/bot-name.png)

* License: Identifier of the license (See https://spdx.org/licenses/)
* [Link to source code](https://github.com/nextcloud/call_summary_bot)
* [Link to installation documentation](https://github.com/nextcloud/call_summary_bot#readme)
```

Here you can find a brief overview of known bots that allows administration to
easily discover your bot and install it on their Nextcloud server.

## Call summary

The call summary bot posts an overview message after the call listing all participants and outlining tasks.

![Screenshot showing a call summary chat message](botscreenshots/call-summary.png)

* License: AGPL-3.0-or-later
* [Link to source code](https://github.com/nextcloud/call_summary_bot)
* [Link to installation documentation](https://github.com/nextcloud/call_summary_bot#readme)
84 changes: 84 additions & 0 deletions docs/bot-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Setup and management bots

Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: (requires the `bots-v1` capability - available since Nextcloud 27.1)

## Get list of bots installed on the server

Lists the bots that are enabled and can be enabled for the conversation

* Required capability: `bots-v1`
* Method: `GET`
* Endpoint: `bot/admin`

* Response:
- Status code:
+ `200 OK`
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data:
List of bots, each bot has at least:

| field | type | Description |
|----------------------|--------|----------------------------------------------------------------------------------------------|
| `id` | int | Unique numeric identifier of the bot on this server |
| `name` | string | Display name of the bot shown as author when it posts a message or reaction |
| `description` | string | A longer description of the bot helping moderators to decide if they want to enable this bot |
| `url` | string | URL endpoint that is triggered by this bot |
| `url_hash` | string | Hash of the URL prefixed with `bot-` serves as `actor_id` |
| `state` | int | One of the [Bot states](constants.md#bot-states) |
| `error_count` | int | Number of consecutive errors |
| `last_error_date` | int | UNIX timestamp of the last error |
nickvergessen marked this conversation as resolved.
Show resolved Hide resolved
| `last_error_message` | string | The last exception message or error response information when trying to reach the bot. |

## Get list of bots for a conversation

Lists the bots that are enabled and can be enabled for the conversation

* Required capability: `bots-v1`
* Method: `GET`
* Endpoint: `bot/{token}`

* Response:
- Status code:
+ `200 OK`
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
- Data:
List of bots, each bot has at least:

| field | type | Description |
|-----------------------|--------|----------------------------------------------------------------------------------------------|
| `id` | int | Unique numeric identifier of the bot on this server |
| `name` | string | Display name of the bot shown as author when it posts a message or reaction |
| `description` | string | A longer description of the bot helping moderators to decide if they want to enable this bot |
| `state` | int | One of the [Bot states](constants.md#bot-states) |

## Enable a bot for a conversation as a moderator

* Required capability: `bots-v1`
* Method: `POST`
* Endpoint: `bot/{token}/{botId}`

* Response:
- Status code:
+ `200 OK` When the bot is already enabled in the conversation
+ `201 Created` When the bot is now enabled in the conversation
+ `400 Bad Request` When the bot ID is unknown on the server
+ `400 Bad Request` When the bot is disabled or set to "no-setup" on the server
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant

## Disable a bot for a conversation as a moderator

* Required capability: `bots-v1`
* Method: `DELETE`
* Endpoint: `bot/{token}/{botId}`

* Response:
- Status code:
+ `200 OK` When the bot is already enabled in the conversation
+ `201 Created` When the bot is now enabled in the conversation
+ `400 Bad Request` When the bot ID is unknown on the server
+ `400 Bad Request` When the bot is disabled or set to "no-setup" on the server
+ `403 Forbidden` When the current user is not a moderator/owner
+ `404 Not Found` When the conversation could not be found for the participant
171 changes: 171 additions & 0 deletions docs/bots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Bots and Webhooks

Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: (requires the `bots-v1` capability - available since Nextcloud 27.1)

Webhook based bots are available with the Nextcloud 27.1 compatible Nextcloud Talk 17.1 release as a first version

!!! note

For security reasons bots can only be added via the
command line. `./occ talk:bot:install --help` gives you
a short overview of the required arguments, but they are
also explained in the [OCC documentation](occ.md#talkbotinstall).

---

## Receiving chat messages

Messages are signed using the shared secret that is specified when installing a bot on the server.
Create a HMAC with SHA256 over the request body and the `RANDOM` header using the shared secret.
Only when the `SIGNATURE` matches the request should be accepted and handled.

**Sample PHP code:**

```php
$digest = hash_hmac('sha256', $_SERVER['HTTP_X_NEXTCLOUD_TALK_RANDOM'] . file_get_contents('php://input'), $secret);

if (!hash_equals($digest, strtolower($_SERVER['HTTP_X_NEXTCLOUD_TALK_SIGNATURE']))) {
exit;
}
```

### Headers

| Header | Content type | Description |
|-----------------------------------|---------------------|------------------------------------------------------|
| `HTTP_X_NEXTCLOUD_TALK_SIGNATURE` | `[a-f0-9]{64}` | SHA265 signature of the body |
| `HTTP_X_NEXTCLOUD_TALK_RANDOM` | `[A-Za-z0-9+\]{64}` | Random string used when signing the body |
| `HTTP_X_NEXTCLOUD_TALK_BACKEND` | URI | Base URL of the Nextcloud server sending the message |

### Content

The content format follows the [ActivityPub](https://www.w3.org/TR/activitypub/) dictionary.

#### Sample chat message

```json
{
"type": "Create",
"actor": {
"type": "Person",
"id": "users/ada-lovelace",
"name": "Ada Lovelace"
},
"object": {
"type": "Note",
"id": "1567",
"name": "message",
"content": "{\"message\":\"hi {mention-call1} !\",\"parameters\":{\"mention-call1\":{\"type\":\"call\",\"id\":\"n3xtc10ud\",\"name\":\"world\",\"call-type\":\"group\",\"icon-url\":\"https:\\/\\/nextcloud.local\\/ocs\\/v2.php\\/apps\\/spreed\\/api\\/v1\\/room\\/n3xtc10ud\\/avatar\"}}}",
"mediaType": "text/markdown"
},
"target": {
"type": "Collection",
"id": "n3xtc10ud",
"name": "world"
}
}
```

#### Explanation

| Path | Description |
|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| actor.id | One of the known [attendee types](constants.md#attendee-types) followed by the `/` slash character and a unique identifier within the given type. For users it is the Nextcloud user ID, for guests a sha1 value. |
| actor.name | The display name of the attendee sending the message. |
| object.id | The message ID of the given message on the origin server. It can be used to react or reply to the given message. |
| object.name | For normal written messages `message`, otherwise one of the known [system message identifiers](chat.md#system-messages). |
| object.content | A JSON encoded dictionary with a `message` and `parameters` key. The message can include placeholders and the [Rich Object parameters](https://github.com/nextcloud/server/blob/master/lib/public/RichObjectStrings/Definitions.php) are rendered into it in the chat view. |
| object.mediaType | `text/markdown` when the message should be interpreted as Markdown. Otherwise `text/plain`. |
| target.id | The token of the conversation in which the message was posted. It can be used to react or reply to the given message. |
| target.name | The name of the conversation in which the message was posted. |

## Sending a chat message

Bots can also send message. On the sending process the same signature/verification method is applied.

* Required capability: `bots-v1`
* Method: `POST`
* Endpoint: `/bot/{token}/message`
* Header:

| Name | Description |
|----------------------------------|-----------------------------------------------------------------|
| `X-Nextcloud-Talk-Bot-Random` | The random value used when signing the request |
| `X-Nextcloud-Talk-Bot-Signature` | The signature to validate the request comes from an enabled bot |
| `OCS-APIRequest` | Needs to be set to `true` to access the ocs/vX.php endpoint |

* Data:

| field | type | Description |
|--------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| `message` | string | The message the user wants to say |
| `replyTo` | int | The message ID this message is a reply to (only allowed for messages from the same conversation and when the message type is not `system` or `command`) |
| `referenceId` | string | A reference string to be able to identify the message again in a "get messages" request, should be a random sha256 |
| `silent` | bool | If sent silent the message will not create chat notifications even for users |

* Response:
- Status code:
+ `201 Created` When the message was posted successfully
+ `400 Bad Request` When the provided replyTo parameter is invalid
+ `401 Unauthenticated` When the bot could not be verified for the conversation
+ `404 Not Found` When the conversation could not be found
+ `413 Payload Too Large` When the message was longer than the allowed limit of 32000 characters (or 1000 until Nextcloud 16.0.1, check the `spreed => config => chat => max-length` capability for the limit)
+ `429 Too Many Requests` When `401 Unauthenticated` was triggered too often

## Reacting to a chat message

Bots can also react to a message. The same signature/verification method is applied.

* Required capability: `bots-v1`
* Method: `POST`
* Endpoint: `/bot/{token}/reaction/{messageId}`
* Header:

| Name | Description |
|----------------------------------|-----------------------------------------------------------------|
| `X-Nextcloud-Talk-Bot-Random` | The random value used when signing the request |
| `X-Nextcloud-Talk-Bot-Signature` | The signature to validate the request comes from an enabled bot |
| `OCS-APIRequest` | Needs to be set to `true` to access the ocs/vX.php endpoint |

* Data:

| field | type | Description |
|------------|--------|----------------|
| `reaction` | string | A single emoji |

* Response:
- Status code:
+ `201 Created` When the reaction was created successfully
+ `400 Bad Request` When the provided emoji was invalid
+ `401 Unauthenticated` When the bot could not be verified for the conversation
+ `404 Not Found` When the conversation or message could not be found
+ `429 Too Many Requests` When `401 Unauthenticated` was triggered too often

## Delete a reaction

Bots can also remove their previous reaction from amessage. The same signature/verification method is applied.

* Required capability: `bots-v1`
* Method: `DELETE`
* Endpoint: `/bot/{token}/reaction/{messageId}`
* Header:

| Name | Description |
|----------------------------------|-----------------------------------------------------------------|
| `X-Nextcloud-Talk-Bot-Random` | The random value used when signing the request |
| `X-Nextcloud-Talk-Bot-Signature` | The signature to validate the request comes from an enabled bot |
| `OCS-APIRequest` | Needs to be set to `true` to access the ocs/vX.php endpoint |

* Data:

| field | type | Description |
|------------|--------|----------------|
| `reaction` | string | A single emoji |

* Response:
- Status code:
+ `200 OK` When the reaction was deleted successfully
+ `400 Bad Request` When the provided emoji was invalid
+ `401 Unauthenticated` When the bot could not be verified for the conversation
+ `404 Not Found` When the conversation or message could not be found
+ `429 Too Many Requests` When `401 Unauthenticated` was triggered too often
Binary file added docs/botscreenshots/call-summary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,4 @@

## 17.1
* `remind-me-later` - Support for "Remind me later" for chat messages exists
* `bots-v1` - Support of the first version for Bots and Webhooks is available
6 changes: 6 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Chat commands

!!! warning

**Deprecation:** Commands are deprecated in favor of [Bots](bots.md).

---

!!! note

For security reasons commands can only be added via the
Expand Down
7 changes: 7 additions & 0 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@
* `0` - Public: Participants can see the result immediately and also who voted for which option
* `1` - Hidden: The result is hidden until the poll is closed and then only the number of votes for each option are displayed

## Bots

### Bot states
* `0` Disabled
* `1` Enabled
* `2` No setup - The bot can neither be enabled nor disabled by a moderator

## Signaling modes
* `internal` - No external signaling server is used
* `external` - A single external signaling server is used
Expand Down
15 changes: 15 additions & 0 deletions lib/Controller/BotController.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ public function deleteReaction(string $token, int $messageId, string $reaction):
return new DataResponse([], Http::STATUS_OK);
}

/**
* Admin required
*/
public function adminListBots(): DataResponse {
$data = [];
$bots = $this->botServerMapper->getAllBots();
foreach ($bots as $bot) {
$botData = $bot->jsonSerialize();
unset($botData['secret']);
$data[] = $botData;
}

return new DataResponse($data);
}

#[NoAdminRequired]
#[RequireLoggedInModeratorParticipant]
public function listBots(): DataResponse {
Expand Down
5 changes: 4 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ nav:
- 'Scalability': 'scalability.md'
- 'Call experience': 'call-experience.md'
- 'Occ commands': 'occ.md'
- 'Commands': 'commands.md'
- 'Bots': 'bot-list.md'
- 'Commands (deprecated)': 'commands.md'
- 'Matterbridge integration': 'matterbridge.md'
- 'Developer documentation':
- 'Bots and webhooks': 'bots.md'
- 'Constants': 'constants.md'
- 'Capabilities': 'capabilities.md'
- 'PHP events': 'events.md'
Expand All @@ -41,6 +43,7 @@ nav:
- 'Reaction management': 'reaction.md'
- 'Poll management': 'poll.md'
- 'Breakout rooms management': 'breakout-rooms.md'
- 'Bots management': 'bot-management.md'
- 'Integration by other apps': 'integration.md'
- 'Webinar management': 'webinar.md'
- 'Settings': 'settings.md'
Expand Down
Loading