Skip to content

Commit

Permalink
feat(call): Direct endpoint to check if call notification should be d…
Browse files Browse the repository at this point in the history
…ismissed

Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Dec 2, 2024
1 parent 49f4e77 commit e295adb
Show file tree
Hide file tree
Showing 9 changed files with 538 additions and 0 deletions.
2 changes: 2 additions & 0 deletions appinfo/routes/routesCallController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'ocs' => [
/** @see \OCA\Talk\Controller\CallController::getPeersForCall() */
['name' => 'Call#getPeersForCall', 'url' => '/api/{apiVersion}/call/{token}', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallCheckController::isCurrentUserInCall() */
['name' => 'CallCheck#isCurrentUserInCall', 'url' => '/api/{apiVersion}/call/{token}/self', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::downloadParticipantsForCall() */
['name' => 'Call#downloadParticipantsForCall', 'url' => '/api/{apiVersion}/call/{token}/download', 'verb' => 'GET', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\CallController::joinCall() */
Expand Down
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,4 @@
## 21
* `config => conversations => force-passwords` - Whether passwords are enforced for public rooms
* `conversation-creation-password` - Whether the endpoints for creating public conversations or making a conversation public support setting a password
* `call-notification-check-api` - Whether the endpoints exists to checking if a call notification should be dismissed
2 changes: 2 additions & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class Capabilities implements IPublicCapability {
'download-call-participants',
'email-csv-import',
'conversation-creation-password',
'call-notification-check-api',
];

public const CONDITIONAL_FEATURES = [
Expand All @@ -130,6 +131,7 @@ class Capabilities implements IPublicCapability {
'note-to-self',
'archived-conversations-v2',
'chat-summary-api',
'call-notification-check-api',
];

public const LOCAL_CONFIGS = [
Expand Down
51 changes: 51 additions & 0 deletions lib/Controller/CallCheckController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Controller;

use OCA\Talk\Service\ParticipantService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

class CallCheckController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
protected ParticipantService $participantService,
protected ?string $userId,
) {
parent::__construct($appName, $request);
}

/**
* Check if the current user is in a call or whether the notification should be dismissed
*
* @param string $token Conversation token to check
* @return DataResponse<Http::STATUS_OK|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, null, array{}>
*
* 200: User has joined the call or there is no call any more, notification shall be dismissed
* 403: Not logged in, invalid API call
* 404: User didn't join the call, notification shall be kept
*/
#[NoAdminRequired]
#[OpenAPI(tags: ['call'])]
public function isCurrentUserInCall(string $token): DataResponse {
$status = Http::STATUS_OK;
if ($this->userId === null) {
$status = Http::STATUS_FORBIDDEN;
} elseif ($this->participantService->isUserMissingFromCall($token, $this->userId)) {
$status = Http::STATUS_NOT_FOUND;
}
return new DataResponse(null, $status);
}
}
42 changes: 42 additions & 0 deletions lib/Service/ParticipantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -1601,6 +1601,48 @@ public function getParticipantsInCall(Room $room, int $maxAge = 0): array {
return $this->getParticipantsFromQuery($query, $room);
}

/**
* Do not try to abstract this into using the Room or Participant class.
* This function is called by {@see CallCheckController::isCurrentUserInCall}
* and mobile as well as desktop clients are basically ddos-ing it, to check
* if the call notification / call screen should be removed.
*/
public function isUserMissingFromCall(string $token, string $userId): bool {
$query = $this->connection->getQueryBuilder();
$query->select('r.active_since', 'a.last_joined_call')
->from('talk_rooms', 'r')
->innerJoin(
'r', 'talk_attendees', 'a',
$query->expr()->eq('r.id', 'a.room_id')
)
->where($query->expr()->eq('r.token', $query->createNamedParameter($token)))
->andWhere($query->expr()->eq('a.actor_type', $query->createNamedParameter(Attendee::ACTOR_USERS)))
->andWhere($query->expr()->eq('a.actor_id', $query->createNamedParameter($userId)));

$result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();

if ($row === false) {
// Not in room => Not missing from call
return false;
}

if ($row['active_since'] === null) {
// No call
return false;
}

try {
$activeSince = new \DateTime($row['active_since']);
} catch (\Throwable) {
return false;
}

// Missing when the call started after the join
return $activeSince->getTimestamp() >= $row['last_joined_call'];
}

/**
* @return Participant[]
*/
Expand Down
143 changes: 143 additions & 0 deletions openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -4560,6 +4560,149 @@
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/call/{token}/self": {
"get": {
"operationId": "call_check-is-current-user-in-call",
"summary": "Check if the current user is in a call or whether the notification should be dismissed",
"tags": [
"call"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "apiVersion",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"v4"
],
"default": "v4"
}
},
{
"name": "token",
"in": "path",
"description": "Conversation token to check",
"required": true,
"schema": {
"type": "string",
"pattern": "^[a-z0-9]{4,30}$"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "User has joined the call or there is no call any more, notification shall be dismissed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"nullable": true
}
}
}
}
}
}
}
},
"403": {
"description": "Not logged in, invalid API call",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"nullable": true
}
}
}
}
}
}
}
},
"404": {
"description": "User didn't join the call, notification shall be kept",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"nullable": true
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/spreed/api/{apiVersion}/call/{token}/download": {
"get": {
"operationId": "call-download-participants-for-call",
Expand Down
Loading

0 comments on commit e295adb

Please sign in to comment.