feat(server): Path A connection gate + undeliverable on zero ringee (#18)

processDmCallStartEvent Path A now skips offline local members (matching
Path B's pre-existing per-member check) and pushes undeliverable when no
member could be rung, instead of creating a stranded FederatedCallEntry.
TDD — two new tests cover zero-online and mixed-online cases.
This commit is contained in:
Jannis Braun
2026-04-24 21:01:19 +02:00
parent 792634c2cf
commit 7533d8ca14
2 changed files with 153 additions and 0 deletions
+13
View File
@@ -4312,6 +4312,11 @@ function processDmCallStartEvent(
// Bug 1 fix: don't ring the caller on this instance
if (homeUserId === event.call.caller.homeUserId) continue;
// #18: skip offline members. Entry-vs-no-entry decision uses the same
// connection-count signal Path B has always used — keeps the two paths
// symmetric in what counts as "ringed."
if (connectionManager.getUserConnections(member.userId).size === 0) continue;
const token = event.call!.tokens![homeUserId];
connectionManager.sendToUser(member.userId, {
type: 'dm_call_incoming',
@@ -4326,6 +4331,14 @@ function processDmCallStartEvent(
ringedUserIds.push(member.userId);
}
if (ringedUserIds.length === 0) {
// #18: no local member was reachable. Do not create a FederatedCallEntry
// (it would strand with no accept/reject path); surface to the caller
// via undeliverable so it can tear down its ring room instead of hanging.
undeliverable.push({ messageId: event.messageId, reason: 'no_recipient' });
return;
}
const entry: FederatedCallEntry = {
dmChannelId: localDmChannelId,
federatedId: event.federatedId,