Skip to content

Commit a8e33e7

Browse files
authored
fix(federation): empty thread id leads to wrong reply (#37023)
1 parent 7678b97 commit a8e33e7

File tree

2 files changed

+26
-25
lines changed

2 files changed

+26
-25
lines changed

ee/packages/federation-matrix/src/FederationMatrix.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'reflect-metadata';
22

3-
import type { PresenceState } from '@hs/core';
3+
import type { FileMessageType, PresenceState } from '@hs/core';
44
import { ConfigService, createFederationContainer, getAllServices } from '@hs/federation-sdk';
55
import type { HomeserverEventSignatures, HomeserverServices, FederationContainerOptions } from '@hs/federation-sdk';
66
import type { EventID } from '@hs/room';
@@ -30,9 +30,7 @@ import { saveExternalUserIdForLocalUser } from './helpers/identifiers';
3030
import { toExternalMessageFormat, toExternalQuoteMessageFormat } from './helpers/message.parsers';
3131
import { MatrixMediaService } from './services/MatrixMediaService';
3232

33-
type MatrixFileTypes = 'm.image' | 'm.video' | 'm.audio' | 'm.file';
34-
35-
export const fileTypes: Record<string, MatrixFileTypes> = {
33+
export const fileTypes: Record<string, FileMessageType> = {
3634
image: 'm.image',
3735
video: 'm.video',
3836
audio: 'm.audio',
@@ -396,7 +394,7 @@ export class FederationMatrix extends ServiceClass implements IFederationMatrixS
396394
}
397395
}
398396

399-
private getMatrixMessageType(mimeType?: string): MatrixFileTypes {
397+
private getMatrixMessageType(mimeType?: string): FileMessageType {
400398
const mainType = mimeType?.split('/')[0];
401399
if (!mainType) {
402400
return fileTypes.file;

ee/packages/federation-matrix/src/events/message.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import type { FileMessageType, MessageType } from '@hs/core';
12
import type { HomeserverEventSignatures } from '@hs/federation-sdk';
3+
import type { EventID } from '@hs/room';
24
import { FederationMatrix, Message, MeteorService } from '@rocket.chat/core-services';
35
import { UserStatus } from '@rocket.chat/core-typings';
46
import type { IUser, IRoom } from '@rocket.chat/core-typings';
@@ -108,7 +110,7 @@ async function getRoomAndEnsureSubscription(matrixRoomId: string, user: IUser):
108110
return room;
109111
}
110112

111-
async function getThreadMessageId(threadRootEventId: string): Promise<{ tmid: string; tshow: boolean } | undefined> {
113+
async function getThreadMessageId(threadRootEventId: EventID): Promise<{ tmid: string; tshow: boolean } | undefined> {
112114
const threadRootMessage = await Messages.findOneByFederationId(threadRootEventId);
113115
if (!threadRootMessage) {
114116
logger.warn('Thread root message not found for event:', threadRootEventId);
@@ -122,7 +124,7 @@ async function getThreadMessageId(threadRootEventId: string): Promise<{ tmid: st
122124
async function handleMediaMessage(
123125
// TODO improve typing
124126
content: any,
125-
msgtype: string,
127+
msgtype: MessageType,
126128
messageBody: string,
127129
user: IUser,
128130
room: IRoom,
@@ -218,8 +220,7 @@ async function handleMediaMessage(
218220
export function message(emitter: Emitter<HomeserverEventSignatures>, serverName: string) {
219221
emitter.on('homeserver.matrix.message', async (data) => {
220222
try {
221-
// TODO remove type casting
222-
const content = data.content as any;
223+
const { content } = data;
223224
const msgtype = content?.msgtype;
224225
const messageBody = content?.body?.toString();
225226

@@ -238,32 +239,34 @@ export function message(emitter: Emitter<HomeserverEventSignatures>, serverName:
238239
return;
239240
}
240241

241-
const replyToRelation = content?.['m.relates_to'];
242-
const threadRelation = content?.['m.relates_to'];
243-
const isThreadMessage = threadRelation?.rel_type === 'm.thread';
244-
const isQuoteMessage = replyToRelation?.['m.in_reply_to']?.event_id && !replyToRelation?.is_falling_back;
245-
const threadRootEventId = isThreadMessage ? threadRelation.event_id : undefined;
246-
const thread = await getThreadMessageId(threadRootEventId);
242+
const relation = content['m.relates_to'];
247243

248-
const isMediaMessage = Object.values(fileTypes).includes(msgtype);
244+
const isThreadMessage = relation && relation.rel_type === 'm.thread';
245+
const threadRootEventId = isThreadMessage && relation.event_id;
249246

250-
const isEditedMessage = data.content['m.relates_to']?.rel_type === 'm.replace';
251-
if (isEditedMessage && data.content['m.relates_to']?.event_id && data.content['m.new_content']) {
247+
const quoteMessageEventId = relation && 'm.in_reply_to' in relation && relation['m.in_reply_to']?.event_id;
248+
249+
const thread = threadRootEventId ? await getThreadMessageId(threadRootEventId) : undefined;
250+
251+
const isMediaMessage = Object.values(fileTypes).includes(msgtype as FileMessageType);
252+
253+
const isEditedMessage = relation?.rel_type === 'm.replace';
254+
if (isEditedMessage && relation?.event_id && data.content['m.new_content']) {
252255
logger.debug('Received edited message from Matrix, updating existing message');
253-
const originalMessage = await Messages.findOneByFederationId(data.content['m.relates_to'].event_id);
256+
const originalMessage = await Messages.findOneByFederationId(relation.event_id);
254257
if (!originalMessage) {
255-
logger.error('Original message not found for edit:', data.content['m.relates_to'].event_id);
258+
logger.error('Original message not found for edit:', relation.event_id);
256259
return;
257260
}
258-
if (originalMessage.federation?.eventId !== data.content['m.relates_to'].event_id) {
261+
if (originalMessage.federation?.eventId !== relation.event_id) {
259262
return;
260263
}
261264
if (originalMessage.msg === data.content['m.new_content']?.body) {
262265
logger.debug('No changes in message content, skipping update');
263266
return;
264267
}
265268

266-
if (isQuoteMessage && room.name) {
269+
if (quoteMessageEventId && room.name) {
267270
const messageToReplyToUrl = await MeteorService.getMessageURLToReplyTo(
268271
room.t as string,
269272
room._id,
@@ -305,10 +308,10 @@ export function message(emitter: Emitter<HomeserverEventSignatures>, serverName:
305308
return;
306309
}
307310

308-
if (isQuoteMessage && room.name) {
309-
const originalMessage = await Messages.findOneByFederationId(replyToRelation?.['m.in_reply_to']?.event_id);
311+
if (quoteMessageEventId && room.name) {
312+
const originalMessage = await Messages.findOneByFederationId(quoteMessageEventId);
310313
if (!originalMessage) {
311-
logger.error('Original message not found for quote:', replyToRelation?.['m.in_reply_to']?.event_id);
314+
logger.error('Original message not found for quote:', quoteMessageEventId);
312315
return;
313316
}
314317
const messageToReplyToUrl = await MeteorService.getMessageURLToReplyTo(room.t as string, room._id, room.name, originalMessage._id);

0 commit comments

Comments
 (0)