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(sip-dialout): ☎️ API "SIP dial-out" #10608

Merged
merged 25 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0aa603a
fix(tests): Fix feature name
nickvergessen Oct 4, 2023
e6959f6
feat(sip-dialout): Allow adding phone numbers as participants
nickvergessen Sep 28, 2023
6509dda
feat(sip-dialout): Add dial-out request
nickvergessen Sep 28, 2023
0ce1216
fix(sip-dialout): Validate the HPB response
nickvergessen Sep 29, 2023
712917f
feat(sip-dialout): Add an endpoint to verify a SIP dialout as the SIP…
nickvergessen Oct 4, 2023
c172fd5
fix(sip-dialout): Add an admin config for SIP dial-out
nickvergessen Oct 4, 2023
cd87f6c
tests(sip-dialout): Add integration test for adding phone number to a…
nickvergessen Oct 4, 2023
0175438
fix(openapi): Regenerate OpenAPI
nickvergessen Oct 9, 2023
50b8f4e
feat(sip-dialout): Add call ID to attendees
nickvergessen Oct 10, 2023
fb36377
feat(call): Add endpoint to send the dial-out request to the HPB
nickvergessen Oct 10, 2023
99b4c1f
Extend OpenAPI definition
nickvergessen Oct 10, 2023
0ca9765
Avoid circular dependency
nickvergessen Oct 11, 2023
2b6cf6c
fix(docs): Add capability configs for all the SIP states
nickvergessen Oct 13, 2023
60b0154
feat(sip-dialout): Add API endpoint for the SIP bridge to cancel a Di…
nickvergessen Oct 13, 2023
8f15fa2
tests(sip-dialout): Add integration tests for the communication with …
nickvergessen Oct 16, 2023
6d2761f
fix(sip-dialout): Log when the signaling server response is empty
nickvergessen Oct 16, 2023
4fd53bf
fix(sip-dialout): Expose the callId to users that are allowed to use …
nickvergessen Oct 16, 2023
29fe68c
Temporary downgrade Valinor to fix CI
nickvergessen Oct 18, 2023
b67fc7b
fix(sip-dialout): Rely on the $options array when receiving a rejecti…
nickvergessen Oct 18, 2023
0ebb317
fix(CI): Typed static property FeatureContext::$phoneNumberToActorId …
nickvergessen Oct 18, 2023
d36ea29
fix(CI): FIx integration tests for Postgres (sort order from DB differs
nickvergessen Oct 23, 2023
fc8e14d
fix(docs): Document new participant fields
nickvergessen Oct 24, 2023
bff46e5
feat(sip-dialout): Add system messages for adding/removing phone numb…
nickvergessen Oct 24, 2023
7447660
techdebt: Add constants for room object types
nickvergessen Oct 24, 2023
abb1fba
feat(sip-dialout): Add phone icon for rooms created by SIP dial-out
nickvergessen Oct 24, 2023
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: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m

]]></description>

<version>18.0.0-dev.8</version>
<version>18.0.0-dev.9</version>
<licence>agpl</licence>

<author>Daniel Calviño Sánchez</author>
Expand Down
2 changes: 2 additions & 0 deletions appinfo/routes/routesCallController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
['name' => 'Call#joinCall', 'url' => '/api/{apiVersion}/call/{token}', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::ringAttendee() */
['name' => 'Call#ringAttendee', 'url' => '/api/{apiVersion}/call/{token}/ring/{attendeeId}', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::sipDialOut() */
['name' => 'Call#sipDialOut', 'url' => '/api/{apiVersion}/call/{token}/dialout/{attendeeId}', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::updateCallFlags() */
['name' => 'Call#updateCallFlags', 'url' => '/api/{apiVersion}/call/{token}', 'verb' => 'PUT', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::leaveCall() */
Expand Down
10 changes: 8 additions & 2 deletions appinfo/routes/routesRoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,18 @@
['name' => 'Room#addToFavorites', 'url' => '/api/{apiVersion}/room/{token}/favorite', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::removeFromFavorites() */
['name' => 'Room#removeFromFavorites', 'url' => '/api/{apiVersion}/room/{token}/favorite', 'verb' => 'DELETE', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::getParticipantByDialInPin() */
['name' => 'Room#getParticipantByDialInPin', 'url' => '/api/{apiVersion}/room/{token}/pin/{pin}', 'verb' => 'GET', 'requirements' => array_merge($requirementsWithToken, [
/** @see \OCA\Talk\Controller\RoomController::verifyDialInPin() */
['name' => 'Room#verifyDialInPin', 'url' => '/api/{apiVersion}/room/{token}/pin/{pin}', 'verb' => 'GET', 'requirements' => array_merge($requirementsWithToken, [
'pin' => '\d{7,32}',
])],
/** @see \OCA\Talk\Controller\RoomController::verifyDialInPin() */
['name' => 'Room#verifyDialInPin', 'url' => '/api/{apiVersion}/room/{token}/verify-dialin', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::verifyDialOutNumber() */
['name' => 'Room#verifyDialOutNumber', 'url' => '/api/{apiVersion}/room/{token}/verify-dialout', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::createGuestByDialIn() */
['name' => 'Room#createGuestByDialIn', 'url' => '/api/{apiVersion}/room/{token}/open-dial-in', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::rejectedDialOutRequest() */
['name' => 'Room#rejectedDialOutRequest', 'url' => '/api/{apiVersion}/room/{token}/rejected-dialout', 'verb' => 'DELETE', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::setNotificationLevel() */
['name' => 'Room#setNotificationLevel', 'url' => '/api/{apiVersion}/room/{token}/notify', 'verb' => 'POST', 'requirements' => $requirementsWithToken],
/** @see \OCA\Talk\Controller\RoomController::setNotificationCalls() */
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
},
"require": {
"bamarni/composer-bin-plugin": "^1.8",
"cuyz/valinor": "^1.6",
"cuyz/valinor": "1.6.0",
"firebase/php-jwt": "^6.3"
},
"extra": {
Expand Down
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions docs/call.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@
+ `404 Not Found` When the conversation could not be found for the participant
+ `412 Precondition Failed` When the lobby is active and the user is not a moderator

## Send SIP dial-out request

* Required capability: `sip-support-dialout`
* Method: `POST`
* Endpoint: `/call/{token}/dialout/{attendeeId}`
* Data:

| field | type | Description |
|--------------|------|-------------------------|
| `attendeeId` | int | The participant to call |

* Response:
- Status code:
+ `200 OK`
+ `400 Bad Request` When the room has no call in process
+ `400 Bad Request` When the actor is not in the call
+ `403 Forbidden` When the current user does not have the "Start call" permission
+ `404 Not Found` When the conversation could not be found for the participant
+ `404 Not Found` When the target participant could not be found or is not a phone number (Guest, group, etc.)
+ `412 Precondition Failed` When the lobby is active and the user is not a moderator
+ `501 Not Implemented` When the SIP functionality is not configured

## Update call flags

* Method: `PUT`
Expand Down
4 changes: 4 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,9 @@
* `session-state` - Sessions can mark themselves as inactive, so the participant receives notifications again
* `note-to-self` - Support for "Note-to-self" conversation exists
* `recording-consent` - Whether admins and moderators can require recording consent before joining a call
* `sip-support-dialout` - Whether admins can enable SIP dial-out
* `config => chat => has-translation-providers` - When true, translation tuples can be loaded from the [OCS Translation API](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-translation-api.html#get-available-translation-options).
* `config => call => recording-consent` - Whether users need to consent into call recording before joining a call (see [constants list](constants.md#recording-consent-required))
* `config => call => sip-enabled` - Whether SIP is configured on the server allowing for SIP dial-in
* `config => call => sip-dialout-enabled` - Whether SIP dial-out is configured on the server, additionally requires `config => call => sip-enabled`
* `config => call => can-enable-sip` - Whether the current user is a member of the groups that are allowed to enable SIP dial-in on a conversation or use SIP dial-out
11 changes: 6 additions & 5 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@

### Object types

| Constant | Can be created | Description | Object ID |
|------------------|----------------|------------------------------------------------------------------|---------------------------------------|
| `file` | No | Conversations about a file in the right sidebar of the files app | File ID |
| `share:password` | No | Video verification to verify the identity of the share recipient | Share token |
| `room` | Yes | Room is a breakout room | Token of the main/parent conversation |
| Constant | Can be created | Description | Object ID |
|------------------|----------------|------------------------------------------------------------------|---------------------------------------------------------|
| `file` | No | Conversations about a file in the right sidebar of the files app | File ID |
| `share:password` | No | Video verification to verify the identity of the share recipient | Share token |
| `room` | Yes | Room is a breakout room | Token of the main/parent conversation |
| `phone` | Yes | Room is created when calling a phone number with SIP dial-out | `phone` (not set atm, just used for the default avatar) |

### Read-only states
* `0` Read-write
Expand Down
72 changes: 71 additions & 1 deletion docs/participant.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
| `statusIcon` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation |
| `statusMessage` | string | v2 | | Optional: Only available with `includeStatus=true`, for users with a set status and when there are less than 100 participants in the conversation |
| `roomToken` | string | v4 | | Optional: Only available with `breakout-rooms-v1` capability |
| `phoneNumber` | string | v4 | | Optional: Only available with `sip-support-dialout` capability and only filled for moderators that are allowed to configure SIP for conversations |
| `callId` | string | v4 | | Optional: Only available with `sip-support-dialout` capability and only filled for moderators that are allowed to configure SIP for conversations |


## Get list of participants in a conversation including its breakout rooms
Expand Down Expand Up @@ -265,7 +267,8 @@ Setting custom permissions for a self-joined user will also make them a permanen

## Get a participant by their pin

Note: This is only allowed with validate SIP bridge requests
Note: Deprecated - Use [Verify a dial-in PIN](#Verify-a-dial-in-PIN) instead
Note: This is only allowed as validate SIP bridge requests

* Required capability: `sip-support`
* Method: `GET`
Expand All @@ -279,6 +282,73 @@ Note: This is only allowed with validate SIP bridge requests

- Data: See array definition in `Get user´s conversations`

## Verify a dial-in PIN

Note: This is only allowed as validate SIP bridge requests

* Required capability: `sip-support-dialout`
* Method: `POST`
* Endpoint: `/room/{token}/verify-dialin`
* Data:

| field | type | Description |
|-------|--------|-------------------------------------|
| `pin` | string | PIN the participant used to dial-in |

* Response:
- Status code:
+ `200 OK`
+ `401 Unauthorized` When the validation as SIP bridge failed
+ `404 Not Found` When the conversation or participant could not be found
+ `501 Not Implemented` When SIP is not configured

- Data: See array definition in `Get user´s conversations`

## Verify a dial-out number

Note: This is only allowed as validate SIP bridge requests

* Required capability: `sip-support-dialout`
* Method: `POST`
* Endpoint: `/room/{token}/verify-dialout`
* Data:

| field | type | Description |
|-----------|--------|---------------------------------------------------------------------------------------------------------------------------------|
| `number` | string | E164 formatted phone number |
| `options` | string | Additional details to verify the validity of the request as JSON encoded object (Should contain actorId, actorType, attendeeId) |

* Response:
- Status code:
+ `200 OK`
+ `401 Unauthorized` When the validation as SIP bridge failed
+ `404 Not Found` When the conversation or participant could not be found
+ `501 Not Implemented` When SIP or SIP dial-out is not configured

- Data: See array definition in `Get user´s conversations`

## Reset call ID of rejected dial-out

Note: This is only allowed as validate SIP bridge requests

* Required capability: `sip-support-dialout`
* Method: `DELETE`
* Endpoint: `/room/{token}/rejected-dialout`
* Data:

| field | type | Description |
|-----------|--------|------------------------------------------------|
| `callId` | string | The call ID that was rejected |
| `options` | string | The options as received in the dialout request |

* Response:
- Status code:
+ `200 OK` Call ID reset
+ `400 Bad Request` Call ID mismatch
+ `401 Unauthorized` SIP request invalid
+ `404 Not Found` Participant was not found
+ `501 Not Implemented` When SIP or SIP dial-out is not configured

## Set display name as a guest

* API: Only `v1`
Expand Down
1 change: 1 addition & 0 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Legend:
| `breakout_rooms` | string<br>`yes` or `no` | `yes` | Yes | | Whether or not breakout rooms are allowed (Will only prevent creating new breakout rooms. Existing conversations are not modified.) |
| `call_recording` | string<br>`yes` or `no` | `yes` | Yes | | Enable call recording |
| `call_recording_transcription` | string<br>`yes` or `no` | `no` | No | | Whether call recordings should automatically be transcripted when a transcription provider is enabled. |
| `sip_dialout` | string<br>`yes` or `no` | `no` | Yes | | SIP dial-out is allowed when a SIP bridge is configured |
| `federation_enabled` | string<br>`yes` or `no` | `no` | Yes | | 🏗️ *Work in progress:* Whether or not federation with this instance is allowed |
| `conversations_files` | string<br>`1` or `0` | `1` | No | 🖌️ | Whether the files app integration is enabled allowing to start conversations in the right sidebar |
| `conversations_files_public_shares` | string<br>`1` or `0` | `1` | No | 🖌️ | Whether the public share integration is enabled allowing to start conversations in the right sidebar on the public share page (Requires `conversations_files` also to be enabled) |
Expand Down
6 changes: 6 additions & 0 deletions img/icon-conversation-phone-bright.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions img/icon-conversation-phone-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public function __construct(
* supported-reactions: string[],
* predefined-backgrounds: string[],
* can-upload-background: bool,
* sip-enabled: bool,
* sip-dialout-enabled: bool,
* can-enable-sip: bool,
* },
* chat: array{
* max-length: int,
Expand Down Expand Up @@ -166,6 +169,7 @@ public function getCapabilities(): array {
'session-state',
'note-to-self',
'recording-consent',
'sip-support-dialout',
nickvergessen marked this conversation as resolved.
Show resolved Hide resolved
],
'config' => [
'attachments' => [
Expand All @@ -177,6 +181,8 @@ public function getCapabilities(): array {
'recording' => $this->talkConfig->isRecordingEnabled(),
'recording-consent' => $this->talkConfig->recordingConsentRequired(),
'supported-reactions' => ['❤️', '🎉', '👏', '👍', '👎', '😂', '🤩', '🤔', '😲', '😥'],
'sip-enabled' => $this->talkConfig->isSIPConfigured(),
'sip-dialout-enabled' => $this->talkConfig->isSIPDialOutEnabled(),
],
'chat' => [
'max-length' => ChatManager::MAX_CHAT_LENGTH,
Expand Down Expand Up @@ -260,8 +266,10 @@ public function getCapabilities(): array {
$quota = Util::computerFileSize($quota);
}
$capabilities['config']['call']['can-upload-background'] = $quota === 'none' || $quota > 0;
$capabilities['config']['call']['can-enable-sip'] = $this->talkConfig->canUserEnableSIP($user);
} else {
$capabilities['config']['call']['can-upload-background'] = false;
$capabilities['config']['call']['can-enable-sip'] = false;
}

return [
Expand Down
Loading