diff --git a/src/app.ts b/src/app.ts index 8f91a84..c71c06c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,12 +2,11 @@ import dotenv from 'dotenv' dotenv.config() import lineByLine from 'n-readlines' import 'reflect-metadata' -import { IdMapping } from './entity/IdMapping' -import { RcUser, createUser, userIsExcluded } from './handlers/users' +import { handle as handleRoom } from './handlers/rooms' +import { handle as handleUser } from './handlers/users' import log from './helpers/logger' -import { getRoomId, getUserId, initStorage, save } from './helpers/storage' +import { initStorage } from './helpers/storage' import { whoami } from './helpers/synapse' -import { RcRoom, createRoom } from './handlers/rooms' log.info('rocketchat2matrix starts.') @@ -45,47 +44,11 @@ async function loadRcExport(entity: Entities) { const item = JSON.parse(line.toString()) switch (entity) { case Entities.Users: - const rcUser: RcUser = item - log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`) - - if (userIsExcluded(rcUser)) { - break - } - - const matrixUserId = await getUserId(rcUser._id) - if (matrixUserId) { - log.debug(`Mapping exists: ${rcUser._id} -> ${matrixUserId}`) - } else { - const matrixUser = await createUser(rcUser) - const mapping = new IdMapping() - mapping.rcId = rcUser._id - mapping.matrixId = matrixUser.user_id - mapping.type = entities[entity].mappingType - mapping.accessToken = matrixUser.access_token - - await save(mapping) - log.debug('Mapping added:', mapping) - } - + await handleUser(item) break case Entities.Rooms: - const rcRoom: RcRoom = item - log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`) - - const matrixRoomId = await getRoomId(rcRoom._id) - if (matrixRoomId) { - log.debug(`Mapping exists: ${rcRoom._id} -> ${matrixRoomId}`) - } else { - const matrixRoom = await createRoom(rcRoom) - const roomMapping = new IdMapping() - roomMapping.rcId = rcRoom._id - roomMapping.matrixId = matrixRoom.room_id - roomMapping.type = entities[entity].mappingType - - await save(roomMapping) - log.debug('Mapping added:', roomMapping) - } + await handleRoom(item) break case Entities.Messages: diff --git a/src/handlers/rooms.test.ts b/src/handlers/rooms.test.ts index bf3e80a..6a7008f 100644 --- a/src/handlers/rooms.test.ts +++ b/src/handlers/rooms.test.ts @@ -6,13 +6,15 @@ import { SessionOptions } from '../helpers/synapse' import { MatrixRoomPresets, MatrixRoomVisibility, + RcRoom, RcRoomTypes, acceptInvitation, + createMapping, getCreator, getFilteredMembers, inviteMember, mapRoom, - parseMemberships, + createDirectChatMemberships, registerRoom, } from './rooms' @@ -110,10 +112,13 @@ test('getting creator', () => { expect(getCreator(rcDirectChat)).toBe('aliceid') expect(getCreator(rcPublicRoom)).toBe(roomCreator._id) expect(getCreator(rcPrivateRoom)).toBe(roomCreator._id) + expect(getCreator({} as RcRoom)).toBe('') }) test('creating memberships for direct chats', async () => { - await expect(parseMemberships(rcDirectChat)).resolves.toBe(undefined) + await expect(createDirectChatMemberships(rcDirectChat)).resolves.toBe( + undefined + ) expect(mockedStorage.createMembership).toHaveBeenCalledWith( rcDirectChat._id, rcDirectChat.uids[0] @@ -127,7 +132,11 @@ test('creating memberships for direct chats', async () => { mockedStorage.createMembership.mockClear() await expect( - parseMemberships({ ...rcDirectChat, _id: 'hoihoi', uids: ['hoi', 'hoi'] }) + createDirectChatMemberships({ + ...rcDirectChat, + _id: 'hoihoi', + uids: ['hoi', 'hoi'], + }) ).resolves.toBe(undefined) expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi') @@ -210,3 +219,15 @@ test('filtering members', async () => { expect(mockedStorage.getMapping).toBeCalledWith('otherExistingUser', 0) expect(mockedStorage.getMapping).toBeCalledWith('excludedUser', 0) }) + +test('creating mapping', async () => { + await expect( + createMapping(rcPublicRoom._id, { ...mapRoom(rcPublicRoom), room_id }) + ).resolves.toBe(undefined) + expect(mockedStorage.save).toHaveBeenCalledWith({ + rcId: rcPublicRoom._id, + matrixId: room_id, + type: 1, + accessToken: undefined, + } as IdMapping) +}) diff --git a/src/handlers/rooms.ts b/src/handlers/rooms.ts index 0ed2897..4c1f980 100644 --- a/src/handlers/rooms.ts +++ b/src/handlers/rooms.ts @@ -4,6 +4,8 @@ import { createMembership, getMapping, getMemberships, + getRoomId, + save, } from '../helpers/storage' import { SessionOptions, @@ -101,7 +103,9 @@ export function getCreator(rcRoom: RcRoom): string { } } -export async function parseMemberships(rcRoom: RcRoom): Promise { +export async function createDirectChatMemberships( + rcRoom: RcRoom +): Promise { if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) { await Promise.all( [...new Set(rcRoom.uids)] // Deduplicate users @@ -182,19 +186,45 @@ export async function getFilteredMembers( return memberMappings } +export async function createMapping( + rcId: string, + matrixRoom: MatrixRoom +): Promise { + const roomMapping = new IdMapping() + roomMapping.rcId = rcId + roomMapping.matrixId = matrixRoom.room_id + roomMapping.type = 1 + + await save(roomMapping) + log.debug('Mapping added:', roomMapping) +} + export async function createRoom(rcRoom: RcRoom): Promise { const room: MatrixRoom = mapRoom(rcRoom) const creatorId = getCreator(rcRoom) - await parseMemberships(rcRoom) + await createDirectChatMemberships(rcRoom) const creatorSessionOptions = await getCreatorSessionOptions(creatorId) log.debug('Creating room:', room) room.room_id = await registerRoom(room, creatorSessionOptions) - const rcMemberIds = await getMemberships(rcRoom._id) + await handleMemberships(rcRoom._id, room, creatorId, creatorSessionOptions) + + return room +} + +async function handleMemberships( + rcRoomId: string, + room: MatrixRoom, + creatorId: string, + creatorSessionOptions: object | SessionOptions +) { + const rcMemberIds = await getMemberships(rcRoomId) const memberMappings = await getFilteredMembers(rcMemberIds, creatorId) log.info( - `Inviting members to room ${rcRoom._id}:`, + `Inviting members to room ${ + room.room_alias_name || room.name || room.room_id + }:`, memberMappings.map((mapping) => mapping.matrixId) ) log.debug( @@ -214,6 +244,16 @@ export async function createRoom(rcRoom: RcRoom): Promise { await acceptInvitation(memberMapping, room.room_id || '') }) ) - - return room +} + +export async function handle(rcRoom: RcRoom): Promise { + log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`) + + const matrixRoomId = await getRoomId(rcRoom._id) + if (matrixRoomId) { + log.debug(`Mapping exists: ${rcRoom._id} -> ${matrixRoomId}`) + } else { + const matrixRoom = await createRoom(rcRoom) + await createMapping(rcRoom._id, matrixRoom) + } } diff --git a/src/handlers/users.test.ts b/src/handlers/users.test.ts index 811ca78..c53995b 100644 --- a/src/handlers/users.test.ts +++ b/src/handlers/users.test.ts @@ -6,11 +6,13 @@ import * as storage from '../helpers/storage' import { MatrixUser, RcUser, + createMapping, createUser, generateHmac, mapUser, userIsExcluded, } from '../handlers/users' +import { IdMapping } from '../entity/IdMapping' jest.mock('axios') const mockedAxios = axios as jest.Mocked @@ -96,3 +98,13 @@ test('users are excluded', () => { }) ).toBeTruthy() }) + +test('creating mapping', async () => { + await expect(createMapping(rcUser._id, matrixUser)).resolves.toBe(undefined) + expect(mockedStorage.save).toHaveBeenCalledWith({ + rcId: rcUser._id, + matrixId: matrixUser.user_id, + type: 0, + accessToken: matrixUser.access_token, + } as IdMapping) +}) diff --git a/src/handlers/users.ts b/src/handlers/users.ts index 74b5566..ac4a9d2 100644 --- a/src/handlers/users.ts +++ b/src/handlers/users.ts @@ -1,7 +1,8 @@ import { createHmac } from 'node:crypto' import log from '../helpers/logger' import { axios } from '../helpers/synapse' -import { createMembership } from '../helpers/storage' +import { createMembership, getUserId, save } from '../helpers/storage' +import { IdMapping } from '../entity/IdMapping' export type RcUser = { _id: string @@ -91,6 +92,20 @@ export function userIsExcluded(rcUser: RcUser): boolean { return false } +export async function createMapping( + rcId: string, + matrixUser: MatrixUser +): Promise { + const mapping = new IdMapping() + mapping.rcId = rcId + mapping.matrixId = matrixUser.user_id + mapping.type = 0 + mapping.accessToken = matrixUser.access_token + + await save(mapping) + log.debug('Mapping added:', mapping) +} + export async function createUser(rcUser: RcUser): Promise { const user = mapUser(rcUser) const nonce = await getUserRegistrationNonce() @@ -104,3 +119,19 @@ export async function createUser(rcUser: RcUser): Promise { return user } + +export async function handle(rcUser: RcUser): Promise { + log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`) + + if (userIsExcluded(rcUser)) { + return undefined + } + + const matrixId = await getUserId(rcUser._id) + if (matrixId) { + log.debug(`Mapping exists: ${rcUser._id} -> ${matrixId}`) + } else { + const matrixUser = await createUser(rcUser) + await createMapping(rcUser._id, matrixUser) + } +}