Browse Source

feat(chat): Store and expose whether a message or call was silent

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/11449/head
Joas Schilling 2 years ago
parent
commit
0d1fc13e36
No known key found for this signature in database GPG Key ID: 74434EFE0D2E2205
  1. 1
      docs/capabilities.md
  2. 11
      docs/chat.md
  3. 1
      lib/Capabilities.php
  4. 22
      lib/Chat/ChatManager.php
  5. 13
      lib/Chat/Parser/SystemMessage.php
  6. 3
      lib/Chat/SystemMessage/Listener.php
  7. 5
      lib/Model/Message.php
  8. 1
      lib/ResponseDefinitions.php
  9. 3
      openapi-backend-sipbridge.json
  10. 3
      openapi-federation.json
  11. 3
      openapi-full.json
  12. 3
      openapi.json
  13. 11
      tests/integration/features/bootstrap/FeatureContext.php
  14. 10
      tests/integration/features/callapi/notifications.feature
  15. 6
      tests/integration/features/chat-1/notifications.feature
  16. 1
      tests/php/CapabilitiesTest.php

1
docs/capabilities.md

@ -142,3 +142,4 @@
## 19
* `delete-messages-unlimited` - Whether messages can be deleted at any time (used to be restricted to 6 hours after posting)
* `edit-messages` - Whether messages can be edited (restricted to 24 hours after posting)
* `silent-send-state` - Whether messages contain a flag that they were sent silently

11
docs/chat.md

@ -44,7 +44,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: since Nextcloud 13
Array of messages, each message has at least:
| field | type | Description |
|----------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|----------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | int | ID of the comment |
| `token` | string | Conversation token |
| `actorType` | string | See [Constants - Actor types of chat messages](constants.md#actor-types-of-chat-messages) |
@ -62,10 +62,11 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: since Nextcloud 13
| `reactions` | int[] | **Optional:** An array map with relation between reaction emoji and total count of reactions with this emoji |
| `reactionsSelf` | string[] | **Optional:** When the user reacted this is the list of emojis the user reacted with |
| `markdown` | bool | **Optional:** Whether the message should be rendered as markdown or shown as plain text |
| `lastEditActorType` | string | Actor type of the last editing author - See [Constants - Actor types of chat messages](constants.md#actor-types-of-chat-messages) (only available with `edit-messages` capability and when the message was actually edited) |
| `lastEditActorId` | string | Actor id of the last editing author (only available with `edit-messages` capability and when the message was actually edited) |
| `lastEditActorDisplayName` | string | Display name of the last editing author (only available with `edit-messages` capability and when the message was actually edited) (can be empty for type `deleted_users` and `guests`) |
| `lastEditTimestamp` | int | Unix time stamp when the message was last edited (only available with `edit-messages` capability and when the message was actually edited) |
| `lastEditActorType` | string | **Optional:** Actor type of the last editing author - See [Constants - Actor types of chat messages](constants.md#actor-types-of-chat-messages) (only available with `edit-messages` capability and when the message was actually edited) |
| `lastEditActorId` | string | **Optional:** Actor id of the last editing author (only available with `edit-messages` capability and when the message was actually edited) |
| `lastEditActorDisplayName` | string | **Optional:** Display name of the last editing author (only available with `edit-messages` capability and when the message was actually edited) (can be empty for type `deleted_users` and `guests`) |
| `lastEditTimestamp` | int | **Optional:** Unix time stamp when the message was last edited (only available with `edit-messages` capability and when the message was actually edited) |
| `silent` | bool | **Optional:** Whether the message was sent silently (only available with `silent-send-state` capability) |
#### Parent data

1
lib/Capabilities.php

@ -172,6 +172,7 @@ class Capabilities implements IPublicCapability {
'sip-support-dialout',
'delete-messages-unlimited',
'edit-messages',
'silent-send-state',
],
'config' => [
'attachments' => [

22
lib/Chat/ChatManager.php

@ -153,6 +153,12 @@ class ChatManager {
$comment->setVerb(self::VERB_SYSTEM);
}
if ($silent) {
$comment->setMetaData([
'silent' => true,
]);
}
$this->setMessageExpiration($chat, $comment);
$shouldFlush = $this->notificationManager->defer();
@ -291,6 +297,12 @@ class ChatManager {
}
}
if ($silent) {
$comment->setMetaData([
'silent' => true,
]);
}
$event = new BeforeChatMessageSentEvent($chat, $comment, $participant, $silent);
$this->dispatcher->dispatchTyped($event);
@ -509,19 +521,25 @@ class ChatManager {
$metaData['last_edited_time'] = $editTime->getTimestamp();
$comment->setMetaData($metaData);
$wasSilent = $metaData['silent'] ?? false;
if (!$wasSilent) {
$mentionsBefore = $comment->getMentions();
$usersDirectlyMentionedBefore = $this->notifier->getMentionedUserIds($comment);
$usersToNotifyBefore = $this->notifier->getUsersToNotify($chat, $comment, []);
}
$comment->setMessage($message, self::MAX_CHAT_LENGTH);
if (!$wasSilent) {
$mentionsAfter = $comment->getMentions();
}
$this->commentsManager->save($comment);
$this->referenceManager->invalidateCache($chat->getToken());
if (!$wasSilent) {
$removedMentions = empty($mentionsAfter) ? $mentionsBefore : array_udiff($mentionsBefore, $mentionsAfter, [$this, 'compareMention']);
$addedMentions = empty($mentionsBefore) ? $mentionsAfter : array_udiff($mentionsAfter, $mentionsBefore, [$this, 'compareMention']);
// FIXME Not needed when it was silent, once it's stored in metadata
if (!empty($removedMentions)) {
$usersToNotifyAfter = $this->notifier->getUsersToNotify($chat, $comment, []);
$removedUsersMentioned = array_udiff($usersToNotifyBefore, $usersToNotifyAfter, [$this, 'compareMention']);
@ -529,7 +547,6 @@ class ChatManager {
$this->notifier->removeMentionNotificationAfterEdit($chat, $comment, $userIds);
}
// FIXME silent support, once it's stored in metadata
if (!empty($addedMentions)) {
$usersDirectlyMentionedAfter = $this->notifier->getMentionedUserIds($comment);
$addedUsersDirectMentioned = array_diff($usersDirectlyMentionedAfter, $usersDirectlyMentionedBefore);
@ -540,6 +557,7 @@ class ChatManager {
$this->participantService->markUsersAsMentioned($chat, $userIds, (int) $comment->getId(), $addedUsersDirectMentioned);
}
}
}
return $this->addSystemMessage(
$chat,

13
lib/Chat/Parser/SystemMessage.php

@ -183,9 +183,20 @@ class SystemMessage implements IEventListener {
$parsedMessage = $this->l->t('An administrator removed the description');
}
} elseif ($message === 'call_started') {
$parsedMessage = $this->l->t('{actor} started a call');
$metaData = $comment->getMetaData() ?? [];
$silentCall = $metaData['silent'] ?? false;
if ($silentCall) {
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You started a silent call');
} else {
$parsedMessage = $this->l->t('{actor} started a silent call');
}
} else {
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You started a call');
} else {
$parsedMessage = $this->l->t('{actor} started a call');
}
}
} elseif ($message === 'call_joined') {
$parsedMessage = $this->l->t('{actor} joined the call');

3
lib/Chat/SystemMessage/Listener.php

@ -134,7 +134,8 @@ class Listener implements IEventListener {
if ($this->participantService->hasActiveSessionsInCall($event->getRoom())) {
$this->sendSystemMessage($event->getRoom(), 'call_joined', [], $event->getParticipant());
} else {
$this->sendSystemMessage($event->getRoom(), 'call_started', [], $event->getParticipant());
$silent = $event->getDetail(AParticipantModifiedEvent::DETAIL_IN_CALL_SILENT) ?? false;
$this->sendSystemMessage($event->getRoom(), 'call_started', [], $event->getParticipant(), silent: $silent);
}
}

5
lib/Model/Message.php

@ -220,6 +220,11 @@ class Message {
$data['deleted'] = true;
}
$metaData = $this->comment->getMetaData() ?? [];
if ($metaData['silent']) {
$data['silent'] = true;
}
return $data;
}
}

1
lib/ResponseDefinitions.php

@ -87,6 +87,7 @@ namespace OCA\Talk;
* lastEditActorId?: string,
* lastEditActorType?: string,
* lastEditTimestamp?: int,
* silent?: bool,
* }
*
* @psalm-type TalkChatMessageWithParent = TalkChatMessage&array{parent?: TalkChatMessage}

3
openapi-backend-sipbridge.json

@ -116,6 +116,9 @@
"lastEditTimestamp": {
"type": "integer",
"format": "int64"
},
"silent": {
"type": "boolean"
}
}
},

3
openapi-federation.json

@ -116,6 +116,9 @@
"lastEditTimestamp": {
"type": "integer",
"format": "int64"
},
"silent": {
"type": "boolean"
}
}
},

3
openapi-full.json

@ -273,6 +273,9 @@
"lastEditTimestamp": {
"type": "integer",
"format": "int64"
},
"silent": {
"type": "boolean"
}
}
},

3
openapi.json

@ -214,6 +214,9 @@
"lastEditTimestamp": {
"type": "integer",
"format": "int64"
},
"silent": {
"type": "boolean"
}
}
},

11
tests/integration/features/bootstrap/FeatureContext.php

@ -2648,7 +2648,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
}
}
Assert::assertEquals($expected, array_map(function ($message) use ($includeParents, $includeReferenceId, $includeReactions, $includeReactionsSelf, $includeLastEdit) {
Assert::assertEquals($expected, array_map(function ($message, $expected) use ($includeParents, $includeReferenceId, $includeReactions, $includeReactionsSelf, $includeLastEdit) {
$data = [
'room' => self::$tokenToIdentifier[$message['token']],
'actorType' => $message['actorType'],
@ -2665,6 +2665,9 @@ class FeatureContext implements Context, SnippetAcceptingContext {
if ($includeReferenceId) {
$data['referenceId'] = $message['referenceId'];
}
if (isset($expected['silent'])) {
$data['silent'] = isset($message['silent']) ? json_encode($message['silent']) : '!ISSET';
}
if ($includeReactions) {
$data['reactions'] = json_encode($message['reactions'], JSON_UNESCAPED_UNICODE);
}
@ -2684,7 +2687,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
}
}
return $data;
}, $messages));
}, $messages, $expected));
}
/**
@ -2773,6 +2776,10 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$data['messageParameters'] = json_encode($message['messageParameters']);
}
if (isset($expected['silent'])) {
$data['silent'] = isset($message['silent']) ? json_encode($message['silent']) : '!ISSET';
}
return $data;
}, $messages, $expected));
}

10
tests/integration/features/callapi/notifications.feature

@ -27,6 +27,11 @@ Feature: callapi/notifications
Given user "participant1" joins room "room" with 200 (v4)
Given user "participant2" joins room "room" with 200 (v4)
Given user "participant1" joins call "room" with 200 (v4)
Then user "participant2" sees the following system messages in room "room" with 200
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
| room | users | participant1 | call_started | {actor} started a call | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
| room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
| room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
Then user "participant2" has the following notifications
| app | object_type | object_id | subject |
| spreed | call | room | A group call has started in room |
@ -44,6 +49,11 @@ Feature: callapi/notifications
Given user "participant2" joins room "room" with 200 (v4)
Given user "participant1" joins call "room" with 200 (v4)
| silent | true |
Then user "participant2" sees the following system messages in room "room" with 200
| room | actorType | actorId | systemMessage | message | silent | messageParameters |
| room | users | participant1 | call_started | {actor} started a silent call | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
| room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"user":{"type":"user","id":"participant2","name":"participant2-displayname"}} |
| room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
Then user "participant2" has the following notifications
| app | object_type | object_id | subject |
Given user "participant1" leaves call "room" with 200 (v4)

6
tests/integration/features/chat-1/notifications.feature

@ -43,6 +43,9 @@ Feature: chat/notifications
Then user "participant2" has the following notifications
| app | object_type | object_id | subject |
| spreed | chat | one-to-one room/Message 1 | participant1-displayname sent you a private message |
Then user "participant2" sees the following messages in room "one-to-one room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters | silent |
| one-to-one room | users | participant1 | participant1-displayname | Message 1 | [] | !ISSET |
Scenario: Silent sent message when recipient is offline in the one-to-one
When user "participant1" creates room "one-to-one room" (v4)
@ -54,6 +57,9 @@ Feature: chat/notifications
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 |
Then user "participant2" sees the following messages in room "one-to-one room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters | silent |
| one-to-one room | users | participant1 | participant1-displayname | Message 1 | [] | true |
Scenario: Normal message when recipient disabled notifications in the one-to-one
When user "participant1" creates room "one-to-one room" (v4)

1
tests/php/CapabilitiesTest.php

@ -146,6 +146,7 @@ class CapabilitiesTest extends TestCase {
'sip-support-dialout',
'delete-messages-unlimited',
'edit-messages',
'silent-send-state',
'message-expiration',
'reactions',
];

Loading…
Cancel
Save