Make conversation queues mutually exclusive

This commit is contained in:
Jamie Brunton committed 2026-08-14 09:25:45 +01:00
1 parent 3bb1dda6bb
commit 2447563c60
20 files changed
+386 -220

No files matched your search

+41
View File
@@ -223,6 +223,32 @@ describe('conversation automation', () => {
});
});
it('kills instead of resuming a queued reply when an off-platform push arrives', async () => {
const observed = incoming(1, 'how is your week going?');
const latest: ActiveChat = {
...observed,
lastMessageTime: 40,
lastMessageSnippet: 'add me on telegram instead',
messages: [
...observed.messages!,
{ eventId: 'telegram-event', sender: 'alice', text: 'add me on telegram instead', timestamp: 40, isSelf: false },
],
};
const fixture = harness([], [], 0, undefined, async () => latest);
fixture.claims.getClaim.mockReturnValue({
conversationId: 'room-alice', incomingKey: 'in-1', status: 'pending',
replyText: 'Mine has been good, thanks.', incomingAt: 3,
claimedAt: 0, lastAttemptAt: null, attemptCount: 0, sentAt: null, supersededAt: null,
} as never);
const result = await fixture.service.processConversation(observed);
expect(result?.kind).toBe('killed');
expect(fixture.claims.markSuperseded).toHaveBeenCalledWith('room-alice', 'in-1');
expect(fixture.chat.sendChatMessage).not.toHaveBeenCalled();
expect(fixture.getSaved()).toMatchObject({ sellerDetected: true, automation: { status: 'killed' } });
});
it('kills a conversation that pushes a seller funnel', async () => {
const { service, chat, ai, triage, getSaved } = harness(['onlyfans'], ['fansly.com']);
const result = await service.processConversation(incoming(1, 'My Fansly is https://www.fansly.com/example'));
@@ -619,6 +645,21 @@ describe('conversation automation', () => {
expect(chat.sendChatMessage).not.toHaveBeenCalled();
});
it('automatically ends a historical read conversation with an off-platform push', async () => {
const { service, chat, triage, getSaved } = harness();
chat.getCachedConversations.mockResolvedValue([{
...incoming(1, 'add me on telegram, I barely use reddit'),
unread: false,
}]);
const results = await service.processPendingConversations();
expect(results).toEqual([expect.objectContaining({ kind: 'killed', conversationId: 'room-alice' })]);
expect(getSaved()).toMatchObject({ sellerDetected: true, automation: { status: 'killed' } });
expect(triage.setStatus).toHaveBeenCalledWith('room-alice', 'done', null, 1_000);
expect(chat.sendChatMessage).not.toHaveBeenCalled();
});
it('retries the same durable reply after an interrupted send', async () => {
const { service, chat, ai, claims } = harness();
chat.sendChatMessage.mockRejectedValueOnce(new Error('interrupted'));