From e50f182035199cdc8f3e6cf4a75b1881bf6a8bc7 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Thu, 12 Mar 2026 08:22:00 +0000 Subject: [PATCH] Matrix: revalidate cached direct targets --- .../matrix/src/matrix/send/targets.test.ts | 34 +++++++++++++++++++ extensions/matrix/src/matrix/send/targets.ts | 7 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/extensions/matrix/src/matrix/send/targets.test.ts b/extensions/matrix/src/matrix/send/targets.test.ts index 280bf2a6005..bd3559c146c 100644 --- a/extensions/matrix/src/matrix/send/targets.test.ts +++ b/extensions/matrix/src/matrix/send/targets.test.ts @@ -166,6 +166,40 @@ describe("resolveMatrixRoomId", () => { await expect(resolveMatrixRoomId(client, userId)).resolves.toBe("!dm-room:example.org"); }); + + it("revalidates cached direct rooms before reuse when membership changes", async () => { + const userId = "@shared:example.org"; + const directRooms = ["!dm-room-1:example.org"]; + const membersByRoom = new Map([ + ["!dm-room-1:example.org", ["@bot:example.org", userId]], + ["!dm-room-2:example.org", ["@bot:example.org", userId]], + ]); + const client = { + getAccountData: vi.fn().mockImplementation(async () => ({ + [userId]: [...directRooms], + })), + getUserId: vi.fn().mockResolvedValue("@bot:example.org"), + getJoinedRooms: vi + .fn() + .mockResolvedValue(["!dm-room-1:example.org", "!dm-room-2:example.org"]), + getJoinedRoomMembers: vi + .fn() + .mockImplementation(async (roomId: string) => membersByRoom.get(roomId) ?? []), + setAccountData: vi.fn(), + resolveRoom: vi.fn(), + } as unknown as MatrixClient; + + await expect(resolveMatrixRoomId(client, userId)).resolves.toBe("!dm-room-1:example.org"); + + directRooms.splice(0, directRooms.length, "!dm-room-1:example.org", "!dm-room-2:example.org"); + membersByRoom.set("!dm-room-1:example.org", [ + "@bot:example.org", + userId, + "@mallory:example.org", + ]); + + await expect(resolveMatrixRoomId(client, userId)).resolves.toBe("!dm-room-2:example.org"); + }); }); describe("normalizeThreadId", () => { diff --git a/extensions/matrix/src/matrix/send/targets.ts b/extensions/matrix/src/matrix/send/targets.ts index d7abb2c5006..e71da63117c 100644 --- a/extensions/matrix/src/matrix/send/targets.ts +++ b/extensions/matrix/src/matrix/send/targets.ts @@ -97,13 +97,16 @@ async function resolveDirectRoomId(client: MatrixClient, userId: string): Promis if (!isMatrixQualifiedUserId(trimmed)) { throw new Error(`Matrix user IDs must be fully qualified (got "${trimmed}")`); } + const selfUserId = (await client.getUserId().catch(() => null))?.trim() || null; const directRoomCache = resolveDirectRoomCache(client); const cached = directRoomCache.get(trimmed); - if (cached) { + if (cached && (await isStrictDirectRoom(client, cached, trimmed, selfUserId))) { return cached; } - const selfUserId = (await client.getUserId().catch(() => null))?.trim() || null; + if (cached) { + directRoomCache.delete(trimmed); + } // 1) Fast path: use account data (m.direct) for *this* logged-in user (the bot). try {