import express, { Request, Response } from "express"; import { storage } from "../storage"; import { prisma as db } from "@repo/db/client"; const router = express.Router(); // POST /api/twilio/webhook/sms (Twilio posts inbound SMS here — no auth) router.post("/webhook/sms", async (req: Request, res: Response): Promise => { try { const { From, Body, MessageSid } = req.body; const normalizedFrom = (From || "").replace(/\D/g, ""); const allPatients = await db.patient.findMany({ select: { id: true, phone: true } }); const patient = allPatients.find( (p: { id: number; phone: string | null }) => p.phone && p.phone.replace(/\D/g, "") === normalizedFrom ); if (patient) { await storage.createCommunication({ patientId: patient.id, channel: "sms", direction: "inbound", status: "delivered", body: Body, twilioSid: MessageSid, }); } res.set("Content-Type", "text/xml"); return res.send(""); } catch (err) { res.set("Content-Type", "text/xml"); return res.send(""); } }); // POST /api/twilio/webhook/voice (Twilio posts here when someone calls — no auth) router.post("/webhook/voice", async (req: Request, res: Response): Promise => { try { const { From, CallSid } = req.body; const normalizedFrom = (From || "").replace(/\D/g, ""); const allPatients = await db.patient.findMany({ select: { id: true, phone: true, userId: true } }); const patient = allPatients.find( (p: { id: number; phone: string | null; userId: number }) => p.phone && p.phone.replace(/\D/g, "") === normalizedFrom ); let greeting = "Thank you for calling. Please leave a message after the beep and we will get back to you shortly."; if (patient) { const settings = await storage.getTwilioSettings(patient.userId); if (settings?.greetingMessage?.trim()) { greeting = settings.greetingMessage.trim(); } } if (patient) { await storage.createCommunication({ patientId: patient.id, channel: "voice", direction: "inbound", status: "completed", body: "(Inbound call — voicemail below)", twilioSid: CallSid, }); } const recordingCallbackUrl = `${process.env.BASE_URL || "https://communitydentistsoflowell.mydentalofficemanagement.com"}/api/twilio/webhook/voice-recording`; const twiml = ` ${greeting} We did not receive a recording. Goodbye. `; res.set("Content-Type", "text/xml"); return res.send(twiml); } catch (err) { res.set("Content-Type", "text/xml"); return res.send(`Thank you for calling. Please try again later.`); } }); // POST /api/twilio/webhook/voice-recording (Twilio posts recording URL here — no auth) router.post("/webhook/voice-recording", async (req: Request, res: Response): Promise => { try { const { CallSid, RecordingUrl } = req.body; if (RecordingUrl && CallSid) { const comm = await db.communication.findFirst({ where: { twilioSid: CallSid } }); if (comm) { await db.communication.update({ where: { id: comm.id }, data: { body: `Voicemail: ${RecordingUrl}.mp3` }, }); } } res.set("Content-Type", "text/xml"); return res.send(""); } catch (err) { res.set("Content-Type", "text/xml"); return res.send(""); } }); export default router;