diff --git a/src/app/api/telegram/webhook/__tests__/route.test.ts b/src/app/api/telegram/webhook/__tests__/route.test.ts index d9a1a364b..6b41c470b 100644 --- a/src/app/api/telegram/webhook/__tests__/route.test.ts +++ b/src/app/api/telegram/webhook/__tests__/route.test.ts @@ -1181,3 +1181,45 @@ describe("Telegram webhook — GET verification is rate-limited first", () => { ); }); }); + +/** + * #1033 — a medication kept as a record is never offered, recorded or snoozed + * from Telegram, the same as on the web. + */ +describe("POST /api/telegram/webhook — record-only medications", () => { + function textUpdate(text: string) { + return { + update_id: 300, + message: { message_id: 30, text, chat: { id: 7777 }, from: { id: 7777 } }, + }; + } + + function whereOf(call: unknown): Record { + return (call as [{ where: Record }])[0].where; + } + + it("/add lists tracked medications only", async () => { + await POST(tgRequest(textUpdate("/add"))); + const where = whereOf(vi.mocked(prisma.medication.findMany).mock.calls[0]); + expect(where).toMatchObject({ active: true, trackIntake: true }); + }); + + it("the 'taken' keyword resolves tracked medications only", async () => { + await POST(tgRequest(textUpdate("taken Ramipril"))); + const where = whereOf(vi.mocked(prisma.medication.findFirst).mock.calls[0]); + expect(where).toMatchObject({ active: true, trackIntake: true }); + }); + + it("a reminder button on a record-only medication records nothing", async () => { + await POST(tgRequest(callbackUpdate("taken:med-1"))); + // The gate lookup: a record-only medication resolves to nothing here, + // so no dose is written (later lookups only bind an admitted dose). + const calls = vi.mocked(prisma.medication.findFirst).mock.calls; + expect(calls.length).toBeGreaterThan(0); + expect(whereOf(calls[0])).toMatchObject({ + id: "med-1", + active: true, + trackIntake: true, + }); + }); +}); diff --git a/src/lib/telegram-webhook-handlers.ts b/src/lib/telegram-webhook-handlers.ts index f458bf6cc..474d54dec 100644 --- a/src/lib/telegram-webhook-handlers.ts +++ b/src/lib/telegram-webhook-handlers.ts @@ -33,6 +33,10 @@ import { isTelegramCapturableType, logTelegramMeasurement, } from "@/lib/measurements/create-from-telegram"; +// v1.39.1 (#1033) — every Telegram path that offers, records or snoozes a +// dose (the /add list, the "taken" keyword, the reminder buttons) leaves a +// medication kept as a record out, as the web and the today list do. +import { TRACKED_INTAKE_WHERE } from "@/lib/medications/intake-tracking"; /** * Telegram webhook update handlers, split out of @@ -301,7 +305,7 @@ async function markMedicationTaken( ): Promise<{ ok: boolean; message: string; medicationName?: string }> { const { t } = getServerTranslator(locale); const medication = await prisma.medication.findFirst({ - where: { id: medicationId, userId, active: true }, + where: { id: medicationId, userId, active: true, ...TRACKED_INTAKE_WHERE }, select: { id: true, name: true }, }); if (!medication) { @@ -518,7 +522,12 @@ export async function handleCallback(update: TelegramUpdate) { } const medication = await prisma.medication.findFirst({ - where: { id: medicationId, userId: user.id, active: true }, + where: { + id: medicationId, + userId: user.id, + active: true, + ...TRACKED_INTAKE_WHERE, + }, select: { id: true, name: true }, }); if (!medication) { @@ -560,7 +569,12 @@ export async function handleCallback(update: TelegramUpdate) { } const medication = await prisma.medication.findFirst({ - where: { id: medicationId, userId: user.id, active: true }, + where: { + id: medicationId, + userId: user.id, + active: true, + ...TRACKED_INTAKE_WHERE, + }, select: { id: true, name: true }, }); if (!medication) { @@ -1189,7 +1203,7 @@ export async function handleTextMessage(update: TelegramUpdate) { if (/^\/add\b/i.test(text)) { const userMsgId = message?.message_id; const meds = await prisma.medication.findMany({ - where: { userId: user.id, active: true }, + where: { userId: user.id, active: true, ...TRACKED_INTAKE_WHERE }, select: { id: true, name: true, dose: true }, orderBy: { name: "asc" }, }); @@ -1297,6 +1311,7 @@ export async function handleTextMessage(update: TelegramUpdate) { where: { userId: user.id, active: true, + ...TRACKED_INTAKE_WHERE, name: { equals: nameInput, mode: "insensitive" }, }, select: { id: true }, @@ -1304,7 +1319,7 @@ export async function handleTextMessage(update: TelegramUpdate) { medicationId = med?.id ?? null; } else { const meds = await prisma.medication.findMany({ - where: { userId: user.id, active: true }, + where: { userId: user.id, active: true, ...TRACKED_INTAKE_WHERE }, select: { id: true, name: true }, orderBy: { name: "asc" }, take: 2,