Pepguy commited on
Commit
e7cbf71
Β·
verified Β·
1 Parent(s): 3716c54

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +7 -87
app.js CHANGED
@@ -148,25 +148,20 @@ app.get("/health", (req, res) => {
148
  });
149
 
150
  // ─────────────────────────────────────────────
151
- // 2. PROXY FOR TELNYX RECORDINGS (FIXED AWS S3 ISSUE)
152
  // ─────────────────────────────────────────────
153
  app.get("/play-recording/:call_control_id", async (req, res) => {
154
  const logEntry = callLogs.find(l => l.call_control_id === req.params.call_control_id);
155
  if (!logEntry || !logEntry.raw_telnyx_url) return res.sendStatus(404);
156
 
157
  try {
158
- // FIX: Telnyx returns an AWS S3 presigned URL.
159
- // Do NOT send the Telnyx Bearer token here, or AWS blocks it with a 400 error!
160
  const s3Res = await fetch(logEntry.raw_telnyx_url);
161
-
162
  if (!s3Res.ok) {
163
  log("PROXY ERROR", `S3 returned ${s3Res.status}`);
164
  return res.sendStatus(s3Res.status);
165
  }
166
-
167
  const arrayBuffer = await s3Res.arrayBuffer();
168
  const buffer = Buffer.from(arrayBuffer);
169
-
170
  res.setHeader("Content-Type", "audio/mpeg");
171
  res.setHeader("Content-Length", buffer.length);
172
  res.send(buffer);
@@ -220,7 +215,7 @@ app.post("/dial", async (req, res) => {
220
  });
221
 
222
  // ─────────────────────────────────────────────
223
- // 4. MANUAL HANG UP ROUTE (Robust Error Checking)
224
  // ─────────────────────────────────────────────
225
  app.post("/call/hangup", async (req, res) => {
226
  const { call_control_id } = req.body;
@@ -237,7 +232,8 @@ app.post("/call/hangup", async (req, res) => {
237
  "Content-Type": "application/json",
238
  Authorization: `Bearer ${TELNYX_API_KEY}`,
239
  },
240
- body: JSON.stringify({ client_state: "manual_hangup" }), // Prevents silent rejection
 
241
  }
242
  );
243
 
@@ -256,7 +252,7 @@ app.post("/call/hangup", async (req, res) => {
256
  });
257
 
258
  // ─────────────────────────────────────────────
259
- // 5. TELNYX WEBHOOK (Turn Detection & Automation)
260
  // ─────────────────────────────────────────────
261
  app.post("/webhook/call", async (req, res) => {
262
  const type = req.body?.data?.event_type;
@@ -266,11 +262,8 @@ app.post("/webhook/call", async (req, res) => {
266
 
267
  if (!type) return res.sendStatus(200);
268
 
269
- // ── FIX: Robust Telnyx Command Dispatcher ──
270
- // If Telnyx rejects a command, this will log exactly why it failed.
271
  const telnyxCmd = async (action, body = {}) => {
272
- // Adding client_state ensures Telnyx tracks the command properly
273
- body.client_state = body.client_state || `cmd_${action}_${randomUUID().split('-')[0]}`;
274
  try {
275
  const response = await fetch(`https://api.telnyx.com/v2/calls/${callId}/actions/${action}`, {
276
  method: "POST",
@@ -341,77 +334,4 @@ app.post("/webhook/call", async (req, res) => {
341
  const voice = getActiveVoice();
342
  const exactPhrase = encodeURIComponent("am I speaking with the business owner?");
343
  await telnyxCmd("playback_start", {
344
- audio_url: `${AI_SERVER_URL}/play-audio/${voice}/${exactPhrase}`,
345
- target_legs: "self"
346
- });
347
- }, 1500);
348
-
349
- // ── FIX: Auto-Hangup fallback. ──
350
- setTimeout(async () => {
351
- log("HANGUP", `Enforcing 60-second limit on call ${callId}`);
352
- await telnyxCmd("hangup", { client_state: "auto_timeout" });
353
- }, 60000);
354
- break;
355
-
356
- case "call.transcription":
357
- const data = payload?.transcription_data;
358
- if (data && data.is_final) {
359
- const transcript = data.transcript;
360
- const voice = getActiveVoice();
361
- log("TRANSCRIPT", `User said: "${transcript}"`);
362
- updateLog({ last_transcript: transcript });
363
-
364
- try {
365
- const aiRes = await fetch(`${AI_SERVER_URL}/api/process-text-turn`, {
366
- method: "POST",
367
- headers: { "Content-Type": "application/json" },
368
- body: JSON.stringify({ text: transcript, voice: voice })
369
- });
370
-
371
- if (aiRes.ok) {
372
- const aiData = await aiRes.json();
373
- if (aiData.speak_url) {
374
- log("AI_REPLY", `Instructing Telnyx to play generated reply.`);
375
- await telnyxCmd("playback_start", {
376
- audio_url: aiData.speak_url,
377
- target_legs: "self"
378
- });
379
- }
380
- }
381
- } catch (e) {
382
- log("AI_SERVER_ERROR", e.message);
383
- }
384
- }
385
- break;
386
-
387
- case "call.recording.saved":
388
- const rawUrl = payload.recording_urls.mp3;
389
- log("RECORDING", `Saved. URL: ${rawUrl}`);
390
- updateLog({
391
- status: "completed",
392
- raw_telnyx_url: rawUrl,
393
- recording_url: `/play-recording/${callId}`
394
- });
395
- break;
396
-
397
- case "call.hangup":
398
- updateLog({ status: "completed" });
399
- log("CALL ENDED", `Call ${callId} has cleanly terminated.`);
400
- break;
401
- }
402
-
403
- res.sendStatus(200);
404
- });
405
-
406
- // ─── SMS WEBHOOK ─────────────────────────────
407
- app.post("/webhook/sms", (req, res) => {
408
- const type = req.body?.data?.event_type;
409
- if (type === "message.received") {
410
- log("SMS INBOUND", `from=${req.body?.data?.payload?.from?.phone_number}`);
411
- }
412
- res.sendStatus(200);
413
- });
414
-
415
- server.listen(PORT, "0.0.0.0", () => {
416
- log("STARTUP", `Telecom Server running on port ${PORT}`);
417
- });
 
148
  });
149
 
150
  // ─────────────────────────────────────────────
151
+ // 2. PROXY FOR TELNYX RECORDINGS
152
  // ─────────────────────────────────────────────
153
  app.get("/play-recording/:call_control_id", async (req, res) => {
154
  const logEntry = callLogs.find(l => l.call_control_id === req.params.call_control_id);
155
  if (!logEntry || !logEntry.raw_telnyx_url) return res.sendStatus(404);
156
 
157
  try {
 
 
158
  const s3Res = await fetch(logEntry.raw_telnyx_url);
 
159
  if (!s3Res.ok) {
160
  log("PROXY ERROR", `S3 returned ${s3Res.status}`);
161
  return res.sendStatus(s3Res.status);
162
  }
 
163
  const arrayBuffer = await s3Res.arrayBuffer();
164
  const buffer = Buffer.from(arrayBuffer);
 
165
  res.setHeader("Content-Type", "audio/mpeg");
166
  res.setHeader("Content-Length", buffer.length);
167
  res.send(buffer);
 
215
  });
216
 
217
  // ─────────────────────────────────────────────
218
+ // 4. MANUAL HANG UP ROUTE (FIXED)
219
  // ─────────────────────────────────────────────
220
  app.post("/call/hangup", async (req, res) => {
221
  const { call_control_id } = req.body;
 
232
  "Content-Type": "application/json",
233
  Authorization: `Bearer ${TELNYX_API_KEY}`,
234
  },
235
+ // FIX: Completely stripped out client_state. Passing an empty object.
236
+ body: JSON.stringify({}),
237
  }
238
  );
239
 
 
252
  });
253
 
254
  // ─────────────────────────────────────────────
255
+ // 5. TELNYX WEBHOOK (FIXED)
256
  // ─────────────────────────────────────────────
257
  app.post("/webhook/call", async (req, res) => {
258
  const type = req.body?.data?.event_type;
 
262
 
263
  if (!type) return res.sendStatus(200);
264
 
265
+ // ── FIX: Clean Telnyx Dispatcher (No base64 string breaking the API) ──
 
266
  const telnyxCmd = async (action, body = {}) => {
 
 
267
  try {
268
  const response = await fetch(`https://api.telnyx.com/v2/calls/${callId}/actions/${action}`, {
269
  method: "POST",
 
334
  const voice = getActiveVoice();
335
  const exactPhrase = encodeURIComponent("am I speaking with the business owner?");
336
  await telnyxCmd("playback_start", {
337
+ audio_url: `${AI_SERVER_URL}/play-aud