Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"@rocket.chat/emitter": "~0.31.25",
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/federation-matrix": "workspace:^",
"@rocket.chat/federation-sdk": "0.3.5",
"@rocket.chat/federation-sdk": "0.3.7",
"@rocket.chat/fuselage": "^0.70.0",
"@rocket.chat/fuselage-forms": "~0.1.1",
"@rocket.chat/fuselage-hooks": "~0.38.1",
Expand Down
2 changes: 1 addition & 1 deletion ee/packages/federation-matrix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@rocket.chat/core-services": "workspace:^",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/emitter": "^0.31.25",
"@rocket.chat/federation-sdk": "0.3.5",
"@rocket.chat/federation-sdk": "0.3.7",
"@rocket.chat/http-router": "workspace:^",
"@rocket.chat/license": "workspace:^",
"@rocket.chat/models": "workspace:^",
Expand Down
48 changes: 9 additions & 39 deletions ee/packages/federation-matrix/src/FederationMatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,53 +260,23 @@ export class FederationMatrix extends ServiceClass implements IFederationMatrixS
try {
this.logger.debug('Creating direct message room in Matrix', { roomId: room._id, memberCount: members.length });

const creator = await Users.findOneById(creatorId);
const creator = await Users.findOneById<Pick<IUser, 'username'>>(creatorId, { projection: { username: 1 } });
if (!creator) {
throw new Error('Creator not found in members list');
}

const actualMatrixUserId = `@${creator.username}:${this.serverName}`;

let matrixRoomResult: { room_id: string; event_id?: string };
if (members.length === 2) {
const otherMember = members.find((member) => member._id !== creatorId);
if (!otherMember) {
throw new Error('Other member not found for 1-on-1 DM');
}
if (!isUserNativeFederated(otherMember)) {
throw new Error('Other member is not federated');
}
const roomId = await federationSDK.createDirectMessageRoom(
userIdSchema.parse(actualMatrixUserId),
userIdSchema.parse(otherMember.username),
);
matrixRoomResult = { room_id: roomId };
} else {
// For group DMs (more than 2 members), create a private room
const roomName = room.name || room.fname || `Group chat with ${members.length} members`;
matrixRoomResult = await federationSDK.createRoom(userIdSchema.parse(actualMatrixUserId), roomName, 'invite');

for await (const member of members) {
if (member._id === creatorId) {
continue;
}

try {
await federationSDK.inviteUserToRoom(
isUserNativeFederated(member) ? userIdSchema.parse(member.username) : `@${member.username}:${this.serverName}`,
roomIdSchema.parse(matrixRoomResult.room_id),
userIdSchema.parse(actualMatrixUserId),
);
} catch (error) {
this.logger.error(error, 'Error creating or updating bridged user for DM');
}
}
}
const roomId = await federationSDK.createDirectMessage({
creatorUserId: userIdSchema.parse(`@${creator.username}:${this.serverName}`),
members: members
.filter((member) => member._id !== creatorId)
.map((member) => userIdSchema.parse(isUserNativeFederated(member) ? member.username : `@${member.username}:${this.serverName}`)),
});

await Rooms.setAsFederated(room._id, {
mrid: matrixRoomResult.room_id,
mrid: roomId,
origin: this.serverName,
});

this.logger.debug({ roomId: room._id, msg: 'Direct message room creation completed successfully' });
} catch (error) {
this.logger.error(error, 'Failed to create direct message room');
Expand Down
106 changes: 67 additions & 39 deletions ee/packages/federation-matrix/tests/end-to-end/dms.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,10 @@ const waitForRoomEvent = async (
});
});

it('should show the room name as the inviter name on Synapse before join', async () => {
expect(hs1Room1.name).toBe(rcUser1.username);
});

it('should display the fname containing the two invited users for the inviter', async () => {
// Check the subscription for the inviter
const sub = await getSubscriptionByRoomId(rcRoom._id, rcUserConfig1.credentials, rcUserConfig1.request);
Expand All @@ -954,16 +958,23 @@ const waitForRoomEvent = async (
expect(sub).toHaveProperty('fname', rcUser1.username);
});

it.failing('should keep the fname to the RC invited user when the Synapse invited user accepts the DM', async () => {
const waitForRoomEventPromise1 = waitForRoomEvent(hs1Room1, RoomStateEvent.Members, ({ event }) => {
expect(event).toHaveProperty('content.membership', 'join');
expect(event).toHaveProperty('state_key', federationConfig.hs1.adminMatrixUserId);
});

it('should accept the invitation on Synapse', async () => {
await hs1AdminApp.matrixClient.joinRoom(rcRoom.federation.mrid);

await waitForRoomEventPromise1;
await retry(
'wait for the join to be processed',
async () => {
expect(hs1Room1.getMyMembership()).toBe('join');
},
{ delayMs: 100 },
);
});

it('should show the room name with all members on Synapse after join', async () => {
expect(hs1Room1.name).toBe(`${rcUser1.username} and ${rcUser2.username}`);
});

it.failing('should keep the fname to the RC invited user when the Synapse invited user accepts the DM', async () => {
await retry(
'this is an async operation, so we need to wait for the event to be processed',
async () => {
Expand Down Expand Up @@ -991,11 +1002,6 @@ const waitForRoomEvent = async (
{ delayMs: 100 },
);
});

it('should validate the room name for group DMs on Synapse', async () => {
// TODO this should probably change
expect(hs1Room1.name).toBe('Group chat with 3 members');
});
});

describe('Permission validations', () => {
Expand Down Expand Up @@ -1103,12 +1109,11 @@ const waitForRoomEvent = async (
});

// TODO maybe we should allow it
// is this working now?
it.failing('should fail if a user from rc try to add another user to the group DM', async () => {
it('should fail if a user from rc try to add another user to the group DM', async () => {
const response = await addUserToRoom({
usernames: [rcUser3.username],
rid: rcRoom._id,
config: rcUserConfig1,
config: rcUserConfig2,
});

expect(response.body).toHaveProperty('success', true);
Expand All @@ -1120,23 +1125,32 @@ const waitForRoomEvent = async (
expect(messageData).toHaveProperty('error.error', 'error-not-allowed');
});

// TODO we're creating DMs with powerlevel 50 for invites, so this is not working
it.failing('should add another user by another user than the initial inviter', async () => {
it('should add another user by another user than the initial inviter', async () => {
await hs1AdminApp.matrixClient.joinRoom(rcRoom.federation.mrid);

await retry('waiting for join', async () => {
const members = await hs1Room1.getMembers();
expect(members.length).toBe(3);
});
await retry(
'waiting for join',
async () => {
expect(hs1Room1.getMyMembership()).toBe('join');

const members = await hs1Room1.getMembers();
expect(members.length).toBe(3);
},
{ delayMs: 100 },
);

await hs1AdminApp.inviteUserToRoom(hs1Room1.roomId, userDmId3);

await retry('waiting for user4 to receive invitation', async () => {
const members = await hs1Room1.getMembers();
const user4Member = members.find((m) => m.userId === userDmId3);
expect(user4Member).toBeDefined();
expect(user4Member?.membership).toBe('invite');
});
await retry(
'waiting for user4 to receive invitation',
async () => {
const members = await hs1Room1.getMembers();
const user4Member = members.find((m) => m.userId === userDmId3);
expect(user4Member).toBeDefined();
expect(user4Member?.membership).toBe('invite');
},
{ delayMs: 100 },
);
});
});

Expand Down Expand Up @@ -1662,7 +1676,7 @@ const waitForRoomEvent = async (
expect(messageData).toHaveProperty('error.error', 'error-cant-invite-for-direct-room');
});

it('should create a 1:1 a federated DM between', async () => {
it('should create a 1:1 federated DM', async () => {
// Create 1:1 DM from RC user to another RC user
const response = await rcUser1.config.request
.post(api('dm.create'))
Expand Down Expand Up @@ -1696,7 +1710,7 @@ const waitForRoomEvent = async (
expect(sub).toHaveProperty('fname', federationConfig.hs1.adminMatrixUserId);
});

it('should show the invite to the third user', async () => {
it('should send an invite to another Synapse user', async () => {
// invite from rocket.chat
const response = await addUserToRoom({
usernames: [federationConfig.hs1.additionalUser1.matrixUserId],
Expand Down Expand Up @@ -1744,19 +1758,33 @@ const waitForRoomEvent = async (
expect(roomInfo.room).toHaveProperty('usersCount', 3);
});

// TODO we're creating DMs with powerlevel 50 for invites, so this is not working
it.failing('should invite a fourth user from Rocket.Chat by a Synapse user', async () => {
it('should invite a fourth Rocket.Chat user by the invited Synapse user', async () => {
await hs1User1.inviteUserToRoom(hs1Room1.roomId, rcUser2.matrixId);

await retry('waiting for fourth user to receive invitation', async () => {
const subscriptionInvite = await getSubscriptionByRoomId(rcRoom._id, rcUser2.config.credentials, rcUser2.config.request);
await retry(
'waiting for user4 to receive invitation',
async () => {
const members = await hs1Room1.getMembers();

expect(subscriptionInvite).toHaveProperty('status', 'INVITED');
expect(subscriptionInvite).toHaveProperty(
'fname',
`${federationConfig.hs1.adminMatrixUserId}, ${federationConfig.hs1.additionalUser1.matrixUserId}, ${rcUser1.matrixId}`,
);
});
const user4Member = members.find((m) => m.userId === rcUser2.matrixId);

expect(user4Member).toBeDefined();
expect(user4Member?.membership).toBe('invite');
},
{ delayMs: 100 },
);

await retry(
'waiting for fourth user to receive invitation',
async () => {
const sub = await getSubscriptionByRoomId(rcRoom._id, rcUser2.config.credentials, rcUser2.config.request);

expect(sub).toHaveProperty('status', 'INVITED');
expect(sub).toHaveProperty('name', federationConfig.hs1.additionalUser1.matrixUserId);
expect(sub).toHaveProperty('fname', federationConfig.hs1.additionalUser1.matrixUserId);
},
{ delayMs: 100 },
);
});
});
});
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8467,7 +8467,7 @@ __metadata:
"@rocket.chat/apps-engine": "workspace:^"
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/federation-sdk": "npm:0.3.5"
"@rocket.chat/federation-sdk": "npm:0.3.7"
"@rocket.chat/http-router": "workspace:^"
"@rocket.chat/icons": "npm:~0.46.0"
"@rocket.chat/jest-presets": "workspace:~"
Expand Down Expand Up @@ -8674,7 +8674,7 @@ __metadata:
"@rocket.chat/ddp-client": "workspace:^"
"@rocket.chat/emitter": "npm:^0.31.25"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/federation-sdk": "npm:0.3.5"
"@rocket.chat/federation-sdk": "npm:0.3.7"
"@rocket.chat/http-router": "workspace:^"
"@rocket.chat/license": "workspace:^"
"@rocket.chat/models": "workspace:^"
Expand All @@ -8700,9 +8700,9 @@ __metadata:
languageName: unknown
linkType: soft

"@rocket.chat/federation-sdk@npm:0.3.5":
version: 0.3.5
resolution: "@rocket.chat/federation-sdk@npm:0.3.5"
"@rocket.chat/federation-sdk@npm:0.3.7":
version: 0.3.7
resolution: "@rocket.chat/federation-sdk@npm:0.3.7"
dependencies:
"@datastructures-js/priority-queue": "npm:^6.3.5"
"@noble/ed25519": "npm:^3.0.0"
Expand All @@ -8715,7 +8715,7 @@ __metadata:
zod: "npm:^3.24.1"
peerDependencies:
typescript: ~5.9.2
checksum: 10/47de2265555649b375620c7a90cf84134b14f3e38d171d84276dee9196b7b303a2d8c6f693223d63ba1c464ce12c128c5f6a795c0bb42c0e5339587b2429d034
checksum: 10/fd3bce12146ad906970ebe88ddc54f118362a52fe600d90d39a99a684aa7c637922b39791b671f1b9de8fb160076468de5034286673da443dfad6a68924fed91
languageName: node
linkType: hard

Expand Down Expand Up @@ -9342,7 +9342,7 @@ __metadata:
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/favicon": "workspace:^"
"@rocket.chat/federation-matrix": "workspace:^"
"@rocket.chat/federation-sdk": "npm:0.3.5"
"@rocket.chat/federation-sdk": "npm:0.3.7"
"@rocket.chat/fuselage": "npm:^0.70.0"
"@rocket.chat/fuselage-forms": "npm:~0.1.1"
"@rocket.chat/fuselage-hooks": "npm:~0.38.1"
Expand Down Expand Up @@ -10797,7 +10797,7 @@ __metadata:
peerDependencies:
"@rocket.chat/layout": "*"
"@rocket.chat/tools": 0.2.4-rc.0
"@rocket.chat/ui-contexts": 26.0.0-rc.0
"@rocket.chat/ui-contexts": 26.0.0-rc.1
"@tanstack/react-query": "*"
react: "*"
react-hook-form: "*"
Expand Down
Loading