diff --git a/lib/Federation/CloudFederationProviderTalk.php b/lib/Federation/CloudFederationProviderTalk.php index 6dfa505a863a..6e44e5213147 100644 --- a/lib/Federation/CloudFederationProviderTalk.php +++ b/lib/Federation/CloudFederationProviderTalk.php @@ -39,8 +39,9 @@ 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\Message; +use OCA\Talk\Model\ProxyCacheMessage; +use OCA\Talk\Model\ProxyCacheMessageMapper; use OCA\Talk\Participant; use OCA\Talk\Room; use OCA\Talk\Service\ParticipantService; @@ -89,7 +90,7 @@ public function __construct( private ISession $session, private IEventDispatcher $dispatcher, private LoggerInterface $logger, - private ProxyCacheMessagesMapper $proxyCacheMessagesMapper, + private ProxyCacheMessageMapper $proxyCacheMessagesMapper, ICacheFactory $cacheFactory, ) { $this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed('talk/pcm/') : null; @@ -337,7 +338,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']); @@ -398,19 +399,30 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra $notification['unreadInfo']['unreadMentionDirect'], ); + $metaData = json_decode($notification['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)) { - // FIXME Check if actually mentioned OR reply - $notification = $this->createNotification($room, $message, 'mention'); - $notification->setUser($participant->getAttendee()->getActorId()); - $this->notificationManager->notify($notification); + // FIXME Check if actually mentioned OR replyTo is "us" + // FIXME transform federated users to local users + if ($metaData[ProxyCacheMessage::METADATA_REPLYTO_TYPE] === $participant->getAttendee()->getActorType() + && $metaData[ProxyCacheMessage::METADATA_REPLYTO_ID] === $participant->getAttendee()->getActorType()) { + $n = $this->createNotification($room, $message, 'mention'); + $n->setUser($participant->getAttendee()->getActorId()); + $this->notificationManager->notify($n); + } } 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); + $n = $this->createNotification($room, $message, 'chat'); + $n->setUser($participant->getAttendee()->getActorId()); + $this->notificationManager->notify($n); } return []; @@ -419,7 +431,7 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra /** * Creates a notification for the given proxy message and mentioned users */ - private function createNotification(Room $chat, ProxyCacheMessages $message, string $subject, array $subjectData = [], ?IComment $reaction = null): INotification { + private function createNotification(Room $chat, ProxyCacheMessage $message, string $subject, array $subjectData = [], ?IComment $reaction = null): INotification { $subjectData['userType'] = $reaction ? $reaction->getActorType() : $message->getActorType(); $subjectData['userId'] = $reaction ? $reaction->getActorId() : $message->getActorId(); @@ -432,7 +444,7 @@ private function createNotification(Room $chat, ProxyCacheMessages $message, str 'proxyId' => $message->getId(), // FIXME Store more info to allow querying remote? ]) - ->setDateTime($reaction ? $reaction->getCreationDateTime() : new \DateTime()); + ->setDateTime($reaction ? $reaction->getCreationDateTime() : $message->getCreationDatetime()); return $notification; } diff --git a/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php b/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php index daecb1483ebf..fb795b80425e 100644 --- a/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php +++ b/lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php @@ -32,6 +32,7 @@ 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; @@ -84,8 +85,8 @@ public function handle(Event $event): void { $metaData = $event->getComment()->getMetaData() ?? []; $parent = $event->getParent(); if ($parent instanceof IComment) { - $metaData['replyToActorType'] = $parent->getActorType(); - $metaData['replyToActorId'] = $parent->getActorId(); + $metaData[ProxyCacheMessage::METADATA_REPLYTO_TYPE] = $parent->getActorType(); + $metaData[ProxyCacheMessage::METADATA_REPLYTO_ID] = $parent->getActorId(); } $messageData = [ diff --git a/lib/Model/Message.php b/lib/Model/Message.php index bb6e348afde0..9bf8eb14bdde 100644 --- a/lib/Model/Message.php +++ b/lib/Model/Message.php @@ -78,11 +78,11 @@ class Message { protected $lastEditTimestamp = 0; public function __construct( - protected Room $room, - protected ?Participant $participant, - protected ?IComment $comment, - protected IL10N $l, - protected ?ProxyCacheMessages $proxy = null, + protected Room $room, + protected ?Participant $participant, + protected ?IComment $comment, + protected IL10N $l, + protected ?ProxyCacheMessage $proxy = null, ) { } diff --git a/lib/Model/ProxyCacheMessages.php b/lib/Model/ProxyCacheMessage.php similarity index 83% rename from lib/Model/ProxyCacheMessages.php rename to lib/Model/ProxyCacheMessage.php index f15738016615..1b870b279155 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 * @@ -54,10 +54,17 @@ * @method string|null getMessage() * @method void setMessageParameters(?string $messageParameters) * @method string|null getMessageParameters() + * @method void setCreationDatetime(?\DateTimeImmutable $creationDatetime) + * @method \DateTimeImmutable|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 = ''; @@ -71,6 +78,8 @@ class ProxyCacheMessages extends Entity implements \JsonSerializable { protected ?\DateTimeImmutable $expirationDatetime = null; protected ?string $message = null; protected ?string $messageParameters = null; + protected ?\DateTimeImmutable $creationDatetime = null; + protected ?string $metaData = null; public function __construct() { $this->addType('localToken', 'string'); @@ -85,16 +94,18 @@ public function __construct() { $this->addType('expirationDatetime', 'datetime'); $this->addType('message', 'string'); $this->addType('messageParameters', 'string'); - // Reply author - // Silent - // Creation date - // Verb?! + $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); + } + /** * @return TalkRoomProxyMessage */ diff --git a/lib/Model/ProxyCacheMessagesMapper.php b/lib/Model/ProxyCacheMessageMapper.php similarity index 83% rename from lib/Model/ProxyCacheMessagesMapper.php rename to lib/Model/ProxyCacheMessageMapper.php index b9c41afdf5b1..2f1664949c84 100644 --- a/lib/Model/ProxyCacheMessagesMapper.php +++ b/lib/Model/ProxyCacheMessageMapper.php @@ -32,24 +32,24 @@ 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 findById(int $proxyId): ProxyCacheMessages { + public function findById(int $proxyId): ProxyCacheMessage { $query = $this->db->getQueryBuilder(); $query->select('*') ->from($this->getTableName()) @@ -61,7 +61,7 @@ public function findById(int $proxyId): ProxyCacheMessages { /** * @throws DoesNotExistException */ - public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessages { + public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessage { $query = $this->db->getQueryBuilder(); $query->select('*') ->from($this->getTableName()) diff --git a/tests/php/Federation/FederationTest.php b/tests/php/Federation/FederationTest.php index f53fa64144eb..20b01718c610 100644 --- a/tests/php/Federation/FederationTest.php +++ b/tests/php/Federation/FederationTest.php @@ -33,7 +33,7 @@ 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\Room; use OCA\Talk\Service\ParticipantService; use OCA\Talk\Service\RoomService; @@ -95,7 +95,7 @@ class FederationTest extends TestCase { /** @var AttendeeMapper|MockObject */ protected $attendeeMapper; - protected ProxyCacheMessagesMapper|MockObject $proxyCacheMessageMapper; + protected ProxyCacheMessageMapper|MockObject $proxyCacheMessageMapper; protected ICacheFactory|MockObject $cacheFactory; public function setUp(): void { @@ -112,7 +112,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(