Skip to content

Commit c7ecd12

Browse files
authored
Merge pull request #32 from MaxLinCode/codex/telegram-route-feedback
Improve Telegram route feedback UX
2 parents 193a7ca + 7f7f1eb commit c7ecd12

4 files changed

Lines changed: 541 additions & 21 deletions

File tree

apps/web/src/app/api/telegram/webhook/route.test.ts

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ import { getDefaultCalendarAdapter, resetCalendarAdapterForTests } from "@atlas/
2121

2222
import { handleTelegramWebhook } from "@/lib/server/telegram-webhook";
2323

24-
const { sendTelegramMessageMock, routeTurnWithResponsesMock } = vi.hoisted(() => ({
24+
const { editTelegramMessageMock, sendTelegramMessageMock, sendTelegramChatActionMock, routeTurnWithResponsesMock } =
25+
vi.hoisted(() => ({
26+
editTelegramMessageMock: vi.fn(),
2527
sendTelegramMessageMock: vi.fn(),
28+
sendTelegramChatActionMock: vi.fn(),
2629
routeTurnWithResponsesMock: vi.fn()
27-
}));
30+
}));
2831

2932
vi.mock("@atlas/integrations", async () => {
3033
const actual = await vi.importActual<typeof import("@atlas/integrations")>("@atlas/integrations");
@@ -62,7 +65,9 @@ vi.mock("@atlas/integrations", async () => {
6265
}
6366
]
6467
}),
68+
editTelegramMessage: editTelegramMessageMock,
6569
routeTurnWithResponses: routeTurnWithResponsesMock,
70+
sendTelegramChatAction: sendTelegramChatActionMock,
6671
sendTelegramMessage: sendTelegramMessageMock
6772
};
6873
});
@@ -122,7 +127,9 @@ beforeEach(async () => {
122127
process.env.GOOGLE_LINK_TOKEN_SECRET = "google-link-secret";
123128
process.env.GOOGLE_CALENDAR_TOKEN_ENCRYPTION_KEY = Buffer.alloc(32, 7).toString("base64");
124129
resetCalendarAdapterForTests();
130+
editTelegramMessageMock.mockReset();
125131
sendTelegramMessageMock.mockReset();
132+
sendTelegramChatActionMock.mockReset();
126133
routeTurnWithResponsesMock.mockReset();
127134
routeTurnWithResponsesMock.mockResolvedValue({
128135
route: "mutation",
@@ -140,6 +147,19 @@ beforeEach(async () => {
140147
text: "Captured and scheduled Review launch checklist."
141148
}
142149
});
150+
editTelegramMessageMock.mockImplementation(async ({ chatId, messageId, text }) => ({
151+
ok: true,
152+
result: {
153+
message_id: messageId,
154+
date: 1_700_000_001,
155+
chat: {
156+
id: chatId,
157+
type: "private"
158+
},
159+
text
160+
}
161+
}));
162+
sendTelegramChatActionMock.mockResolvedValue(undefined);
143163
resetIncomingTelegramIngressStoreForTests();
144164
resetInboxProcessingStoreForTests();
145165
resetGoogleCalendarConnectionStoreForTests();
@@ -422,7 +442,7 @@ describe("telegram webhook route", () => {
422442
outcome: "planned"
423443
},
424444
outboundDelivery: {
425-
status: "sent",
445+
status: "edited",
426446
attempts: 1
427447
}
428448
});
@@ -440,9 +460,18 @@ describe("telegram webhook route", () => {
440460
expect(listPlannerRunsForTests()).toHaveLength(1);
441461
expect(listTasksForTests()).toHaveLength(1);
442462
expect(listScheduleBlocksForTests()).toHaveLength(1);
463+
expect(sendTelegramChatActionMock).toHaveBeenCalledWith({
464+
chatId: "999",
465+
action: "typing"
466+
});
443467
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(1);
444468
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
445469
chatId: "999",
470+
text: "Checking your schedule"
471+
});
472+
expect(editTelegramMessageMock).toHaveBeenCalledWith({
473+
chatId: "999",
474+
messageId: 88,
446475
text: expect.stringContaining("Scheduled 'Review launch checklist'")
447476
});
448477
});
@@ -489,14 +518,15 @@ describe("telegram webhook route", () => {
489518
outcome: "planned"
490519
},
491520
outboundDelivery: {
492-
status: "sent",
521+
status: "edited",
493522
attempts: 1
494523
}
495524
});
496525
expect(listPlannerRunsForTests()).toHaveLength(1);
497526
expect(listTasksForTests()).toHaveLength(1);
498527
expect(listScheduleBlocksForTests()).toHaveLength(1);
499528
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(1);
529+
expect(editTelegramMessageMock).toHaveBeenCalledTimes(1);
500530
});
501531

502532
it("treats confirmed mutation turns as write-capable when recovery finds one concrete proposal", async () => {
@@ -628,8 +658,14 @@ describe("telegram webhook route", () => {
628658
expect(listPlannerRunsForTests()).toHaveLength(1);
629659
expect(listTasksForTests()).toHaveLength(1);
630660
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(1);
631-
expect(sendTelegramMessageMock).toHaveBeenCalledWith(
661+
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
662+
chatId: "999",
663+
text: "Applying that"
664+
});
665+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
632666
expect.objectContaining({
667+
chatId: "999",
668+
messageId: 88,
633669
text: expect.stringContaining("Scheduled 'Dentist reminder'")
634670
})
635671
);
@@ -695,6 +731,7 @@ describe("telegram webhook route", () => {
695731
}
696732
);
697733
sendTelegramMessageMock.mockClear();
734+
editTelegramMessageMock.mockClear();
698735

699736
const response = await handleTelegramWebhook(
700737
buildRequest({
@@ -752,8 +789,10 @@ describe("telegram webhook route", () => {
752789
})
753790
})
754791
);
755-
expect(sendTelegramMessageMock).toHaveBeenCalledWith(
792+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
756793
expect.objectContaining({
794+
chatId: "999",
795+
messageId: 88,
757796
text: expect.stringContaining("Moved it to")
758797
})
759798
);
@@ -818,6 +857,7 @@ describe("telegram webhook route", () => {
818857
}
819858
);
820859
sendTelegramMessageMock.mockClear();
860+
editTelegramMessageMock.mockClear();
821861

822862
const response = await handleTelegramWebhook(
823863
buildRequest({
@@ -885,8 +925,10 @@ describe("telegram webhook route", () => {
885925
externalCalendarEventId: null,
886926
externalCalendarId: null
887927
});
888-
expect(sendTelegramMessageMock).toHaveBeenCalledWith(
928+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
889929
expect.objectContaining({
930+
chatId: "999",
931+
messageId: 88,
890932
text: "Marked 'Journaling session' as done."
891933
})
892934
);
@@ -949,6 +991,17 @@ describe("telegram webhook route", () => {
949991
expect(listPlannerRunsForTests()).toHaveLength(0);
950992
expect(listTasksForTests()).toHaveLength(0);
951993
expect(listScheduleBlocksForTests()).toHaveLength(0);
994+
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
995+
chatId: "999",
996+
text: "Applying that"
997+
});
998+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
999+
expect.objectContaining({
1000+
chatId: "999",
1001+
messageId: 88,
1002+
text: "I have two recent proposals in view. Which one do you want me to apply?"
1003+
})
1004+
);
9521005
});
9531006

9541007
it("keeps conversation turns non-writing", async () => {
@@ -1116,7 +1169,7 @@ describe("telegram webhook route", () => {
11161169
reply: "Let's sort the week by deadline and energy first."
11171170
},
11181171
outboundDelivery: {
1119-
status: "sent",
1172+
status: "edited",
11201173
attempts: 1
11211174
}
11221175
});
@@ -1200,8 +1253,15 @@ describe("telegram webhook route", () => {
12001253
});
12011254
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
12021255
chatId: "999",
1203-
text: "Let's sort the week by deadline and energy first."
1256+
text: "Thinking"
12041257
});
1258+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
1259+
expect.objectContaining({
1260+
chatId: "999",
1261+
messageId: 88,
1262+
text: "Let's sort the week by deadline and energy first."
1263+
})
1264+
);
12051265
} finally {
12061266
vi.useRealTimers();
12071267
}
@@ -1256,7 +1316,7 @@ describe("telegram webhook route", () => {
12561316
reply: "We can explore tomorrow morning options first, then I can make the change after you confirm."
12571317
},
12581318
outboundDelivery: {
1259-
status: "sent",
1319+
status: "edited",
12601320
attempts: 1
12611321
}
12621322
});
@@ -1281,8 +1341,15 @@ describe("telegram webhook route", () => {
12811341
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(1);
12821342
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
12831343
chatId: "999",
1284-
text: "We can explore tomorrow morning options first, then I can make the change after you confirm."
1344+
text: "Thinking"
12851345
});
1346+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
1347+
expect.objectContaining({
1348+
chatId: "999",
1349+
messageId: 88,
1350+
text: "We can explore tomorrow morning options first, then I can make the change after you confirm."
1351+
})
1352+
);
12861353
});
12871354

12881355
it("falls back to recent turns when summary generation fails", async () => {
@@ -1341,6 +1408,10 @@ describe("telegram webhook route", () => {
13411408
expect(listPlannerRunsForTests()).toHaveLength(0);
13421409
expect(listTasksForTests()).toHaveLength(0);
13431410
expect(listScheduleBlocksForTests()).toHaveLength(0);
1411+
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
1412+
chatId: "999",
1413+
text: "Thinking"
1414+
});
13441415
});
13451416

13461417
it("keeps write-adjacent conversation replies hedged instead of making hard system claims", async () => {
@@ -1394,13 +1465,25 @@ describe("telegram webhook route", () => {
13941465
expect(listPlannerRunsForTests()).toHaveLength(0);
13951466
expect(listTasksForTests()).toHaveLength(0);
13961467
expect(listScheduleBlocksForTests()).toHaveLength(0);
1468+
expect(sendTelegramMessageMock).toHaveBeenCalledWith({
1469+
chatId: "999",
1470+
text: "Thinking"
1471+
});
1472+
expect(editTelegramMessageMock).toHaveBeenCalledWith(
1473+
expect.objectContaining({
1474+
chatId: "999",
1475+
messageId: 88,
1476+
text:
1477+
"From our recent exchange, it sounds like you mean the dentist reminder, but I am not treating that as confirmed state."
1478+
})
1479+
);
13971480
});
13981481

13991482
it("retries once when Telegram follow-up delivery fails before succeeding", async () => {
14001483
process.env.TELEGRAM_WEBHOOK_SECRET = "test-webhook-secret";
14011484
sendTelegramMessageMock
14021485
.mockRejectedValueOnce(new Error("temporary network error"))
1403-
.mockResolvedValueOnce({
1486+
.mockResolvedValue({
14041487
ok: true,
14051488
result: {
14061489
message_id: 89,
@@ -1443,11 +1526,12 @@ describe("telegram webhook route", () => {
14431526
expect(response.body).toMatchObject({
14441527
accepted: true,
14451528
outboundDelivery: {
1446-
status: "sent",
1529+
status: "edited",
14471530
attempts: 2
14481531
}
14491532
});
14501533
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(2);
1534+
expect(editTelegramMessageMock).toHaveBeenCalledTimes(1);
14511535
expect(listOutgoingBotEventsForTests()).toHaveLength(1);
14521536
});
14531537

@@ -1493,7 +1577,7 @@ describe("telegram webhook route", () => {
14931577
error: "telegram unavailable"
14941578
}
14951579
});
1496-
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(2);
1580+
expect(sendTelegramMessageMock).toHaveBeenCalledTimes(4);
14971581
expect(listOutgoingBotEventsForTests()).toHaveLength(1);
14981582
expect(listOutgoingBotEventsForTests()[0]?.retryState).toBe("failed");
14991583
});

0 commit comments

Comments
 (0)