feat: add AI-driven phone calls to patient connection
Adds an "AI Call" option alongside Call/SMS/Chat that places an outbound call where Lisa converses live with the patient via Twilio speech gather + TTS, using the same conversational AI as chat. Includes a separate, user-editable Call Template (Settings > AI Chat/Call Settings) so the call greeting can be customized independently of SMS templates. Also fixes the outbound-call webhook URL to always use the fixed public Twilio hostname (CLOUDFLARE_HOST) instead of deriving it from the triggering request, since staff-browser requests arrive over the LAN-only hostname, which Twilio can never reach. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
33
apps/Backend/src/utils/publicUrl.ts
Normal file
33
apps/Backend/src/utils/publicUrl.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Request } from "express";
|
||||
|
||||
/**
|
||||
* Public HTTPS origin this request arrived on (e.g. https://broadwaydental.mydentalofficemanagement.com).
|
||||
* Derived from the Host header rather than an env var so it's automatically correct
|
||||
* per-office — each office's nginx forwards its own public hostname in Host,
|
||||
* and Cloudflare/Let's Encrypt terminate TLS in front of it either way.
|
||||
*
|
||||
* Only valid for requests that actually arrived via the public tunnel (i.e. inside
|
||||
* a Twilio webhook handler). For anything triggered from the staff browser — which
|
||||
* reaches the backend over the LAN-only local-* hostname — use getTwilioPublicBaseUrl()
|
||||
* instead, since Twilio can never reach that LAN hostname to fetch a callback URL.
|
||||
*/
|
||||
export function getPublicBaseUrl(req: Request): string {
|
||||
return `https://${req.get("host")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed public origin Twilio must use to call back into this office's server
|
||||
* (e.g. for outbound-call TwiML webhooks). Comes from CLOUDFLARE_HOST, the
|
||||
* per-office env var already reserved for this — not derived from the
|
||||
* triggering request, since that request may have arrived over the LAN-only
|
||||
* hostname, which Twilio cannot reach.
|
||||
*/
|
||||
export function getTwilioPublicBaseUrl(): string {
|
||||
const host = process.env.CLOUDFLARE_HOST?.trim();
|
||||
if (!host) {
|
||||
throw new Error(
|
||||
"CLOUDFLARE_HOST is not set — required to build a Twilio-reachable callback URL. Set it in .env to this office's public tunnel hostname."
|
||||
);
|
||||
}
|
||||
return `https://${host}`;
|
||||
}
|
||||
Reference in New Issue
Block a user