Skip to content

Commit

Permalink
feat(federation): Notifications
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Feb 29, 2024
1 parent b941660 commit 4156e18
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 29 deletions.
20 changes: 20 additions & 0 deletions lib/Chat/MessageParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use OCA\Talk\MatterbridgeManager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\Message;
use OCA\Talk\Model\ProxyCacheMessages;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\BotService;
Expand Down Expand Up @@ -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, ProxyCacheMessages $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(), []);

Expand Down
43 changes: 43 additions & 0 deletions lib/Federation/CloudFederationProviderTalk.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
use OCP\IUser;
use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
use OCP\Notification\INotification;
use OCP\Share\Exceptions\ShareNotFound;
use Psr\Log\LoggerInterface;
use SensitiveParameter;
Expand Down Expand Up @@ -374,6 +375,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->proxyCacheMessagesMapper->findByRemote(
$notification['remoteServerUrl'],
$notification['remoteToken'],
$notification['messageData']['remoteMessageId'],
);
}

try {
Expand All @@ -390,9 +397,45 @@ private function messagePosted(int $remoteAttendeeId, array $notification): arra
$notification['unreadInfo']['unreadMentionDirect'],
);

// 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);
} 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);
}

return [];
}

/**
* 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 {

Check failure on line 421 in lib/Federation/CloudFederationProviderTalk.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/CloudFederationProviderTalk.php:421:121: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\IComment does not exist (see https://psalm.dev/019)
$subjectData['userType'] = $reaction ? $reaction->getActorType() : $message->getActorType();
$subjectData['userId'] = $reaction ? $reaction->getActorId() : $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($reaction ? $reaction->getCreationDateTime() : new \DateTime());

return $notification;
}

/**
* @throws AuthenticationFailedException
* @throws ActionNotSupportedException
Expand Down
13 changes: 11 additions & 2 deletions lib/Model/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ class Message {
public function __construct(
protected Room $room,
protected ?Participant $participant,
protected IComment $comment,
protected ?IComment $comment,
protected IL10N $l,
protected ?ProxyCacheMessages $proxy = null,
) {
}

Expand All @@ -89,7 +90,7 @@ public function getRoom(): Room {
return $this->room;
}

public function getComment(): IComment {
public function getComment(): ?IComment {
return $this->comment;
}

Expand All @@ -105,6 +106,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;
}
Expand Down
10 changes: 9 additions & 1 deletion lib/Model/ProxyCacheMessages.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ public function __construct() {
$this->addType('expirationDatetime', 'datetime');
$this->addType('message', 'string');
$this->addType('messageParameters', 'string');
// Reply author
// Silent
// Creation date
// Verb?!
}

public function getParsedMessageParameters(): array {
return json_decode($this->getMessageParameters() ?? '[]', true);
}

/**
Expand All @@ -104,7 +112,7 @@ public function jsonSerialize(): array {
'messageType' => $this->getMessageType(),
'systemMessage' => $this->getSystemMessage() ?? '',
'message' => $this->getMessage() ?? '',
'messageParameters' => json_decode($this->getMessageParameters() ?? '[]', true),
'messageParameters' => $this->getParsedMessageParameters(),
];
}
}
12 changes: 12 additions & 0 deletions lib/Model/ProxyCacheMessagesMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ public function __construct(
parent::__construct($db, 'talk_proxy_messages', ProxyCacheMessages::class);
}

/**
* @throws DoesNotExistException
*/
public function findById(int $proxyId): ProxyCacheMessages {
$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
*/
Expand Down
64 changes: 38 additions & 26 deletions lib/Notification/Notifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -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\ProxyCacheMessagesMapper;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\AvatarService;
Expand Down Expand Up @@ -83,6 +85,7 @@ public function __construct(
protected AvatarService $avatarService,
protected INotificationManager $notificationManager,
CommentsManager $commentManager,
protected ProxyCacheMessagesMapper $proxyCacheMessagesMapper,
protected MessageParser $messageParser,
protected IRootFolder $rootFolder,
protected ITimeFactory $timeFactory,
Expand Down Expand Up @@ -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->proxyCacheMessagesMapper->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();
}

Expand All @@ -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 = [
Expand Down

0 comments on commit 4156e18

Please sign in to comment.