diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2022-05-13 13:09:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-13 13:09:23 +0200 |
commit | a4f3e569e2777c20b4ab497109bbf9e44a7eeed6 (patch) | |
tree | e1f7861a36e3d5338353365b11c66b9b32c39538 | |
parent | 2742779a7621e13bb92a059eda34b4a433f739f9 (diff) | |
parent | 866201678f95301ef37d2a93ea05707edb0f6927 (diff) |
Merge pull request #7331 from nextcloud/feature/6257/silent-send
Add an option for "Silent send" to the API
-rw-r--r-- | docs/capabilities.md | 1 | ||||
-rw-r--r-- | docs/chat.md | 1 | ||||
-rw-r--r-- | lib/Capabilities.php | 1 | ||||
-rw-r--r-- | lib/Chat/ChatManager.php | 12 | ||||
-rw-r--r-- | lib/Chat/Notifier.php | 35 | ||||
-rw-r--r-- | lib/Controller/ChatController.php | 5 | ||||
-rw-r--r-- | lib/Events/ChatParticipantEvent.php | 9 | ||||
-rw-r--r-- | lib/Flow/Operation.php | 3 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/FeatureContext.php | 13 | ||||
-rw-r--r-- | tests/integration/features/chat/notifications.feature | 35 | ||||
-rw-r--r-- | tests/php/CapabilitiesTest.php | 1 | ||||
-rw-r--r-- | tests/php/Chat/ChatManagerTest.php | 2 | ||||
-rw-r--r-- | tests/php/Chat/NotifierTest.php | 4 |
13 files changed, 94 insertions, 28 deletions
diff --git a/docs/capabilities.md b/docs/capabilities.md index ef882095d..6208c6412 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -95,5 +95,6 @@ title: Capabilities ## 15 * `chat-permission` - When permission 128 is required to post chat messages, reaction or share items to the conversation +* `silent-send` - Whether the chat API allows to send chat messages without triggering notifications * `sip-support-nopin` - Whether SIP can be configured to not require a custom attendee PIN * `config => call => enabled` - Whether calling is enabled on the instance or not diff --git a/docs/chat.md b/docs/chat.md index adbc9af52..13be99302 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -79,6 +79,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1` `actorDisplayName` | string | Guest display name (ignored for logged in users) `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 (only available with `chat-reference-id` capability) + `slient` | bool | If sent silent the message will not create chat notifications even for mentions (only available with `silent-send` capability) * Response: - Status code: diff --git a/lib/Capabilities.php b/lib/Capabilities.php index 3b1433f22..18b4b2362 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -103,6 +103,7 @@ class Capabilities implements IPublicCapability { 'rich-object-list-media', 'rich-object-delete', 'chat-permission', + 'silent-send', ], 'config' => [ 'attachments' => [ diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 398a5f6cf..b20021f75 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -179,7 +179,7 @@ class ChatManager { } if ($sendNotifications) { - $this->notifier->notifyOtherParticipant($chat, $comment, []); + $this->notifier->notifyOtherParticipant($chat, $comment, [], false); } $this->dispatcher->dispatch(self::EVENT_AFTER_SYSTEM_MESSAGE_SEND, $event); @@ -238,7 +238,7 @@ class ChatManager { * @param string $referenceId * @return IComment */ - public function sendMessage(Room $chat, Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId): IComment { + public function sendMessage(Room $chat, Participant $participant, string $actorType, string $actorId, string $message, \DateTime $creationDateTime, ?IComment $replyTo, string $referenceId, bool $silent): IComment { $comment = $this->commentsManager->create($actorType, $actorId, 'chat', (string) $chat->getId()); $comment->setMessage($message, self::MAX_CHAT_LENGTH); $comment->setCreationDateTime($creationDateTime); @@ -255,7 +255,7 @@ class ChatManager { $comment->setReferenceId($referenceId); } - $event = new ChatParticipantEvent($chat, $comment, $participant); + $event = new ChatParticipantEvent($chat, $comment, $participant, $silent); $this->dispatcher->dispatch(self::EVENT_BEFORE_MESSAGE_SEND, $event); $shouldFlush = $this->notificationManager->defer(); @@ -273,20 +273,20 @@ class ChatManager { $alreadyNotifiedUsers = []; $usersDirectlyMentioned = $this->notifier->getMentionedUserIds($comment); if ($replyTo instanceof IComment) { - $alreadyNotifiedUsers = $this->notifier->notifyReplyToAuthor($chat, $comment, $replyTo); + $alreadyNotifiedUsers = $this->notifier->notifyReplyToAuthor($chat, $comment, $replyTo, $silent); if ($replyTo->getActorType() === Attendee::ACTOR_USERS) { $usersDirectlyMentioned[] = $replyTo->getActorId(); } } - $alreadyNotifiedUsers = $this->notifier->notifyMentionedUsers($chat, $comment, $alreadyNotifiedUsers); + $alreadyNotifiedUsers = $this->notifier->notifyMentionedUsers($chat, $comment, $alreadyNotifiedUsers, $silent); if (!empty($alreadyNotifiedUsers)) { $userIds = array_column($alreadyNotifiedUsers, 'id'); $this->participantService->markUsersAsMentioned($chat, $userIds, (int) $comment->getId(), $usersDirectlyMentioned); } // User was not mentioned, send a normal notification - $this->notifier->notifyOtherParticipant($chat, $comment, $alreadyNotifiedUsers); + $this->notifier->notifyOtherParticipant($chat, $comment, $alreadyNotifiedUsers, $silent); $this->dispatcher->dispatch(self::EVENT_AFTER_MESSAGE_SEND, $event); } catch (NotFoundException $e) { diff --git a/lib/Chat/Notifier.php b/lib/Chat/Notifier.php index 7891db273..5874d05bf 100644 --- a/lib/Chat/Notifier.php +++ b/lib/Chat/Notifier.php @@ -87,19 +87,25 @@ class Notifier { * @return string[] Users that were mentioned * @psalm-return array<int, array{id: string, type: string, ?attendee: Attendee}> */ - public function notifyMentionedUsers(Room $chat, IComment $comment, array $alreadyNotifiedUsers): array { + public function notifyMentionedUsers(Room $chat, IComment $comment, array $alreadyNotifiedUsers, bool $silent): array { $usersToNotify = $this->getUsersToNotify($chat, $comment, $alreadyNotifiedUsers); if (!$usersToNotify) { return $alreadyNotifiedUsers; } - $notification = $this->createNotification($chat, $comment, 'mention'); - $shouldFlush = $this->notificationManager->defer(); + + $shouldFlush = false; + if (!$silent) { + $notification = $this->createNotification($chat, $comment, 'mention'); + $shouldFlush = $this->notificationManager->defer(); + } foreach ($usersToNotify as $mentionedUser) { if ($this->shouldMentionedUserBeNotified($mentionedUser['id'], $comment, $chat, $mentionedUser['attendee'] ?? null)) { - $notification->setUser($mentionedUser['id']); - $this->notificationManager->notify($notification); + if (!$silent) { + $notification->setUser($mentionedUser['id']); + $this->notificationManager->notify($notification); + } $alreadyNotifiedUsers[] = $mentionedUser; } } @@ -193,11 +199,11 @@ class Notifier { * @param Room $chat * @param IComment $comment * @param IComment $replyTo - * @param string $subject + * @param bool $silent * @return array[] Actor that was replied to * @psalm-return array<int, array{id: string, type: string}> */ - public function notifyReplyToAuthor(Room $chat, IComment $comment, IComment $replyTo, string $subject = 'reply'): array { + public function notifyReplyToAuthor(Room $chat, IComment $comment, IComment $replyTo, bool $silent): array { if ($replyTo->getActorType() !== Attendee::ACTOR_USERS) { // No reply notification when the replyTo-author was not a user return []; @@ -207,9 +213,11 @@ class Notifier { return []; } - $notification = $this->createNotification($chat, $comment, $subject); - $notification->setUser($replyTo->getActorId()); - $this->notificationManager->notify($notification); + if (!$silent) { + $notification = $this->createNotification($chat, $comment, 'reply'); + $notification->setUser($replyTo->getActorId()); + $this->notificationManager->notify($notification); + } return [ [ @@ -231,9 +239,14 @@ class Notifier { * @param Room $chat * @param IComment $comment * @param array[] $alreadyNotifiedUsers + * @param bool $silent * @psalm-param array<int, array{id: string, type: string, ?attendee: Attendee}> $alreadyNotifiedUsers */ - public function notifyOtherParticipant(Room $chat, IComment $comment, array $alreadyNotifiedUsers): void { + public function notifyOtherParticipant(Room $chat, IComment $comment, array $alreadyNotifiedUsers, bool $silent): void { + if ($silent) { + return; + } + $participants = $this->participantService->getParticipantsByNotificationLevel($chat, Participant::NOTIFY_ALWAYS); $notification = $this->createNotification($chat, $comment, 'chat'); diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 8e508d0b0..f0033649c 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -192,11 +192,12 @@ class ChatController extends AEnvironmentAwareController { * @param string $actorDisplayName for guests * @param string $referenceId for the message to be able to later identify it again * @param int $replyTo Parent id which this message is a reply to + * @param bool $silent If sent silent the chat message will not create any notifications * @return DataResponse the status code is "201 Created" if successful, and * "404 Not found" if the room or session for a guest user was not * found". */ - public function sendMessage(string $message, string $actorDisplayName = '', string $referenceId = '', int $replyTo = 0): DataResponse { + public function sendMessage(string $message, string $actorDisplayName = '', string $referenceId = '', int $replyTo = 0, bool $silent = false): DataResponse { [$actorType, $actorId] = $this->getActorInfo($actorDisplayName); if (!$actorId) { return new DataResponse([], Http::STATUS_NOT_FOUND); @@ -222,7 +223,7 @@ class ChatController extends AEnvironmentAwareController { $creationDateTime = $this->timeFactory->getDateTime('now', new \DateTimeZone('UTC')); try { - $comment = $this->chatManager->sendMessage($this->room, $this->participant, $actorType, $actorId, $message, $creationDateTime, $parent, $referenceId); + $comment = $this->chatManager->sendMessage($this->room, $this->participant, $actorType, $actorId, $message, $creationDateTime, $parent, $referenceId, $silent); } catch (MessageTooLongException $e) { return new DataResponse([], Http::STATUS_REQUEST_ENTITY_TOO_LARGE); } catch (\Exception $e) { diff --git a/lib/Events/ChatParticipantEvent.php b/lib/Events/ChatParticipantEvent.php index b33dce216..0a7c967ed 100644 --- a/lib/Events/ChatParticipantEvent.php +++ b/lib/Events/ChatParticipantEvent.php @@ -29,14 +29,19 @@ use OCP\Comments\IComment; class ChatParticipantEvent extends ChatEvent { protected Participant $participant; + protected bool $silent; - - public function __construct(Room $room, IComment $message, Participant $participant) { + public function __construct(Room $room, IComment $message, Participant $participant, bool $silent) { parent::__construct($room, $message); $this->participant = $participant; + $this->silent = $silent; } public function getParticipant(): Participant { return $this->participant; } + + public function isSilentMessage(): bool { + return $this->silent; + } } diff --git a/lib/Flow/Operation.php b/lib/Flow/Operation.php index bae71fd12..c130722c1 100644 --- a/lib/Flow/Operation.php +++ b/lib/Flow/Operation.php @@ -126,7 +126,8 @@ class Operation implements IOperation { $this->prepareMention($mode, $participant) . $message, new \DateTime(), null, - '' + '', + false ); } catch (UnexpectedValueException $e) { continue; diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 65e30539f..264a57629 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -1399,19 +1399,26 @@ class FeatureContext implements Context, SnippetAcceptingContext { } /** - * @Then /^user "([^"]*)" sends message "([^"]*)" to room "([^"]*)" with (\d+)(?: \((v1)\))?$/ + * @Then /^user "([^"]*)" (silent sends|sends) message "([^"]*)" to room "([^"]*)" with (\d+)(?: \((v1)\))?$/ * * @param string $user + * @param string $sendingMode * @param string $message * @param string $identifier * @param string $statusCode * @param string $apiVersion */ - public function userSendsMessageToRoom($user, $message, $identifier, $statusCode, $apiVersion = 'v1') { + public function userSendsMessageToRoom(string $user, string $sendingMode, string $message, string $identifier, string $statusCode, string $apiVersion = 'v1') { + if ($sendingMode === 'silent sends') { + $body = new TableNode([['message', $message], ['silent', true]]); + } else { + $body = new TableNode([['message', $message]]); + } + $this->setCurrentUser($user); $this->sendRequest( 'POST', '/apps/spreed/api/' . $apiVersion . '/chat/' . self::$identifierToToken[$identifier], - new TableNode([['message', $message]]) + $body ); $this->assertStatusCode($this->response, $statusCode); sleep(1); // make sure Postgres manages the order of the messages diff --git a/tests/integration/features/chat/notifications.feature b/tests/integration/features/chat/notifications.feature index 4d62db44c..8301f8345 100644 --- a/tests/integration/features/chat/notifications.feature +++ b/tests/integration/features/chat/notifications.feature @@ -25,6 +25,17 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | one-to-one room/Message 1 | participant1-displayname sent you a private message | + Scenario: Silent sent message when recipient is offline in the one-to-one + When user "participant1" creates room "one-to-one room" (v4) + | roomType | 1 | + | invite | participant2 | + # Join and leave to clear the invite notification + Given user "participant2" joins room "one-to-one room" with 200 (v4) + Given user "participant2" leaves room "one-to-one room" with 200 (v4) + When user "participant1" silent sends message "Message 1" to room "one-to-one room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + Scenario: Normal message when recipient disabled notifications in the one-to-one When user "participant1" creates room "one-to-one room" (v4) | roomType | 1 | @@ -152,6 +163,18 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | room/Hi @participant2 bye | participant1-displayname mentioned you in conversation room | + Scenario: Silent mention when recipient is online in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + When user "participant1" silent sends message "Hi @participant2 bye" to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + Scenario: Mention when recipient is offline in the group room When user "participant1" creates room "room" (v4) | roomType | 2 | @@ -202,6 +225,18 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + Scenario: Silent at-all when recipient is offline in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + When user "participant1" silent sends message "Hi @all bye" to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + Scenario: At-all when recipient with disabled notifications in the group room When user "participant1" creates room "room" (v4) | roomType | 2 | diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php index e64a66c9d..60cb07ab2 100644 --- a/tests/php/CapabilitiesTest.php +++ b/tests/php/CapabilitiesTest.php @@ -107,6 +107,7 @@ class CapabilitiesTest extends TestCase { 'rich-object-list-media', 'rich-object-delete', 'chat-permission', + 'silent-send', 'reactions', ]; } diff --git a/tests/php/Chat/ChatManagerTest.php b/tests/php/Chat/ChatManagerTest.php index 86750e8c4..e0e79869f 100644 --- a/tests/php/Chat/ChatManagerTest.php +++ b/tests/php/Chat/ChatManagerTest.php @@ -275,7 +275,7 @@ class ChatManagerTest extends TestCase { $participant = $this->createMock(Participant::class); - $return = $this->chatManager->sendMessage($chat, $participant, 'users', $userId, $message, $creationDateTime, $replyTo, $referenceId); + $return = $this->chatManager->sendMessage($chat, $participant, 'users', $userId, $message, $creationDateTime, $replyTo, $referenceId, false); $this->assertCommentEquals($commentExpected, $return); } diff --git a/tests/php/Chat/NotifierTest.php b/tests/php/Chat/NotifierTest.php index 0acec8008..25133491b 100644 --- a/tests/php/Chat/NotifierTest.php +++ b/tests/php/Chat/NotifierTest.php @@ -177,7 +177,7 @@ class NotifierTest extends TestCase { 'type' => Attendee::ACTOR_USERS, ]; }, $expectedReturn); - $actual = $notifier->notifyMentionedUsers($room, $comment, $alreadyNotifiedUsers); + $actual = $notifier->notifyMentionedUsers($room, $comment, $alreadyNotifiedUsers, false); $this->assertEqualsCanonicalizing($expectedReturn, $actual); } @@ -341,7 +341,7 @@ class NotifierTest extends TestCase { $this->assertCount(count($return), $actual); foreach ($actual as $key => $value) { $this->assertIsArray($value); - if (key_exists('attendee', $valu |