fix: re-derive conversation authorship from history
Rooms that Reddit returns without timeline events in /sync fell back to isFromUs=false, so an unanswered opener of ours was projected as inbound and surfaced in the dashboard's "Needs reply" queue. loadDetails already paginates the authoritative history for lastMessageSnippet/Time, so take isFromUs from the same newest message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
38d175e4a1
commit
7defff422d
2 files changed
+58
-2
No files matched your search
@@ -1135,8 +1135,14 @@ export class ChatService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.lastMessageSnippet = chat.messages[chat.messages.length - 1].text;
|
const newest = chat.messages[chat.messages.length - 1];
|
||||||
chat.lastMessageTime = chat.messages[chat.messages.length - 1].timestamp;
|
chat.lastMessageSnippet = newest.text;
|
||||||
|
chat.lastMessageTime = newest.timestamp;
|
||||||
|
// Re-derive authorship from the authoritative history. Room summaries built
|
||||||
|
// from /sync default `isFromUs` to false when the room carries no timeline
|
||||||
|
// events (Reddit omits them for idle rooms), which would otherwise leave an
|
||||||
|
// unanswered opener of ours sitting in the operator's "Needs reply" queue.
|
||||||
|
chat.isFromUs = newest.isSelf;
|
||||||
|
|
||||||
const firstSelf = chat.messages.find((m) => m.isSelf);
|
const firstSelf = chat.messages.find((m) => m.isSelf);
|
||||||
if (firstSelf) chat.originMessage = firstSelf.text;
|
if (firstSelf) chat.originMessage = firstSelf.text;
|
||||||
|
|||||||
@@ -214,6 +214,56 @@ describe('ChatService progressive full backfill', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('derives isFromUs from history when /sync returns no timeline for the room', async () => {
|
||||||
|
const logger = createMockLogger();
|
||||||
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'chat-authorship-backfill-'));
|
||||||
|
try {
|
||||||
|
const matrix = new MatrixClient(credentials, logger, { minRequestIntervalMs: 0 });
|
||||||
|
// Reddit omits timeline events for idle rooms, so the seeded summary
|
||||||
|
// carries no authorship signal at all.
|
||||||
|
matrix.sync = vi.fn(async () => ({ next_batch: 's1', rooms: { join: {} } })) as typeof matrix.sync;
|
||||||
|
matrix.getJoinedRooms = vi.fn(async () => ['!unanswered:reddit.com', '!answered:reddit.com']);
|
||||||
|
matrix.getRoomMessages = vi.fn(async (roomId: string) => ({
|
||||||
|
start: 'start',
|
||||||
|
chunk: roomId === '!unanswered:reddit.com'
|
||||||
|
? [{
|
||||||
|
event_id: '$opener',
|
||||||
|
type: 'm.room.message',
|
||||||
|
sender: '@me:reddit.com',
|
||||||
|
origin_server_ts: 1_000,
|
||||||
|
content: { body: 'Hello there' },
|
||||||
|
}]
|
||||||
|
: [{
|
||||||
|
event_id: '$reply',
|
||||||
|
type: 'm.room.message',
|
||||||
|
sender: '@t2_bob:reddit.com',
|
||||||
|
origin_server_ts: 2_000,
|
||||||
|
content: { body: 'Hi back' },
|
||||||
|
}, {
|
||||||
|
event_id: '$opener',
|
||||||
|
type: 'm.room.message',
|
||||||
|
sender: '@me:reddit.com',
|
||||||
|
origin_server_ts: 1_000,
|
||||||
|
content: { body: 'Hello there' },
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const service = new ChatService(
|
||||||
|
matrix, logger, undefined, undefined, undefined, undefined,
|
||||||
|
new ChatCache(logger, path.join(tmpDir, 'chat-cache.json')),
|
||||||
|
undefined, undefined, authenticatedAccountAuth,
|
||||||
|
);
|
||||||
|
|
||||||
|
const chats = await service.fetchAllMessages({ requireComplete: true });
|
||||||
|
const unanswered = chats.find((c) => c.roomId === '!unanswered:reddit.com');
|
||||||
|
const answered = chats.find((c) => c.roomId === '!answered:reddit.com');
|
||||||
|
expect(unanswered?.isFromUs).toBe(true);
|
||||||
|
expect(answered?.isFromUs).toBe(false);
|
||||||
|
} finally {
|
||||||
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('paginates beyond the former 50-page history ceiling', async () => {
|
it('paginates beyond the former 50-page history ceiling', async () => {
|
||||||
const logger = createMockLogger();
|
const logger = createMockLogger();
|
||||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'chat-long-backfill-'));
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'chat-long-backfill-'));
|
||||||
|
|||||||
Reference in new issue
Block a user