From 774f52671bb30340aff10ccadb90dc95e9c8e97a Mon Sep 17 00:00:00 2001 From: Alan Sikora Date: Thu, 21 Apr 2022 21:14:05 -0300 Subject: [PATCH] [NEW] Alpha Matrix Federation (#23688) Implement alpha support for Matrix Federation Co-authored-by: Aaron Ogle Co-authored-by: Marcos Defendi Co-authored-by: Rodrigo Nascimento --- .../meteor/app/federation-v2/server/bridge.ts | 63 ++ .../meteor/app/federation-v2/server/config.ts | 32 + .../server/data-interface/index.ts | 9 + .../server/data-interface/message.ts | 17 + .../server/data-interface/room.ts | 8 + .../server/data-interface/user.ts | 8 + .../server/definitions/IMatrixEvent.ts | 16 + .../IMatrixEventContentAddMemberToRoom.ts | 10 + .../IMatrixEventContentCreateRoom.ts | 4 + .../IMatrixEventContentSendMessage.ts | 8 + .../IMatrixEventContentSetRoomJoinRules.ts | 8 + .../IMatrixEventContentSetRoomName.ts | 3 + .../IMatrixEventContentSetRoomTopic.ts | 3 + .../definitions/IMatrixEventContent/index.ts | 16 + .../server/definitions/MatrixEventType.ts | 12 + .../app/federation-v2/server/eventHandler.ts | 50 ++ .../federation-v2/server/events/createRoom.ts | 44 ++ .../app/federation-v2/server/events/index.ts | 6 + .../server/events/roomMembership.ts | 134 ++++ .../server/events/sendMessage.ts | 22 + .../server/events/setRoomJoinRules.ts | 43 ++ .../server/events/setRoomName.ts | 37 ++ .../server/events/setRoomTopic.ts | 22 + apps/meteor/app/federation-v2/server/index.ts | 20 + .../meteor/app/federation-v2/server/logger.ts | 6 + .../server/matrix-client/index.ts | 9 + .../server/matrix-client/message.ts | 25 + .../server/matrix-client/room.ts | 48 ++ .../server/matrix-client/user.ts | 148 +++++ .../server/methods/checkBridgedRoomExists.ts | 7 + apps/meteor/app/federation-v2/server/queue.ts | 16 + .../app/federation-v2/server/settings.ts | 140 ++++ apps/meteor/app/federation-v2/server/tools.ts | 4 + .../app/federation/server/startup/settings.ts | 10 +- .../app/lib/client/methods/sendMessage.js | 9 +- .../app/lib/server/methods/sendMessage.js | 9 +- apps/meteor/app/models/server/index.js | 3 + .../models/server/models/MatrixBridgedRoom.ts | 28 + .../models/server/models/MatrixBridgedUser.ts | 37 ++ .../app/slashcommands-bridge/client/index.ts | 14 + .../app/slashcommands-bridge/server/index.ts | 43 ++ apps/meteor/app/ui-sidenav/client/roomList.js | 5 + apps/meteor/client/importPackages.ts | 1 + apps/meteor/package.json | 3 + .../rocketchat-i18n/i18n/en.i18n.json | 10 + apps/meteor/server/importPackages.ts | 2 + .../server/modules/watchers/publishFields.ts | 3 + apps/meteor/tests/data/rooms.helper.js | 2 +- yarn.lock | 627 +++++++++++++++++- 49 files changed, 1760 insertions(+), 44 deletions(-) create mode 100644 apps/meteor/app/federation-v2/server/bridge.ts create mode 100644 apps/meteor/app/federation-v2/server/config.ts create mode 100644 apps/meteor/app/federation-v2/server/data-interface/index.ts create mode 100644 apps/meteor/app/federation-v2/server/data-interface/message.ts create mode 100644 apps/meteor/app/federation-v2/server/data-interface/room.ts create mode 100644 apps/meteor/app/federation-v2/server/data-interface/user.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEvent.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentAddMemberToRoom.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSendMessage.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomName.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomTopic.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts create mode 100644 apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts create mode 100644 apps/meteor/app/federation-v2/server/eventHandler.ts create mode 100644 apps/meteor/app/federation-v2/server/events/createRoom.ts create mode 100644 apps/meteor/app/federation-v2/server/events/index.ts create mode 100644 apps/meteor/app/federation-v2/server/events/roomMembership.ts create mode 100644 apps/meteor/app/federation-v2/server/events/sendMessage.ts create mode 100644 apps/meteor/app/federation-v2/server/events/setRoomJoinRules.ts create mode 100644 apps/meteor/app/federation-v2/server/events/setRoomName.ts create mode 100644 apps/meteor/app/federation-v2/server/events/setRoomTopic.ts create mode 100644 apps/meteor/app/federation-v2/server/index.ts create mode 100644 apps/meteor/app/federation-v2/server/logger.ts create mode 100644 apps/meteor/app/federation-v2/server/matrix-client/index.ts create mode 100644 apps/meteor/app/federation-v2/server/matrix-client/message.ts create mode 100644 apps/meteor/app/federation-v2/server/matrix-client/room.ts create mode 100644 apps/meteor/app/federation-v2/server/matrix-client/user.ts create mode 100644 apps/meteor/app/federation-v2/server/methods/checkBridgedRoomExists.ts create mode 100644 apps/meteor/app/federation-v2/server/queue.ts create mode 100644 apps/meteor/app/federation-v2/server/settings.ts create mode 100644 apps/meteor/app/federation-v2/server/tools.ts create mode 100644 apps/meteor/app/models/server/models/MatrixBridgedRoom.ts create mode 100644 apps/meteor/app/models/server/models/MatrixBridgedUser.ts create mode 100644 apps/meteor/app/slashcommands-bridge/client/index.ts create mode 100644 apps/meteor/app/slashcommands-bridge/server/index.ts diff --git a/apps/meteor/app/federation-v2/server/bridge.ts b/apps/meteor/app/federation-v2/server/bridge.ts new file mode 100644 index 000000000000..1bf4045efd07 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/bridge.ts @@ -0,0 +1,63 @@ +import { Bridge, AppServiceRegistration } from 'matrix-appservice-bridge'; + +import { IMatrixEvent } from './definitions/IMatrixEvent'; +import { MatrixEventType } from './definitions/MatrixEventType'; +import { addToQueue } from './queue'; +import { config } from './config'; + +/* eslint-disable @typescript-eslint/camelcase */ +const registrationConfig = AppServiceRegistration.fromObject({ + id: config.id, + hs_token: config.hsToken, + as_token: config.asToken, + url: config.bridgeUrl, + sender_localpart: config.bridgeLocalpart, + namespaces: { + users: [ + { + exclusive: false, + // Reserve these MXID's (usernames) + regex: `.*`, + }, + ], + aliases: [ + { + exclusive: false, + // Reserve these room aliases + regex: `.*`, + }, + ], + rooms: [ + { + exclusive: false, + // This regex is used to define which rooms we listen to with the bridge. + // This does not reserve the rooms like the other namespaces. + regex: '.*', + }, + ], + }, + rate_limited: false, + protocols: null, +}); +/* eslint-enable @typescript-eslint/camelcase */ + +export const matrixBridge = new Bridge({ + homeserverUrl: config.homeserverUrl, + domain: config.homeserverDomain, + registration: registrationConfig, + disableStores: true, + controller: { + onAliasQuery: (alias, matrixRoomId): void => { + console.log('onAliasQuery', alias, matrixRoomId); + }, + onEvent: async (request /* , context*/): Promise => { + // Get the event + const event = request.getData() as unknown as IMatrixEvent; + + addToQueue(event); + }, + onLog: async (line, isError): Promise => { + console.log(line, isError); + }, + }, +}); diff --git a/apps/meteor/app/federation-v2/server/config.ts b/apps/meteor/app/federation-v2/server/config.ts new file mode 100644 index 000000000000..37898a31bc0c --- /dev/null +++ b/apps/meteor/app/federation-v2/server/config.ts @@ -0,0 +1,32 @@ +import { settings } from '../../settings/server'; + +type bridgeUrlString = `${string}://${string}:${string}`; +export type bridgeUrlTuple = [string, string, number]; + +interface IBridgeConfig { + id: string; + hsToken: string; + asToken: string; + homeserverUrl: string; + homeserverDomain: string; + bridgeUrl: bridgeUrlString; + bridgeLocalpart: string; +} + +function _getConfig(): IBridgeConfig { + return { + id: settings.get('Federation_Matrix_id') as string, + hsToken: settings.get('Federation_Matrix_hs_token') as string, + asToken: settings.get('Federation_Matrix_as_token') as string, + homeserverUrl: settings.get('Federation_Matrix_homeserver_url') as string, + homeserverDomain: settings.get('Federation_Matrix_homeserver_domain') as string, + bridgeUrl: settings.get('Federation_Matrix_bridge_url') as bridgeUrlString, + bridgeLocalpart: settings.get('Federation_Matrix_bridge_localpart') as string, + } as IBridgeConfig; +} + +export let config: IBridgeConfig = _getConfig(); + +export function getConfig() { + config = _getConfig(); +} diff --git a/apps/meteor/app/federation-v2/server/data-interface/index.ts b/apps/meteor/app/federation-v2/server/data-interface/index.ts new file mode 100644 index 000000000000..18e2fbf7020f --- /dev/null +++ b/apps/meteor/app/federation-v2/server/data-interface/index.ts @@ -0,0 +1,9 @@ +import * as message from './message'; +import * as room from './room'; +import * as user from './user'; + +export const dataInterface = { + message: message.normalize, + room: room.normalize, + user: user.normalize, +}; diff --git a/apps/meteor/app/federation-v2/server/data-interface/message.ts b/apps/meteor/app/federation-v2/server/data-interface/message.ts new file mode 100644 index 000000000000..7d27732f93e6 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/data-interface/message.ts @@ -0,0 +1,17 @@ +import { IMessage, IUser } from '@rocket.chat/core-typings'; + +import { dataInterface } from '.'; + +interface INormalizedMessage extends IMessage { + u: Required>; +} + +export const normalize = async (message: IMessage): Promise => { + // TODO: normalize the entire payload (if needed) + const normalizedMessage: INormalizedMessage = message as INormalizedMessage; + + // Normalize the user + normalizedMessage.u = (await dataInterface.user(message.u._id)) as Required>; + + return normalizedMessage; +}; diff --git a/apps/meteor/app/federation-v2/server/data-interface/room.ts b/apps/meteor/app/federation-v2/server/data-interface/room.ts new file mode 100644 index 000000000000..df1d2163badf --- /dev/null +++ b/apps/meteor/app/federation-v2/server/data-interface/room.ts @@ -0,0 +1,8 @@ +import { IRoom } from '@rocket.chat/core-typings'; + +import { Rooms } from '../../../models/server'; + +export const normalize = async (roomId: string): Promise => { + // Normalize the user + return Rooms.findOneById(roomId); +}; diff --git a/apps/meteor/app/federation-v2/server/data-interface/user.ts b/apps/meteor/app/federation-v2/server/data-interface/user.ts new file mode 100644 index 000000000000..15fb48843428 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/data-interface/user.ts @@ -0,0 +1,8 @@ +import { IUser } from '@rocket.chat/core-typings'; + +import { Users } from '../../../models/server'; + +export const normalize = async (userId: string): Promise => { + // Normalize the user + return Users.findOneById(userId); +}; diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEvent.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEvent.ts new file mode 100644 index 000000000000..7111057ec55e --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEvent.ts @@ -0,0 +1,16 @@ +import { MatrixEventType } from './MatrixEventType'; +import { EventContent } from './IMatrixEventContent'; + +export interface IMatrixEvent { + age: number; + content: EventContent[T]; + invite_room_state?: IMatrixEvent[]; + event_id: string; + origin_server_ts: number; + room_id: string; + sender: string; + state_key: string; + type: T; + unsigned: { age: number }; + user_id: string; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentAddMemberToRoom.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentAddMemberToRoom.ts new file mode 100644 index 000000000000..2accfbffbc8b --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentAddMemberToRoom.ts @@ -0,0 +1,10 @@ +export enum AddMemberToRoomMembership { + JOIN = 'join', + INVITE = 'invite', + LEAVE = 'leave', +} + +export interface IMatrixEventContentAddMemberToRoom { + displayname: string; + membership: AddMemberToRoomMembership; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts new file mode 100644 index 000000000000..b374135c9309 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentCreateRoom.ts @@ -0,0 +1,4 @@ +export interface IMatrixEventContentCreateRoom { + creator: string; + room_version: string; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSendMessage.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSendMessage.ts new file mode 100644 index 000000000000..8158e21f74d0 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSendMessage.ts @@ -0,0 +1,8 @@ +export enum MatrixSendMessageType { + 'm.text', +} + +export interface IMatrixEventContentSendMessage { + body: string; + msgtype: MatrixSendMessageType; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts new file mode 100644 index 000000000000..920f9bb53777 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules.ts @@ -0,0 +1,8 @@ +export enum SetRoomJoinRules { + JOIN = 'public', + INVITE = 'invite', +} + +export interface IMatrixEventContentSetRoomJoinRules { + join_rule: SetRoomJoinRules; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomName.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomName.ts new file mode 100644 index 000000000000..5ac3f72e15c6 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomName.ts @@ -0,0 +1,3 @@ +export interface IMatrixEventContentSetRoomName { + name: string; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomTopic.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomTopic.ts new file mode 100644 index 000000000000..54e91e0881c0 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/IMatrixEventContentSetRoomTopic.ts @@ -0,0 +1,3 @@ +export interface IMatrixEventContentSetRoomTopic { + topic: string; +} diff --git a/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts new file mode 100644 index 000000000000..7615779282e1 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/IMatrixEventContent/index.ts @@ -0,0 +1,16 @@ +import { MatrixEventType } from '../MatrixEventType'; +import { IMatrixEventContentCreateRoom } from './IMatrixEventContentCreateRoom'; +import { IMatrixEventContentAddMemberToRoom } from './IMatrixEventContentAddMemberToRoom'; +import { IMatrixEventContentSendMessage } from './IMatrixEventContentSendMessage'; +import { IMatrixEventContentSetRoomJoinRules } from './IMatrixEventContentSetRoomJoinRules'; +import { IMatrixEventContentSetRoomName } from './IMatrixEventContentSetRoomName'; +import { IMatrixEventContentSetRoomTopic } from './IMatrixEventContentSetRoomTopic'; + +export type EventContent = { + [MatrixEventType.CREATE_ROOM]: IMatrixEventContentCreateRoom; + [MatrixEventType.ROOM_MEMBERSHIP]: IMatrixEventContentAddMemberToRoom; + [MatrixEventType.SET_ROOM_JOIN_RULES]: IMatrixEventContentSetRoomJoinRules; + [MatrixEventType.SET_ROOM_NAME]: IMatrixEventContentSetRoomName; + [MatrixEventType.SET_ROOM_TOPIC]: IMatrixEventContentSetRoomTopic; + [MatrixEventType.SEND_MESSAGE]: IMatrixEventContentSendMessage; +}; diff --git a/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts b/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts new file mode 100644 index 000000000000..14d4f0bb0ecb --- /dev/null +++ b/apps/meteor/app/federation-v2/server/definitions/MatrixEventType.ts @@ -0,0 +1,12 @@ +export enum MatrixEventType { + CREATE_ROOM = 'm.room.create', + ROOM_MEMBERSHIP = 'm.room.member', + // SET_ROOM_POWER_LEVELS = 'm.room.power_levels', + // SET_ROOM_CANONICAL_ALIAS = 'm.room.canonical_alias', + SET_ROOM_JOIN_RULES = 'm.room.join_rules', + // SET_ROOM_HISTORY_VISIBILITY = 'm.room.history_visibility', + // SET_ROOM_GUEST_ACCESS = 'm.room.guest_access', + SET_ROOM_NAME = 'm.room.name', + SET_ROOM_TOPIC = 'm.room.topic', + SEND_MESSAGE = 'm.room.message', +} diff --git a/apps/meteor/app/federation-v2/server/eventHandler.ts b/apps/meteor/app/federation-v2/server/eventHandler.ts new file mode 100644 index 000000000000..166ed1199adc --- /dev/null +++ b/apps/meteor/app/federation-v2/server/eventHandler.ts @@ -0,0 +1,50 @@ +import { IMatrixEvent } from './definitions/IMatrixEvent'; +import { MatrixEventType } from './definitions/MatrixEventType'; +import { handleRoomMembership, handleCreateRoom, handleSendMessage, setRoomJoinRules, setRoomName, setRoomTopic } from './events'; + +export const eventHandler = async (event: IMatrixEvent): Promise => { + console.log(`Processing ${event.type}...`, JSON.stringify(event, null, 2)); + + switch (event.type) { + case MatrixEventType.CREATE_ROOM: { + await handleCreateRoom(event as IMatrixEvent); + + break; + } + case MatrixEventType.ROOM_MEMBERSHIP: { + await handleRoomMembership(event as IMatrixEvent); + + break; + } + case MatrixEventType.SET_ROOM_JOIN_RULES: { + await setRoomJoinRules(event as IMatrixEvent); + + break; + } + case MatrixEventType.SET_ROOM_NAME: { + await setRoomName(event as IMatrixEvent); + + break; + } + case MatrixEventType.SET_ROOM_TOPIC: { + await setRoomTopic(event as IMatrixEvent); + + break; + } + case MatrixEventType.SEND_MESSAGE: { + await handleSendMessage(event as IMatrixEvent); + + break; + } + // case MatrixEventType.SET_ROOM_POWER_LEVELS: + // case MatrixEventType.SET_ROOM_CANONICAL_ALIAS: + // case MatrixEventType.SET_ROOM_HISTORY_VISIBILITY: + // case MatrixEventType.SET_ROOM_GUEST_ACCESS: { + // console.log(`Ignoring ${event.type}`); + // + // break; + // } + default: + console.log(`Could not find handler for ${event.type}`, event); + } +}; diff --git a/apps/meteor/app/federation-v2/server/events/createRoom.ts b/apps/meteor/app/federation-v2/server/events/createRoom.ts new file mode 100644 index 000000000000..746f1f95c999 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/createRoom.ts @@ -0,0 +1,44 @@ +import { MatrixBridgedRoom, MatrixBridgedUser, Users } from '../../../models/server'; +import { createRoom } from '../../../lib/server'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; +import { checkBridgedRoomExists } from '../methods/checkBridgedRoomExists'; +import { matrixClient } from '../matrix-client'; + +export const handleCreateRoom = async (event: IMatrixEvent): Promise => { + const { room_id: matrixRoomId, sender } = event; + + return new Promise((resolve) => { + setTimeout(async () => { + // Check if the room already exists and if so, ignore + const roomExists = await checkBridgedRoomExists(matrixRoomId); + + if (roomExists) { + return resolve(); + } + + // Find the bridged user id + const bridgedUserId = await MatrixBridgedUser.getId(sender); + let user; + + // Create the user if necessary + if (!bridgedUserId) { + const { uid } = await matrixClient.user.createLocal(sender); + + user = Users.findOneById(uid); + } else { + user = await Users.findOneById(bridgedUserId); + } + + // Create temp room name + const roomName = `Federation-${matrixRoomId.split(':')[0].replace('!', '')}`; + + // @ts-ignore TODO: typing of legacy functions + const { rid: roomId } = createRoom('c', roomName, user.username); + + MatrixBridgedRoom.insert({ rid: roomId, mri: matrixRoomId }); + + resolve(); + }, 500); + }); +}; diff --git a/apps/meteor/app/federation-v2/server/events/index.ts b/apps/meteor/app/federation-v2/server/events/index.ts new file mode 100644 index 000000000000..ef403e8e78cd --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/index.ts @@ -0,0 +1,6 @@ +export * from './createRoom'; +export * from './roomMembership'; +export * from './sendMessage'; +export * from './setRoomJoinRules'; +export * from './setRoomName'; +export * from './setRoomTopic'; diff --git a/apps/meteor/app/federation-v2/server/events/roomMembership.ts b/apps/meteor/app/federation-v2/server/events/roomMembership.ts new file mode 100644 index 000000000000..66479a4265cd --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/roomMembership.ts @@ -0,0 +1,134 @@ +import { MatrixBridgedUser, MatrixBridgedRoom, Users, Rooms } from '../../../models/server'; +import { addUserToRoom, createRoom, removeUserFromRoom } from '../../../lib/server'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; +import { AddMemberToRoomMembership } from '../definitions/IMatrixEventContent/IMatrixEventContentAddMemberToRoom'; +import { setRoomJoinRules } from './setRoomJoinRules'; +import { setRoomName } from './setRoomName'; +import { addToQueue } from '../queue'; +import { matrixClient } from '../matrix-client'; + +const ensureRoom = async ( + matrixRoomId: string, + roomId: string, + username: string, + roomState?: IMatrixEvent[], +): Promise => { + const room = await Rooms.findOneById(roomId); + // If the room does not exist, create it + if (!room) { + // Create temp room name + const roomName = `Federation-${matrixRoomId.split(':')[0].replace('!', '')}`; + + // @ts-ignore TODO: typing of legacy functions + const { rid: createdRoomId } = createRoom('c', roomName, username); + + roomId = createdRoomId; + + MatrixBridgedRoom.insert({ rid: roomId, mri: matrixRoomId }); + + // TODO: this should be better + /* eslint-disable no-await-in-loop */ + for (const state of roomState || []) { + switch (state.type) { + case 'm.room.create': + continue; + case 'm.room.join_rules': { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/camelcase + await setRoomJoinRules({ room_id: roomId, ...state }); + + break; + } + case 'm.room.name': { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/camelcase + await setRoomName({ room_id: roomId, ...state }); + + break; + } + case 'm.room.member': { + // @ts-ignore + if (state.content.membership === 'join') { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/camelcase,@typescript-eslint/no-use-before-define + await handleRoomMembership({ room_id: roomId, ...state }); + } + + break; + } + } + } + /* eslint-enable no-await-in-loop */ + } + + return roomId; +}; + +export const handleRoomMembership = async (event: IMatrixEvent): Promise => { + const { + room_id: matrixRoomId, + sender: senderMatrixUserId, + state_key: affectedMatrixUserId, + content: { membership }, + invite_room_state: roomState, + } = event; + + // Find the bridged room id + let roomId = await MatrixBridgedRoom.getId(matrixRoomId); + + // If there is no room id, throw error + if (!roomId) { + throw new Error(`Could not find room with matrixRoomId: ${matrixRoomId}`); + } + + // Find the sender user + const senderUserId = await MatrixBridgedUser.getId(senderMatrixUserId); + let senderUser = await Users.findOneById(senderUserId); + // If the sender user does not exist, it means we need to create it + if (!senderUser) { + const { uid } = await matrixClient.user.createLocal(senderMatrixUserId); + + senderUser = Users.findOneById(uid); + } + + // Find the affected user + const affectedUserId = await MatrixBridgedUser.getId(affectedMatrixUserId); + let affectedUser = await Users.findOneById(affectedUserId); + // If the affected user does not exist, it means we need to create it + if (!affectedUser) { + const { uid } = await matrixClient.user.createLocal(affectedMatrixUserId); + + affectedUser = Users.findOneById(uid); + } + + switch (membership) { + case AddMemberToRoomMembership.JOIN: + roomId = await ensureRoom(matrixRoomId, roomId, senderUser.username, roomState); + + addUserToRoom(roomId, affectedUser); + break; + case AddMemberToRoomMembership.INVITE: + // Re-run the state first + if (!roomId) { + for (const state of roomState || []) { + // eslint-disable-next-line @typescript-eslint/camelcase,no-await-in-loop + addToQueue({ ...state, room_id: matrixRoomId }); + } + + addToQueue(event); + + return; + } + + // If the room exists, then just add the user + // TODO: this should be a local invite + addUserToRoom(roomId, affectedUser, senderUser); + break; + case AddMemberToRoomMembership.LEAVE: + removeUserFromRoom(roomId, affectedUser, { + byUser: senderUser, + }); + break; + } +}; diff --git a/apps/meteor/app/federation-v2/server/events/sendMessage.ts b/apps/meteor/app/federation-v2/server/events/sendMessage.ts new file mode 100644 index 000000000000..d2d35ac992df --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/sendMessage.ts @@ -0,0 +1,22 @@ +// @ts-ignore +import { MatrixBridgedRoom, MatrixBridgedUser, Messages, Users } from '../../../models'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; + +export const handleSendMessage = async (event: IMatrixEvent): Promise => { + const { room_id: matrixRoomId, sender } = event; + + // Find the bridged user id + const userId = await MatrixBridgedUser.getId(sender); + + // Find the user + const user = await Users.findOneById(userId); + + // Find the bridged room id + const roomId = await MatrixBridgedRoom.getId(matrixRoomId); + + Messages.createWithTypeRoomIdMessageAndUser('m', roomId, event.content.body, { + _id: user._id, + username: user.username, + }); +}; diff --git a/apps/meteor/app/federation-v2/server/events/setRoomJoinRules.ts b/apps/meteor/app/federation-v2/server/events/setRoomJoinRules.ts new file mode 100644 index 000000000000..534e0b3043e6 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/setRoomJoinRules.ts @@ -0,0 +1,43 @@ +import { MatrixBridgedRoom, Rooms, Subscriptions } from '../../../models/server'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; +import { SetRoomJoinRules } from '../definitions/IMatrixEventContent/IMatrixEventContentSetRoomJoinRules'; + +export const setRoomJoinRules = async (event: IMatrixEvent): Promise => { + const { + room_id: matrixRoomId, + content: { join_rule: joinRule }, + } = event; + + // Find the bridged room id + const roomId = await MatrixBridgedRoom.getId(matrixRoomId); + + let type; + + switch (joinRule) { + case SetRoomJoinRules.INVITE: + type = 'p'; + break; + case SetRoomJoinRules.JOIN: + default: + type = 'c'; + } + + Rooms.update( + { _id: roomId }, + { + $set: { + t: type, + }, + }, + ); + + Subscriptions.update( + { rid: roomId }, + { + $set: { + t: type, + }, + }, + ); +}; diff --git a/apps/meteor/app/federation-v2/server/events/setRoomName.ts b/apps/meteor/app/federation-v2/server/events/setRoomName.ts new file mode 100644 index 000000000000..9e71970ddfc3 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/setRoomName.ts @@ -0,0 +1,37 @@ +import { MatrixBridgedRoom, Rooms, Subscriptions } from '../../../models/server'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; + +export const setRoomName = async (event: IMatrixEvent): Promise => { + const { + room_id: matrixRoomId, + content: { name }, + } = event; + + // Normalize room name + const normalizedName = name.replace('@', ''); + + // Find the bridged room id + const roomId = await MatrixBridgedRoom.getId(matrixRoomId); + + Rooms.update( + { _id: roomId }, + { + $set: { + name: normalizedName, + fname: normalizedName, + bridged: true, // TODO: this should not be here + }, + }, + ); + + Subscriptions.update( + { rid: roomId }, + { + $set: { + name: normalizedName, + fname: normalizedName, + }, + }, + ); +}; diff --git a/apps/meteor/app/federation-v2/server/events/setRoomTopic.ts b/apps/meteor/app/federation-v2/server/events/setRoomTopic.ts new file mode 100644 index 000000000000..d75d38df651e --- /dev/null +++ b/apps/meteor/app/federation-v2/server/events/setRoomTopic.ts @@ -0,0 +1,22 @@ +import { MatrixBridgedRoom, Rooms } from '../../../models/server'; +import { IMatrixEvent } from '../definitions/IMatrixEvent'; +import { MatrixEventType } from '../definitions/MatrixEventType'; + +export const setRoomTopic = async (event: IMatrixEvent): Promise => { + const { + room_id: matrixRoomId, + content: { topic }, + } = event; + + // Find the bridged room id + const roomId = await MatrixBridgedRoom.getId(matrixRoomId); + + Rooms.update( + { _id: roomId }, + { + $set: { + description: topic, + }, + }, + ); +}; diff --git a/apps/meteor/app/federation-v2/server/index.ts b/apps/meteor/app/federation-v2/server/index.ts new file mode 100644 index 000000000000..e0acacb01f94 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/index.ts @@ -0,0 +1,20 @@ +import { matrixBridge } from './bridge'; +import { bridgeUrlTuple, config } from './config'; +import { bridgeLogger } from './logger'; +import './settings'; +import { isFederationMatrixEnabled } from './tools'; + +((): void => { + if (!isFederationMatrixEnabled()) return; + + bridgeLogger.info(`Running Federation V2: + id: ${config.id} + bridgeUrl: ${config.bridgeUrl} + homeserverURL: ${config.homeserverUrl} + homeserverDomain: ${config.homeserverDomain} + `); + + const [, , port] = config.bridgeUrl.split(':') as bridgeUrlTuple; + + matrixBridge.run(port); +})(); diff --git a/apps/meteor/app/federation-v2/server/logger.ts b/apps/meteor/app/federation-v2/server/logger.ts new file mode 100644 index 000000000000..0b88f48bfde6 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/logger.ts @@ -0,0 +1,6 @@ +import { Logger } from '../../logger/server'; + +const logger = new Logger('Federation_Matrix'); + +export const bridgeLogger = logger.section('bridge'); +export const setupLogger = logger.section('setup'); diff --git a/apps/meteor/app/federation-v2/server/matrix-client/index.ts b/apps/meteor/app/federation-v2/server/matrix-client/index.ts new file mode 100644 index 000000000000..68664d6e9cdf --- /dev/null +++ b/apps/meteor/app/federation-v2/server/matrix-client/index.ts @@ -0,0 +1,9 @@ +import * as message from './message'; +import * as room from './room'; +import * as user from './user'; + +export const matrixClient = { + message, + room, + user, +}; diff --git a/apps/meteor/app/federation-v2/server/matrix-client/message.ts b/apps/meteor/app/federation-v2/server/matrix-client/message.ts new file mode 100644 index 000000000000..ef48ba607ecd --- /dev/null +++ b/apps/meteor/app/federation-v2/server/matrix-client/message.ts @@ -0,0 +1,25 @@ +import { IMessage } from '@rocket.chat/core-typings'; + +import { MatrixBridgedRoom, MatrixBridgedUser } from '../../../models/server'; +import { matrixBridge } from '../bridge'; + +export const send = async (message: IMessage): Promise => { + // Retrieve the matrix user + const userMatrixId = MatrixBridgedUser.getMatrixId(message.u._id); + + // Retrieve the matrix room + const roomMatrixId = MatrixBridgedRoom.getMatrixId(message.rid); + + if (!userMatrixId) { + throw new Error(`Could not find user matrix id for ${message.u._id}`); + } + + if (!roomMatrixId) { + throw new Error(`Could not find room matrix id for ${message.rid}`); + } + + const intent = matrixBridge.getIntent(userMatrixId); + await intent.sendText(roomMatrixId, message.msg || '...not-supported...'); + + return message; +}; diff --git a/apps/meteor/app/federation-v2/server/matrix-client/room.ts b/apps/meteor/app/federation-v2/server/matrix-client/room.ts new file mode 100644 index 000000000000..820e1e77b6cd --- /dev/null +++ b/apps/meteor/app/federation-v2/server/matrix-client/room.ts @@ -0,0 +1,48 @@ +import { IRoom, IUser } from '@rocket.chat/core-typings'; + +import { MatrixBridgedRoom, MatrixBridgedUser } from '../../../models/server'; +import { matrixBridge } from '../bridge'; + +interface ICreateRoomResult { + rid: string; + mri: string; +} + +export const create = async (user: IUser, room: IRoom): Promise => { + // Check if this room already exists (created by another method) + // and if so, ignore the callback + const roomMatrixId = MatrixBridgedRoom.getMatrixId(room._id); + if (roomMatrixId) { + return { rid: room._id, mri: roomMatrixId }; + } + + // Retrieve the matrix user + const userMatrixId = MatrixBridgedUser.getMatrixId(user._id); + + if (!userMatrixId) { + throw new Error(`Could not find user matrix id for ${user._id}`); + } + + const intent = matrixBridge.getIntent(userMatrixId); + + const roomName = `@${room.name}`; + + // Create the matrix room + const matrixRoom = await intent.createRoom({ + createAsClient: true, + options: { + name: roomName, + topic: room.topic, + visibility: room.t === 'p' ? 'invite' : 'public', + preset: room.t === 'p' ? 'private_chat' : 'public_chat', + }, + }); + + // Add to the map + MatrixBridgedRoom.insert({ rid: room._id, mri: matrixRoom.room_id }); + + // Add our user TODO: Doing this I think is un-needed since our user is the creator of the room. With it in.. there were errors + // await intent.invite(matrixRoom.room_id, userMatrixId); + + return { rid: room._id, mri: matrixRoom.room_id }; +}; diff --git a/apps/meteor/app/federation-v2/server/matrix-client/user.ts b/apps/meteor/app/federation-v2/server/matrix-client/user.ts new file mode 100644 index 000000000000..8620a524d233 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/matrix-client/user.ts @@ -0,0 +1,148 @@ +import { MatrixProfileInfo } from 'matrix-bot-sdk'; +import { IUser } from '@rocket.chat/core-typings'; + +import { matrixBridge } from '../bridge'; +import { MatrixBridgedUser, MatrixBridgedRoom, Users } from '../../../models/server'; +import { addUserToRoom } from '../../../lib/server/functions'; +import { config } from '../config'; +import { matrixClient } from '.'; +import { dataInterface } from '../data-interface'; + +interface ICreateUserResult { + uid: string; + mui: string; + remote: boolean; +} + +export const invite = async (inviterId: string, roomId: string, invitedId: string): Promise => { + console.log(`[${inviterId}-${invitedId}-${roomId}] Inviting user ${invitedId} to ${roomId}...`); + + // Find the inviter user + let inviterUser = MatrixBridgedUser.getById(inviterId); + // Get the user + const user = await dataInterface.user(inviterId); + + // The inviters user doesn't yet exist in matrix + if (!inviterUser) { + console.log(`[${inviterId}-${invitedId}-${roomId}] Creating remote inviter user...`); + + // Create the missing user + inviterUser = await matrixClient.user.createRemote(user); + + console.log(`[${inviterId}-${invitedId}-${roomId}] Inviter user created as ${inviterUser.mui}...`); + } + + // Find the bridged room id + let matrixRoomId = await MatrixBridgedRoom.getMatrixId(roomId); + + if (!matrixRoomId) { + console.log(`[${inviterId}-${invitedId}-${roomId}] Creating remote room...`); + + // Get the room + const room = await dataInterface.room(roomId); + + // Create the missing room + const { mri } = await matrixClient.room.create({ _id: inviterId } as IUser, room); + + matrixRoomId = mri; + + console.log(`[${inviterId}-${invitedId}-${roomId}] Remote room created as ${matrixRoomId}...`); + } + + // Determine if the user is local or remote + let invitedUserMatrixId = invitedId; + const invitedUserDomain = invitedId.includes(':') ? invitedId.split(':').pop() : ''; + const invitedUserIsRemote = invitedUserDomain && invitedUserDomain !== config.homeserverDomain; + + // Find the invited user in Rocket.Chats users + let invitedUser = Users.findOneByUsername(invitedId.replace('@', '')); + + if (!invitedUser) { + // Create the invited user + invitedUser = await matrixClient.user.createLocal(invitedUserMatrixId); + } + + // If the invited user is not remote, let's ensure it exists remotely + if (!invitedUserIsRemote) { + console.log(`[${inviterId}-${invitedId}-${roomId}] Creating remote invited user...`); + + // Check if we already have a matrix id for that user + const existingMatrixId = MatrixBridgedUser.getMatrixId(invitedUser._id); + + if (!existingMatrixId) { + const { mui } = await matrixClient.user.createRemote(invitedUser); + + invitedUserMatrixId = mui; + } else { + invitedUserMatrixId = existingMatrixId; + } + + console.log(`[${inviterId}-${invitedId}-${roomId}] Invited user created as ${invitedUserMatrixId}...`); + } + + console.log(`[${inviterId}-${invitedId}-${roomId}] Inviting the user to the room...`); + + // Invite && Auto-join if the user is Rocket.Chat controlled + if (!invitedUserIsRemote) { + // Invite the user to the room + await matrixBridge.getIntent(inviterUser.mui).invite(matrixRoomId, invitedUserMatrixId); + + console.log(`[${inviterId}-${invitedId}-${roomId}] Auto-join room...`); + + await matrixBridge.getIntent(invitedUserMatrixId).join(matrixRoomId); + } else { + // Invite the user to the room but don't wait as this is dependent on the user accepting the invite because we don't control this user + matrixBridge.getIntent(inviterUser.mui).invite(matrixRoomId, invitedUserMatrixId); + } + + // Add the matrix user to the invited room + addUserToRoom(roomId, invitedUser, user, false); +}; + +export const createRemote = async (u: IUser): Promise => { + const matrixUserId = `@${u.username?.toLowerCase()}:${config.homeserverDomain}`; + + console.log(`Creating remote user ${matrixUserId}...`); + + const intent = matrixBridge.getIntent(matrixUserId); + + await intent.ensureProfile(u.name); + + await intent.setDisplayName(`${u.username} (${u.name})`); + + const payload = { uid: u._id, mui: matrixUserId, remote: true }; + + MatrixBridgedUser.upsert({ uid: u._id }, payload); + + return payload; +}; + +export const createLocal = async (matrixUserId: string): Promise => { + console.log(`Creating local user ${matrixUserId}...`); + + const intent = matrixBridge.getIntent(matrixUserId); + + let currentProfile: MatrixProfileInfo = {}; + + try { + currentProfile = await intent.getProfileInfo(matrixUserId); + } catch (err) { + // no-op + } + + const uid = Users.create({ + username: matrixUserId.replace('@', ''), + type: 'user', + status: 'online', + active: true, + roles: ['user'], + name: currentProfile.displayname, + requirePasswordChange: false, + }); + + const payload = { uid, mui: matrixUserId, remote: false }; + + MatrixBridgedUser.upsert({ uid }, payload); + + return payload; +}; diff --git a/apps/meteor/app/federation-v2/server/methods/checkBridgedRoomExists.ts b/apps/meteor/app/federation-v2/server/methods/checkBridgedRoomExists.ts new file mode 100644 index 000000000000..7db759e1cc38 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/methods/checkBridgedRoomExists.ts @@ -0,0 +1,7 @@ +import { MatrixBridgedRoom } from '../../../models/server'; + +export const checkBridgedRoomExists = async (matrixRoomId: string): Promise => { + const existingRoomId = MatrixBridgedRoom.getId(matrixRoomId); + + return !!existingRoomId; +}; diff --git a/apps/meteor/app/federation-v2/server/queue.ts b/apps/meteor/app/federation-v2/server/queue.ts new file mode 100644 index 000000000000..f1f0ea02061e --- /dev/null +++ b/apps/meteor/app/federation-v2/server/queue.ts @@ -0,0 +1,16 @@ +// Create the queue +import { queueAsPromised } from 'fastq'; +import * as fastq from 'fastq'; + +import { IMatrixEvent } from './definitions/IMatrixEvent'; +import { MatrixEventType } from './definitions/MatrixEventType'; +import { eventHandler } from './eventHandler'; + +export const matrixEventQueue: queueAsPromised> = fastq.promise(eventHandler, 1); + +export const addToQueue = (event: IMatrixEvent): void => { + console.log(`Queueing ${event.type}...`); + + // TODO: Handle error + matrixEventQueue.push(event).catch((err) => console.error(err)); +}; diff --git a/apps/meteor/app/federation-v2/server/settings.ts b/apps/meteor/app/federation-v2/server/settings.ts new file mode 100644 index 000000000000..c32257f39f64 --- /dev/null +++ b/apps/meteor/app/federation-v2/server/settings.ts @@ -0,0 +1,140 @@ +import yaml from 'js-yaml'; +import { SHA256 } from 'meteor/sha'; + +import { settings, settingsRegistry } from '../../settings/server'; +import { Settings } from '../../models/server/raw'; +import { setupLogger } from './logger'; +import { getConfig, config } from './config'; + +settingsRegistry.addGroup('Federation', function () { + this.section('Matrix Bridge', async function () { + this.add('Federation_Matrix_enabled', false, { + readonly: false, + type: 'boolean', + i18nLabel: 'Federation_Matrix_enabled', + i18nDescription: 'Federation_Matrix_enabled_desc', + alert: 'Federation_Matrix_Enabled_Alert', + }); + + const uniqueId = await settings.get('uniqueID'); + const hsToken = SHA256(`hs_${uniqueId}`); + const asToken = SHA256(`as_${uniqueId}`); + + this.add('Federation_Matrix_id', `rocketchat_${uniqueId}`, { + readonly: true, + type: 'string', + i18nLabel: 'Federation_Matrix_id', + i18nDescription: 'Federation_Matrix_id_desc', + }); + + this.add('Federation_Matrix_hs_token', hsToken, { + readonly: true, + type: 'string', + i18nLabel: 'Federation_Matrix_hs_token', + i18nDescription: 'Federation_Matrix_hs_token_desc', + }); + + this.add('Federation_Matrix_as_token', asToken, { + readonly: true, + type: 'string', + i18nLabel: 'Federation_Matrix_as_token', + i18nDescription: 'Federation_Matrix_as_token_desc', + }); + + this.add('Federation_Matrix_homeserver_url', 'http://localhost:8008', { + type: 'string', + i18nLabel: 'Federation_Matrix_homeserver_url', + i18nDescription: 'Federation_Matrix_homeserver_url_desc', + }); + + this.add('Federation_Matrix_homeserver_domain', 'local.rocket.chat', { + type: 'string', + i18nLabel: 'Federation_Matrix_homeserver_domain', + i18nDescription: 'Federation_Matrix_homeserver_domain_desc', + }); + + this.add('Federation_Matrix_bridge_url', 'http://host.docker.internal:3300', { + type: 'string', + i18nLabel: 'Federation_Matrix_bridge_url', + i18nDescription: 'Federation_Matrix_bridge_url_desc', + }); + + this.add('Federation_Matrix_bridge_localpart', 'rocket.cat', { + type: 'string', + i18nLabel: 'Federation_Matrix_bridge_localpart', + i18nDescription: 'Federation_Matrix_bridge_localpart_desc', + }); + + this.add('Federation_Matrix_registration_file', '', { + readonly: true, + type: 'code', + i18nLabel: 'Federation_Matrix_registration_file', + i18nDescription: 'Federation_Matrix_registration_file_desc', + }); + }); +}); + +let registrationFile = {}; + +const updateRegistrationFile = async function (): Promise { + // Refresh config + getConfig(); + + let { bridgeUrl } = config; + + if (!bridgeUrl.includes(':')) { + bridgeUrl = `${bridgeUrl}:3300`; + } + + /* eslint-disable @typescript-eslint/camelcase */ + registrationFile = { + id: config.id, + hs_token: config.hsToken, + as_token: config.asToken, + url: bridgeUrl, + sender_localpart: config.bridgeLocalpart, + namespaces: { + users: [ + { + exclusive: false, + regex: '.*', + }, + ], + rooms: [ + { + exclusive: false, + regex: '.*', + }, + ], + aliases: [ + { + exclusive: false, + regex: '.*', + }, + ], + }, + }; + /* eslint-enable @typescript-eslint/camelcase */ + + // Update the registration file + await Settings.updateValueById('Federation_Matrix_registration_file', yaml.dump(registrationFile)); +}; + +// TODO: Changes here should re-initialize the bridge instead of needing a restart +// Add settings listeners +settings.watch('Federation_Matrix_enabled', (value) => { + setupLogger.info(`Federation Matrix is ${value ? 'enabled' : 'disabled'}`); +}); + +settings.watchMultiple( + [ + 'Federation_Matrix_id', + 'Federation_Matrix_hs_token', + 'Federation_Matrix_as_token', + 'Federation_Matrix_homeserver_url', + 'Federation_Matrix_homeserver_domain', + 'Federation_Matrix_bridge_url', + 'Federation_Matrix_bridge_localpart', + ], + updateRegistrationFile, +); diff --git a/apps/meteor/app/federation-v2/server/tools.ts b/apps/meteor/app/federation-v2/server/tools.ts new file mode 100644 index 000000000000..d961bf0becba --- /dev/null +++ b/apps/meteor/app/federation-v2/server/tools.ts @@ -0,0 +1,4 @@ +import { settings } from '../../settings/server'; +import { matrixBridge } from './bridge'; + +export const isFederationMatrixEnabled = () => settings.get('Federation_Matrix_enabled') && matrixBridge; diff --git a/apps/meteor/app/federation/server/startup/settings.ts b/apps/meteor/app/federation/server/startup/settings.ts index 317aabd6d9af..746a2b5e694b 100644 --- a/apps/meteor/app/federation/server/startup/settings.ts +++ b/apps/meteor/app/federation/server/startup/settings.ts @@ -1,5 +1,3 @@ -import { Meteor } from 'meteor/meteor'; - import { settingsRegistry, settings } from '../../../settings/server'; import { updateStatus, updateEnabled, isRegisteringOrEnabled } from '../functions/helpers'; import { getFederationDomain } from '../lib/getFederationDomain'; @@ -10,10 +8,8 @@ import { setupLogger } from '../lib/logger'; import { FederationKeys } from '../../../models/server/raw'; import { STATUS_ENABLED, STATUS_REGISTERING, STATUS_ERROR_REGISTERING, STATUS_DISABLED } from '../constants'; -Meteor.startup(async function () { - const federationPublicKey = await FederationKeys.getPublicKeyString(); - - settingsRegistry.addGroup('Federation', function () { +settingsRegistry.addGroup('Federation', function () { + this.section('Rocket.Chat Federation', async function () { this.add('FEDERATION_Enabled', false, { type: 'boolean', i18nLabel: 'Enabled', @@ -36,6 +32,8 @@ Meteor.startup(async function () { // disableReset: true, }); + const federationPublicKey = await FederationKeys.getPublicKeyString(); + this.add('FEDERATION_Public_Key', federationPublicKey || '', { readonly: true, type: 'string', diff --git a/apps/meteor/app/lib/client/methods/sendMessage.js b/apps/meteor/app/lib/client/methods/sendMessage.js index 29c54e7a0d6a..8dfafeeb5f94 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.js +++ b/apps/meteor/app/lib/client/methods/sendMessage.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { TimeSync } from 'meteor/mizzao:timesync'; import s from 'underscore.string'; -import { ChatMessage } from '../../../models'; +import { ChatMessage, Rooms } from '../../../models'; import { settings } from '../../../settings'; import { callbacks } from '../../../../lib/callbacks'; import { t } from '../../../utils/client'; @@ -31,6 +31,13 @@ Meteor.methods({ if (settings.get('Message_Read_Receipt_Enabled')) { message.unread = true; } + + // If the room is bridged, send the message to matrix only + const { bridged } = Rooms.findOne({ _id: message.rid }, { fields: { bridged: 1 } }); + if (bridged) { + return; + } + message = callbacks.run('beforeSaveMessage', message); onClientMessageReceived(message).then(function (message) { ChatMessage.insert(message); diff --git a/apps/meteor/app/lib/server/methods/sendMessage.js b/apps/meteor/app/lib/server/methods/sendMessage.js index 6ddbbf851f96..91e223322b36 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.js +++ b/apps/meteor/app/lib/server/methods/sendMessage.js @@ -7,12 +7,13 @@ import { hasPermission } from '../../../authorization'; import { metrics } from '../../../metrics'; import { settings } from '../../../settings'; import { messageProperties } from '../../../ui-utils'; -import { Users, Messages } from '../../../models'; +import { Users, Messages, Rooms } from '../../../models'; import { sendMessage } from '../functions'; import { RateLimiter } from '../lib'; import { canSendMessage } from '../../../authorization/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { api } from '../../../../server/sdk/api'; +import { matrixClient } from '../../../federation-v2/server/matrix-client'; export function executeSendMessage(uid, message) { if (message.tshow && !message.tmid) { @@ -105,6 +106,12 @@ Meteor.methods({ } try { + // If the room is bridged, send the message to matrix only + const { bridged } = Rooms.findOne({ _id: message.rid }, { fields: { bridged: 1 } }); + if (bridged) { + return matrixClient.message.send({ ...message, u: { _id: uid } }); + } + return executeSendMessage(uid, message); } catch (error) { if ((error.error || error.message) === 'error-not-allowed') { diff --git a/apps/meteor/app/models/server/index.js b/apps/meteor/app/models/server/index.js index 9d31a7a7d4cb..24957884d25d 100644 --- a/apps/meteor/app/models/server/index.js +++ b/apps/meteor/app/models/server/index.js @@ -20,6 +20,9 @@ export { AppsPersistenceModel } from './models/apps-persistence-model'; export { AppsModel } from './models/apps-model'; export { FederationRoomEvents } from './models/FederationRoomEvents'; +export { MatrixBridgedRoom } from './models/MatrixBridgedRoom'; +export { MatrixBridgedUser } from './models/MatrixBridgedUser'; + export { Base, BaseDb, diff --git a/apps/meteor/app/models/server/models/MatrixBridgedRoom.ts b/apps/meteor/app/models/server/models/MatrixBridgedRoom.ts new file mode 100644 index 000000000000..bfdfc3d1301f --- /dev/null +++ b/apps/meteor/app/models/server/models/MatrixBridgedRoom.ts @@ -0,0 +1,28 @@ +import { Base } from './_Base'; + +interface IMatrixBridgedRoom { + rid: string; + mri: string; +} + +class MatrixBridgedRoomModel extends Base { + constructor() { + super('matrix_bridged_rooms'); + this.tryEnsureIndex({ rid: 1 }, { unique: true, sparse: true }); + this.tryEnsureIndex({ mri: 1 }, { unique: true, sparse: true }); + } + + getMatrixId(rid: string): string | null { + const bridgedRoom: IMatrixBridgedRoom = this.findOne({ rid }); + + return bridgedRoom ? bridgedRoom.mri : null; + } + + getId(mri: string): string | null { + const bridgedRoom: IMatrixBridgedRoom = this.findOne({ mri }); + + return bridgedRoom ? bridgedRoom.rid : null; + } +} + +export const MatrixBridgedRoom = new MatrixBridgedRoomModel(); diff --git a/apps/meteor/app/models/server/models/MatrixBridgedUser.ts b/apps/meteor/app/models/server/models/MatrixBridgedUser.ts new file mode 100644 index 000000000000..b2eabb9d686e --- /dev/null +++ b/apps/meteor/app/models/server/models/MatrixBridgedUser.ts @@ -0,0 +1,37 @@ +import { Base } from './_Base'; + +export interface IMatrixBridgedUser { + uid: string; + mui: string; + remote: boolean; +} + +class MatrixBridgedUserModel extends Base { + constructor() { + super('matrix_bridged_users'); + this.tryEnsureIndex({ uid: 1 }, { unique: true, sparse: true }); + this.tryEnsureIndex({ mui: 1 }, { unique: true, sparse: true }); + } + + getMatrixId(uid: string): string | null { + const bridgedUser: IMatrixBridgedUser = this.findOne({ uid }); + + return bridgedUser ? bridgedUser.mui : null; + } + + getByMatrixId(mui: string): IMatrixBridgedUser | null { + return this.findOne({ mui }); + } + + getId(mui: string): string | null { + const bridgedUser: IMatrixBridgedUser = this.findOne({ mui }); + + return bridgedUser ? bridgedUser.uid : null; + } + + getById(uid: string): IMatrixBridgedUser | null { + return this.findOne({ uid }); + } +} + +export const MatrixBridgedUser = new MatrixBridgedUserModel(); diff --git a/apps/meteor/app/slashcommands-bridge/client/index.ts b/apps/meteor/app/slashcommands-bridge/client/index.ts new file mode 100644 index 000000000000..d7beb4be531e --- /dev/null +++ b/apps/meteor/app/slashcommands-bridge/client/index.ts @@ -0,0 +1,14 @@ +import { slashCommands } from '../../utils/lib/slashCommand'; + +slashCommands.add( + 'bridge', + undefined, + { + description: 'Invites_an_user_to_a_bridged_room', + params: '#command #user', + }, + undefined, + false, + undefined, + undefined, +); diff --git a/apps/meteor/app/slashcommands-bridge/server/index.ts b/apps/meteor/app/slashcommands-bridge/server/index.ts new file mode 100644 index 000000000000..eccc2d61955d --- /dev/null +++ b/apps/meteor/app/slashcommands-bridge/server/index.ts @@ -0,0 +1,43 @@ +import { Meteor } from 'meteor/meteor'; +import { Match } from 'meteor/check'; + +import { slashCommands } from '../../utils/lib/slashCommand'; +import { matrixClient } from '../../federation-v2/server/matrix-client'; + +function Bridge(_command: 'bridge', stringParams: string, item: Record): void { + if (_command !== 'bridge' || !Match.test(stringParams, String)) { + return; + } + + const [command, ...params] = stringParams.split(' '); + + const { rid: roomId } = item; + + switch (command) { + case 'invite': + // Invite a user + // Example: /bridge invite rc_helena:b.rc.allskar.com + const [userId] = params; + + const currentUserId = Meteor.userId(); + + if (currentUserId) { + Promise.await(matrixClient.user.invite(currentUserId, roomId, `@${userId.replace('@', '')}`)); + } + + break; + } +} + +slashCommands.add( + 'bridge', + Bridge, + { + description: 'Invites_an_user_to_a_bridged_room', + params: '#command #user', + }, + undefined, + false, + undefined, + undefined, +); diff --git a/apps/meteor/app/ui-sidenav/client/roomList.js b/apps/meteor/app/ui-sidenav/client/roomList.js index 282c980656b1..902441e0b0fa 100644 --- a/apps/meteor/app/ui-sidenav/client/roomList.js +++ b/apps/meteor/app/ui-sidenav/client/roomList.js @@ -172,6 +172,7 @@ const mergeSubRoom = (subscription) => { departmentId: 1, source: 1, queuedAt: 1, + bridged: 1, }, }; @@ -213,6 +214,7 @@ const mergeSubRoom = (subscription) => { ts, source, queuedAt, + bridged, } = room; subscription.lm = subscription.lr ? new Date(Math.max(subscription.lr, lastRoomUpdate)) : lastRoomUpdate; @@ -251,6 +253,7 @@ const mergeSubRoom = (subscription) => { ts, source, queuedAt, + bridged, }); }; @@ -294,6 +297,7 @@ const mergeRoomSub = (room) => { ts, source, queuedAt, + bridged, } = room; Subscriptions.update( @@ -334,6 +338,7 @@ const mergeRoomSub = (room) => { ts, source, queuedAt, + bridged, ...getLowerCaseNames(room, sub.name, sub.fname), }, }, diff --git a/apps/meteor/client/importPackages.ts b/apps/meteor/client/importPackages.ts index 3cd75189e5cf..fc5877e0faf4 100644 --- a/apps/meteor/client/importPackages.ts +++ b/apps/meteor/client/importPackages.ts @@ -44,6 +44,7 @@ import '../app/apps/client'; import '../app/slackbridge/client'; import '../app/slashcommands-archiveroom/client'; import '../app/slashcommand-asciiarts/client'; +import '../app/slashcommands-bridge/client'; import '../app/slashcommands-create/client'; import '../app/slashcommands-hide/client'; import '../app/slashcommands-invite/client'; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 63c765a0022d..9ddb79b59470 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -97,6 +97,7 @@ "@types/fibers": "^3.1.1", "@types/google-libphonenumber": "^7.4.21", "@types/imap": "^0.8.35", + "@types/js-yaml": "^4.0.5", "@types/jsdom": "^16.2.12", "@types/jsdom-global": "^3.0.2", "@types/jsrsasign": "^9.0.3", @@ -249,6 +250,7 @@ "exif-be-gone": "^1.2.0", "express": "^4.17.1", "express-rate-limit": "^5.2.6", + "fastq": "^1.13.0", "fflate": "^0.7.1", "fibers": "^5.0.1", "file-type": "^10.11.0", @@ -284,6 +286,7 @@ "lodash.get": "^4.4.2", "mailparser": "^3.4.0", "marked": "^0.7.0", + "matrix-appservice-bridge": "^3.2.0", "mem": "^8.1.1", "meteor-node-stubs": "^1.1.0", "mime-db": "^1.48.0", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 1a255d598cad..4fb3e7404636 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1954,6 +1954,16 @@ "FEDERATION_Test_Setup_Success": "Your federation setup is working and other servers can find you!", "FEDERATION_Unique_Id": "Unique ID", "FEDERATION_Unique_Id_Description": "This is your federation unique ID, used to identify your peer on the mesh.", + "Federation_Matrix": "Federation V2", + "Federation_Matrix_enabled": "Enabled", + "Federation_Matrix_Enabled_Alert": "Matrix Federation Support is in alpha. Use on a production system is not recommended at this time. RESTART CURRENTLY REQUIRED AFTER ALL SETTING CHANGES. More Information about Matrix Federation support can be found here", + "Federation_Matrix_id": "AppService ID", + "Federation_Matrix_hs_token": "Homeserver Token", + "Federation_Matrix_as_token": "AppService Token", + "Federation_Matrix_homeserver_url": "Homeserver URL", + "Federation_Matrix_homeserver_domain": "Homeserver Domain", + "Federation_Matrix_bridge_url": "Bridge URL", + "Federation_Matrix_bridge_localpart": "AppService User Localpart", "Field": "Field", "Field_removed": "Field removed", "Field_required": "Field required", diff --git a/apps/meteor/server/importPackages.ts b/apps/meteor/server/importPackages.ts index 86938fc06980..44ff3394678b 100644 --- a/apps/meteor/server/importPackages.ts +++ b/apps/meteor/server/importPackages.ts @@ -23,6 +23,7 @@ import '../app/emoji-custom/server'; import '../app/emoji-emojione/server'; import '../app/error-handler'; import '../app/federation/server'; +import '../app/federation-v2/server'; import '../app/file'; import '../app/file-upload'; import '../app/github-enterprise/server'; @@ -62,6 +63,7 @@ import '../app/apps/server'; import '../app/slackbridge/server'; import '../app/slashcommands-archiveroom/server'; import '../app/slashcommand-asciiarts/server'; +import '../app/slashcommands-bridge/server'; import '../app/slashcommands-create/server'; import '../app/slashcommands-help'; import '../app/slashcommands-hide/server'; diff --git a/apps/meteor/server/modules/watchers/publishFields.ts b/apps/meteor/server/modules/watchers/publishFields.ts index 550c7579f7f7..4b877955bb62 100644 --- a/apps/meteor/server/modules/watchers/publishFields.ts +++ b/apps/meteor/server/modules/watchers/publishFields.ts @@ -103,6 +103,9 @@ export const roomFields = { waitingResponse: 1, queuedAt: 1, + // Federation fields + bridged: 1, + // fields used by DMs usernames: 1, uids: 1, diff --git a/apps/meteor/tests/data/rooms.helper.js b/apps/meteor/tests/data/rooms.helper.js index 1081d79cf402..60e2775c0e93 100644 --- a/apps/meteor/tests/data/rooms.helper.js +++ b/apps/meteor/tests/data/rooms.helper.js @@ -2,7 +2,7 @@ import { api, credentials, request } from './api-data'; export const createRoom = ({ name, type, username, token, agentId, members = [], credentials: customCredentials }) => { if (!type) { - throw new Error('"type" is required in "createRoom" test helper'); + throw new Error('"type" is required in "createRoom.ts" test helper'); } if (type === 'v') { /* Special handling for voip type of rooms. diff --git a/yarn.lock b/yarn.lock index 494012db5003..57fa332c7326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 6 cacheKey: 8 +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: bdc35758b552bcf045733ac047fb7f9a07c4678b944c641adfbd41f798b4b91fffd0fdc0df2578d9b0afc7b4d636aa6e110ead5d6281a2adc1ab90efd7f057f8 + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.1.0": version: 2.1.2 resolution: "@ampproject/remapping@npm:2.1.2" @@ -1770,6 +1777,13 @@ __metadata: languageName: node linkType: hard +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 + languageName: node + linkType: hard + "@cspotcode/source-map-consumer@npm:0.8.0": version: 0.8.0 resolution: "@cspotcode/source-map-consumer@npm:0.8.0" @@ -1822,6 +1836,17 @@ __metadata: languageName: node linkType: hard +"@dabh/diagnostics@npm:^2.0.2": + version: 2.0.3 + resolution: "@dabh/diagnostics@npm:2.0.3" + dependencies: + colorspace: 1.1.x + enabled: 2.0.x + kuler: ^2.0.0 + checksum: 4879600c55c8315a0fb85fbb19057bad1adc08f0a080a8cb4e2b63f723c379bfc4283b68123a2b078d367b327dd8df12fcb27464efe791addc0a48b9df6d79a1 + languageName: node + linkType: hard + "@discoveryjs/json-ext@npm:^0.5.3": version: 0.5.7 resolution: "@discoveryjs/json-ext@npm:0.5.7" @@ -2489,6 +2514,15 @@ __metadata: languageName: node linkType: hard +"@napi-rs/cli@npm:^2.2.0": + version: 2.6.2 + resolution: "@napi-rs/cli@npm:2.6.2" + bin: + napi: scripts/index.js + checksum: 3cc260aabbdb19f6354f7b24e423ce75f981b9ab4db9d7f0c5110791c72872fc24e8a16b5ac33bf3e9511a9e992dce46becd813155164d07fbe53601d27d775d + languageName: node + linkType: hard + "@napi-rs/pinyin-android-arm-eabi@npm:1.7.0": version: 1.7.0 resolution: "@napi-rs/pinyin-android-arm-eabi@npm:1.7.0" @@ -3656,6 +3690,7 @@ __metadata: "@types/fibers": ^3.1.1 "@types/google-libphonenumber": ^7.4.21 "@types/imap": ^0.8.35 + "@types/js-yaml": ^4.0.5 "@types/jsdom": ^16.2.12 "@types/jsdom-global": ^3.0.2 "@types/jsrsasign": ^9.0.3 @@ -3754,6 +3789,7 @@ __metadata: express: ^4.17.1 express-rate-limit: ^5.2.6 fast-glob: ^3.2.5 + fastq: ^1.13.0 fflate: ^0.7.1 fibers: ^5.0.1 file-type: ^10.11.0 @@ -3791,6 +3827,7 @@ __metadata: lodash.get: ^4.4.2 mailparser: ^3.4.0 marked: ^0.7.0 + matrix-appservice-bridge: ^3.2.0 mem: ^8.1.1 meteor-node-stubs: ^1.1.0 mime-db: ^1.48.0 @@ -5355,6 +5392,16 @@ __metadata: languageName: node linkType: hard +"@turt2live/matrix-sdk-crypto-nodejs@npm:^0.1.0-beta.10": + version: 0.1.0-beta.10 + resolution: "@turt2live/matrix-sdk-crypto-nodejs@npm:0.1.0-beta.10" + dependencies: + "@napi-rs/cli": ^2.2.0 + shelljs: ^0.8.4 + checksum: 0cd6d7237cbac5366c39ebe8c69619b467ab253067c48d5bfd7020895caf1afb290a917e03a0ee62c57307cd0f8c656590d9a9ce9b289ad1b7eb3f1cb89389cb + languageName: node + linkType: hard + "@types/adm-zip@npm:^0.4.34": version: 0.4.34 resolution: "@types/adm-zip@npm:0.4.34" @@ -5623,7 +5670,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.12, @types/express@npm:^4.17.13": +"@types/express@npm:*, @types/express@npm:^4.17.12, @types/express@npm:^4.17.13, @types/express@npm:^4.17.8": version: 4.17.13 resolution: "@types/express@npm:4.17.13" dependencies: @@ -5771,6 +5818,13 @@ __metadata: languageName: node linkType: hard +"@types/js-yaml@npm:^4.0.5": + version: 4.0.5 + resolution: "@types/js-yaml@npm:4.0.5" + checksum: 7dcac8c50fec31643cc9d6444b5503239a861414cdfaa7ae9a38bc22597c4d850c4b8cec3d82d73b3fbca408348ce223b0408d598b32e094470dfffc6d486b4d + languageName: node + linkType: hard + "@types/jsdom-global@npm:^3.0.2": version: 3.0.2 resolution: "@types/jsdom-global@npm:3.0.2" @@ -7259,6 +7313,13 @@ __metadata: languageName: node linkType: hard +"another-json@npm:^0.2.0": + version: 0.2.0 + resolution: "another-json@npm:0.2.0" + checksum: b1d27bd5d7a35364ff2e8eb66e65ef9a53f3ab84f7a4ae38495bf14ba0b346654f107487b4f2f247dbe6a22a8fb4ca2a94330738176516b78494e641fce05178 + languageName: node + linkType: hard + "ansi-align@npm:^3.0.0": version: 3.0.1 resolution: "ansi-align@npm:3.0.1" @@ -7826,6 +7887,13 @@ __metadata: languageName: node linkType: hard +"async@npm:0.2.10": + version: 0.2.10 + resolution: "async@npm:0.2.10" + checksum: 9ea83419ba67dc4ecf42d4eb0d93ce0ac80a67cabb612f160ed096aef5d001e3184ec9e9be5d4dc39b2c51a34e2ae16f9b21d737068b0f615fccb4eea16d0138 + languageName: node + linkType: hard + "async@npm:2.6.0": version: 2.6.0 resolution: "async@npm:2.6.0" @@ -7844,7 +7912,7 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.0, async@npm:~3.2.0": +"async@npm:^3.2.0, async@npm:^3.2.3, async@npm:~3.2.0": version: 3.2.3 resolution: "async@npm:3.2.3" checksum: c4bee57ab2249af3dc83ca3ef9acfa8e822c0d5e5aa41bae3eaf7f673648343cd64ecd7d26091ffd357f3f044428b17b5f00098494b6cf8b6b3e9681f0636ca1 @@ -8354,6 +8422,15 @@ __metadata: languageName: node linkType: hard +"basic-auth@npm:~2.0.1": + version: 2.0.1 + resolution: "basic-auth@npm:2.0.1" + dependencies: + safe-buffer: 5.1.2 + checksum: 3419b805d5dfc518f3a05dcf42aa53aa9ce820e50b6df5097f9e186322e1bc733c36722b624802cd37e791035aa73b828ed814d8362333d42d7f5cd04d7a5e48 + languageName: node + linkType: hard + "batch-processor@npm:1.0.0": version: 1.0.0 resolution: "batch-processor@npm:1.0.0" @@ -8438,6 +8515,15 @@ __metadata: languageName: node linkType: hard +"binary-search-tree@npm:0.2.5": + version: 0.2.5 + resolution: "binary-search-tree@npm:0.2.5" + dependencies: + underscore: ~1.4.4 + checksum: 04d2fbdce33ecbaafab9cd58ea286ecbadbc4ead0262bc0489a7d1a1eeef03109fd17f16a5de92b7113c08f31ac0083ab86b283a961b661d4f5f0d7e81d88586 + languageName: node + linkType: hard + "bindings@npm:^1.3.0, bindings@npm:^1.5.0": version: 1.5.0 resolution: "bindings@npm:1.5.0" @@ -8600,7 +8686,7 @@ __metadata: languageName: node linkType: hard -"bluebird@npm:^3.1.5, bluebird@npm:^3.3.5, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": +"bluebird@npm:^3.1.5, bluebird@npm:^3.3.5, bluebird@npm:^3.5.0, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": version: 3.7.2 resolution: "bluebird@npm:3.7.2" checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef @@ -8646,7 +8732,7 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^1.20.0": +"body-parser@npm:^1.19.0, body-parser@npm:^1.20.0": version: 1.20.0 resolution: "body-parser@npm:1.20.0" dependencies: @@ -8783,6 +8869,13 @@ __metadata: languageName: node linkType: hard +"browser-request@npm:^0.3.3": + version: 0.3.3 + resolution: "browser-request@npm:0.3.3" + checksum: 8f8db4f95aa33341ffa8d83dd069033310144f439d90bf5aafd4ec0a9495f4cc3fc1515a7d3e15d6968552169f72b7dd90b62efa3647adc4ce9e8be91f907240 + languageName: node + linkType: hard + "browser-stdout@npm:1.3.1": version: 1.3.1 resolution: "browser-stdout@npm:1.3.1" @@ -8887,7 +8980,7 @@ __metadata: languageName: node linkType: hard -"bs58@npm:^4.0.0": +"bs58@npm:^4.0.0, bs58@npm:^4.0.1": version: 4.0.1 resolution: "bs58@npm:4.0.1" dependencies: @@ -9407,7 +9500,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1": +"chalk@npm:^4, chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -9911,7 +10004,7 @@ __metadata: languageName: node linkType: hard -"color-string@npm:^1.9.0": +"color-string@npm:^1.6.0, color-string@npm:^1.9.0": version: 1.9.0 resolution: "color-string@npm:1.9.0" dependencies: @@ -9930,6 +10023,16 @@ __metadata: languageName: node linkType: hard +"color@npm:^3.1.3": + version: 3.2.1 + resolution: "color@npm:3.2.1" + dependencies: + color-convert: ^1.9.3 + color-string: ^1.6.0 + checksum: f81220e8b774d35865c2561be921f5652117638dcda7ca4029262046e37fc2444ac7bbfdd110cf1fd9c074a4ee5eda8f85944ffbdda26186b602dd9bb05f6400 + languageName: node + linkType: hard + "color@npm:^4.0.1": version: 4.2.3 resolution: "color@npm:4.2.3" @@ -9961,6 +10064,16 @@ __metadata: languageName: node linkType: hard +"colorspace@npm:1.1.x": + version: 1.1.4 + resolution: "colorspace@npm:1.1.4" + dependencies: + color: ^3.1.3 + text-hex: 1.0.x + checksum: bb3934ef3c417e961e6d03d7ca60ea6e175947029bfadfcdb65109b01881a1c0ecf9c2b0b59abcd0ee4a0d7c1eae93beed01b0e65848936472270a0b341ebce8 + languageName: node + linkType: hard + "combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.5, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -10188,7 +10301,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:~1.0.4": +"content-type@npm:^1.0.4, content-type@npm:~1.0.4": version: 1.0.4 resolution: "content-type@npm:1.0.4" checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 @@ -11319,7 +11432,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0": +"depd@npm:2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a @@ -11952,6 +12065,13 @@ __metadata: languageName: node linkType: hard +"enabled@npm:2.0.x": + version: 2.0.0 + resolution: "enabled@npm:2.0.0" + checksum: 9d256d89f4e8a46ff988c6a79b22fa814b4ffd82826c4fdacd9b42e9b9465709d3b748866d0ab4d442dfc6002d81de7f7b384146ccd1681f6a7f868d2acca063 + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -12709,7 +12829,7 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.7": +"eventemitter3@npm:^4.0.4, eventemitter3@npm:^4.0.7": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 @@ -12888,7 +13008,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.1, express@npm:^4.17.3": +"express@npm:^4.17.1, express@npm:^4.17.2, express@npm:^4.17.3": version: 4.17.3 resolution: "express@npm:4.17.3" dependencies: @@ -13132,7 +13252,7 @@ __metadata: languageName: node linkType: hard -"fastq@npm:^1.6.0": +"fastq@npm:^1.13.0, fastq@npm:^1.6.0": version: 1.13.0 resolution: "fastq@npm:1.13.0" dependencies: @@ -13190,6 +13310,13 @@ __metadata: languageName: node linkType: hard +"fecha@npm:^4.2.0": + version: 4.2.3 + resolution: "fecha@npm:4.2.3" + checksum: f94e2fb3acf5a7754165d04549460d3ae6c34830394d20c552197e3e000035d69732d74af04b9bed3283bf29fe2a9ebdcc0085e640b0be3cc3658b9726265e31 + languageName: node + linkType: hard + "fflate@npm:^0.7.1": version: 0.7.3 resolution: "fflate@npm:0.7.3" @@ -13252,6 +13379,15 @@ __metadata: languageName: node linkType: hard +"file-stream-rotator@npm:^0.6.1": + version: 0.6.1 + resolution: "file-stream-rotator@npm:0.6.1" + dependencies: + moment: ^2.29.1 + checksum: ebdf6a9e7ca886a50f4dafb2284d4569cefd5bdf4e4451ead25f4d68b7f9776b2620a3d110d534edd40935d1e17f37d818e2129303201870ff89c71b19b49ac1 + languageName: node + linkType: hard + "file-system-cache@npm:^1.0.5": version: 1.0.5 resolution: "file-system-cache@npm:1.0.5" @@ -13487,6 +13623,13 @@ __metadata: languageName: node linkType: hard +"fn.name@npm:1.x.x": + version: 1.1.0 + resolution: "fn.name@npm:1.1.0" + checksum: e357144f48cfc9a7f52a82bbc6c23df7c8de639fce049cac41d41d62cabb740cdb9f14eddc6485e29c933104455bdd7a69bb14a9012cef9cd4fa252a4d0cf293 + languageName: node + linkType: hard + "focus-visible@npm:^5.2.0": version: 5.2.0 resolution: "focus-visible@npm:5.2.0" @@ -14001,6 +14144,24 @@ __metadata: languageName: node linkType: hard +"generate-function@npm:^2.0.0": + version: 2.3.1 + resolution: "generate-function@npm:2.3.1" + dependencies: + is-property: ^1.0.2 + checksum: 652f083de206ead2bae4caf9c7eeb465e8d98c0b8ed2a29c6afc538cef0785b5c6eea10548f1e13cc586d3afd796c13c830c2cb3dc612ec2457b2aadda5f57c9 + languageName: node + linkType: hard + +"generate-object-property@npm:^1.1.0": + version: 1.2.0 + resolution: "generate-object-property@npm:1.2.0" + dependencies: + is-property: ^1.0.0 + checksum: 5141ca5fd545f0aabd24fd13f9f3ecf9cfea2255db00d46e282d65141d691d560c70b6361c3c0c4982f86f600361925bfd4773e0350c66d0210e6129ae553a09 + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.1, gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -14230,7 +14391,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.0.5, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": +"glob@npm:7.2.0, glob@npm:^7.0.0, glob@npm:^7.0.3, glob@npm:^7.0.5, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -14527,7 +14688,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -14753,7 +14914,7 @@ __metadata: languageName: node linkType: hard -"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": version: 1.1.7 resolution: "hash.js@npm:1.1.7" dependencies: @@ -15081,6 +15242,22 @@ __metadata: languageName: node linkType: hard +"html-to-text@npm:^8.1.0": + version: 8.2.0 + resolution: "html-to-text@npm:8.2.0" + dependencies: + "@selderee/plugin-htmlparser2": ^0.6.0 + deepmerge: ^4.2.2 + he: ^1.2.0 + htmlparser2: ^6.1.0 + minimist: ^1.2.6 + selderee: ^0.6.0 + bin: + html-to-text: bin/cli.js + checksum: 98df25eb6963cc5ac2de52a9b3e1c7f1ac156bfd5d0134e8b4356c980c6c0ecf671e1490f331d1af56b610d707904b45f88033a38577b7d25702f2188c462765 + languageName: node + linkType: hard + "html-void-elements@npm:^1.0.0": version: 1.0.5 resolution: "html-void-elements@npm:1.0.5" @@ -15107,6 +15284,13 @@ __metadata: languageName: node linkType: hard +"htmlencode@npm:^0.0.4": + version: 0.0.4 + resolution: "htmlencode@npm:0.0.4" + checksum: b5c5a280213569f59807d22ae2d68c77360f6337b6936cdfa4e2aeaa12e3a69bb36b426ad18ed9333a7fb3f7d55c14ea3b46040d3271af7a586f262aaf4092ad + languageName: node + linkType: hard + "htmlparser2@npm:^3.10.0, htmlparser2@npm:^3.9.1": version: 3.10.1 resolution: "htmlparser2@npm:3.10.1" @@ -15133,7 +15317,7 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^6.1.0": +"htmlparser2@npm:^6.0.0, htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" dependencies: @@ -15630,6 +15814,13 @@ __metadata: languageName: node linkType: hard +"interpret@npm:^1.0.0": + version: 1.4.0 + resolution: "interpret@npm:1.4.0" + checksum: 2e5f51268b5941e4a17e4ef0575bc91ed0ab5f8515e3cf77486f7c14d13f3010df9c0959f37063dcc96e78d12dc6b0bb1b9e111cdfe69771f4656d2993d36155 + languageName: node + linkType: hard + "interpret@npm:^2.2.0": version: 2.2.0 resolution: "interpret@npm:2.2.0" @@ -16024,6 +16215,26 @@ __metadata: languageName: node linkType: hard +"is-my-ip-valid@npm:^1.0.0": + version: 1.0.1 + resolution: "is-my-ip-valid@npm:1.0.1" + checksum: 0a50180a9c0842503a2199ca0ba03888069e7c093f71236c65632e9b0f496ea57536856e1ad3d1635010cb5959c551496ea84cfc56088a8e7879fe30b9d71943 + languageName: node + linkType: hard + +"is-my-json-valid@npm:^2.20.5": + version: 2.20.6 + resolution: "is-my-json-valid@npm:2.20.6" + dependencies: + generate-function: ^2.0.0 + generate-object-property: ^1.1.0 + is-my-ip-valid: ^1.0.0 + jsonpointer: ^5.0.0 + xtend: ^4.0.0 + checksum: d3519e18e6a0f4c777d5a2027b5c80d05abd0949179b94795bd2aa6c54e8f44c23b8789cb7d44332015b86cfd73dca57331e7fa53202b28e40aa4620e7f61166 + languageName: node + linkType: hard + "is-nan@npm:^1.2.1": version: 1.3.2 resolution: "is-nan@npm:1.3.2" @@ -16101,7 +16312,7 @@ __metadata: languageName: node linkType: hard -"is-plain-object@npm:5.0.0": +"is-plain-object@npm:5.0.0, is-plain-object@npm:^5.0.0": version: 5.0.0 resolution: "is-plain-object@npm:5.0.0" checksum: e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c @@ -16124,6 +16335,20 @@ __metadata: languageName: node linkType: hard +"is-promise@npm:^2.1.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 18bf7d1c59953e0ad82a1ed963fb3dc0d135c8f299a14f89a17af312fc918373136e56028e8831700e1933519630cc2fd4179a777030330fde20d34e96f40c78 + languageName: node + linkType: hard + +"is-property@npm:^1.0.0, is-property@npm:^1.0.2": + version: 1.0.2 + resolution: "is-property@npm:1.0.2" + checksum: 33b661a3690bcc88f7e47bb0a21b9e3187e76a317541ea7ec5e8096d954f441b77a46d8930c785f7fbf4ef8dfd624c25495221e026e50f74c9048fe501773be5 + languageName: node + linkType: hard + "is-regex@npm:^1.1.2, is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -17085,7 +17310,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": +"js-yaml@npm:4.1.0, js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" dependencies: @@ -17370,6 +17595,13 @@ __metadata: languageName: node linkType: hard +"jsonpointer@npm:^5.0.0": + version: 5.0.0 + resolution: "jsonpointer@npm:5.0.0" + checksum: c7ec0b6bb596b81de687bc12945586bbcdc80dfb54919656d2690d76334f796a936270067ee9f1b5bbc2d9ecc551afb366ac35e6685aa61f07b5b68d1e5e857d + languageName: node + linkType: hard + "jsontokens@npm:^2.0.2": version: 2.0.2 resolution: "jsontokens@npm:2.0.2" @@ -17602,6 +17834,13 @@ __metadata: languageName: node linkType: hard +"kuler@npm:^2.0.0": + version: 2.0.0 + resolution: "kuler@npm:2.0.0" + checksum: 9e10b5a1659f9ed8761d38df3c35effabffbd19fc6107324095238e4ef0ff044392cae9ac64a1c2dda26e532426485342226b93806bd97504b174b0dcf04ed81 + languageName: node + linkType: hard + "lamejs@git+https://github.com/zhuker/lamejs.git": version: 1.2.1 resolution: "lamejs@https://github.com/zhuker/lamejs.git#commit=582bbba6a12f981b984d8fb9e1874499fed85675" @@ -17923,7 +18162,7 @@ __metadata: languageName: node linkType: hard -"localforage@npm:^1.9.0": +"localforage@npm:^1.3.0, localforage@npm:^1.9.0": version: 1.10.0 resolution: "localforage@npm:1.10.0" dependencies: @@ -18208,6 +18447,13 @@ __metadata: languageName: node linkType: hard +"lodash@npm:4, lodash@npm:^4.14.0, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 + languageName: node + linkType: hard + "lodash@npm:^3.2.0": version: 3.10.1 resolution: "lodash@npm:3.10.1" @@ -18215,13 +18461,6 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.14.0, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 - languageName: node - linkType: hard - "log-driver@npm:^1.2.7": version: 1.2.7 resolution: "log-driver@npm:1.2.7" @@ -18251,6 +18490,26 @@ __metadata: languageName: node linkType: hard +"logform@npm:^2.3.2, logform@npm:^2.4.0": + version: 2.4.0 + resolution: "logform@npm:2.4.0" + dependencies: + "@colors/colors": 1.5.0 + fecha: ^4.2.0 + ms: ^2.1.1 + safe-stable-stringify: ^2.3.1 + triple-beam: ^1.3.0 + checksum: e75ccccc1a2664612ade3c7f3d3185787198b4028e54ea2795df87901f28b3881eddd8d7e73ce03f4420dca638a1cbe6d42254179685ab2075e4ac38a71ffb6c + languageName: node + linkType: hard + +"loglevel@npm:^1.7.1": + version: 1.8.0 + resolution: "loglevel@npm:1.8.0" + checksum: 41aeea17de24aba8dba68084a31fe9189648bce4f39c1277e021bb276c3c53a75b0d337395919cf271068ad40ecefabad0e4fdeb4a8f11908beee532b898f4a7 + languageName: node + linkType: hard + "long@npm:^2.4.0": version: 2.4.0 resolution: "long@npm:2.4.0" @@ -18299,6 +18558,19 @@ __metadata: languageName: node linkType: hard +"lowdb@npm:^1": + version: 1.0.0 + resolution: "lowdb@npm:1.0.0" + dependencies: + graceful-fs: ^4.1.3 + is-promise: ^2.1.0 + lodash: 4 + pify: ^3.0.0 + steno: ^0.4.1 + checksum: 7ae89e3d6e00963129f72c4d4e1fe8e4cda5c08a46b4f4e525109483147e799df90c07d95aeced1c270cc10f4a24c6660fe1601cc4b3a6e2c3f922ad64517eab + languageName: node + linkType: hard + "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -18561,6 +18833,83 @@ __metadata: languageName: node linkType: hard +"matrix-appservice-bridge@npm:^3.2.0": + version: 3.2.0 + resolution: "matrix-appservice-bridge@npm:3.2.0" + dependencies: + "@alloc/quick-lru": ^5.2.0 + chalk: ^4.1.0 + extend: ^3.0.2 + is-my-json-valid: ^2.20.5 + js-yaml: ^4.0.0 + matrix-appservice: ^0.10.0 + matrix-bot-sdk: ^0.6.0-beta.2 + matrix-js-sdk: ^12.4.1 + nedb: ^1.8.0 + nopt: ^5.0.0 + p-queue: ^6.6.2 + prom-client: ^14.0.0 + winston: ^3.3.3 + winston-daily-rotate-file: ^4.5.1 + checksum: c5209600008c7e4c3c556b3762b2df9c15b7631ba528dbe3d72bc420086424a3d552e2b9b7c5447607e6e71ad8c20e2531280789139867819657f4061bdbde57 + languageName: node + linkType: hard + +"matrix-appservice@npm:^0.10.0": + version: 0.10.0 + resolution: "matrix-appservice@npm:0.10.0" + dependencies: + "@types/express": ^4.17.8 + body-parser: ^1.19.0 + express: ^4.17.1 + js-yaml: ^4.1.0 + morgan: ^1.10.0 + checksum: 09eac24253744dd257f5529666bd02aa2b8cd655410ed4a008eb709d229a65f2bc3e23078af229da3509e13d918bbb0919e02ccb1b8a0ca4281ed1e2956feb25 + languageName: node + linkType: hard + +"matrix-bot-sdk@npm:^0.6.0-beta.2": + version: 0.6.0-beta.7 + resolution: "matrix-bot-sdk@npm:0.6.0-beta.7" + dependencies: + "@turt2live/matrix-sdk-crypto-nodejs": ^0.1.0-beta.10 + "@types/express": ^4.17.13 + another-json: ^0.2.0 + chalk: ^4 + express: ^4.17.2 + glob-to-regexp: ^0.4.1 + hash.js: ^1.1.7 + html-to-text: ^8.1.0 + htmlencode: ^0.0.4 + lowdb: ^1 + lru-cache: ^6.0.0 + mkdirp: ^1.0.4 + morgan: ^1.10.0 + request: ^2.88.2 + request-promise: ^4.2.6 + sanitize-html: ^2.6.1 + checksum: 12f5a03f26a8c70ba5b95fbc6feb811586d36730870bcbb3471513c11f17f2c5ec475ce387377f2924e4114217b1b1810a7f58d13b085f02081f957c91e7b44d + languageName: node + linkType: hard + +"matrix-js-sdk@npm:^12.4.1": + version: 12.5.0 + resolution: "matrix-js-sdk@npm:12.5.0" + dependencies: + "@babel/runtime": ^7.12.5 + another-json: ^0.2.0 + browser-request: ^0.3.3 + bs58: ^4.0.1 + content-type: ^1.0.4 + loglevel: ^1.7.1 + p-retry: ^4.5.0 + qs: ^6.9.6 + request: ^2.88.2 + unhomoglyph: ^1.0.6 + checksum: 31caa2a539f293ba277e85b998320f9d2cd391e0ac4d2499a99a84dad7ff569e3ae83fbd1869675b6cca67a62ac1471d3814b5da63cfce688be1ff1bb3f420e9 + languageName: node + linkType: hard + "md5-file@npm:^5.0.0": version: 5.0.0 resolution: "md5-file@npm:5.0.0" @@ -19288,7 +19637,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.0, mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3, mkdirp@npm:^0.5.5": +"mkdirp@npm:^0.5.0, mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3, mkdirp@npm:^0.5.5, mkdirp@npm:~0.5.1": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -19580,6 +19929,19 @@ __metadata: languageName: node linkType: hard +"morgan@npm:^1.10.0": + version: 1.10.0 + resolution: "morgan@npm:1.10.0" + dependencies: + basic-auth: ~2.0.1 + debug: 2.6.9 + depd: ~2.0.0 + on-finished: ~2.3.0 + on-headers: ~1.0.2 + checksum: fb41e226ab5a1abf7e8909e486b387076534716d60207e361acfb5df78b84d703a7b7ea58f3046a9fd0b83d3c94bfabde32323341a1f1b26ce50680abd2ea5dd + languageName: node + linkType: hard + "move-concurrently@npm:^1.0.1": version: 1.0.1 resolution: "move-concurrently@npm:1.0.1" @@ -19738,6 +20100,19 @@ __metadata: languageName: node linkType: hard +"nedb@npm:^1.8.0": + version: 1.8.0 + resolution: "nedb@npm:1.8.0" + dependencies: + async: 0.2.10 + binary-search-tree: 0.2.5 + localforage: ^1.3.0 + mkdirp: ~0.5.1 + underscore: ~1.4.4 + checksum: bcce49eca940dd5cc7012dc95071e550979da7c5e08045909876c78a80183756fc0921c07dc58f56ce443272271b870fe24a4b8e9e1079eddc39f190a6ccf690 + languageName: node + linkType: hard + "needle@npm:2.4.0": version: 2.4.0 resolution: "needle@npm:2.4.0" @@ -20293,6 +20668,13 @@ __metadata: languageName: node linkType: hard +"object-hash@npm:^2.0.1": + version: 2.2.0 + resolution: "object-hash@npm:2.2.0" + checksum: 55ba841e3adce9c4f1b9b46b41983eda40f854e0d01af2802d3ae18a7085a17168d6b81731d43fdf1d6bcbb3c9f9c56d22c8fea992203ad90a38d7d919bc28f1 + languageName: node + linkType: hard + "object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0": version: 1.12.0 resolution: "object-inspect@npm:1.12.0" @@ -20463,6 +20845,15 @@ __metadata: languageName: node linkType: hard +"one-time@npm:^1.0.0": + version: 1.0.0 + resolution: "one-time@npm:1.0.0" + dependencies: + fn.name: 1.x.x + checksum: fd008d7e992bdec1c67f53a2f9b46381ee12a9b8c309f88b21f0223546003fb47e8ad7c1fd5843751920a8d276c63bd4b45670ef80c61fb3e07dbccc962b5c7d + languageName: node + linkType: hard + "onetime@npm:^5.1.0, onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -20772,6 +21163,16 @@ __metadata: languageName: node linkType: hard +"p-queue@npm:^6.6.2": + version: 6.6.2 + resolution: "p-queue@npm:6.6.2" + dependencies: + eventemitter3: ^4.0.4 + p-timeout: ^3.2.0 + checksum: 832642fcc4ab6477b43e6d7c30209ab10952969ed211c6d6f2931be8a4f9935e3578c72e8cce053dc34f2eb6941a408a2c516a54904e989851a1a209cf19761c + languageName: node + linkType: hard + "p-retry@npm:*": version: 5.1.0 resolution: "p-retry@npm:5.1.0" @@ -20791,7 +21192,17 @@ __metadata: languageName: node linkType: hard -"p-timeout@npm:^3.1.0": +"p-retry@npm:^4.5.0": + version: 4.6.1 + resolution: "p-retry@npm:4.6.1" + dependencies: + "@types/retry": ^0.12.0 + retry: ^0.13.1 + checksum: e6d540413bb3d0b96e0db44f74a7af1dce41f5005e6e84d617960110b148348c86a3987be07797749e3ddd55817dd3a8ffd6eae3428758bc2994d987e48c3a70 + languageName: node + linkType: hard + +"p-timeout@npm:^3.1.0, p-timeout@npm:^3.2.0": version: 3.2.0 resolution: "p-timeout@npm:3.2.0" dependencies: @@ -20925,6 +21336,13 @@ __metadata: languageName: node linkType: hard +"parse-srcset@npm:^1.0.2": + version: 1.0.2 + resolution: "parse-srcset@npm:1.0.2" + checksum: 3a0380380c6082021fcce982f0b89fb8a493ce9dfd7d308e5e6d855201e80db8b90438649b31fdd82a3d6089a8ca17dccddaa2b730a718389af4c037b8539ebf + languageName: node + linkType: hard + "parse5-htmlparser2-tree-adapter@npm:^6.0.1": version: 6.0.1 resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1" @@ -21864,7 +22282,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:~8": +"postcss@npm:^8.3.11, postcss@npm:~8": version: 8.4.12 resolution: "postcss@npm:8.4.12" dependencies: @@ -22078,6 +22496,15 @@ __metadata: languageName: node linkType: hard +"prom-client@npm:^14.0.0": + version: 14.0.1 + resolution: "prom-client@npm:14.0.1" + dependencies: + tdigest: ^0.1.1 + checksum: 864c19b7086eda8fae652385bc8b8aeb155f85922e58672d07a64918a603341e120e65e08f9d77ccab546518dc18930284da8743c2aac3c968f626d7063d6bba + languageName: node + linkType: hard + "prometheus-gc-stats@npm:^0.6.3": version: 0.6.3 resolution: "prometheus-gc-stats@npm:0.6.3" @@ -22367,7 +22794,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.10.3, qs@npm:^6.10.0, qs@npm:^6.10.1, qs@npm:^6.5.1, qs@npm:^6.9.4": +"qs@npm:6.10.3, qs@npm:^6.10.0, qs@npm:^6.10.1, qs@npm:^6.5.1, qs@npm:^6.9.4, qs@npm:^6.9.6": version: 6.10.3 resolution: "qs@npm:6.10.3" dependencies: @@ -23177,6 +23604,15 @@ __metadata: languageName: node linkType: hard +"rechoir@npm:^0.6.2": + version: 0.6.2 + resolution: "rechoir@npm:0.6.2" + dependencies: + resolve: ^1.1.6 + checksum: fe76bf9c21875ac16e235defedd7cbd34f333c02a92546142b7911a0f7c7059d2e16f441fe6fb9ae203f459c05a31b2bcf26202896d89e390eda7514d5d2702b + languageName: node + linkType: hard + "recompose@npm:^0.30.0": version: 0.30.0 resolution: "recompose@npm:0.30.0" @@ -23548,6 +23984,20 @@ __metadata: languageName: node linkType: hard +"request-promise@npm:^4.2.6": + version: 4.2.6 + resolution: "request-promise@npm:4.2.6" + dependencies: + bluebird: ^3.5.0 + request-promise-core: 1.1.4 + stealthy-require: ^1.1.1 + tough-cookie: ^2.3.3 + peerDependencies: + request: ^2.34 + checksum: 1856c718cb4b888db8b6a206537ecac47390ea4fbdaef00da9a6169a03dd66a95c38493713f927f78a19bfeb28dcb28f5d20f6ab927d644d9e0626847d808f9f + languageName: node + linkType: hard + "request@npm:2.85.0": version: 2.85.0 resolution: "request@npm:2.85.0" @@ -23578,7 +24028,7 @@ __metadata: languageName: node linkType: hard -"request@npm:^2.51.0, request@npm:^2.68.0, request@npm:^2.87.0, request@npm:^2.88.0": +"request@npm:^2.51.0, request@npm:^2.68.0, request@npm:^2.87.0, request@npm:^2.88.0, request@npm:^2.88.2": version: 2.88.2 resolution: "request@npm:2.88.2" dependencies: @@ -23706,7 +24156,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.3.2": +"resolve@npm:^1.1.6, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.3.2": version: 1.22.0 resolution: "resolve@npm:1.22.0" dependencies: @@ -23729,7 +24179,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.3.2#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.3.2#~builtin": version: 1.22.0 resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=07638b" dependencies: @@ -24020,7 +24470,7 @@ __metadata: languageName: node linkType: hard -"safe-stable-stringify@npm:^2.1.0": +"safe-stable-stringify@npm:^2.1.0, safe-stable-stringify@npm:^2.3.1": version: 2.3.1 resolution: "safe-stable-stringify@npm:2.3.1" checksum: a0a0bad0294c3e2a9d1bf3cf2b1096dfb83c162d09a5e4891e488cce082120bd69161d2a92aae7fc48255290f17700decae9c89a07fe139794e61b5c8b411377 @@ -24053,6 +24503,20 @@ __metadata: languageName: node linkType: hard +"sanitize-html@npm:^2.6.1": + version: 2.7.0 + resolution: "sanitize-html@npm:2.7.0" + dependencies: + deepmerge: ^4.2.2 + escape-string-regexp: ^4.0.0 + htmlparser2: ^6.0.0 + is-plain-object: ^5.0.0 + parse-srcset: ^1.0.2 + postcss: ^8.3.11 + checksum: 73a4d66f69578bace3506519ca0734279b7117e15c33c7e4d075cdb483b1586261a18acd35934f6c6109f3b2a4a82f82c242171a94d5dc23fba5b09b01ea5b22 + languageName: node + linkType: hard + "saslprep@npm:^1.0.0, saslprep@npm:^1.0.3": version: 1.0.3 resolution: "saslprep@npm:1.0.3" @@ -24439,6 +24903,19 @@ __metadata: languageName: node linkType: hard +"shelljs@npm:^0.8.4": + version: 0.8.5 + resolution: "shelljs@npm:0.8.5" + dependencies: + glob: ^7.0.0 + interpret: ^1.0.0 + rechoir: ^0.6.2 + bin: + shjs: bin/shjs + checksum: 7babc46f732a98f4c054ec1f048b55b9149b98aa2da32f6cf9844c434b43c6251efebd6eec120937bd0999e13811ebd45efe17410edb3ca938f82f9381302748 + languageName: node + linkType: hard + "shimmer@npm:^1.1.0, shimmer@npm:^1.2.0": version: 1.2.1 resolution: "shimmer@npm:1.2.1" @@ -24979,7 +25456,7 @@ __metadata: languageName: node linkType: hard -"stack-trace@npm:0.0.10": +"stack-trace@npm:0.0.10, stack-trace@npm:0.0.x": version: 0.0.10 resolution: "stack-trace@npm:0.0.10" checksum: 473036ad32f8c00e889613153d6454f9be0536d430eb2358ca51cad6b95cea08a3cc33cc0e34de66b0dad221582b08ed2e61ef8e13f4087ab690f388362d6610 @@ -25040,6 +25517,15 @@ __metadata: languageName: node linkType: hard +"steno@npm:^0.4.1": + version: 0.4.4 + resolution: "steno@npm:0.4.4" + dependencies: + graceful-fs: ^4.1.3 + checksum: 87df4121cf8159fceb3dc925111aff1e237bdea2d37f6684eabbcdea63bfcff79b3234f2a61ffe8de5cf17fcb97e2cf09075a2a98993251f79e2868fe0d5ba1e + languageName: node + linkType: hard + "store2@npm:^2.12.0": version: 2.13.2 resolution: "store2@npm:2.13.2" @@ -25948,6 +26434,13 @@ __metadata: languageName: node linkType: hard +"text-hex@npm:1.0.x": + version: 1.0.0 + resolution: "text-hex@npm:1.0.0" + checksum: 1138f68adc97bf4381a302a24e2352f04992b7b1316c5003767e9b0d3367ffd0dc73d65001ea02b07cd0ecc2a9d186de0cf02f3c2d880b8a522d4ccb9342244a + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -26320,6 +26813,13 @@ __metadata: languageName: node linkType: hard +"triple-beam@npm:^1.3.0": + version: 1.3.0 + resolution: "triple-beam@npm:1.3.0" + checksum: 7d7b77d8625fb252c126c24984a68de462b538a8fcd1de2abd0a26421629cf3527d48e23b3c2264f08f4a6c3bc40a478a722176f4d7b6a1acc154cb70c359f2b + languageName: node + linkType: hard + "triplesec@npm:^3.0.26": version: 3.0.27 resolution: "triplesec@npm:3.0.27" @@ -26889,6 +27389,13 @@ __metadata: languageName: node linkType: hard +"underscore@npm:~1.4.4": + version: 1.4.4 + resolution: "underscore@npm:1.4.4" + checksum: d9d731fe7ef51fbf8cf3d1b72659a9d0da51d655a67ed866ee33d563d7ce474df37086fa9f37a2fddf85750aee5b9bf5297e80bf41024eeb447fd5e11add83ba + languageName: node + linkType: hard + "unfetch@npm:^4.2.0": version: 4.2.0 resolution: "unfetch@npm:4.2.0" @@ -26906,6 +27413,13 @@ __metadata: languageName: node linkType: hard +"unhomoglyph@npm:^1.0.6": + version: 1.0.6 + resolution: "unhomoglyph@npm:1.0.6" + checksum: 2401fa3f8129fb1093d9ae59680b1dc8e395016e48401f9e706861a6edd28cafc60b0ac7e001cc127ee544942ed16236881b969afd856c28ebfe5d77afa1f0c2 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -28032,6 +28546,49 @@ __metadata: languageName: node linkType: hard +"winston-daily-rotate-file@npm:^4.5.1": + version: 4.6.1 + resolution: "winston-daily-rotate-file@npm:4.6.1" + dependencies: + file-stream-rotator: ^0.6.1 + object-hash: ^2.0.1 + triple-beam: ^1.3.0 + winston-transport: ^4.4.0 + peerDependencies: + winston: ^3 + checksum: b58f41d98794524f2046b08fd5e7dd4acce01ee7ca6e3a5ef03d329edd1c7a392e979d3b61bf03466977f9366fb92a08028278b45fcb044169cc78a395f5d616 + languageName: node + linkType: hard + +"winston-transport@npm:^4.4.0, winston-transport@npm:^4.5.0": + version: 4.5.0 + resolution: "winston-transport@npm:4.5.0" + dependencies: + logform: ^2.3.2 + readable-stream: ^3.6.0 + triple-beam: ^1.3.0 + checksum: a56e5678a80b88a73e77ed998fc6e19d0db19c989a356b137ec236782f2bf58ae4511b11c29163f99391fa4dc12102c7bc5738dcb6543f28877fa2819adc3ee9 + languageName: node + linkType: hard + +"winston@npm:^3.3.3": + version: 3.7.2 + resolution: "winston@npm:3.7.2" + dependencies: + "@dabh/diagnostics": ^2.0.2 + async: ^3.2.3 + is-stream: ^2.0.0 + logform: ^2.4.0 + one-time: ^1.0.0 + readable-stream: ^3.4.0 + safe-stable-stringify: ^2.3.1 + stack-trace: 0.0.x + triple-beam: ^1.3.0 + winston-transport: ^4.5.0 + checksum: f1f1a860d2fa228b50880b20aaa6cc121085907791fe0d814ff9c062640f6b65da321726322094e7667eb63088b3bb67e7b4e219d998f29efcc6f583185a1cd3 + languageName: node + linkType: hard + "word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": version: 1.2.3 resolution: "word-wrap@npm:1.2.3"