mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
feat(hpb): support chat relay
Signed-off-by: Anna Larch <anna@nextcloud.com>
This commit is contained in:
parent
6f32ca490b
commit
fa44dd0b3e
6 changed files with 244 additions and 6 deletions
|
|
@ -332,6 +332,8 @@ class Application extends App implements IBootstrap {
|
|||
$context->registerEventListener(ChatMessageSentEvent::class, SignalingListener::class);
|
||||
$context->registerEventListener(SystemMessageSentEvent::class, SignalingListener::class);
|
||||
$context->registerEventListener(SystemMessagesMultipleSentEvent::class, SignalingListener::class);
|
||||
$context->registerEventListener(ReactionAddedEvent::class, SignalingListener::class);
|
||||
$context->registerEventListener(ReactionRemovedEvent::class, SignalingListener::class);
|
||||
|
||||
// Signaling listeners (Both)
|
||||
$context->registerEventListener(BeforeRoomDeletedEvent::class, SignalingListener::class);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Talk\Signaling;
|
||||
|
||||
use OCA\Talk\AppInfo\Application;
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\MessageParser;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\AMessageSentEvent;
|
||||
use OCA\Talk\Events\AParticipantModifiedEvent;
|
||||
use OCA\Talk\Events\AReactionEvent;
|
||||
use OCA\Talk\Events\ARoomEvent;
|
||||
use OCA\Talk\Events\ARoomModifiedEvent;
|
||||
use OCA\Talk\Events\ASystemMessageSentEvent;
|
||||
|
|
@ -27,6 +31,8 @@ use OCA\Talk\Events\GuestJoinedRoomEvent;
|
|||
use OCA\Talk\Events\GuestsCleanedUpEvent;
|
||||
use OCA\Talk\Events\LobbyModifiedEvent;
|
||||
use OCA\Talk\Events\ParticipantModifiedEvent;
|
||||
use OCA\Talk\Events\ReactionAddedEvent;
|
||||
use OCA\Talk\Events\ReactionRemovedEvent;
|
||||
use OCA\Talk\Events\RoomExtendedEvent;
|
||||
use OCA\Talk\Events\RoomModifiedEvent;
|
||||
use OCA\Talk\Events\RoomSyncedEvent;
|
||||
|
|
@ -41,9 +47,12 @@ use OCA\Talk\Participant;
|
|||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\SessionService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Server;
|
||||
|
||||
/**
|
||||
|
|
@ -75,6 +84,9 @@ class Listener implements IEventListener {
|
|||
protected ParticipantService $participantService,
|
||||
protected SessionService $sessionService,
|
||||
protected ITimeFactory $timeFactory,
|
||||
protected MessageParser $messageParser,
|
||||
protected ThreadService $threadService,
|
||||
protected IFactory $l10nFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +156,8 @@ class Listener implements IEventListener {
|
|||
ChatMessageSentEvent::class,
|
||||
SystemMessageSentEvent::class,
|
||||
SystemMessagesMultipleSentEvent::class => $this->notifyMessageSent($event),
|
||||
ReactionAddedEvent::class,
|
||||
ReactionRemovedEvent::class => $this->notifyReactionSent($event),
|
||||
default => null, // Ignoring events subscribed by the internal signaling
|
||||
};
|
||||
}
|
||||
|
|
@ -458,17 +472,116 @@ class Listener implements IEventListener {
|
|||
}
|
||||
|
||||
protected function notifyMessageSent(AMessageSentEvent $event): void {
|
||||
$comment = $event->getComment();
|
||||
if ($event instanceof ASystemMessageSentEvent && $event->shouldSkipLastActivityUpdate()) {
|
||||
return;
|
||||
$messageDecoded = json_decode($comment->getMessage(), true);
|
||||
$messageType = $messageDecoded['message'] ?? '';
|
||||
if ($messageType !== 'message_deleted' && $messageType !== 'message_edited') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$room = $event->getRoom();
|
||||
$message = [
|
||||
$data = [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
],
|
||||
];
|
||||
$this->externalSignaling->sendRoomMessage($room, $message);
|
||||
|
||||
if ($event instanceof ASystemMessageSentEvent && $comment->getVerb() === ChatManager::VERB_SYSTEM && $event->shouldSkipLastActivityUpdate() === false) {
|
||||
$this->externalSignaling->sendRoomMessage($room, $data);
|
||||
return;
|
||||
}
|
||||
|
||||
$l10n = $this->l10nFactory->get(Application::APP_ID, 'en');
|
||||
$message = $this->messageParser->createMessage($event->getRoom(), null, $comment, $l10n);
|
||||
$this->messageParser->parseMessage($message);
|
||||
if ($message->getVisibility() === false) {
|
||||
$this->externalSignaling->sendRoomMessage($room, $data);
|
||||
return;
|
||||
}
|
||||
|
||||
$thread = null;
|
||||
if (!isset($messageType)) {
|
||||
$threadId = (int)$comment->getTopmostParentId() ?: $comment->getId();
|
||||
try {
|
||||
$thread = $this->threadService->findByThreadId($room->getId(), (int)$threadId);
|
||||
} catch (DoesNotExistException) {
|
||||
}
|
||||
}
|
||||
|
||||
$data['chat']['comment'] = $message->toArray('json', $thread);
|
||||
|
||||
if ($event instanceof ASystemMessageSentEvent && $event->getParent() !== null) {
|
||||
$parent = $event->getParent();
|
||||
$parentMessage = $this->messageParser->createMessage($event->getRoom(), null, $parent, $l10n);
|
||||
$this->messageParser->parseMessage($parentMessage);
|
||||
$data['chat']['comment']['parent'] = $parentMessage->toArray('json', $thread);
|
||||
}
|
||||
|
||||
$this->externalSignaling->sendRoomMessage($room, $data);
|
||||
}
|
||||
|
||||
protected function notifyReactionSent(AReactionEvent $event): void {
|
||||
$room = $event->getRoom();
|
||||
$data = [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$comment = $event->getMessage();
|
||||
$messageType = $event instanceof ReactionAddedEvent ? ChatManager::VERB_REACTION : 'reaction_revoked';
|
||||
|
||||
$threadId = (int)$comment->getTopmostParentId() ?: $comment->getId();
|
||||
try {
|
||||
$thread = $this->threadService->findByThreadId($room->getId(), (int)$threadId);
|
||||
} catch (DoesNotExistException) {
|
||||
$thread = null;
|
||||
}
|
||||
|
||||
$reactions = $comment->getReactions();
|
||||
if ($event instanceof ReactionRemovedEvent) {
|
||||
if (array_key_exists($event->getReaction(), $reactions) && $reactions[$event->getReaction()] > 1) {
|
||||
--$reactions[$event->getReaction()];
|
||||
} else {
|
||||
unset($reactions[$event->getReaction()]);
|
||||
}
|
||||
} elseif ($event instanceof ReactionAddedEvent) {
|
||||
if (array_key_exists($event->getReaction(), $reactions)) {
|
||||
++$reactions[$event->getReaction()];
|
||||
} else {
|
||||
$reactions[$event->getReaction()] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$comment->setReactions($reactions);
|
||||
$l10n = $this->l10nFactory->get(Application::APP_ID, 'en');
|
||||
$message = $this->messageParser->createMessage($event->getRoom(), null, $comment, $l10n);
|
||||
$this->messageParser->parseMessage($message);
|
||||
// Build reaction message data
|
||||
$data['chat']['comment'] = [
|
||||
'id' => null,
|
||||
'token' => $event->getRoom()->getToken(),
|
||||
'actorType' => $event->getActorType(),
|
||||
'actorId' => $event->getActorId(),
|
||||
'actorDisplayName' => $event->getActorDisplayName(),
|
||||
'timestamp' => $this->timeFactory->getTime(),
|
||||
'message' => $event->getReaction(),
|
||||
'messageParameters' => [],
|
||||
'systemMessage' => $messageType,
|
||||
'messageType' => ChatManager::VERB_SYSTEM,
|
||||
'isReplyable' => false,
|
||||
'referenceId' => '',
|
||||
'reactions' => [],
|
||||
'markdown' => false ,
|
||||
'expirationTimestamp' => $message->getExpirationDateTime()?->getTimestamp(), // base on parent post timestamp + room expiration
|
||||
'threadId' => $threadId,
|
||||
];
|
||||
|
||||
$data['chat']['comment']['parent'] = $message->toArray('json', $thread);
|
||||
$this->externalSignaling->sendRoomMessage($room, $data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ class Manager {
|
|||
return array_values(array_diff([
|
||||
'dialout',
|
||||
'join-features',
|
||||
'chat-relay',
|
||||
], $features));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1024,7 +1024,7 @@ Signaling.Standalone.prototype.sendHello = function() {
|
|||
} else {
|
||||
helloVersion = '1.0'
|
||||
}
|
||||
const features = []
|
||||
const features = ['chat-relay']
|
||||
Encryption.isSupported()
|
||||
.then(() => {
|
||||
features.push('encryption')
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Talk\Tests\php\Signaling;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\MessageParser;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\ARoomModifiedEvent;
|
||||
use OCA\Talk\Events\BeforeRoomDeletedEvent;
|
||||
|
|
@ -18,15 +20,20 @@ use OCA\Talk\Events\RoomModifiedEvent;
|
|||
use OCA\Talk\Events\SystemMessageSentEvent;
|
||||
use OCA\Talk\Events\SystemMessagesMultipleSentEvent;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Message;
|
||||
use OCA\Talk\Model\Thread;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\SessionService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCA\Talk\Signaling\BackendNotifier;
|
||||
use OCA\Talk\Signaling\Listener;
|
||||
use OCA\Talk\Signaling\Messages;
|
||||
use OCA\Talk\Webinary;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\IL10N;
|
||||
use OCP\L10N\IFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
|
@ -40,6 +47,9 @@ class ListenerTest extends TestCase {
|
|||
protected SessionService&MockObject $sessionService;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected ?Listener $listener;
|
||||
protected MessageParser&MockObject $messageParser;
|
||||
protected ThreadService&MockObject $threadService;
|
||||
protected IFactory $l10nFactory;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
|
@ -49,6 +59,9 @@ class ListenerTest extends TestCase {
|
|||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->sessionService = $this->createMock(SessionService::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->messageParser = $this->createMock(MessageParser::class);
|
||||
$this->threadService = $this->createMock(ThreadService::class);
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
|
||||
$this->listener = new Listener(
|
||||
$this->createMock(Config::class),
|
||||
|
|
@ -58,6 +71,9 @@ class ListenerTest extends TestCase {
|
|||
$this->participantService,
|
||||
$this->sessionService,
|
||||
$this->timeFactory,
|
||||
$this->messageParser,
|
||||
$this->threadService,
|
||||
$this->l10nFactory,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -228,15 +244,27 @@ class ListenerTest extends TestCase {
|
|||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageSentEvent(): void {
|
||||
public function testChatMessageInvisibleSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage');
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage');
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($this->createMock(IL10N::class));
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
|
|
@ -249,9 +277,101 @@ class ListenerTest extends TestCase {
|
|||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
$message = $this->createConfiguredMock(Message::class, [
|
||||
'getVisibility' => true,
|
||||
'toArray' => [],
|
||||
'getMessageId' => 123,
|
||||
]);
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($l10n);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($room, null, $comment, $l10n)
|
||||
->willReturn($message);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage')
|
||||
->with($message);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
'comment' => [],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageSentWithThreadEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
$message = $this->createConfiguredMock(Message::class, [
|
||||
'getVisibility' => true,
|
||||
'toArray' => [],
|
||||
'getMessageId' => 123,
|
||||
]);
|
||||
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
$thread = $this->createMock(Thread::class);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($l10n);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($room, null, $comment, $l10n)
|
||||
->willReturn($message);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage')
|
||||
->with($message);
|
||||
$this->threadService->expects($this->once())
|
||||
->method('findByThreadId')
|
||||
->willReturn($thread);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
'comment' => [],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testSystemMessageSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_SYSTEM);
|
||||
|
||||
$event = new SystemMessageSentEvent(
|
||||
$room,
|
||||
|
|
@ -274,6 +394,7 @@ class ListenerTest extends TestCase {
|
|||
public function testSystemMessageSentEventSkippingUpdate(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getMessage')->willReturn(json_encode(['message' => 'test']));
|
||||
|
||||
$event = new SystemMessageSentEvent(
|
||||
$room,
|
||||
|
|
@ -290,6 +411,7 @@ class ListenerTest extends TestCase {
|
|||
public function testSystemMessagesMultipleSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_SYSTEM);
|
||||
|
||||
$event = new SystemMessagesMultipleSentEvent(
|
||||
$room,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class Manager implements \OCP\Comments\ICommentsManager {
|
|||
// TODO: Implement getNumberOfUnreadCommentsForFolder() method.
|
||||
}
|
||||
|
||||
public function create($actorType, $actorId, $objectType, $objectId) {
|
||||
public function create($actorType, $actorId, $objectType, $objectId) :IComment {
|
||||
// TODO: Implement create() method.
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue