summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2023-08-08 14:02:28 +0200
committerJoas Schilling <coding@schilljs.com>2023-08-09 08:51:50 +0200
commit834084902176c025ffd3eedf071a2f3a409a563e (patch)
tree938e9f8a76ee578f60ccc0b5f1fbb00b94ce7bfb
parent02f0551f3174369788a5a828b7188214561faaa5 (diff)
feat(chat): Option to get and clear reminders
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r--appinfo/info.xml12
-rw-r--r--appinfo/routes/routesChatController.php10
-rw-r--r--docs/chat.md71
-rw-r--r--lib/BackgroundJob/ChatMessageReminder.php97
-rw-r--r--lib/BackgroundJob/Reminder.php49
-rw-r--r--lib/Chat/ChatManager.php37
-rw-r--r--lib/Controller/ChatController.php52
-rw-r--r--lib/Migration/Version18000Date20230504205823.php2
-rw-r--r--lib/Migration/Version18000Date20230808120823.php74
-rw-r--r--lib/Model/Reminder.php61
-rw-r--r--lib/Model/ReminderMapper.php81
-rw-r--r--lib/Notification/Notifier.php2
-rw-r--r--lib/Service/ReminderService.php132
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php10
-rw-r--r--tests/integration/features/chat-2/reminder.feature33
-rw-r--r--tests/integration/spreedcheats/lib/Controller/ApiController.php3
-rw-r--r--tests/php/Chat/ChatManagerTest.php7
-rw-r--r--tests/php/Controller/ChatControllerTest.php5
18 files changed, 554 insertions, 184 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index a93c4205c..5f49fc0da 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
- <version>17.1.0-dev.1</version>
+ <version>17.1.0-dev.2</version>
<licence>agpl</licence>
<author>Daniel Calviño Sánchez</author>
@@ -56,13 +56,15 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
</dependencies>
<background-jobs>
- <job>OCA\Talk\BackgroundJob\ExpireSignalingMessage</job>
- <job>OCA\Talk\BackgroundJob\RemoveEmptyRooms</job>
- <job>OCA\Talk\BackgroundJob\ResetAssignedSignalingServer</job>
- <job>OCA\Talk\BackgroundJob\CheckReferenceIdColumn</job>
<job>OCA\Talk\BackgroundJob\CheckHostedSignalingServer</job>
<job>OCA\Talk\BackgroundJob\CheckMatterbridges</job>
+ <job>OCA\Talk\BackgroundJob\CheckReferenceIdColumn</job>
+ <job>OCA\Talk\BackgroundJob\CheckTurnCertificate</job>
<job>OCA\Talk\BackgroundJob\ExpireChatMessages</job>
+ <job>OCA\Talk\BackgroundJob\ExpireSignalingMessage</job>
+ <job>OCA\Talk\BackgroundJob\RemoveEmptyRooms</job>
+ <job>OCA\Talk\BackgroundJob\Reminder</job>
+ <job>OCA\Talk\BackgroundJob\ResetAssignedSignalingServer</job>
</background-jobs>
<repair-steps>
diff --git a/appinfo/routes/routesChatController.php b/appinfo/routes/routesChatController.php
index 39a66f7d8..8ef8ac2a9 100644
--- a/appinfo/routes/routesChatController.php
+++ b/appinfo/routes/routesChatController.php
@@ -46,10 +46,12 @@ return [
['name' => 'Chat#deleteMessage', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId],
/** @see \OCA\Talk\Controller\ChatController::getMessageContext() */
['name' => 'Chat#getMessageContext', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/context', 'verb' => 'GET', 'requirements' => $requirementsWithMessageId],
- /** @see \OCA\Talk\Controller\ChatController::remindLater() */
- ['name' => 'Chat#remindLater', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'POST', 'requirements' => $requirementsWithMessageId],
- /** @see \OCA\Talk\Controller\ChatController::dismissReminder() */
- ['name' => 'Chat#dismissReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId],
+ /** @see \OCA\Talk\Controller\ChatController::setReminder() */
+ ['name' => 'Chat#setReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'POST', 'requirements' => $requirementsWithMessageId],
+ /** @see \OCA\Talk\Controller\ChatController::getReminder() */
+ ['name' => 'Chat#getReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'GET', 'requirements' => $requirementsWithMessageId],
+ /** @see \OCA\Talk\Controller\ChatController::deleteReminder() */
+ ['name' => 'Chat#deleteReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId],
/** @see \OCA\Talk\Controller\ChatController::setReadMarker() */
['name' => 'Chat#setReadMarker', 'url' => '/api/{apiVersion}/chat/{token}/read', 'verb' => 'POST', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\ChatController::markUnread() */
diff --git a/docs/chat.md b/docs/chat.md
index c69f67684..654dc6c9b 100644
--- a/docs/chat.md
+++ b/docs/chat.md
@@ -94,12 +94,12 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: since Nextcloud 13
| `limit` | int | Number of chat messages to receive into each direction (50 by default, 100 at most) |
* Response:
- - Status code:
- + `200 OK`
- + `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
+ - Status code:
+ + `200 OK`
+ + `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
- - Header:
+ - Header:
| field | type | Description |
|---------------------------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -300,7 +300,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
The parent message is the object of the deleted message with the replaced text "Message deleted by you".
This message should **NOT** be displayed to the user but instead be used to remove the original message from any cache/storage of the device.
-## Remind me later
+## Set reminder for chat message
* Required capability: `remind-me-later`
* Method: `POST`
@@ -312,26 +312,59 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
| `timestamp` | int | Timestamp when the notification should be triggered. Preferable options for 6pm today, 8am tomorrow, Saturday 8am and Monday 8am should be offered. |
* Response:
- - Status code:
- + `201 Created`
- + `401 Unauthorized` when the user is not logged in
- + `404 Not Found` When the message could not be found in the room
- + `404 Not Found` When the room could not be found for the participant,
- or the participant is a guest.
+ - Status code:
+ + `201 Created`
+ + `401 Unauthorized` when the user is not logged in
+ + `404 Not Found` When the message could not be found in the room
+ + `404 Not Found` When the room could not be found for the participant,
+ or the participant is a guest.
+ - Data:
+ Array with the details of the reminder
+
+| field | type | Description |
+|-------------|--------|----------------------------------------------|
+| `userId` | string | The user id of the user |
+| `token` | string | The token of the conversation of the message |
+| `messageId` | int | The message id this reminder is for |
+| `timestamp` | int | The timestamp when the reminder is triggered |
-## Delete reminder notification
+## Get reminder for chat message
+
+* Required capability: `remind-me-later`
+* Method: `GET`
+* Endpoint: `/chat/{token}/{messageId}/reminder`
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `401 Unauthorized` when the user is not logged in
+ + `404 Not Found` When the message could not be found in the room
+ + `404 Not Found` When the room could not be found for the participant,
+ or the participant is a guest.
+ + `404 Not Found` When the user has no reminder for this message
+ - Data:
+ Array with the details of the reminder
+
+| field | type | Description |
+|-------------|--------|----------------------------------------------|
+| `userId` | string | The user id of the user |
+| `token` | string | The token of the conversation of the message |
+| `messageId` | int | The message id this reminder is for |
+| `timestamp` | int | The timestamp when the reminder is triggered |
+
+## Delete reminder for chat message
* Required capability: `remind-me-later`
* Method: `DELETE`
* Endpoint: `/chat/{token}/{messageId}/reminder`
* Response:
- - Status code:
- + `200 OK`
- + `401 Unauthorized` when the user is not logged in
- + `404 Not Found` When the message could not be found in the room
- + `404 Not Found` When the room could not be found for the participant,
- or the participant is a guest.
+ - Status code:
+ + `200 OK`
+ + `401 Unauthorized` when the user is not logged in
+ + `404 Not Found` When the message could not be found in the room
+ + `404 Not Found` When the room could not be found for the participant,
+ or the participant is a guest.
## Mark chat as read
diff --git a/lib/BackgroundJob/ChatMessageReminder.php b/lib/BackgroundJob/ChatMessageReminder.php
deleted file mode 100644
index 4c23ddf32..000000000
--- a/lib/BackgroundJob/ChatMessageReminder.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/*
- * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
- *
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-namespace OCA\Talk\BackgroundJob;
-
-use OCA\Talk\AppInfo\Application;
-use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\BackgroundJob\IJobList;
-use OCP\BackgroundJob\Job;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use OCP\Notification\IManager;
-use Psr\Log\LoggerInterface;
-
-class ChatMessageReminder extends Job {
- public function __construct(
- ITimeFactory $time,
- protected IDBConnection $connection,
- protected IManager $notificationManager,
- protected LoggerInterface $logger,
- ) {
- parent::__construct($time);
- }
-
- public function start(IJobList $jobList): void {
- $timeUntilExecution = $this->argument['execute-after'] - $this->time->getTime();
-
- if ($timeUntilExecution <= 0) {
- parent::start($jobList);
- $jobList->remove($this, $this->argument);
- } elseif ($timeUntilExecution > 900) {
- // Execution is quite far in the future. In order to not check the
- // job too often, we update it's test time to be closer to the execution
- $this->setLastRunCloseToExecutionTime(
- $this->argument['execute-after'],
- $this->argument['token'],
- $this->argument['user'],
- $this->argument['message_id'],
- );
- }
- }
-
- /**
- * @psalm-param array{token: string, message_id: string, message_actor_type: string, message_actor_id: string, user: string, execute-after: int} $argument
- */
- protected function run($argument): void {
- $notification = $this->notificationManager->createNotification();
- $notification->setApp(Application::APP_ID)
- ->setUser($argument['user'])
- ->setObject('reminder', $argument['token'])
- ->setDateTime($this->time->getDateTime('@' . $this->argument['execute-after']))
- ->setSubject('reminder', [
- 'token' => $argument['token'],
- 'message' => $argument['message_id'],
- 'userType' => $argument['message_actor_type'],
- 'userId' => $argument['message_actor_id'],
- ])
- ->setMessage('reminder', [
- 'commentId' => $argument['message_id'],
- ]);
- $this->notificationManager->notify($notification);
- }
-
- protected function setLastRunCloseToExecutionTime(int $timestamp, string $token, string $userId, string $message): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->update('jobs')
- ->set('last_run', $query->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT))
- ->where($query->expr()->eq('id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT)));
- $query->executeStatement();
-
- $this->logger->debug('Updated chat message reminder last_run to ' . $timestamp . ' for token "' . $token . '" user "' . $userId . '" message ' . $message);
- }
-}
diff --git a/lib/BackgroundJob/Reminder.php b/lib/BackgroundJob/Reminder.php
new file mode 100644
index 000000000..d7d6e59d3
--- /dev/null
+++ b/lib/BackgroundJob/Reminder.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\BackgroundJob;
+
+use OCA\Talk\Service\ReminderService;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+
+class Reminder extends TimedJob {
+ public function __construct(
+ ITimeFactory $time,
+ protected ReminderService $reminderService,
+ ) {
+ parent::__construct($time);
+ // Every time (5 minutes minus 1 second)
+ $this->setInterval(5 * 60 - 1);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function run($argument): void {
+ $this->reminderService->executeReminders($this->time->getDateTime());
+ }
+}
diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php
index 104d5fcd7..d438a9880 100644
--- a/lib/Chat/ChatManager.php
+++ b/lib/Chat/ChatManager.php
@@ -27,8 +27,6 @@ namespace OCA\Talk\Chat;
use DateInterval;
use OC\Memcache\ArrayCache;
use OC\Memcache\NullCache;
-use OCA\Talk\AppInfo\Application;
-use OCA\Talk\BackgroundJob\ChatMessageReminder;
use OCA\Talk\Events\ChatEvent;
use OCA\Talk\Events\ChatParticipantEvent;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
@@ -43,10 +41,8 @@ use OCA\Talk\Service\RoomService;
use OCA\Talk\Share\RoomShareProvider;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\BackgroundJob\IJobList;
use OCP\Collaboration\Reference\IReferenceManager;
use OCP\Comments\IComment;
-use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ICache;
@@ -117,7 +113,6 @@ class ChatManager {
ITimeFactory $timeFactory,
AttachmentService $attachmentService,
IReferenceManager $referenceManager,
- protected IJobList $jobList,
) {
$this->commentsManager = $commentsManager;
$this->dispatcher = $dispatcher;
@@ -739,13 +734,13 @@ class ChatManager {
}
/**
- * Search for comments with a given content
+ * Get messages for the given chat by ID
*
* @param Room $chat
* @param int[] $commentIds
* @return IComment[]
*/
- public function getMessagesById(Room $chat, array $commentIds): array {
+ public function getMessagesForRoomById(Room $chat, array $commentIds): array {
$comments = $this->commentsManager->getCommentsById(array_map('strval', $commentIds));
$comments = array_filter($comments, static function (IComment $comment) use ($chat) {
@@ -756,26 +751,14 @@ class ChatManager {
return $comments;
}
- public function queueRemindLaterBackgroundJob(Room $chat, IComment $comment, Attendee $attendee, int $timestamp): void {
- $this->jobList->add(ChatMessageReminder::class, [
- 'execute-after' => $timestamp,
- 'token' => $chat->getToken(),
- 'message_id' => $comment->getId(),
- 'message_actor_type' => $comment->getActorType(),
- 'message_actor_id' => $comment->getActorId(),
- 'user' => $attendee->getActorId(),
- ]);
- }
-
- public function dismissReminderNotification(Room $chat, IComment $comment, Attendee $attendee): void {
- $notification = $this->notificationManager->createNotification();
- $notification->setApp(Application::APP_ID)
- ->setUser($attendee->getActorId())
- ->setObject('reminder', $chat->getToken())
- ->setMessage('reminder', [
- 'commentId' => $comment->getId(),
- ]);
- $this->notificationManager->markProcessed($notification);
+ /**
+ * Get messages by ID
+ *
+ * @param int[] $commentIds
+ * @return IComment[] Key is the message id
+ */
+ public function getMessagesById(array $commentIds): array {
+ return $this->commentsManager->getCommentsById(array_map('strval', $commentIds));
}
/**
diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php
index 784396d23..477fa49e0 100644
--- a/lib/Controller/ChatController.php
+++ b/lib/Controller/ChatController.php
@@ -46,9 +46,11 @@ use OCA\Talk\Room;
use OCA\Talk\Service\AttachmentService;
use OCA\Talk\Service\AvatarService;
use OCA\Talk\Service\ParticipantService;
+use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\SessionService;
use OCA\Talk\Share\RoomShareProvider;
use OCP\App\IAppManager;
+use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -110,6 +112,7 @@ class ChatController extends AEnvironmentAwareController {
SessionService $sessionService,
AttachmentService $attachmentService,
avatarService $avatarService,
+ protected ReminderService $reminderService,
GuestManager $guestManager,
MessageParser $messageParser,
RoomShareProvider $shareProvider,
@@ -734,37 +737,58 @@ class ChatController extends AEnvironmentAwareController {
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
#[UserRateLimit(limit: 60, period: 3600)]
- public function remindLater(int $messageId, int $timestamp): DataResponse {
+ public function setReminder(int $messageId, int $timestamp): DataResponse {
try {
- $message = $this->chatManager->getComment($this->room, (string) $messageId);
+ $this->chatManager->getComment($this->room, (string) $messageId);
} catch (NotFoundException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
- $this->chatManager->queueRemindLaterBackgroundJob(
- $this->room,
- $message,
- $this->participant->getAttendee(),
+ $reminder = $this->reminderService->setReminder(
+ $this->participant->getAttendee()->getActorId(),
+ $this->room->getToken(),
+ $messageId,
$timestamp
);
- return new DataResponse([], Http::STATUS_CREATED);
+ return new DataResponse($reminder->jsonSerialize(), Http::STATUS_CREATED);
}
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
- public function dismissReminder(int $messageId): DataResponse {
+ public function getReminder(int $messageId): DataResponse {
try {
- $message = $this->chatManager->getComment($this->room, (string) $messageId);
+ $this->chatManager->getComment($this->room, (string) $messageId);
} catch (NotFoundException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
- $this->chatManager->dismissReminderNotification(
- $this->room,
- $message,
- $this->participant->getAttendee()
+ try {
+ $reminder = $this->reminderService->getReminder(
+ $this->participant->getAttendee()->getActorId(),
+ $messageId,
+ );
+ return new DataResponse($reminder->jsonSerialize(), Http::STATUS_OK);
+ } catch (DoesNotExistException) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+ }
+
+ #[NoAdminRequired]
+ #[RequireModeratorOrNoLobby]
+ #[RequireLoggedInParticipant]
+ public function deleteReminder(int $messageId): DataResponse {
+ try {
+ $this->chatManager->getComment($this->room, (string) $messageId);
+ } catch (NotFoundException) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ $this->reminderService->deleteReminder(
+ $this->participant->getAttendee()->getActorId(),
+ $this->room->getToken(),
+ $messageId,
);
return new DataResponse([], Http::STATUS_OK)