feat: fix caller exclusion (Bug 1) and add Path B receiver processing

This commit is contained in:
Jannis Braun
2026-04-08 03:09:16 +02:00
parent 2538bb5e4c
commit 2c7eefc7b1
2 changed files with 125 additions and 40 deletions
+107 -23
View File
@@ -3401,13 +3401,6 @@ function processDmCallStartEvent(
.where(eq(schema.dmChannels.federatedId, event.federatedId)) .where(eq(schema.dmChannels.federatedId, event.federatedId))
.get(); .get();
if (!channel) {
rejected.push({ messageId: event.messageId, reason: 'channel_not_found' });
return;
}
const localDmChannelId = channel.id;
// Resolve caller to local stub // Resolve caller to local stub
const callerStub = resolveOrCreateReplicatedUser( const callerStub = resolveOrCreateReplicatedUser(
event.call.caller.homeUserId, event.call.caller.homeUserId,
@@ -3416,30 +3409,20 @@ function processDmCallStartEvent(
{ username: event.call.caller.displayName }, { username: event.call.caller.displayName },
); );
if (!callerStub) { if (!callerStub) {
// Caller's identity has been deleted — can't initiate a call as a tombstoned user
rejected.push({ messageId: event.messageId, reason: 'participant_not_found' }); rejected.push({ messageId: event.messageId, reason: 'participant_not_found' });
return; return;
} }
// Create federated call entry in registry const ringedUserIds: string[] = [];
const entry: FederatedCallEntry = {
dmChannelId: localDmChannelId,
federatedId: event.federatedId,
callerId: callerStub.id,
callerHomeUserId: event.call.caller.homeUserId,
federatedCallHost: sourceInstance.startsWith('http') ? sourceInstance : `https://${sourceInstance}`,
livekitUrl: event.call.livekitUrl,
tokens: new Map(Object.entries(event.call.tokens)),
state: 'ringing',
startedAt: Date.now(),
};
connectionManager.createFederatedCall(entry); if (channel) {
// ── Path A: DM exists locally ──
const localDmChannelId = channel.id;
// Send dm_call_incoming to each local member
const localMembers = db.select({ const localMembers = db.select({
userId: schema.dmMembers.userId, userId: schema.dmMembers.userId,
homeUserId: schema.users.homeUserId, homeUserId: schema.users.homeUserId,
homeInstance: schema.users.homeInstance,
}) })
.from(schema.dmMembers) .from(schema.dmMembers)
.innerJoin(schema.users, eq(schema.dmMembers.userId, schema.users.id)) .innerJoin(schema.users, eq(schema.dmMembers.userId, schema.users.id))
@@ -3448,16 +3431,117 @@ function processDmCallStartEvent(
for (const member of localMembers) { for (const member of localMembers) {
const homeUserId = member.homeUserId || member.userId; const homeUserId = member.homeUserId || member.userId;
const token = event.call!.tokens![homeUserId]; // Bug 1 fix: don't ring the caller on this instance
if (homeUserId === event.call.caller.homeUserId) continue;
const token = event.call!.tokens![homeUserId];
connectionManager.sendToUser(member.userId, { connectionManager.sendToUser(member.userId, {
type: 'dm_call_incoming', type: 'dm_call_incoming',
dmChannelId: localDmChannelId, dmChannelId: localDmChannelId,
federatedCallId: event.federatedId,
callerId: callerStub.id, callerId: callerStub.id,
callerName: callerStub.displayName ?? callerStub.username, callerName: callerStub.displayName ?? callerStub.username,
livekitUrl: event.call!.livekitUrl, livekitUrl: event.call!.livekitUrl,
livekitToken: token, livekitToken: token,
callOrigin: event.call!.caller.homeInstance,
}); });
ringedUserIds.push(member.userId);
}
const entry: FederatedCallEntry = {
dmChannelId: localDmChannelId,
federatedId: event.federatedId,
callerId: callerStub.id,
callerHomeUserId: event.call.caller.homeUserId,
federatedCallHost: sourceInstance.startsWith('http') ? sourceInstance : `https://${sourceInstance}`,
livekitUrl: event.call.livekitUrl,
tokens: new Map(Object.entries(event.call.tokens)),
ringedUserIds,
state: 'ringing',
startedAt: Date.now(),
};
connectionManager.createFederatedCall(entry);
} else {
// ── Path B: DM doesn't exist locally — match by participant identity ──
if (!event.call.participants || !Array.isArray(event.call.participants)) {
// Old-format relay without participants — backwards-compatible rejection
rejected.push({ messageId: event.messageId, reason: 'channel_not_found' });
return;
}
const ourDomain = extractDomain(getOurOrigin());
for (const p of event.call.participants) {
const participantDomain = extractDomain(p.homeInstance);
// Skip the caller — strict match on BOTH homeUserId AND homeInstance
if (p.homeUserId === event.call.caller.homeUserId
&& participantDomain === extractDomain(event.call.caller.homeInstance)) {
continue;
}
// Strict identity resolution: homeUserId is only unique within its homeInstance
const localUser = db.select({ id: schema.users.id, homeUserId: schema.users.homeUserId })
.from(schema.users)
.where(
or(
// Replicated stub or federated account from the participant's home instance
and(
eq(schema.users.homeUserId, p.homeUserId),
sql`replace(replace(coalesce(${schema.users.homeInstance}, ''), 'https://', ''), 'http://', '') = ${participantDomain}`,
),
// Native user whose ID matches and participant's home matches our domain
and(
eq(schema.users.id, p.homeUserId),
isNull(schema.users.homeInstance),
sql`${participantDomain} = ${ourDomain}`,
),
),
)
.get();
if (!localUser) continue;
// Check if user has an active WS connection
const connections = connectionManager.getUserConnections(localUser.id);
if (connections.size === 0) continue;
const homeUserId = localUser.homeUserId || localUser.id;
const token = event.call!.tokens![homeUserId];
if (!token) continue;
connectionManager.sendToUser(localUser.id, {
type: 'dm_call_incoming',
dmChannelId: null,
federatedCallId: event.federatedId,
callerId: callerStub.id,
callerName: callerStub.displayName ?? callerStub.username,
livekitUrl: event.call!.livekitUrl,
livekitToken: token,
callOrigin: event.call!.caller.homeInstance,
});
ringedUserIds.push(localUser.id);
}
if (ringedUserIds.length === 0) {
// No connected users found — silently accept (not an error)
accepted.push(event.messageId);
return;
}
const entry: FederatedCallEntry = {
dmChannelId: null,
federatedId: event.federatedId,
callerId: callerStub.id,
callerHomeUserId: event.call.caller.homeUserId,
federatedCallHost: sourceInstance.startsWith('http') ? sourceInstance : `https://${sourceInstance}`,
livekitUrl: event.call.livekitUrl,
tokens: new Map(Object.entries(event.call.tokens)),
ringedUserIds,
state: 'ringing',
startedAt: Date.now(),
};
connectionManager.createFederatedCall(entry);
} }
accepted.push(event.messageId); accepted.push(event.messageId);
+2 -1
View File
@@ -409,7 +409,7 @@ export type ServerEvent =
| { type: 'channel_ack'; channelId: string; messageId: string } | { type: 'channel_ack'; channelId: string; messageId: string }
| { type: 'friend_request_received'; request: FriendRequest } | { type: 'friend_request_received'; request: FriendRequest }
| { type: 'friend_request_accepted'; friend: Friend; requestId: string } | { type: 'friend_request_accepted'; friend: Friend; requestId: string }
| { type: 'dm_call_incoming'; dmChannelId: string; callerId: string; callerName: string; livekitUrl?: string; livekitToken?: string } | { type: 'dm_call_incoming'; dmChannelId: string | null; federatedCallId?: string; callerId: string; callerName: string; livekitUrl?: string; livekitToken?: string; callOrigin?: string }
| { type: 'dm_call_accepted'; dmChannelId: string } | { type: 'dm_call_accepted'; dmChannelId: string }
| { type: 'dm_call_rejected'; dmChannelId: string } | { type: 'dm_call_rejected'; dmChannelId: string }
| { type: 'dm_call_ended'; dmChannelId: string } | { type: 'dm_call_ended'; dmChannelId: string }
@@ -864,6 +864,7 @@ export interface FederationCallPayload {
acceptor?: { homeUserId: string; homeInstance: string }; acceptor?: { homeUserId: string; homeInstance: string };
rejector?: { homeUserId: string; homeInstance: string }; rejector?: { homeUserId: string; homeInstance: string };
endedBy?: { homeUserId: string; homeInstance: string }; endedBy?: { homeUserId: string; homeInstance: string };
participants?: FederationRelayParticipant[]; // All DM members for Path B identity matching
} }
export interface FederationMembershipPayload { export interface FederationMembershipPayload {