RocketChat/Rocket.Chat

View on GitHub
apps/meteor/server/services/federation/application/room/message/receiver/MessageServiceReceiver.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import { isMessageFromMatrixFederation } from '@rocket.chat/core-typings';

import type { IFederationBridge } from '../../../../domain/IFederationBridge';
import type { RocketChatFileAdapter } from '../../../../infrastructure/rocket-chat/adapters/File';
import type { RocketChatMessageAdapter } from '../../../../infrastructure/rocket-chat/adapters/Message';
import type { RocketChatRoomAdapter } from '../../../../infrastructure/rocket-chat/adapters/Room';
import type { RocketChatSettingsAdapter } from '../../../../infrastructure/rocket-chat/adapters/Settings';
import type { RocketChatUserAdapter } from '../../../../infrastructure/rocket-chat/adapters/User';
import { AbstractFederationApplicationService } from '../../../AbstractFederationApplicationService';
import type { FederationMessageReactionEventDto } from '../../input/MessageReceiverDto';

export class FederationMessageServiceReceiver extends AbstractFederationApplicationService {
    constructor(
        protected internalRoomAdapter: RocketChatRoomAdapter,
        protected internalUserAdapter: RocketChatUserAdapter,
        protected internalMessageAdapter: RocketChatMessageAdapter,
        protected internalFileAdapter: RocketChatFileAdapter,
        protected internalSettingsAdapter: RocketChatSettingsAdapter,
        protected bridge: IFederationBridge,
    ) {
        super(bridge, internalUserAdapter, internalFileAdapter, internalSettingsAdapter);
    }

    public async onMessageReaction(messageReactionEventInput: FederationMessageReactionEventDto): Promise<void> {
        const {
            externalRoomId,
            emoji,
            externalSenderId,
            externalEventId: externalReactionEventId,
            externalReactedEventId: externalMessageId,
        } = messageReactionEventInput;

        const federatedRoom = await this.internalRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
        if (!federatedRoom) {
            return;
        }

        const federatedUser = await this.internalUserAdapter.getFederatedUserByExternalId(externalSenderId);
        if (!federatedUser) {
            return;
        }
        const message = await this.internalMessageAdapter.getMessageByFederationId(externalMessageId);
        if (!message) {
            return;
        }
        if (!isMessageFromMatrixFederation(message)) {
            return;
        }
        // TODO: move this to a Message entity in the domain layer
        const userAlreadyReacted = Boolean(
            federatedUser.getUsername() && message.reactions?.[emoji]?.usernames?.includes(federatedUser.getUsername() as string),
        );
        if (userAlreadyReacted) {
            return;
        }

        await this.internalMessageAdapter.reactToMessage(federatedUser, message, emoji, externalReactionEventId);
    }
}