feat(federation): handle group DM messages via federatedId lookup in relay processor

In processCreateEvent, branch on event.federatedId: group DM messages now
look up the pre-bootstrapped local channel by federatedId instead of
computing a pair hash from two participants. In queueDmRelay, fetch the
channel's federatedId and ownerId and include federatedId in the outgoing
relay payload for group DMs so receiving instances can route correctly.
This commit is contained in:
Jannis Braun
2026-03-26 20:26:23 +01:00
parent 242004e8ee
commit f7ba391678
2 changed files with 42 additions and 10 deletions
+33 -10
View File
@@ -1002,16 +1002,39 @@ function processCreateEvent(
} }
const authorUser = authorEntry.localUser; const authorUser = authorEntry.localUser;
// Compute federated ID from participants' home user IDs and find/create channel // Resolve local DM channel: group DMs carry a federatedId and the channel
const federatedId = computeFederatedId( // must already exist (bootstrapped by a prior member_add event); 1-on-1 DMs
resolvedParticipants[0]!.homeUserId, // are computed from the pair of home user IDs and created on demand.
resolvedParticipants[1]!.homeUserId, let localDmChannelId: string;
);
const localDmChannelId = findOrCreateDmChannel( if (event.federatedId) {
federatedId, // Group DM: look up by federated_id (channel must already exist from member_add bootstrap)
[resolvedParticipants[0]!.localUser.id, resolvedParticipants[1]!.localUser.id], const channel = db
db, .select()
); .from(schema.dmChannels)
.where(and(
eq(schema.dmChannels.federatedId, event.federatedId),
isNull(schema.dmChannels.deletedAt),
))
.get();
if (!channel) {
rejected.push({ messageId: event.messageId, reason: 'channel_not_found' });
return;
}
localDmChannelId = channel.id;
} else {
// 1-on-1 DM: compute federated_id from pair and find/create channel
const federatedId = computeFederatedId(
resolvedParticipants[0]!.homeUserId,
resolvedParticipants[1]!.homeUserId,
);
localDmChannelId = findOrCreateDmChannel(
federatedId,
[resolvedParticipants[0]!.localUser.id, resolvedParticipants[1]!.localUser.id],
db,
);
}
// Insert the message // Insert the message
const localMessageId = generateSnowflake(); const localMessageId = generateSnowflake();
@@ -318,8 +318,17 @@ export function queueDmRelay(
const targetOrigins = getGroupDmTargetOrigins(dmChannelId); const targetOrigins = getGroupDmTargetOrigins(dmChannelId);
// Fetch channel to check if it's a group DM with a federatedId
const db = getDb();
const channel = db
.select({ federatedId: schema.dmChannels.federatedId, ownerId: schema.dmChannels.ownerId })
.from(schema.dmChannels)
.where(eq(schema.dmChannels.id, dmChannelId))
.get();
appendMutationLog(message.id, dmChannelId, eventType); appendMutationLog(message.id, dmChannelId, eventType);
queueOutboxEvent(message.id, dmChannelId, eventType, JSON.stringify({ queueOutboxEvent(message.id, dmChannelId, eventType, JSON.stringify({
...(channel?.federatedId && channel.ownerId ? { federatedId: channel.federatedId } : {}),
message: { message: {
...buildRelayPayload(message, message.user), ...buildRelayPayload(message, message.user),
attachments: attachments.length > 0 ? attachments : undefined, attachments: attachments.length > 0 ? attachments : undefined,