diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2024-03-06 22:37:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-06 22:37:58 +0100 |
commit | 9e5194963281e7e046a6f845bf310b87bbc4a9c3 (patch) | |
tree | 4c540f4a16f9eacaf9e8d6eb103df46db3107982 /lib | |
parent | 232cb8ee8a4f81ba0a8643233904b6b76c6788b2 (diff) | |
parent | cdc988c7ffc28f212f6df847a02f8136ada0d9ea (diff) |
Merge pull request #11691 from nextcloud/feat/11272/notifications
feat(federation): Implement notifications for mentions, reply and full
Diffstat (limited to 'lib')
20 files changed, 430 insertions, 83 deletions
diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 7a731213d..6c0d3b478 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -34,6 +34,7 @@ use OCA\Talk\Events\SystemMessageSentEvent; 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 @@ class ChatManager { if ($silent) { $comment->setMetaData([ - 'silent' => true, + Message::METADATA_SILENT => true, ]); } @@ -173,7 +174,7 @@ class ChatManager { $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 @@ class ChatManager { } } - $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 @@ class ChatManager { 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 @@ class ChatManager { // 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 @@ class ChatManager { $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 @@ class ChatManager { } $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 77e42316a..de88ab506 100644 --- a/lib/Chat/MessageParser.php +++ b/lib/Chat/MessageParser.php @@ -29,6 +29,7 @@ use OCA\Talk\Exceptions\ParticipantNotFoundException; 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 @@ class MessageParser { 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 d7f48e611..645e0e147 100644 --- a/lib/Chat/Parser/SystemMessage.php +++ b/lib/Chat/Parser/SystemMessage.php @@ -207,7 +207,7 @@ class SystemMessage implements IEventListener { } } 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 257ae64ee..1b241088e 100644 --- a/lib/Chat/Parser/UserMention.php +++ b/lib/Chat/Parser/UserMention.php @@ -162,7 +162,7 @@ class UserMention implements IEventListener { $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 ddd680ab2..8f0ff6839 100644 --- a/lib/Chat/SystemMessage/Listener.php +++ b/lib/Chat/SystemMessage/Listener.php @@ -43,6 +43,7 @@ use OCA\Talk\Events\RoomModifiedEvent; 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 @@ class Listener implements IEventListener { } } - 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 5b6a98049..bad6ee7ea 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -530,7 +530,7 @@ class ChatController extends AEnvironmentAwareController { $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 8f3654c31..9c8a5d7f4 100644 --- a/lib/Events/AMessageSentEvent.php +++ b/lib/Events/AMessageSentEvent.php @@ -33,6 +33,7 @@ abstract class AMessageSentEvent extends ARoomEvent { protected IComment $comment, protected ?Participant $participant = null, protected bool $silent = false, + protected ?IComment $parent = null, ) { parent::__construct( $room, @@ -50,4 +51,8 @@ abstract class AMessageSentEvent extends ARoomEvent { 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 a540edc5a..4dd55374b 100644 --- a/lib/Events/ASystemMessageSentEvent.php +++ b/lib/Events/ASystemMessageSentEvent.php @@ -33,13 +33,15 @@ abstract class ASystemMessageSentEvent extends AMessageSentEvent { 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 1b13b260e..ccb422b23 100644 --- a/lib/Federation/BackendNotifier.php +++ b/lib/Federation/BackendNotifier.php @@ -313,6 +313,7 @@ class BackendNotifier { * 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 adf439b65..abe0dccd6 100644 --- a/lib/Federation/CloudFederationProviderTalk.php +++ b/lib/Federation/CloudFederationProviderTalk.php @@ -34,13 +34,15 @@ use OCA\Talk\Events\ARoomModifiedEvent; 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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { 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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { /** * @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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { 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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { $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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { $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 @@ class CloudFederationProviderTalk implements ICloudFederationProvider { $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 e6920cc06..fb795b804 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\SystemMessageSentEvent; 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 @@ class MessageSentListener implements IEventListener { } $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 @@ class MessageSentListener implements IEventListener { '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 debc01883..d58aa8e40 100644 --- a/lib/Federation/Proxy/TalkV1/UserConverter.php +++ b/lib/Federation/Proxy/TalkV1/UserConverter.php @@ -45,6 +45,26 @@ class UserConverter { ) { } + /** + * @return array{type: string, id: string} + */ + public function convertTypeAndId(Room $room, string $type, string $id): array { + if ($type === Attend |