Download app.js from Pepguy/Jjj: direct link, hf CLI and curl.
- Browser
- Download file 13.4 kB
-
https://huggingface.co/spaces/Pepguy/Jjj/resolve/main/app.js
- Command line
-
hf download hf://spaces/Pepguy/Jjj/app.js
-
curl -L -o app.js https://huggingface.co/spaces/Pepguy/Jjj/resolve/main/app.js
13.4 kB
| import express from "express"; | |
| import { createServer } from "http"; | |
| import { WebSocketServer } from "ws"; | |
| const app = express(); | |
| const server = createServer(app); | |
| const wss = new WebSocketServer({ server, path: "/audio-stream" }); | |
| app.use(express.static("public", { index: false })); | |
| app.use(express.json()); | |
| const PORT = process.env.PORT || 7860; | |
| const TELNYX_API_KEY = process.env.TELNYX_API_KEY; | |
| const TELNYX_CONNECTION_ID = process.env.TELNYX_CONNECTION_ID; | |
| const TELNYX_FROM_NUMBER = process.env.TELNYX_FROM_NUMBER; | |
| const AI_SERVER_URL = process.env.AI_SERVER_URL; | |
| const TELNYX_ALPHA_SENDER = process.env.TELNYX_ALPHA_SENDER || TELNYX_FROM_NUMBER; | |
| const TELNYX_MESSAGING_PROFILE_ID = process.env.TELNYX_MESSAGING_PROFILE_ID; | |
| const ACTIVE_VOICE = "Enceladus_3"; | |
| const log = (tag, ...args) => console.log(`[${new Date().toISOString()}] [${tag}]`, ...args); | |
| let pendingCalls = new Map(); | |
| let activeLeadIds = new Map(); | |
| const incomingCallDebounce = new Map(); | |
| const MY_CELL_NUMBER = "+2347066923490"; // <-- Your agent phone number | |
| // ── MU-LAW VAD ── | |
| const MU_LAW_TABLE = new Int16Array(256); | |
| for (let i = 0; i < 256; i++) { | |
| const sign = (i & 0x80) ? -1 : 1; | |
| const exponent = (~i >> 4) & 0x07; | |
| const mantissa = ~i & 0x0f; | |
| const sample = ((mantissa << 3) + 132) << exponent; | |
| MU_LAW_TABLE[i] = sign * (sample - 132); | |
| } | |
| function decodeMuLaw(buffer) { | |
| const out = new Int16Array(buffer.length); | |
| for (let i = 0; i < buffer.length; i++) out[i] = MU_LAW_TABLE[buffer[i]]; | |
| return out; | |
| } | |
| function calculateRMS(pcm) { | |
| let sum = 0; | |
| for (let i = 0; i < pcm.length; i++) sum += pcm[i] * pcm[i]; | |
| return Math.sqrt(sum / pcm.length); | |
| } | |
| const sendTelnyxCmd = async (callId, action, body = {}) => { | |
| try { | |
| const url = callId ? `https://api.telnyx.com/v2/calls/${callId}/actions/${action}` : `https://api.telnyx.com/v2/calls`; | |
| const response = await fetch(url, { | |
| method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${TELNYX_API_KEY}` }, | |
| body: JSON.stringify(body) | |
| }); | |
| if (!response.ok) log("TELNYX CMD ERROR", `[${action}] ${await response.text()}`); | |
| return response; | |
| } catch (e) { log("TELNYX CMD NETWORK ERROR", e.message); } | |
| }; | |
| app.post("/api/call-action", async (req, res) => { | |
| const { call_control_id, action, lead_id } = req.body; | |
| if (!pendingCalls.has(call_control_id)) return res.status(404).json({ error: "Call not pending" }); | |
| if (lead_id) activeLeadIds.set(call_control_id, lead_id); | |
| pendingCalls.set(call_control_id, action); | |
| if (action === "reject") { | |
| await sendTelnyxCmd(call_control_id, "hangup"); | |
| pendingCalls.delete(call_control_id); | |
| } else { | |
| await sendTelnyxCmd(call_control_id, "answer"); | |
| } | |
| res.json({ ok: true }); | |
| }); | |
| app.post("/api/hangup", async (req, res) => { | |
| await sendTelnyxCmd(req.body.call_control_id, "hangup"); | |
| res.json({ ok: true }); | |
| }); | |
| app.post("/api/send-sms", async (req, res) => { | |
| log("SMS", `Attempting outbound SMS to ${req.body.to}...`); | |
| try { | |
| const sendRequest = async (fromNumber) => { | |
| const payload = { from: fromNumber, to: req.body.to, text: req.body.text }; | |
| if (TELNYX_MESSAGING_PROFILE_ID) payload.messaging_profile_id = TELNYX_MESSAGING_PROFILE_ID; | |
| return await fetch("https://api.telnyx.com/v2/messages", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${TELNYX_API_KEY}` }, body: JSON.stringify(payload) }); | |
| }; | |
| let response = await sendRequest(TELNYX_ALPHA_SENDER); | |
| let data = await response.json(); | |
| if (!response.ok && data.errors && data.errors.some(e => e.code === '40305' || e.code === '40012')) { | |
| log("SMS WARNING", `Alpha Sender rejected. Falling back to standard US number ${TELNYX_FROM_NUMBER}...`); | |
| response = await sendRequest(TELNYX_FROM_NUMBER); | |
| data = await response.json(); | |
| } | |
| if (!response.ok) log("SMS ERROR", JSON.stringify(data)); | |
| else log("SMS SUCCESS", `Message accepted by Telnyx for delivery to ${req.body.to}`); | |
| res.json(data); | |
| } catch(e) { res.status(500).json({ error: e.message }); } | |
| }); | |
| app.post("/dial", async (req, res) => { | |
| const { to, lead_id, action = "manual" } = req.body; | |
| try { | |
| let callPayload = { connection_id: TELNYX_CONNECTION_ID, from: TELNYX_FROM_NUMBER }; | |
| if (action === "manual") { | |
| log("DIAL", `[Manual] Dialing Agent First (${MY_CELL_NUMBER})...`); | |
| // We dial YOU first. We pass the customer's number securely in the client_state. | |
| const stateObj = { type: "agent_first", customer_number: to, lead_id: lead_id }; | |
| callPayload.to = MY_CELL_NUMBER; | |
| callPayload.client_state = Buffer.from(JSON.stringify(stateObj)).toString("base64"); | |
| } else { | |
| log("DIAL", `[AI] Dialing Customer directly (${to})...`); | |
| // Normal AI dialing directly to customer | |
| callPayload.to = to; | |
| } | |
| const response = await fetch("https://api.telnyx.com/v2/calls", { | |
| method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${TELNYX_API_KEY}` }, | |
| body: JSON.stringify(callPayload) | |
| }); | |
| const data = await response.json(); | |
| if (!response.ok) return res.status(400).json({ ok: false, error: data?.errors?.[0]?.detail }); | |
| const ccid = data.data.call_control_id; | |
| pendingCalls.set(ccid, action); | |
| if (lead_id) activeLeadIds.set(ccid, lead_id); | |
| await fetch(`${AI_SERVER_URL}/api/log-outbound`, { | |
| method: "POST", headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ call_control_id: ccid, action, lead_id }) | |
| }).catch(() => {}); | |
| res.json({ ok: true, call_control_id: ccid }); | |
| } catch (err) { res.status(500).json({ ok: false, error: err.message }); } | |
| }); | |
| app.post("/webhook/call", async (req, res) => { | |
| res.sendStatus(200); | |
| const type = req.body?.data?.event_type; | |
| const payload = req.body?.data?.payload; | |
| const callId = payload?.call_control_id; | |
| if (!type) return; | |
| switch (type) { | |
| case "call.initiated": | |
| if (payload?.direction === "incoming") { | |
| const fromNumber = payload.from; | |
| const now = Date.now(); | |
| if (incomingCallDebounce.has(fromNumber) && now - incomingCallDebounce.get(fromNumber) < 10000) return; | |
| incomingCallDebounce.set(fromNumber, now); | |
| log("INCOMING", `From: ${fromNumber}`); | |
| pendingCalls.set(callId, "waiting"); | |
| await fetch(`${AI_SERVER_URL}/api/incoming-call`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ call_control_id: callId, from: fromNumber }) }).catch(()=>{}); | |
| } | |
| break; | |
| case "call.answered": | |
| // 1. CHECK IF THIS IS A BRIDGING STATE (Agent picked up, or Customer picked up) | |
| if (payload?.client_state) { | |
| try { | |
| const stateStr = Buffer.from(payload.client_state, "base64").toString("utf8"); | |
| const stateObj = JSON.parse(stateStr); | |
| // SCENARIO A: Agent just answered their cell phone. | |
| if (stateObj.type === "agent_first") { | |
| log("BRIDGING", `Agent picked up! Dialing Customer (${stateObj.customer_number})...`); | |
| // Start recording the agent leg so we capture the whole conversation | |
| await sendTelnyxCmd(callId, "record_start", { format: "mp3", channels: "dual", play_beep: false }); | |
| // Play ringing to the AGENT so they know the customer is being called | |
| await sendTelnyxCmd(callId, "playback_start", { audio_url: "https://actions.telnyx.com/sample-assets/ringback.wav", target_legs: "self" }); | |
| // Now dial the CUSTOMER, passing the Agent's call ID forward | |
| const nextState = { type: "customer_answered", leg_a_id: callId }; | |
| await sendTelnyxCmd(null, "dial", { | |
| to: stateObj.customer_number, | |
| from: TELNYX_FROM_NUMBER, | |
| connection_id: TELNYX_CONNECTION_ID, | |
| client_state: Buffer.from(JSON.stringify(nextState)).toString("base64") | |
| }); | |
| return; | |
| } | |
| // SCENARIO B: Customer just answered the phone. | |
| if (stateObj.type === "customer_answered") { | |
| log("BRIDGING", `Customer picked up! Bridging them to the Agent...`); | |
| // Stop the ringing in the Agent's ear | |
| await sendTelnyxCmd(stateObj.leg_a_id, "playback_stop"); | |
| // Bridge the Customer (callId) to the Agent (leg_a_id) | |
| await sendTelnyxCmd(callId, "bridge", { call_control_id: stateObj.leg_a_id }); | |
| return; | |
| } | |
| } catch (e) { log("STATE ERROR", "Failed to parse client state JSON."); } | |
| } | |
| // 2. IF NOT BRIDGING, IT IS AN AI CALL OR INCOMING CALL | |
| const actionType = pendingCalls.get(callId) || "ai"; | |
| if (actionType === "ai") { | |
| log("AI CONNECTING", `Starting stream for ${callId}`); | |
| await sendTelnyxCmd(callId, "record_start", { format: "mp3", channels: "dual", play_beep: false }); | |
| await sendTelnyxCmd(callId, "playback_start", { audio_url: `http://${req.headers.host}/ambience.mp3`, target_legs: "caller", loop: "infinity" }); | |
| await sendTelnyxCmd(callId, "streaming_start", { stream_url: `wss://${req.headers.host}/audio-stream`, stream_track: "inbound_track" }); | |
| await sendTelnyxCmd(callId, "transcription_start", { transcription_engine: "Telnyx", transcription_tracks: "inbound" }); | |
| setTimeout(async () => { log("HANGUP", `Enforcing 90s limit on ${callId}`); await sendTelnyxCmd(callId, "hangup"); }, 90000); | |
| } | |
| break; | |
| case "call.transcription": | |
| if (!payload?.transcription_data?.is_final) break; | |
| try { | |
| const aiRes = await fetch(`${AI_SERVER_URL}/api/process-text-turn`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: payload.transcription_data.transcript.trim(), call_id: callId, lead_id: activeLeadIds.get(callId) || null }) }); | |
| if (aiRes.ok) { | |
| const aiData = await aiRes.json(); | |
| if (aiData.speak_url) await sendTelnyxCmd(callId, "playback_start", { audio_url: aiData.speak_url, target_legs: "self" }); | |
| } | |
| } catch (e) { log("AI_SERVER_ERROR", e.message); } | |
| break; | |
| case "call.recording.saved": | |
| if (payload.recording_urls?.mp3) { | |
| await fetch(`${AI_SERVER_URL}/api/save-recording`, { | |
| method: "POST", headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ call_control_id: callId, recording_url: payload.recording_urls.mp3 }) | |
| }).then(async (r) => { if (!r.ok) log("RECORDING ERROR", `AI Server status: ${r.status}`); }).catch((err) => log("RECORDING NET ERROR", err.message)); | |
| } | |
| break; | |
| case "call.hangup": | |
| log("HANGUP", `Call ended. Reason: ${payload.hangup_cause}`); | |
| pendingCalls.delete(callId); | |
| activeLeadIds.delete(callId); | |
| await fetch(`${AI_SERVER_URL}/api/call-ended`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ call_control_id: callId }) }).catch(() => {}); | |
| break; | |
| } | |
| }); | |
| wss.on("connection", (ws) => { | |
| let isSpeaking = false; | |
| let silenceFrames = 0; | |
| let callControlId = null; | |
| let hasPlayedFiller = false; | |
| let hasGreeted = false; | |
| let totalNoiseFrames = 0; | |
| ws.on("message", async (raw) => { | |
| try { | |
| const msg = JSON.parse(raw); | |
| if (msg.event === "start") { | |
| callControlId = msg.start?.call_control_id; | |
| setTimeout(async () => { | |
| if (hasGreeted || !callControlId) return; | |
| hasGreeted = true; | |
| const greetingPhrase = "Hi, this is Nathan."; | |
| await sendTelnyxCmd(callControlId, "playback_start", { audio_url: `${AI_SERVER_URL}/play-audio?voice=${ACTIVE_VOICE}&phrase=${encodeURIComponent(greetingPhrase)}`, target_legs: "self" }); | |
| fetch(`${AI_SERVER_URL}/api/register-greeting`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ call_id: callControlId, phrase: greetingPhrase, lead_id: activeLeadIds.get(callControlId) || null }) }).catch(() => {}); | |
| }, 1500); | |
| } | |
| if (msg.event === "media") { | |
| const pcm16 = decodeMuLaw(Buffer.from(msg.media.payload, "base64")); | |
| const rms = calculateRMS(pcm16); | |
| if (rms > 500) { | |
| hasGreeted = true; | |
| silenceFrames = 0; | |
| totalNoiseFrames++; | |
| if (!isSpeaking) { | |
| isSpeaking = true; | |
| hasPlayedFiller = false; | |
| await sendTelnyxCmd(callControlId, "playback_stop"); | |
| } | |
| } else if (isSpeaking) { | |
| silenceFrames++; | |
| if (silenceFrames > 35 && !hasPlayedFiller) { | |
| hasPlayedFiller = true; | |
| isSpeaking = false; | |
| if (totalNoiseFrames > 40) { | |
| const fillers = ["Right.", "Got it.", "Okay.", "Yeah.", "Ah, okay."]; | |
| sendTelnyxCmd(callControlId, "playback_start", { audio_url: `${AI_SERVER_URL}/play-audio?voice=${ACTIVE_VOICE}&phrase=${encodeURIComponent(fillers[Math.floor(Math.random() * fillers.length)])}`, target_legs: "self" }).catch(() => {}); | |
| } | |
| totalNoiseFrames = 0; | |
| } | |
| } | |
| } | |
| } catch {} | |
| }); | |
| }); | |
| server.listen(PORT, "0.0.0.0", () => { | |
| log("STARTUP", `Telecom routing server running on port ${PORT}`); | |
| }); |