RocketChat/Rocket.Chat

View on GitHub
apps/meteor/server/modules/core-apps/mention.module.ts

Summary

Maintainability
C
1 day
Test Coverage
import { api } from '@rocket.chat/core-services';
import type { IUiKitCoreApp } from '@rocket.chat/core-services';
import type { IMessage } from '@rocket.chat/core-typings';
import { Subscriptions, Messages } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { processWebhookMessage } from '../../../app/lib/server/functions/processWebhookMessage';
import { addUsersToRoomMethod } from '../../../app/lib/server/methods/addUsersToRoom';
import { i18n } from '../../lib/i18n';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';

const retrieveMentionsFromPayload = (stringifiedMentions: string): Exclude<IMessage['mentions'], undefined> => {
    try {
        const mentions = JSON.parse(stringifiedMentions);
        if (!Array.isArray(mentions) || !mentions.length || !('username' in mentions[0])) {
            throw new Error('Invalid payload');
        }
        return mentions;
    } catch (error) {
        throw new Error('Invalid payload');
    }
};

export class MentionModule implements IUiKitCoreApp {
    appId = 'mention-core';

    async blockAction(payload: any): Promise<any> {
        const {
            actionId,
            payload: { value: stringifiedMentions, blockId: referenceMessageId },
        } = payload;

        const mentions = retrieveMentionsFromPayload(stringifiedMentions);

        const usernames = mentions.map(({ username }) => username);

        const message = await Messages.findOneById(referenceMessageId, { projection: { _id: 1, tmid: 1 } });

        if (!message) {
            throw new Error('Mention bot - Failed to retrieve message information');
        }

        const joinedUsernames = `@${usernames.join(', @')}`;

        if (actionId === 'dismiss') {
            void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, {
                msg: i18n.t(
                    'You_mentioned___mentions__but_theyre_not_in_this_room',
                    {
                        mentions: joinedUsernames,
                    },
                    payload.user.language,
                ),
                _id: payload.message,
                tmid: message.tmid,
                mentions,
            });
            return;
        }

        if (actionId === 'add-users') {
            void addUsersToRoomMethod(payload.user._id, { rid: payload.room, users: usernames as string[] }, payload.user);
            void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, {
                msg: i18n.t(
                    'You_mentioned___mentions__but_theyre_not_in_this_room',
                    {
                        mentions: joinedUsernames,
                    },
                    payload.user.language,
                ),
                tmid: message.tmid,
                _id: payload.message,
                mentions,
            });
            return;
        }

        if (actionId === 'share-message') {
            const sub = await Subscriptions.findOneByRoomIdAndUserId(payload.room, payload.user._id, { projection: { t: 1, rid: 1, name: 1 } });
            // this should exist since the event is fired from withing the room (e.g the user sent a message)
            if (!sub) {
                throw new Error('Mention bot - Failed to retrieve room information');
            }

            const roomPath = roomCoordinator.getRouteLink(sub.t, { rid: sub.rid, name: sub.name });
            if (!roomPath) {
                throw new Error('Mention bot - Failed to retrieve path to room');
            }

            const messageText = i18n.t(
                'Youre_not_a_part_of__channel__and_I_mentioned_you_there',
                {
                    channel: `#${sub.name}`,
                },
                payload.user.language,
            );

            const link = new URL(Meteor.absoluteUrl(roomPath));
            link.searchParams.set('msg', message._id);
            const text = `[ ](${link.toString()})\n${messageText}`;

            // forwards message to all DMs
            await processWebhookMessage(
                {
                    roomId: mentions.map(({ _id }) => _id),
                    text,
                },
                payload.user,
            );

            void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, {
                msg: i18n.t(
                    'You_mentioned___mentions__but_theyre_not_in_this_room_You_let_them_know_via_dm',
                    {
                        mentions: joinedUsernames,
                    },
                    payload.user.language,
                ),
                tmid: message.tmid,
                _id: payload.message,
                mentions,
            });
        }
    }
}