feat: fix caller exclusion (Bug 1) and add Path B receiver processing
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user