fix: Bug - Users doesn't receive dm messages

Match direct messages against every local identity alias (home id and provisioned signal-server actor ids) so recipients accept traffic addressed to their per-server presence id instead of silently dropping it.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-13 20:59:57 +02:00
co-authored by Cursor
parent b2a2d9d770
commit baa350e90a
6 changed files with 258 additions and 24 deletions
@@ -14,6 +14,7 @@ import { OfflineMessageQueueService } from './offline-message-queue.service';
import { PeerDeliveryService } from './peer-delivery.service';
import { AttachmentFacade } from '../../../attachment';
import { CustomEmojiService } from '../../../custom-emoji';
import { SignalServerCredentialStoreService } from '../../../authentication/application/services/signal-server-credential-store.service';
import {
advanceDirectMessageStatus,
createDirectConversation,
@@ -27,6 +28,7 @@ import {
updateMessageStatusInConversation,
upsertDirectMessage
} from '../../domain/logic/direct-message.logic';
import { collectDirectMessageSelfUserIds, isSelfDirectMessageSender } from '../../domain/logic/direct-message-identity.rules';
import {
DirectMessage,
DirectMessageConversation,
@@ -67,6 +69,7 @@ export class DirectMessageService {
private readonly delivery = inject(PeerDeliveryService);
private readonly attachments = inject(AttachmentFacade);
private readonly customEmoji = inject(CustomEmojiService);
private readonly credentialStore = inject(SignalServerCredentialStoreService);
private readonly store = inject(Store);
private readonly router = inject(Router);
private readonly currentUser = this.store.selectSignal(selectCurrentUser);
@@ -501,8 +504,9 @@ export class DirectMessageService {
private async handleIncomingMessage(payload: DirectMessageEventPayload): Promise<void> {
const ownerId = this.getCurrentUserIdOrThrow();
const currentUser = this.requireCurrentUser();
const selfUserIds = this.getSelfUserIds();
if (!directMessageEventIncludesUser(payload, ownerId) || payload.sender.userId === ownerId || payload.message.senderId === ownerId) {
if (!directMessageEventIncludesUser(payload, selfUserIds) || isSelfDirectMessageSender(payload, selfUserIds)) {
return;
}
@@ -571,8 +575,9 @@ export class DirectMessageService {
private async handleIncomingMutation(payload: DirectMessageMutationEventPayload): Promise<void> {
const ownerId = this.getCurrentUserIdOrThrow();
const conversation = await this.findConversation(ownerId, payload.conversationId);
const selfUserIds = this.getSelfUserIds();
if (!conversation || !directMessageConversationIncludesUser(conversation, ownerId)) {
if (!conversation || !directMessageConversationIncludesUser(conversation, selfUserIds)) {
return;
}
@@ -580,16 +585,16 @@ export class DirectMessageService {
}
private handleIncomingTyping(payload: DirectMessageTypingEventPayload): void {
const currentUserId = this.getCurrentUserId();
const selfUserIds = this.getSelfUserIds();
if (!currentUserId || payload.sender.userId === currentUserId) {
if (selfUserIds.size === 0 || selfUserIds.has(payload.sender.userId)) {
return;
}
const conversation = this.conversationsSignal().find((entry) => entry.id === payload.conversationId);
if (!conversation
|| !directMessageConversationIncludesUser(conversation, currentUserId)
|| !directMessageConversationIncludesUser(conversation, selfUserIds)
|| !directMessageConversationIncludesUser(conversation, payload.sender.userId)) {
return;
}
@@ -621,10 +626,11 @@ export class DirectMessageService {
const ownerId = this.getCurrentUserIdOrThrow();
const currentUser = this.requireCurrentUser();
const conversation = await this.findConversation(ownerId, payload.conversationId);
const selfUserIds = this.getSelfUserIds();
if (!conversation
|| payload.sender.userId === ownerId
|| !directMessageConversationIncludesUser(conversation, ownerId)
|| selfUserIds.has(payload.sender.userId)
|| !directMessageConversationIncludesUser(conversation, selfUserIds)
|| !directMessageConversationIncludesUser(conversation, payload.sender.userId)) {
return;
}
@@ -647,12 +653,13 @@ export class DirectMessageService {
const ownerId = this.getCurrentUserIdOrThrow();
const currentUser = this.requireCurrentUser();
const currentParticipant = toDirectMessageParticipant(currentUser);
const selfUserIds = this.getSelfUserIds();
if (payload.sender.userId === ownerId) {
if (selfUserIds.has(payload.sender.userId)) {
return;
}
if (!directMessageSyncIncludesUser(payload, ownerId) || !directMessageSyncIncludesUser(payload, payload.sender.userId)) {
if (!directMessageSyncIncludesUser(payload, selfUserIds) || !directMessageSyncIncludesUser(payload, payload.sender.userId)) {
return;
}
@@ -929,7 +936,9 @@ export class DirectMessageService {
return [];
}
return conversation.participants.filter((participantId) => participantId !== currentUserId);
const selfUserIds = this.getSelfUserIds();
return conversation.participants.filter((participantId) => !selfUserIds.has(participantId));
}
private conversationKind(conversation: DirectMessageConversation): 'direct' | 'group' {
@@ -991,4 +1000,16 @@ export class DirectMessageService {
return ownerId;
}
private getSelfUserIds(): ReadonlySet<string> {
const currentUser = this.currentUser();
if (!currentUser) {
return new Set();
}
const actorUserIds = this.credentialStore.listValidCredentials().map((credential) => credential.userId);
return collectDirectMessageSelfUserIds(currentUser, actorUserIds);
}
}