diff --git a/appinfo/info.xml b/appinfo/info.xml
index 8af180d8da9..7eaad44889f 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
]]>
- 19.0.0-dev.3
+ 19.0.0-dev.4
agpl
Daniel Calviño Sánchez
diff --git a/docs/events.md b/docs/events.md
index ad21c67ff42..ea6bd421ec4 100644
--- a/docs/events.md
+++ b/docs/events.md
@@ -134,6 +134,7 @@ Used to parse mentions, replace parameters in messages with rich objects, transf
* Before event: `OCA\Talk\Events\BeforeChatMessageSentEvent`
* After event: `OCA\Talk\Events\ChatMessageSentEvent`
* Since: 18.0.0
+* Since: 19.0.0 - Method `getParent()` was added
### Duplicate share sent
@@ -153,6 +154,7 @@ listen to the `OCA\Talk\Events\SystemMessagesMultipleSentEvent` event instead.
* After event: `OCA\Talk\Events\SystemMessageSentEvent`
* Final event: `OCA\Talk\Events\SystemMessagesMultipleSentEvent` - Only sent once as per above explanation
* Since: 18.0.0
+* Since: 19.0.0 - Method `getParent()` was added
### Deprecated events
diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php
index 7a731213da3..6c0d3b4786d 100644
--- a/lib/Chat/ChatManager.php
+++ b/lib/Chat/ChatManager.php
@@ -34,6 +34,7 @@
use OCA\Talk\Exceptions\MessagingNotAllowedException;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Model\Attendee;
+use OCA\Talk\Model\Message;
use OCA\Talk\Model\Poll;
use OCA\Talk\Participant;
use OCA\Talk\Room;
@@ -165,7 +166,7 @@ public function addSystemMessage(
if ($silent) {
$comment->setMetaData([
- 'silent' => true,
+ Message::METADATA_SILENT => true,
]);
}
@@ -173,7 +174,7 @@ public function addSystemMessage(
$shouldFlush = $this->notificationManager->defer();
- $event = new BeforeSystemMessageSentEvent($chat, $comment, silent: $silent, skipLastActivityUpdate: $shouldSkipLastMessageUpdate);
+ $event = new BeforeSystemMessageSentEvent($chat, $comment, silent: $silent, skipLastActivityUpdate: $shouldSkipLastMessageUpdate, parent: $replyTo);
$this->dispatcher->dispatchTyped($event);
try {
$this->commentsManager->save($comment);
@@ -228,7 +229,7 @@ public function addSystemMessage(
}
}
- $event = new SystemMessageSentEvent($chat, $comment, silent: $silent, skipLastActivityUpdate: $shouldSkipLastMessageUpdate);
+ $event = new SystemMessageSentEvent($chat, $comment, silent: $silent, skipLastActivityUpdate: $shouldSkipLastMessageUpdate, parent: $replyTo);
$this->dispatcher->dispatchTyped($event);
} catch (NotFoundException $e) {
}
@@ -322,11 +323,11 @@ public function sendMessage(Room $chat, ?Participant $participant, string $actor
if ($silent) {
$comment->setMetaData([
- 'silent' => true,
+ Message::METADATA_SILENT => true,
]);
}
- $event = new BeforeChatMessageSentEvent($chat, $comment, $participant, $silent);
+ $event = new BeforeChatMessageSentEvent($chat, $comment, $participant, $silent, $replyTo);
$this->dispatcher->dispatchTyped($event);
$shouldFlush = $this->notificationManager->defer();
@@ -371,7 +372,7 @@ public function sendMessage(Room $chat, ?Participant $participant, string $actor
// User was not mentioned, send a normal notification
$this->notifier->notifyOtherParticipant($chat, $comment, $alreadyNotifiedUsers, $silent);
- $event = new ChatMessageSentEvent($chat, $comment, $participant, $silent);
+ $event = new ChatMessageSentEvent($chat, $comment, $participant, $silent, $replyTo);
$this->dispatcher->dispatchTyped($event);
} catch (NotFoundException $e) {
}
@@ -500,11 +501,11 @@ public function deleteMessage(Room $chat, IComment $comment, Participant $partic
$comment->setVerb(self::VERB_MESSAGE_DELETED);
$metaData = $comment->getMetaData() ?? [];
- if (isset($metaData['last_edited_by_type'])) {
+ if (isset($metaData[Message::METADATA_LAST_EDITED_BY_TYPE])) {
unset(
- $metaData['last_edited_by_type'],
- $metaData['last_edited_by_id'],
- $metaData['last_edited_time']
+ $metaData[Message::METADATA_LAST_EDITED_BY_TYPE],
+ $metaData[Message::METADATA_LAST_EDITED_BY_ID],
+ $metaData[Message::METADATA_LAST_EDITED_TIME],
);
$comment->setMetaData($metaData);
}
@@ -557,12 +558,12 @@ public function editMessage(Room $chat, IComment $comment, Participant $particip
}
$metaData = $comment->getMetaData() ?? [];
- $metaData['last_edited_by_type'] = $participant->getAttendee()->getActorType();
- $metaData['last_edited_by_id'] = $participant->getAttendee()->getActorId();
- $metaData['last_edited_time'] = $editTime->getTimestamp();
+ $metaData[Message::METADATA_LAST_EDITED_BY_TYPE] = $participant->getAttendee()->getActorType();
+ $metaData[Message::METADATA_LAST_EDITED_BY_ID] = $participant->getAttendee()->getActorId();
+ $metaData[Message::METADATA_LAST_EDITED_TIME] = $editTime->getTimestamp();
$comment->setMetaData($metaData);
- $wasSilent = $metaData['silent'] ?? false;
+ $wasSilent = $metaData[Message::METADATA_SILENT] ?? false;
if (!$wasSilent) {
$mentionsBefore = $comment->getMentions();
diff --git a/lib/Chat/MessageParser.php b/lib/Chat/MessageParser.php
index 77e42316a9b..de88ab506bc 100644
--- a/lib/Chat/MessageParser.php
+++ b/lib/Chat/MessageParser.php
@@ -29,6 +29,7 @@
use OCA\Talk\MatterbridgeManager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\Message;
+use OCA\Talk\Model\ProxyCacheMessage;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\BotService;
@@ -51,10 +52,10 @@ class MessageParser {
protected array $botNames = [];
public function __construct(
- protected IEventDispatcher $dispatcher,
- protected IUserManager $userManager,
+ protected IEventDispatcher $dispatcher,
+ protected IUserManager $userManager,
protected ParticipantService $participantService,
- protected BotService $botService,
+ protected BotService $botService,
) {
}
@@ -62,6 +63,25 @@ public function createMessage(Room $room, ?Participant $participant, IComment $c
return new Message($room, $participant, $comment, $l);
}
+ public function createMessageFromProxyCache(Room $room, ?Participant $participant, ProxyCacheMessage $proxy, IL10N $l): Message {
+ $message = new Message($room, $participant, null, $l, $proxy);
+
+ $message->setActor(
+ $proxy->getActorType(),
+ $proxy->getActorId(),
+ $proxy->getActorDisplayName(),
+ );
+
+ $message->setMessageType($proxy->getMessageType());
+
+ $message->setMessage(
+ $proxy->getMessage(),
+ $proxy->getParsedMessageParameters()
+ );
+
+ return $message;
+ }
+
public function parseMessage(Message $message): void {
$message->setMessage($message->getComment()->getMessage(), []);
diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php
index d7f48e61106..645e0e14765 100644
--- a/lib/Chat/Parser/SystemMessage.php
+++ b/lib/Chat/Parser/SystemMessage.php
@@ -207,7 +207,7 @@ protected function parseMessage(Message $chatMessage): void {
}
} elseif ($message === 'call_started') {
$metaData = $comment->getMetaData() ?? [];
- $silentCall = $metaData['silent'] ?? false;
+ $silentCall = $metaData[Message::METADATA_SILENT] ?? false;
if ($silentCall) {
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You started a silent call');
diff --git a/lib/Chat/Parser/UserMention.php b/lib/Chat/Parser/UserMention.php
index 257ae64eeaa..1b241088ea9 100644
--- a/lib/Chat/Parser/UserMention.php
+++ b/lib/Chat/Parser/UserMention.php
@@ -162,7 +162,7 @@ protected function parseMessage(Message $chatMessage): void {
$messageParameters[$mentionParameterId] = [
'type' => $mention['type'],
'id' => $chatMessage->getRoom()->getToken(),
- 'name' => $chatMessage->getRoom()->getDisplayName($userId),
+ 'name' => $chatMessage->getRoom()->getDisplayName($userId, true),
'call-type' => $this->getRoomType($chatMessage->getRoom()),
'icon-url' => $this->avatarService->getAvatarUrl($chatMessage->getRoom()),
];
diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php
index ddd680ab22d..8f0ff6839ad 100644
--- a/lib/Chat/SystemMessage/Listener.php
+++ b/lib/Chat/SystemMessage/Listener.php
@@ -43,6 +43,7 @@
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\BreakoutRoom;
+use OCA\Talk\Model\Message;
use OCA\Talk\Model\Session;
use OCA\Talk\Participant;
use OCA\Talk\Room;
@@ -425,8 +426,8 @@ protected function fixMimeTypeOfVoiceMessage(ShareCreatedEvent|BeforeDuplicateSh
}
}
- if (isset($metaData['silent'])) {
- $silent = (bool) $metaData['silent'];
+ if (isset($metaData[Message::METADATA_SILENT])) {
+ $silent = (bool) $metaData[Message::METADATA_SILENT];
} else {
$silent = false;
}
diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php
index 5b6a9804902..bad6ee7ea7e 100644
--- a/lib/Controller/ChatController.php
+++ b/lib/Controller/ChatController.php
@@ -530,7 +530,7 @@ protected function prepareCommentsAsDataResponse(array $comments, int $lastCommo
$message = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
$this->messageParser->parseMessage($message);
- $expireDate = $message->getComment()->getExpireDate();
+ $expireDate = $message->getExpirationDateTime();
if ($expireDate instanceof \DateTime && $expireDate < $now) {
$commentIdToIndex[$id] = null;
continue;
diff --git a/lib/Events/AMessageSentEvent.php b/lib/Events/AMessageSentEvent.php
index 8f3654c3121..9c8a5d7f47b 100644
--- a/lib/Events/AMessageSentEvent.php
+++ b/lib/Events/AMessageSentEvent.php
@@ -33,6 +33,7 @@ public function __construct(
protected IComment $comment,
protected ?Participant $participant = null,
protected bool $silent = false,
+ protected ?IComment $parent = null,
) {
parent::__construct(
$room,
@@ -50,4 +51,8 @@ public function getParticipant(): ?Participant {
public function isSilentMessage(): bool {
return $this->silent;
}
+
+ public function getParent(): ?IComment {
+ return $this->parent;
+ }
}
diff --git a/lib/Events/ASystemMessageSentEvent.php b/lib/Events/ASystemMessageSentEvent.php
index a540edc5aa9..4dd55374b9d 100644
--- a/lib/Events/ASystemMessageSentEvent.php
+++ b/lib/Events/ASystemMessageSentEvent.php
@@ -33,13 +33,15 @@ public function __construct(
IComment $comment,
?Participant $participant = null,
bool $silent = false,
+ ?IComment $parent = null,
protected bool $skipLastActivityUpdate = false,
) {
parent::__construct(
$room,
$comment,
$participant,
- $silent
+ $silent,
+ $parent,
);
}
diff --git a/lib/Federation/BackendNotifier.php b/lib/Federation/BackendNotifier.php
index 1b13b260ee7..ccb422b23bd 100644
--- a/lib/Federation/BackendNotifier.php
+++ b/lib/Federation/BackendNotifier.php
@@ -313,6 +313,7 @@ public function sendRoomModifiedUpdate(
* Send information to remote participants that a message was posted
* Sent from Host server to Remote participant server
*
+ * @param array{remoteMessageId: int, actorType: string, actorId: string, actorDisplayName: string, messageType: string, systemMessage: string, expirationDatetime: string, message: string, messageParameter: string, creationDatetime: string, metaData: string} $messageData
* @param array{unreadMessages: int, unreadMention: bool, unreadMentionDirect: bool} $unreadInfo
*/
public function sendMessageUpdate(
diff --git a/lib/Federation/CloudFederationProviderTalk.php b/lib/Federation/CloudFederationProviderTalk.php
index adf439b65c2..abe0dccd632 100644
--- a/lib/Federation/CloudFederationProviderTalk.php
+++ b/lib/Federation/CloudFederationProviderTalk.php
@@ -34,13 +34,15 @@
use OCA\Talk\Events\AttendeesAddedEvent;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
+use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\AttendeeMapper;
use OCA\Talk\Model\Invitation;
use OCA\Talk\Model\InvitationMapper;
-use OCA\Talk\Model\ProxyCacheMessages;
-use OCA\Talk\Model\ProxyCacheMessagesMapper;
+use OCA\Talk\Model\ProxyCacheMessage;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
+use OCA\Talk\Notification\FederationChatNotifier;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
@@ -87,7 +89,9 @@ public function __construct(
private ISession $session,
private IEventDispatcher $dispatcher,
private LoggerInterface $logger,
- private ProxyCacheMessagesMapper $proxyCacheMessagesMapper,
+ private ProxyCacheMessageMapper $proxyCacheMessageMapper,
+ private FederationChatNotifier $federationChatNotifier,
+ private UserConverter $userConverter,
ICacheFactory $cacheFactory,
) {
$this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed('talk/pcm/') : null;
@@ -316,7 +320,7 @@ private function roomModified(int $remoteAttendeeId, array $notification): array
/**
* @param int $remoteAttendeeId
- * @param array{remoteServerUrl: string, sharedSecret: string, remoteToken: string, messageData: array{remoteMessageId: int, actorType: string, actorId: string, actorDisplayName: string, messageType: string, systemMessage: string, expirationDatetime: string, message: string, messageParameter: string}, unreadInfo: array{unreadMessages: int, unreadMention: bool, unreadMentionDirect: bool}} $notification
+ * @param array{remoteServerUrl: string, sharedSecret: string, remoteToken: string, messageData: array{remoteMessageId: int, actorType: string, actorId: string, actorDisplayName: string, messageType: string, systemMessage: string, expirationDatetime: string, message: string, messageParameter: string, creationDatetime: string, metaData: string}, unreadInfo: array{unreadMessages: int, unreadMention: bool, unreadMentionDirect: bool}} $notification
* @return array
* @throws ActionNotSupportedException
* @throws AuthenticationFailedException
@@ -335,7 +339,7 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra
throw new ShareNotFound();
}
- $message = new ProxyCacheMessages();
+ $message = new ProxyCacheMessage();
$message->setLocalToken($room->getToken());
$message->setRemoteServerUrl($notification['remoteServerUrl']);
$message->setRemoteToken($notification['remoteToken']);
@@ -346,12 +350,25 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra
$message->setMessageType($notification['messageData']['messageType']);
$message->setSystemMessage($notification['messageData']['systemMessage']);
if ($notification['messageData']['expirationDatetime']) {
- $message->setExpirationDatetime(new \DateTimeImmutable($notification['messageData']['expirationDatetime']));
+ $message->setExpirationDatetime(new \DateTime($notification['messageData']['expirationDatetime']));
}
+
+ // We transform the parameters when storing in the PCM, so we only have
+ // to do it once for each message.
+ $convertedParameters = $this->userConverter->convertMessageParameters($room, [
+ 'message' => $notification['messageData']['message'],
+ 'messageParameters' => json_decode($notification['messageData']['messageParameter'], true, flags: JSON_THROW_ON_ERROR),
+ ]);
+ $notification['messageData']['message'] = $convertedParameters['message'];
+ $notification['messageData']['messageParameter'] = json_encode($convertedParameters['messageParameters'], JSON_THROW_ON_ERROR);
+
$message->setMessage($notification['messageData']['message']);
$message->setMessageParameters($notification['messageData']['messageParameter']);
+ $message->setCreationDatetime(new \DateTime($notification['messageData']['creationDatetime']));
+ $message->setMetaData($notification['messageData']['metaData']);
+
try {
- $this->proxyCacheMessagesMapper->insert($message);
+ $this->proxyCacheMessageMapper->insert($message);
$lastMessageId = $room->getLastMessageId();
if ($notification['messageData']['remoteMessageId'] > $lastMessageId) {
@@ -374,6 +391,12 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra
$this->logger->error('Error saving proxy cache message failed: ' . $e->getMessage(), ['exception' => $e]);
throw $e;
}
+
+ $message = $this->proxyCacheMessageMapper->findByRemote(
+ $notification['remoteServerUrl'],
+ $notification['remoteToken'],
+ $notification['messageData']['remoteMessageId'],
+ );
}
try {
@@ -390,6 +413,8 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra
$notification['unreadInfo']['unreadMentionDirect'],
);
+ $this->federationChatNotifier->handleChatMessage($room, $participant, $message, $notification);
+
return [];
}
diff --git a/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php b/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php
index e6920cc0654..fb795b80425 100644
--- a/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php
+++ b/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php
@@ -32,7 +32,9 @@
use OCA\Talk\Events\SystemMessagesMultipleSentEvent;
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Model\Attendee;
+use OCA\Talk\Model\ProxyCacheMessage;
use OCA\Talk\Service\ParticipantService;
+use OCP\Comments\IComment;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Federation\ICloudIdManager;
@@ -78,6 +80,14 @@ public function handle(Event $event): void {
}
$expireDate = $event->getComment()->getExpireDate();
+ $creationDate = $event->getComment()->getCreationDateTime();
+
+ $metaData = $event->getComment()->getMetaData() ?? [];
+ $parent = $event->getParent();
+ if ($parent instanceof IComment) {
+ $metaData[ProxyCacheMessage::METADATA_REPLYTO_TYPE] = $parent->getActorType();
+ $metaData[ProxyCacheMessage::METADATA_REPLYTO_ID] = $parent->getActorId();
+ }
$messageData = [
'remoteMessageId' => (int) $event->getComment()->getId(),
@@ -88,7 +98,9 @@ public function handle(Event $event): void {
'systemMessage' => $chatMessage->getMessageType() === ChatManager::VERB_SYSTEM ? $chatMessage->getMessageRaw() : '',
'expirationDatetime' => $expireDate ? $expireDate->format(\DateTime::ATOM) : '',
'message' => $chatMessage->getMessage(),
- 'messageParameter' => json_encode($chatMessage->getMessageParameters()),
+ 'messageParameter' => json_encode($chatMessage->getMessageParameters(), JSON_THROW_ON_ERROR),
+ 'creationDatetime' => $creationDate->format(\DateTime::ATOM),
+ 'metaData' => json_encode($metaData, JSON_THROW_ON_ERROR),
];
$participants = $this->participantService->getParticipantsByActorType($event->getRoom(), Attendee::ACTOR_FEDERATED_USERS);
diff --git a/lib/Federation/Proxy/TalkV1/UserConverter.php b/lib/Federation/Proxy/TalkV1/UserConverter.php
index debc018839a..d58aa8e4004 100644
--- a/lib/Federation/Proxy/TalkV1/UserConverter.php
+++ b/lib/Federation/Proxy/TalkV1/UserConverter.php
@@ -45,6 +45,26 @@ public function __construct(
) {
}
+ /**
+ * @return array{type: string, id: string}
+ */
+ public function convertTypeAndId(Room $room, string $type, string $id): array {
+ if ($type === Attendee::ACTOR_USERS) {
+ $type = Attendee::ACTOR_FEDERATED_USERS;
+ $id .= '@' . $room->getRemoteServer();
+ } elseif ($type === Attendee::ACTOR_FEDERATED_USERS) {
+ $localParticipants = $this->getLocalParticipants($room);
+ if (isset($localParticipants[$id])) {
+ $local = $localParticipants[$id];
+
+ $type = Attendee::ACTOR_USERS;
+ $id = $local['userId'];
+ }
+ }
+
+ return ['type' => $type, 'id' => $id];
+ }
+
public function convertAttendee(Room $room, array $entry, string $typeField, string $idField, string $displayNameField): array {
if (!isset($entry[$typeField])) {
return $entry;
@@ -89,7 +109,7 @@ protected function convertMessageParameter(Room $room, array $parameter): array
return $parameter;
}
- protected function convertMessageParameters(Room $room, array $message): array {
+ public function convertMessageParameters(Room $room, array $message): array {
$message['messageParameters'] = array_map(
fn (array $message): array => $this->convertMessageParameter($room, $message),
$message['messageParameters']
diff --git a/lib/Manager.php b/lib/Manager.php
index a3ff4435722..2761fb8726d 100644
--- a/lib/Manager.php
+++ b/lib/Manager.php
@@ -1096,10 +1096,6 @@ public function resolveRoomDisplayName(Room $room, string $userId, bool $forceNa
return $this->l->t('Talk updates ✅');
}
- if ($forceName) {
- return $room->getName();
- }
-
if ($this->federationAuthenticator->isFederationRequest()) {
try {
$authenticatedRoom = $this->federationAuthenticator->getRoom();
@@ -1110,7 +1106,7 @@ public function resolveRoomDisplayName(Room $room, string $userId, bool $forceNa
}
}
- if ($userId === '' && $room->getType() !== Room::TYPE_PUBLIC) {
+ if (!$forceName && $userId === '' && $room->getType() !== Room::TYPE_PUBLIC) {
return $this->l->t('Private conversation');
}
@@ -1151,6 +1147,10 @@ public function resolveRoomDisplayName(Room $room, string $userId, bool $forceNa
return $otherParticipant;
}
+ if ($forceName) {
+ return $room->getName();
+ }
+
if (!$this->isRoomListableByUser($room, $userId)) {
try {
if ($userId === '') {
diff --git a/lib/Migration/Version19000Date20240305115243.php b/lib/Migration/Version19000Date20240305115243.php
new file mode 100644
index 00000000000..58545a26b74
--- /dev/null
+++ b/lib/Migration/Version19000Date20240305115243.php
@@ -0,0 +1,63 @@
+
+ *
+ * @author Joas Schilling
+ *
+ * @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 .
+ *
+ */
+
+namespace OCA\Talk\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Add creation datetime and meta-data columns to the proxy cache
+ */
+class Version19000Date20240305115243 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('talk_proxy_messages');
+ if (!$table->hasColumn('creation_datetime')) {
+ $table->addColumn('creation_datetime', Types::DATETIME, [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('meta_data', Types::TEXT, [
+ 'notnull' => false,
+ ]);
+
+ return $schema;
+ }
+
+ return null;
+ }
+}
diff --git a/lib/Model/Message.php b/lib/Model/Message.php
index d279f583db7..6f2eb993301 100644
--- a/lib/Model/Message.php
+++ b/lib/Model/Message.php
@@ -36,6 +36,10 @@
* @psalm-import-type TalkChatMessage from ResponseDefinitions
*/
class Message {
+ public const METADATA_LAST_EDITED_BY_TYPE = 'last_edited_by_type';
+ public const METADATA_LAST_EDITED_BY_ID = 'last_edited_by_id';
+ public const METADATA_LAST_EDITED_TIME = 'last_edited_time';
+ public const METADATA_SILENT = 'silent';
/** @var bool */
protected $visible = true;
@@ -76,8 +80,9 @@ class Message {
public function __construct(
protected Room $room,
protected ?Participant $participant,
- protected IComment $comment,
+ protected ?IComment $comment,
protected IL10N $l,
+ protected ?ProxyCacheMessage $proxy = null,
) {
}
@@ -89,7 +94,7 @@ public function getRoom(): Room {
return $this->room;
}
- public function getComment(): IComment {
+ public function getComment(): ?IComment {
return $this->comment;
}
@@ -105,6 +110,14 @@ public function getParticipant(): ?Participant {
* Parsed message information
*/
+ public function getMessageId(): int {
+ return $this->comment ? (int) $this->comment->getId() : $this->proxy->getRemoteMessageId();
+ }
+
+ public function getExpirationDateTime(): ?\DateTimeInterface {
+ return $this->comment ? $this->comment->getExpireDate() : $this->proxy->getExpirationDatetime();
+ }
+
public function setVisibility(bool $visible): void {
$this->visible = $visible;
}
@@ -226,8 +239,8 @@ public function toArray(string $format): array {
}
$metaData = $this->comment->getMetaData() ?? [];
- if (!empty($metaData['silent'])) {
- $data['silent'] = true;
+ if (!empty($metaData[self::METADATA_SILENT])) {
+ $data[self::METADATA_SILENT] = true;
}
return $data;
diff --git a/lib/Model/ProxyCacheMessages.php b/lib/Model/ProxyCacheMessage.php
similarity index 75%
rename from lib/Model/ProxyCacheMessages.php
rename to lib/Model/ProxyCacheMessage.php
index 9803f3e2b70..e08afbb3914 100644
--- a/lib/Model/ProxyCacheMessages.php
+++ b/lib/Model/ProxyCacheMessage.php
@@ -3,7 +3,7 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Joas Schilling
+ * @copyright Copyright (c) 2024 Joas Schilling
*
* @author Joas Schilling
*
@@ -48,16 +48,23 @@
* @method string getMessageType()
* @method void setSystemMessage(?string $systemMessage)
* @method string|null getSystemMessage()
- * @method void setExpirationDatetime(?\DateTimeImmutable $expirationDatetime)
- * @method \DateTimeImmutable|null getExpirationDatetime()
+ * @method void setExpirationDatetime(?\DateTime $expirationDatetime)
+ * @method \DateTime|null getExpirationDatetime()
* @method void setMessage(?string $message)
* @method string|null getMessage()
* @method void setMessageParameters(?string $messageParameters)
* @method string|null getMessageParameters()
+ * @method void setCreationDatetime(?\DateTime $creationDatetime)
+ * @method \DateTime|null getCreationDatetime()
+ * @method void setMetaData(?string $metaData)
+ * @method string|null getMetaData()
*
* @psalm-import-type TalkRoomProxyMessage from ResponseDefinitions
*/
-class ProxyCacheMessages extends Entity implements \JsonSerializable {
+class ProxyCacheMessage extends Entity implements \JsonSerializable {
+ public const METADATA_REPLYTO_TYPE = 'replyToActorType';
+ public const METADATA_REPLYTO_ID = 'replyToActorId';
+
protected string $localToken = '';
protected string $remoteServerUrl = '';
@@ -68,9 +75,11 @@ class ProxyCacheMessages extends Entity implements \JsonSerializable {
protected ?string $actorDisplayName = null;
protected ?string $messageType = null;
protected ?string $systemMessage = null;
- protected ?\DateTimeImmutable $expirationDatetime = null;
+ protected ?\DateTime $expirationDatetime = null;
protected ?string $message = null;
protected ?string $messageParameters = null;
+ protected ?\DateTime $creationDatetime = null;
+ protected ?string $metaData = null;
public function __construct() {
$this->addType('localToken', 'string');
@@ -85,6 +94,16 @@ public function __construct() {
$this->addType('expirationDatetime', 'datetime');
$this->addType('message', 'string');
$this->addType('messageParameters', 'string');
+ $this->addType('creationDatetime', 'datetime');
+ $this->addType('metaData', 'string');
+ }
+
+ public function getParsedMessageParameters(): array {
+ return json_decode($this->getMessageParameters() ?? '[]', true);
+ }
+
+ public function getParsedMetaData(): array {
+ return json_decode($this->getMetaData() ?? '[]', true);
}
/**
@@ -100,11 +119,12 @@ public function jsonSerialize(): array {
'actorType' => $this->getActorType(),
'actorId' => $this->getActorId(),
'actorDisplayName' => $this->getActorDisplayName(),
+ 'timestamp' => $this->getCreationDatetime()->getTimestamp(),
'expirationTimestamp' => $expirationTimestamp,
'messageType' => $this->getMessageType(),
'systemMessage' => $this->getSystemMessage() ?? '',
'message' => $this->getMessage() ?? '',
- 'messageParameters' => json_decode($this->getMessageParameters() ?? '[]', true),
+ 'messageParameters' => $this->getParsedMessageParameters(),
];
}
}
diff --git a/lib/Model/ProxyCacheMessagesMapper.php b/lib/Model/ProxyCacheMessageMapper.php
similarity index 72%
rename from lib/Model/ProxyCacheMessagesMapper.php
rename to lib/Model/ProxyCacheMessageMapper.php
index 7d6855fa143..2f1664949c8 100644
--- a/lib/Model/ProxyCacheMessagesMapper.php
+++ b/lib/Model/ProxyCacheMessageMapper.php
@@ -32,24 +32,36 @@
use OCP\IDBConnection;
/**
- * @method ProxyCacheMessages mapRowToEntity(array $row)
- * @method ProxyCacheMessages findEntity(IQueryBuilder $query)
- * @method ProxyCacheMessages[] findEntities(IQueryBuilder $query)
- * @template-extends QBMapper
+ * @method ProxyCacheMessage mapRowToEntity(array $row)
+ * @method ProxyCacheMessage findEntity(IQueryBuilder $query)
+ * @method ProxyCacheMessage[] findEntities(IQueryBuilder $query)
+ * @template-extends QBMapper
*/
-class ProxyCacheMessagesMapper extends QBMapper {
+class ProxyCacheMessageMapper extends QBMapper {
use TTransactional;
public function __construct(
IDBConnection $db,
) {
- parent::__construct($db, 'talk_proxy_messages', ProxyCacheMessages::class);
+ parent::__construct($db, 'talk_proxy_messages', ProxyCacheMessage::class);
}
/**
* @throws DoesNotExistException
*/
- public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessages {
+ public function findById(int $proxyId): ProxyCacheMessage {
+ $query = $this->db->getQueryBuilder();
+ $query->select('*')
+ ->from($this->getTableName())
+ ->where($query->expr()->eq('id', $query->createNamedParameter($proxyId, IQueryBuilder::PARAM_INT)));
+
+ return $this->findEntity($query);
+ }
+
+ /**
+ * @throws DoesNotExistException
+ */
+ public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessage {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from($this->getTableName())
diff --git a/lib/Notification/FederationChatNotifier.php b/lib/Notification/FederationChatNotifier.php
new file mode 100644
index 00000000000..8a630c541b0
--- /dev/null
+++ b/lib/Notification/FederationChatNotifier.php
@@ -0,0 +1,140 @@
+
+ *
+ * @author Joas Schilling
+ *
+ * @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 .
+ *
+ */
+
+namespace OCA\Talk\Notification;
+
+use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
+use OCA\Talk\Model\Attendee;
+use OCA\Talk\Model\Message;
+use OCA\Talk\Model\ProxyCacheMessage;
+use OCA\Talk\Participant;
+use OCA\Talk\Room;
+use OCP\AppFramework\Services\IAppConfig;
+use OCP\Notification\IManager;
+use OCP\Notification\INotification;
+
+class FederationChatNotifier {
+ public function __construct(
+ protected IAppConfig $appConfig,
+ protected IManager $notificationManager,
+ protected UserConverter $userConverter,
+ ) {
+ }
+
+ /**
+ * @param array{remoteServerUrl: string, sharedSecret: string, remoteToken: string, messageData: array{remoteMessageId: int, actorType: string, actorId: string, actorDisplayName: string, messageType: string, systemMessage: string, expirationDatetime: string, message: string, messageParameter: string, creationDatetime: string, metaData: string}, unreadInfo: array{unreadMessages: int, unreadMention: bool, unreadMentionDirect: bool}} $inboundNotification
+ */
+ public function handleChatMessage(Room $room, Participant $participant, ProxyCacheMessage $message, array $inboundNotification): void {
+ $metaData = json_decode($inboundNotification['messageData']['metaData'] ?? '', true, flags: JSON_THROW_ON_ERROR);
+
+ if (isset($metaData[Message::METADATA_SILENT])) {
+ // Silent message, skip notification handling
+ return;
+ }
+
+ // Also notify default participants in one-to-one chats or when the admin default is "always"
+ $defaultLevel = $this->appConfig->getAppValueInt('default_group_notification', Participant::NOTIFY_MENTION);
+ if ($participant->getAttendee()->getNotificationLevel() === Participant::NOTIFY_MENTION
+ || ($defaultLevel !== Participant::NOTIFY_NEVER && $participant->getAttendee()->getNotificationLevel() === Participant::NOTIFY_DEFAULT)) {
+ if ($this->isRepliedTo($room, $participant, $metaData)) {
+ $notification = $this->createNotification($room, $message, 'reply');
+ $notification->setUser($participant->getAttendee()->getActorId());
+ $this->notificationManager->notify($notification);
+ } elseif ($this->isMentioned($participant, $message)) {
+ $notification = $this->createNotification($room, $message, 'mention');
+ $notification->setUser($participant->getAttendee()->getActorId());
+ $this->notificationManager->notify($notification);
+ } elseif ($this->isMentionedAll($room, $message)) {
+ $notification = $this->createNotification($room, $message, 'mention_all');
+ $notification->setUser($participant->getAttendee()->getActorId());
+ $this->notificationManager->notify($notification);
+ }
+ } elseif ($participant->getAttendee()->getNotificationLevel() === Participant::NOTIFY_ALWAYS
+ || ($defaultLevel === Participant::NOTIFY_ALWAYS && $participant->getAttendee()->getNotificationLevel() === Participant::NOTIFY_DEFAULT)) {
+ $notification = $this->createNotification($room, $message, 'chat');
+ $notification->setUser($participant->getAttendee()->getActorId());
+ $this->notificationManager->notify($notification);
+ }
+ }
+
+ protected function isRepliedTo(Room $room, Participant $participant, array $metaData): bool {
+ if ($metaData[ProxyCacheMessage::METADATA_REPLYTO_TYPE] !== Attendee::ACTOR_FEDERATED_USERS) {
+ return false;
+ }
+
+ $repliedTo = $this->userConverter->convertTypeAndId($room, $metaData[ProxyCacheMessage::METADATA_REPLYTO_TYPE], $metaData[ProxyCacheMessage::METADATA_REPLYTO_ID]);
+ return $repliedTo['type'] === $participant->getAttendee()->getActorType()
+ && $repliedTo['id'] === $participant->getAttendee()->getActorId();
+ }
+
+ protected function isMentioned(Participant $participant, ProxyCacheMessage $message): bool {
+ if ($participant->getAttendee()->getActorType() !== Attendee::ACTOR_USERS) {
+ return false;
+ }
+
+ foreach ($message->getParsedMessageParameters() as $parameter) {
+ if ($parameter['type'] === 'user' // RichObjectDefinition, not Attendee::ACTOR_USERS
+ && $parameter['id'] === $participant->getAttendee()->getActorId()
+ && empty($parameter['server'])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function isMentionedAll(Room $room, ProxyCacheMessage $message): bool {
+ foreach ($message->getParsedMessageParameters() as $parameter) {
+ if ($parameter['type'] === 'call' // RichObjectDefinition
+ && $parameter['id'] === $room->getRemoteToken()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates a notification for the given proxy message and mentioned users
+ */
+ protected function createNotification(Room $chat, ProxyCacheMessage $message, string $subject, array $subjectData = []): INotification {
+ $subjectData['userType'] = $message->getActorType();
+ $subjectData['userId'] = $message->getActorId();
+
+ $notification = $this->notificationManager->createNotification();
+ $notification
+ ->setApp('spreed')
+ ->setObject('chat', $chat->getToken())
+ ->setSubject($subject, $subjectData)
+ ->setMessage($message->getMessageType(), [
+ 'proxyId' => $message->getId(),
+ // FIXME Store more info to allow querying remote?
+ ])
+ ->setDateTime($message->getCreationDatetime());
+
+ return $notification;
+ }
+}
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
index e29f7b7d6ff..939c7b8da90 100644
--- a/lib/Notification/Notifier.php
+++ b/lib/Notification/Notifier.php
@@ -36,6 +36,8 @@
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\BotServerMapper;
+use OCA\Talk\Model\Message;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\AvatarService;
@@ -83,6 +85,7 @@ public function __construct(
protected AvatarService $avatarService,
protected INotificationManager $notificationManager,
CommentsManager $commentManager,
+ protected ProxyCacheMessageMapper $proxyCacheMessageMapper,
protected MessageParser $messageParser,
protected IRootFolder $rootFolder,
protected ITimeFactory $timeFactory,
@@ -517,42 +520,51 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
];
$messageParameters = $notification->getMessageParameters();
- if (!isset($messageParameters['commentId'])) {
+ if (!isset($messageParameters['commentId']) && !isset($messageParameters['proxyId'])) {
throw new AlreadyProcessedException();
}
- if (!$this->notificationManager->isPreparingPushNotification()
- && $notification->getObjectType() === 'chat'
- /**
- * Notification only contains the message id of the target comment
- * not the one of the reaction, so we can't determine if it was read.
- * @see Listener::markReactionNotificationsRead()
- */
- && $notification->getSubject() !== 'reaction'
- && ((int) $messageParameters['commentId']) <= $participant->getAttendee()->getLastReadMessage()) {
- // Mark notifications of messages that are read as processed
- throw new AlreadyProcessedException();
- }
+ if (isset($messageParameters['commentId'])) {
+ if (!$this->notificationManager->isPreparingPushNotification()
+ && $notification->getObjectType() === 'chat'
+ /**
+ * Notification only contains the message id of the target comment
+ * not the one of the reaction, so we can't determine if it was read.
+ * @see Listener::markReactionNotificationsRead()
+ */
+ && $notification->getSubject() !== 'reaction'
+ && ((int) $messageParameters['commentId']) <= $participant->getAttendee()->getLastReadMessage()) {
+ // Mark notifications of messages that are read as processed
+ throw new AlreadyProcessedException();
+ }
- try {
- $comment = $this->commentManager->get($messageParameters['commentId']);
- } catch (NotFoundException $e) {
- throw new AlreadyProcessedException();
- }
+ try {
+ $comment = $this->commentManager->get($messageParameters['commentId']);
+ } catch (NotFoundException $e) {
+ throw new AlreadyProcessedException();
+ }
- $message = $this->messageParser->createMessage($room, $participant, $comment, $l);
- $this->messageParser->parseMessage($message);
+ $message = $this->messageParser->createMessage($room, $participant, $comment, $l);
+ $this->messageParser->parseMessage($message);
- if (!$message->getVisibility()) {
- throw new AlreadyProcessedException();
+ if (!$message->getVisibility()) {
+ throw new AlreadyProcessedException();
+ }
+ } else {
+ try {
+ $proxy = $this->proxyCacheMessageMapper->findById($messageParameters['proxyId']);
+ $message = $this->messageParser->createMessageFromProxyCache($room, $participant, $proxy, $l);
+ } catch (DoesNotExistException) {
+ throw new AlreadyProcessedException();
+ }
}
// Set the link to the specific message
- $notification->setLink($this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()]) . '#message_' . $comment->getId());
+ $notification->setLink($this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()]) . '#message_' . $message->getMessageId());
$now = $this->timeFactory->getDateTime();
- $expireDate = $message->getComment()->getExpireDate();
- if ($expireDate instanceof \DateTime && $expireDate < $now) {
+ $expireDate = $message->getExpirationDateTime();
+ if ($expireDate instanceof \DateTimeInterface && $expireDate < $now) {
throw new AlreadyProcessedException();
}
@@ -576,7 +588,7 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
$notification->setRichMessage($message->getMessage(), $message->getMessageParameters());
// Forward the message ID as well to the clients, so they can quote the message on replies
- $notification->setObject($notification->getObjectType(), $notification->getObjectId() . '/' . $comment->getId());
+ $notification->setObject($notification->getObjectType(), $notification->getObjectId() . '/' . $message->getMessageId());
}
$richSubjectParameters = [
diff --git a/lib/Service/RoomFormatter.php b/lib/Service/RoomFormatter.php
index 1cd8d9fb5f3..9f2455433c0 100644
--- a/lib/Service/RoomFormatter.php
+++ b/lib/Service/RoomFormatter.php
@@ -31,7 +31,7 @@
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\BreakoutRoom;
-use OCA\Talk\Model\ProxyCacheMessagesMapper;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
use OCA\Talk\Model\Session;
use OCA\Talk\Participant;
use OCA\Talk\ResponseDefinitions;
@@ -64,7 +64,7 @@ public function __construct(
protected IAppManager $appManager,
protected IManager $userStatusManager,
protected IUserManager $userManager,
- protected ProxyCacheMessagesMapper $proxyCacheMessagesMapper,
+ protected ProxyCacheMessageMapper $proxyCacheMessageMapper,
protected UserConverter $userConverter,
protected IL10N $l10n,
protected ?string $userId,
@@ -390,7 +390,7 @@ public function formatRoomV4(
);
} elseif ($room->getRemoteServer() !== '') {
try {
- $cachedMessage = $this->proxyCacheMessagesMapper->findByRemote(
+ $cachedMessage = $this->proxyCacheMessageMapper->findByRemote(
$room->getRemoteServer(),
$room->getRemoteToken(),
$room->getLastMessageId(),
diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php
index a462025eccb..98209188dd2 100644
--- a/tests/integration/features/bootstrap/FeatureContext.php
+++ b/tests/integration/features/bootstrap/FeatureContext.php
@@ -1946,6 +1946,8 @@ public function userSeesPeersInCall(string $user, int $numPeers, string $identif
public function userSendsMessageToRoom(string $user, string $sendingMode, string $message, string $identifier, string $statusCode, string $apiVersion = 'v1') {
$message = substr($message, 1, -1);
$message = str_replace('\n', "\n", $message);
+ $message = str_replace('{$BASE_URL}', $this->baseUrl, $message);
+ $message = str_replace('{$REMOTE_URL}', $this->baseRemoteUrl, $message);
if ($message === '413 Payload Too Large') {
$message .= "\n" . str_repeat('1', 32000);
@@ -2728,16 +2730,16 @@ protected function compareDataResponse(TableNode $formData = null) {
$data['reactionsSelf'] = null;
}
}
+
if ($includeLastEdit) {
$data['lastEditActorType'] = $message['lastEditActorType'] ?? '';
$data['lastEditActorDisplayName'] = $message['lastEditActorDisplayName'] ?? '';
$data['lastEditActorId'] = $message['lastEditActorId'] ?? '';
- if ($message['lastEditActorType'] === 'guests') {
+ if (($message['lastEditActorType'] ?? '') === 'guests') {
$data['lastEditActorId'] = self::$sessionIdToUser[$message['lastEditActorId']];
}
}
-
return $data;
}, $messages, $expected));
}
@@ -3285,7 +3287,12 @@ private function assertNotifications($notifications, TableNode $formData) {
if (isset($expectedNotification['object_id'])) {
if (strpos($notification['object_id'], '/') !== false) {
[$roomToken, $message] = explode('/', $notification['object_id']);
- $data['object_id'] = self::$tokenToIdentifier[$roomToken] . '/' . self::$messageIdToText[$message] ?? 'UNKNOWN_MESSAGE';
+ $messageText = self::$messageIdToText[$message] ?? 'UNKNOWN_MESSAGE';
+
+ $messageText = str_replace($this->baseUrl, '{$BASE_URL}', $messageText);
+ $messageText = str_replace($this->baseRemoteUrl, '{$REMOTE_URL}', $messageText);
+
+ $data['object_id'] = self::$tokenToIdentifier[$roomToken] . '/' . $messageText;
} elseif (strpos($expectedNotification['object_id'], 'INVITE_ID') !== false) {
$data['object_id'] = 'INVITE_ID(' . self::$inviteIdToRemote[$notification['object_id']] . ')';
} else {
diff --git a/tests/integration/features/federation/chat.feature b/tests/integration/features/federation/chat.feature
index a797d15fc24..c56f18ff243 100644
--- a/tests/integration/features/federation/chat.feature
+++ b/tests/integration/features/federation/chat.feature
@@ -150,3 +150,32 @@ Feature: federation/chat
| id | type |
| room | 2 |
And user "participant2" sends message "413 Payload Too Large" to room "LOCAL::room" with 413
+
+ Scenario: Mentioning a federated user triggers a notification for them
+ Given the following "spreed" app config is set
+ | federation_enabled | yes |
+ Given user "participant1" creates room "room" (v4)
+ | roomType | 2 |
+ | roomName | room |
+ And user "participant1" adds federated_user "participant2" to room "room" with 200 (v4)
+ And user "participant2" has the following invitations (v1)
+ | remoteServerUrl | remoteToken | state | inviterCloudId | inviterDisplayName |
+ | LOCAL | room | 0 | participant1@http://localhost:8080 | participant1-displayname |
+ And user "participant2" accepts invite to room "room" of server "LOCAL" with 200 (v1)
+ | id | name | type | remoteServer | remoteToken |
+ | room | room | 2 | LOCAL | room |
+ Then user "participant2" is participant of the following rooms (v4)
+ | id | type |
+ | room | 2 |
+ # Join and leave to clear the invite notification
+ Given user "participant2" joins room "LOCAL::room" with 200 (v4)
+ Given user "participant2" leaves room "LOCAL::room" with 200 (v4)
+ And user "participant2" sends message "Message 1" to room "LOCAL::room" with 201
+ When user "participant1" sends reply "Message 1-1" on message "Message 1" to room "room" with 201
+ And user "participant1" sends message 'Hi @"federated_user/participant2@{$REMOTE_URL}" bye' to room "room" with 201
+ And user "participant1" sends message 'Hi @all bye' to room "room" with 201
+ Then user "participant2" has the following notifications
+ | app | object_type | object_id | subject | message |
+ | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | Hi room bye |
+ | spreed | chat | room/Hi @"federated_user/participant2@{$REMOTE_URL}" bye | participant1-displayname mentioned you in conversation room | Hi @participant2-displayname bye |
+ | spreed | chat | room/Message 1-1 | participant1-displayname replied to your message in conversation room | Message 1-1 |
diff --git a/tests/php/Federation/FederationTest.php b/tests/php/Federation/FederationTest.php
index f53fa64144e..2d94caca280 100644
--- a/tests/php/Federation/FederationTest.php
+++ b/tests/php/Federation/FederationTest.php
@@ -28,12 +28,14 @@
use OCA\Talk\Federation\BackendNotifier;
use OCA\Talk\Federation\CloudFederationProviderTalk;
use OCA\Talk\Federation\FederationManager;
+use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\AttendeeMapper;
use OCA\Talk\Model\Invitation;
use OCA\Talk\Model\InvitationMapper;
-use OCA\Talk\Model\ProxyCacheMessagesMapper;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
+use OCA\Talk\Notification\FederationChatNotifier;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RoomService;
@@ -95,7 +97,9 @@ class FederationTest extends TestCase {
/** @var AttendeeMapper|MockObject */
protected $attendeeMapper;
- protected ProxyCacheMessagesMapper|MockObject $proxyCacheMessageMapper;
+ protected ProxyCacheMessageMapper|MockObject $proxyCacheMessageMapper;
+ protected FederationChatNotifier|MockObject $federationChatNotifier;
+ protected UserConverter|MockObject $userConverter;
protected ICacheFactory|MockObject $cacheFactory;
public function setUp(): void {
@@ -112,7 +116,7 @@ public function setUp(): void {
$this->appManager = $this->createMock(IAppManager::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->url = $this->createMock(IURLGenerator::class);
- $this->proxyCacheMessageMapper = $this->createMock(ProxyCacheMessagesMapper::class);
+ $this->proxyCacheMessageMapper = $this->createMock(ProxyCacheMessageMapper::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->backendNotifier = new BackendNotifier(
@@ -130,6 +134,8 @@ public function setUp(): void {
$this->federationManager = $this->createMock(FederationManager::class);
$this->notificationManager = $this->createMock(INotificationManager::class);
+ $this->federationChatNotifier = $this->createMock(FederationChatNotifier::class);
+ $this->userConverter = $this->createMock(UserConverter::class);
$this->cloudFederationProvider = new CloudFederationProviderTalk(
$this->cloudIdManager,
@@ -148,6 +154,8 @@ public function setUp(): void {
$this->createMock(IEventDispatcher::class),
$this->logger,
$this->proxyCacheMessageMapper,
+ $this->federationChatNotifier,
+ $this->userConverter,
$this->cacheFactory,
);
}
diff --git a/tests/php/Notification/NotifierTest.php b/tests/php/Notification/NotifierTest.php
index 7f2b362d411..db22eff66f1 100644
--- a/tests/php/Notification/NotifierTest.php
+++ b/tests/php/Notification/NotifierTest.php
@@ -33,6 +33,7 @@
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\BotServerMapper;
use OCA\Talk\Model\Message;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
use OCA\Talk\Notification\Notifier;
use OCA\Talk\Participant;
use OCA\Talk\Room;
@@ -81,6 +82,7 @@ class NotifierTest extends TestCase {
protected $notificationManager;
/** @var CommentsManager|MockObject */
protected $commentsManager;
+ protected ProxyCacheMessageMapper|MockObject $proxyCacheMessageMapper;
/** @var MessageParser|MockObject */
protected $messageParser;
/** @var IRootFolder|MockObject */
@@ -112,6 +114,7 @@ public function setUp(): void {
$this->avatarService = $this->createMock(AvatarService::class);
$this->notificationManager = $this->createMock(INotificationManager::class);
$this->commentsManager = $this->createMock(CommentsManager::class);
+ $this->proxyCacheMessageMapper = $this->createMock(ProxyCacheMessageMapper::class);
$this->messageParser = $this->createMock(MessageParser::class);
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
@@ -133,6 +136,7 @@ public function setUp(): void {
$this->avatarService,
$this->notificationManager,
$this->commentsManager,
+ $this->proxyCacheMessageMapper,
$this->messageParser,
$this->rootFolder,
$this->timeFactory,