Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat; Reactions notifications in fully subscribed federated convos #13183

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/Federation/BackendNotifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,29 @@ protected function getServerRemoteUrl(): string {

return $server;
}

public function sendReactionNotification(
string $remoteServer,
int $localAttendeeId,
string $accessToken,
string $localToken,
array $reactionData
): ?bool {
$remote = $this->prepareRemoteUrl($remoteServer);

$notification = $this->cloudFederationFactory->getCloudFederationNotification();
$notification->setMessage(
FederationManager::NOTIFICATION_REACTION_ADDED,
FederationManager::TALK_ROOM_RESOURCE,
(string)$localAttendeeId,
[
'remoteServerUrl' => $this->getServerRemoteUrl(),
'sharedSecret' => $accessToken,
'remoteToken' => $localToken,
'reactionData' => $reactionData,
]
);

return $this->sendUpdateToRemote($remote, $notification);
}
}
1 change: 1 addition & 0 deletions lib/Federation/FederationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class FederationManager {
public const NOTIFICATION_PARTICIPANT_MODIFIED = 'PARTICIPANT_MODIFIED';
public const NOTIFICATION_ROOM_MODIFIED = 'ROOM_MODIFIED';
public const NOTIFICATION_MESSAGE_POSTED = 'MESSAGE_POSTED';
public const NOTIFICATION_REACTION_ADDED = 'talk-reaction-added';
public const TOKEN_LENGTH = 64;

public function __construct(
Expand Down
90 changes: 90 additions & 0 deletions lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
protected MessageParser $messageParser,
protected IFactory $l10nFactory,
protected ChatManager $chatManager,
protected IUserManager $userManager,

Check failure on line 40 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:40:3: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\IUserManager does not exist (see https://psalm.dev/019)
protected IAppConfig $appConfig,

Check failure on line 41 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:41:3: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\IAppConfig does not exist (see https://psalm.dev/019)
) {
}

Expand All @@ -57,6 +59,14 @@
$chatMessage = $this->messageParser->createMessage($event->getRoom(), null, $event->getComment(), $l);
$this->messageParser->parseMessage($chatMessage);

$messageType = $chatMessage->getMessageType();

// Handle reaction events
if ($messageType === ChatManager::VERB_REACTION) {
$this->createReactionNotification($event->getRoom(), $event->getComment());

Check failure on line 66 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

InvalidArgument

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:66:38: InvalidArgument: Argument 1 of OCA\Talk\Federation\Proxy\TalkV1\Notifier\MessageSentListener::createReactionNotification expects OCA\Talk\Federation\Proxy\TalkV1\Notifier\Room, but OCA\Talk\Room provided (see https://psalm.dev/004)
return;
}

$systemMessage = $chatMessage->getMessageType() === ChatManager::VERB_SYSTEM ? $chatMessage->getMessageRaw() : '';
if ($systemMessage !== 'message_edited'
&& $systemMessage !== 'message_deleted'
Expand Down Expand Up @@ -124,4 +134,84 @@
}
}
}

private function createReactionNotification(Room $chat, IComment $reaction): void {

Check failure on line 138 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:138:46: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\Room does not exist (see https://psalm.dev/019)
$originalCommentId = $reaction->getObjectId();
$originalComment = $this->chatManager->getCommentById($originalCommentId);

if ($originalComment === null) {
return;
}

// Check if the original comment's author should be notified
if ($originalComment->getActorType() === Attendee::ACTOR_USERS || $originalComment->getActorType() === Attendee::ACTOR_FEDERATED_USERS) {
try {
$participant = $this->participantService->getParticipant($chat, $originalComment->getActorId(), false);
} catch (ParticipantNotFoundException $e) {
return;
}

$notificationLevel = $participant->getAttendee()->getNotificationLevel();
if ($notificationLevel === Participant::NOTIFY_DEFAULT) {
if ($chat->getType() === Room::TYPE_ONE_TO_ONE) {
$notificationLevel = Participant::NOTIFY_ALWAYS;
} else {
$notificationLevel = $this->getDefaultGroupNotification();
}
}

if ($notificationLevel === Participant::NOTIFY_ALWAYS) {
$this->sendReactionNotification(
$participant,
$chat,
$reaction,
$originalComment
);
}
}
}

private function sendReactionNotification(
Participant $participant,

Check failure on line 175 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:175:3: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\Participant does not exist (see https://psalm.dev/019)
Room $room,

Check failure on line 176 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:176:3: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\Room does not exist (see https://psalm.dev/019)
IComment $reaction,
IComment $originalComment
): void {
$attendee = $participant->getAttendee();
$cloudId = $this->cloudIdManager->resolveCloudId($attendee->getActorId());

$notificationData = [
'remoteMessageId' => (int)$originalComment->getId(),
'reaction' => $reaction->getMessage(),
'reactorType' => $reaction->getActorType(),
'reactorId' => $reaction->getActorId(),
'reactorDisplayName' => $this->getDisplayName($reaction->getActorType(), $reaction->getActorId()),
'creationDatetime' => $reaction->getCreationDateTime()->format(\DateTime::ATOM),
];

// Send the notification using BackendNotifier
$success = $this->backendNotifier->sendReactionNotification(
$cloudId->getRemote(),
$attendee->getId(),
$attendee->getAccessToken(),
$room->getToken(),
$notificationData
);

if ($success === null) {
$this->participantService->removeAttendee($room, $participant, AAttendeeRemovedEvent::REASON_LEFT);
}
}

private function getDisplayName(string $actorType, string $actorId): string {
if ($actorType === Attendee::ACTOR_USERS) {
$user = $this->userManager->get($actorId);

Check failure on line 208 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:208:12: UndefinedClass: Class, interface or enum named OCA\Talk\Federation\Proxy\TalkV1\Notifier\IUserManager does not exist (see https://psalm.dev/019)
return $user ? $user->getDisplayName() : $actorId;
} elseif ($actorType === Attendee::ACTOR_FEDERATED_USERS) {
$cloudId = $this->cloudIdManager->resolveCloudId($actorId);
return $cloudId->getDisplayName() ?? $actorId;

Check failure on line 212 in lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedInterfaceMethod

lib/Federation/Proxy/TalkV1/Notifier/MessageSentListener.php:212:21: UndefinedInterfaceMethod: Method OCP\Federation\ICloudId::getDisplayName does not exist (see https://psalm.dev/181)
}
return $actorId;
}

}
41 changes: 41 additions & 0 deletions tests/integration/features/federation/chat.feature
Original file line number Diff line number Diff line change
Expand Up @@ -509,3 +509,44 @@ Feature: federation/chat
| actorType | actorId | actorDisplayName | reaction |
| users | participant1 | participant1-displayname | 🚀 |
| federated_users | participant2@{$REMOTE_URL} | participant2-displayname | 🚀 |

# Scenario: Verifying Notifications for Local Participants

# Given the following "spreed" app config is set
# | federation_enabled | yes |
# And user "participant1" creates room "room" (v4)
# | roomType | 2 |
# | roomName | room |
# And user "participant1" adds federated_user "participant3" to room "room" with 200 (v4)
# And user "participant1" adds user "participant2" to room "room" with 200 (v4)
# And user "participant3" has the following invitations (v1)
# | remoteServerUrl | remoteToken | state | inviterCloudId | inviterDisplayName |
# | LOCAL | room | 0 | participant1@http://localhost:8080 | participant1-displayname |
# And user "participant3" accepts invite to room "room" of server "LOCAL" with 200 (v1)
# | id | name | type | remoteServer | remoteToken |
# | LOCAL::room | room | 2 | LOCAL | room |

# # Enable Notifications for Local User
# And user "participant2" sets notifications to all for room "room" (v4)

# #fed user
# #And user "participant3" sets notifications to all for room "room" (v4)
# And user "participant3" sets notifications to all for room "LOCAL::room" (v4)

# # Sending a Message
# When user "participant1" sends message "Message 1" to room "room" with 201

# And user "participant1" react with "🚀" on message "Message 1" to room "room" with 201
# | actorType | actorId | actorDisplayName | reaction |
# | users | participant1 | participant1-displayname | 🚀 |

# # Verification for Message Notification
# Then user "participant2" has the following notifications
# | app | object_type | object_id | subject | message |
# | spreed | chat | room/Message 1 | participant1-displayname sent a message in conversation room | Message 1 |
# | spreed | room | room | participant1-displayname invited you to a group conversation: room | |

# Then user "participant3" has the following notifications
# | app | object_type | object_id | subject | message |
# | spreed | chat | LOCAL::room/Message 1 | participant1-displayname sent a message in conversation room | Message 1 |
# #| spreed | room | LOCAL::room | participant1-displayname invited you to a group conversation: room | |
Loading