Download app(2).js from Pepguy/Jjj: direct link, hf CLI and curl.
- Browser
- Download file 17.5 kB
-
https://huggingface.co/spaces/Pepguy/Jjj/resolve/main/app(2).js
- Command line
-
hf download 'hf://spaces/Pepguy/Jjj/app(2).js'
-
curl -L -o 'app(2).js' 'https://huggingface.co/spaces/Pepguy/Jjj/resolve/main/app(2).js'
17.5 kB
| import express from "express"; | |
| import { createServer } from "http"; | |
| import { WebSocketServer } from "ws"; | |
| import { randomUUID } from "crypto"; | |
| 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()); | |
| app.use(express.urlencoded({ extended: true })); | |
| 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 ACTIVE_VOICE = "Enceladus_3"; | |
| const log = (tag, ...args) => console.log(`[${new Date().toISOString()}] [${tag}]`, ...args); | |
| let callLogs = []; | |
| const sendTelnyxCmd = async (callId, action, body = {}) => { | |
| if (action === "playback_start") { | |
| updateLog(callId, { is_playing: true, playback_started_at: Date.now() }); | |
| } | |
| try { | |
| const response = await fetch(`https://api.telnyx.com/v2/calls/${callId}/actions/${action}`, { | |
| 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()}`); | |
| else log("TELNYX CMD SUCCESS", `[${action}] accepted.`); | |
| return response; | |
| } catch (e) { log("TELNYX CMD NETWORK ERROR", e.message); } | |
| }; | |
| const updateLog = (callId, updates) => { | |
| const entry = callLogs.find(l => l.call_control_id === callId); | |
| if (entry) Object.assign(entry, updates); | |
| return entry; | |
| }; | |
| // ββ 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); | |
| } | |
| // ββ DASHBOARD ββ | |
| app.get("/", (req, res) => { | |
| res.send(`<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Elite Telecom Control Panel</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/react@18.2.0/umd/react.production.min.js"></script> | |
| <script src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script> | |
| <script src="https://unpkg.com/@babel/standalone@7.23.10/babel.min.js"></script> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100 p-8 font-sans"> | |
| <div id="root" class="max-w-4xl mx-auto"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect } = React; | |
| function App() { | |
| const [logs, setLogs] = useState([]); | |
| const [targetNumber, setTargetNumber] = useState("+1"); | |
| const [status, setStatus] = useState(""); | |
| const fetchLogs = () => fetch("/api/logs").then(r => r.json()).then(setLogs); | |
| useEffect(() => { fetchLogs(); const t = setInterval(fetchLogs, 2000); return () => clearInterval(t); }, []); | |
| const dial = async () => { | |
| setStatus("Dialing..."); | |
| const res = await fetch("/dial", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ to: targetNumber }) }); | |
| const data = await res.json(); | |
| setStatus(data.ok ? "Call initiated!" : "Error: " + data.error); | |
| fetchLogs(); | |
| }; | |
| const hangup = async (id) => { | |
| await fetch("/call/hangup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ call_control_id: id }) }); | |
| fetchLogs(); | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <h1 className="text-3xl font-bold border-b border-gray-700 pb-4">Web Agency Telecom Dashboard</h1> | |
| <div className="bg-gray-800 p-6 rounded-lg flex gap-4 items-end"> | |
| <div className="flex-1"> | |
| <label className="block text-sm text-gray-400 mb-1">Target Phone Number</label> | |
| <input type="text" value={targetNumber} onChange={e => setTargetNumber(e.target.value)} className="w-full bg-gray-900 border border-gray-700 rounded p-2 text-white outline-none focus:border-blue-500" /> | |
| </div> | |
| <button onClick={dial} className="bg-blue-600 hover:bg-blue-500 px-8 py-2 rounded font-bold transition">Trigger Outbound Call</button> | |
| </div> | |
| {status && <div className="text-sm text-yellow-400">{status}</div>} | |
| <div className="bg-gray-800 rounded-lg overflow-hidden"> | |
| <table className="w-full text-left"> | |
| <thead className="bg-gray-900 text-gray-400 text-sm"> | |
| <tr><th className="p-4">Call ID</th><th className="p-4">Number</th><th className="p-4">Last Transcript</th><th className="p-4">Status</th><th className="p-4">Action</th><th className="p-4">Recording</th></tr> | |
| </thead> | |
| <tbody className="divide-y divide-gray-700"> | |
| {logs.map(l => ( | |
| <tr key={l.id}> | |
| <td className="p-4 text-xs font-mono text-gray-500">{l.call_control_id.slice(0,8)}...</td> | |
| <td className="p-4 font-mono">{l.target_number}</td> | |
| <td className="p-4 text-xs text-gray-300 italic truncate max-w-[200px]" title={l.last_transcript}>{l.last_transcript || "..."}</td> | |
| <td className="p-4"> | |
| <span className={\`inline-block px-2 py-1 rounded text-xs \${l.status === 'completed' ? 'bg-green-900 text-green-300' : 'bg-yellow-900 text-yellow-300'}\`}>{l.status}</span> | |
| {l.is_playing && <span className="block text-xs text-purple-400 animate-pulse mt-1">AI speaking...</span>} | |
| </td> | |
| <td className="p-4"> | |
| {l.status !== 'completed' ? <button onClick={() => hangup(l.call_control_id)} className="bg-red-600 hover:bg-red-500 text-xs px-3 py-1 rounded font-bold">Force Hang Up</button> : <span className="text-gray-500 text-xs">Ended</span>} | |
| </td> | |
| <td className="p-4">{l.recording_url ? <audio controls src={l.recording_url} className="h-8 w-48"></audio> : <span className="text-gray-500 text-sm">Recording...</span>}</td> | |
| </tr> | |
| ))} | |
| {logs.length === 0 && <tr><td colSpan="6" className="p-4 text-center text-gray-500">No calls yet</td></tr>} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| ReactDOM.createRoot(document.getElementById('root')).render(<App />); | |
| </script> | |
| </body> | |
| </html>`); | |
| }); | |
| app.get("/api/logs", (req, res) => { | |
| res.json(callLogs.map(({ timers, ...e }) => e).reverse()); | |
| }); | |
| app.get("/health", (req, res) => res.json({ status: "ok" })); | |
| app.get("/play-recording/:ccid", async (req, res) => { | |
| const entry = callLogs.find(l => l.call_control_id === req.params.ccid); | |
| if (!entry?.raw_telnyx_url) return res.sendStatus(404); | |
| try { | |
| const s3 = await fetch(entry.raw_telnyx_url); | |
| if (!s3.ok) return res.sendStatus(s3.status); | |
| const buf = Buffer.from(await s3.arrayBuffer()); | |
| res.setHeader("Content-Type", "audio/mpeg"); | |
| res.setHeader("Content-Length", buf.length); | |
| res.send(buf); | |
| } catch { res.sendStatus(500); } | |
| }); | |
| app.post("/dial", async (req, res) => { | |
| const { to } = req.body; | |
| log("DIAL", `Dialing ${to}...`); | |
| try { | |
| 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({ | |
| connection_id: TELNYX_CONNECTION_ID, | |
| to, from: TELNYX_FROM_NUMBER, | |
| custom_headers: [{ name: "X-Voice-Selection", value: ACTIVE_VOICE }] | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (!response.ok) return res.status(400).json({ ok: false, error: data?.errors?.[0]?.detail }); | |
| callLogs.push({ | |
| id: randomUUID(), call_control_id: data.data.call_control_id, | |
| target_number: to, voice_used: ACTIVE_VOICE, status: "dialing", | |
| last_transcript: "", recording_url: null, raw_telnyx_url: null, | |
| is_playing: false, playback_started_at: null, | |
| answered_at: null, timers: {}, created_at: new Date().toISOString() | |
| }); | |
| res.json({ ok: true }); | |
| } catch (err) { res.status(500).json({ ok: false, error: err.message }); } | |
| }); | |
| app.post("/call/hangup", async (req, res) => { | |
| const { call_control_id } = req.body; | |
| if (!call_control_id) return res.status(400).json({ error: "Missing call_control_id" }); | |
| await sendTelnyxCmd(call_control_id, "hangup"); | |
| res.json({ ok: true }); | |
| }); | |
| // ββ WEBHOOK ββ | |
| app.post("/webhook/call", async (req, res) => { | |
| const type = req.body?.data?.event_type; | |
| const payload = req.body?.data?.payload; | |
| const callId = payload?.call_control_id; | |
| if (!type) return res.sendStatus(200); | |
| const entry = callLogs.find(l => l.call_control_id === callId); | |
| if (entry?.status === "completed" && type !== "call.recording.saved") return res.sendStatus(200); | |
| switch (type) { | |
| case "call.initiated": | |
| log("INITIATED", `dir=${payload?.direction}`); | |
| if (payload?.direction === "incoming") { | |
| callLogs.push({ | |
| id: randomUUID(), call_control_id: callId, target_number: payload?.from, | |
| voice_used: ACTIVE_VOICE, status: "in-progress", last_transcript: "", | |
| recording_url: null, raw_telnyx_url: null, is_playing: false, | |
| playback_started_at: null, answered_at: null, timers: {}, created_at: new Date().toISOString() | |
| }); | |
| await sendTelnyxCmd(callId, "answer"); | |
| } | |
| break; | |
| case "call.answered": | |
| log("ANSWERED", `Call ${callId} connected.`); | |
| updateLog(callId, { status: "in-progress", answered_at: new Date().toISOString() }); | |
| await sendTelnyxCmd(callId, "record_start", { format: "mp3", channels: "dual", play_beep: false }); | |
| 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" | |
| }); | |
| if (entry) { | |
| entry.timers.hangup = setTimeout(async () => { | |
| if (entry.status === "completed") return; | |
| log("HANGUP", "Enforcing 90-second limit."); | |
| await sendTelnyxCmd(callId, "hangup"); | |
| }, 90000); | |
| } | |
| break; | |
| case "call.playback.ended": | |
| case "call.playback.stopped": | |
| updateLog(callId, { is_playing: false }); | |
| break; | |
| case "call.transcription": | |
| const tData = payload?.transcription_data; | |
| if (!tData) break; | |
| const voice = entry?.voice_used || ACTIVE_VOICE; | |
| if (!tData.is_final && entry?.is_playing) { | |
| log("BARGE-IN", "User speaking β stopping AI."); | |
| updateLog(callId, { is_playing: false }); | |
| await sendTelnyxCmd(callId, "playback_stop"); | |
| } | |
| if (tData.is_final) { | |
| const transcript = tData.transcript.trim(); | |
| if (!transcript) break; | |
| log("TRANSCRIPT", `"${transcript}"`); | |
| updateLog(callId, { last_transcript: transcript }); | |
| try { | |
| const aiRes = await fetch(`${AI_SERVER_URL}/api/process-text-turn`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ text: transcript, voice, call_id: callId }) | |
| }); | |
| if (aiRes.ok) { | |
| const aiData = await aiRes.json(); | |
| if (aiData.speak_url && entry?.status !== "completed") { | |
| log("AI_REPLY", `"${aiData.raw_text}"`); | |
| 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": | |
| const rawUrl = payload.recording_urls.mp3; | |
| log("RECORDING", rawUrl); | |
| updateLog(callId, { raw_telnyx_url: rawUrl, recording_url: `/play-recording/${callId}` }); | |
| break; | |
| case "call.hangup": | |
| updateLog(callId, { status: "completed", is_playing: false }); | |
| if (entry?.timers) Object.values(entry.timers).forEach(clearTimeout); | |
| log("CALL ENDED", "Terminated."); | |
| break; | |
| } | |
| res.sendStatus(200); | |
| }); | |
| // ββ WEBSOCKET: VAD + ECHO SUPPRESSION ββ | |
| wss.on("connection", (ws) => { | |
| log("WS", "VAD WebSocket connected"); | |
| let isSpeaking = false; | |
| let silenceFrames = 0; | |
| let callControlId = null; | |
| let hasPlayedFiller = false; | |
| let hasGreeted = false; | |
| let totalNoiseFrames = 0; | |
| let wasAiPlaying = false; // Tracks AI playing state across frames for transition detection | |
| const SILENCE_THRESHOLD_FRAMES = 35; // 700ms | |
| const VOLUME_THRESHOLD = 500; | |
| const ECHO_GRACE_PERIOD = 2000; // 2s after AI starts playing, ignore inbound volume | |
| ws.on("message", async (raw) => { | |
| try { | |
| const msg = JSON.parse(raw); | |
| if (msg.event === "start") { | |
| callControlId = msg.start?.call_control_id; | |
| // Greeting fires 1.5s after call connects. | |
| // Uses "Hi, this is Nathan." then registers the turn to the AI server | |
| // so the LLM conversation history knows the intro already happened. | |
| setTimeout(async () => { | |
| if (hasGreeted || !callControlId) return; | |
| const e = callLogs.find(l => l.call_control_id === callControlId); | |
| if (e?.status !== "in-progress") return; | |
| hasGreeted = true; | |
| const greetingPhrase = "Hi, this is Nathan."; | |
| log("SMART_GREETING", `Playing: "${greetingPhrase}"`); | |
| await sendTelnyxCmd(callControlId, "playback_start", { | |
| audio_url: `${AI_SERVER_URL}/play-audio?voice=${e.voice_used}&phrase=${encodeURIComponent(greetingPhrase)}`, | |
| target_legs: "self" | |
| }); | |
| // Register greeting into conversation history on the AI server | |
| fetch(`${AI_SERVER_URL}/api/register-greeting`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ call_id: callControlId, phrase: greetingPhrase }) | |
| }).catch(() => {}); | |
| }, 1500); | |
| } | |
| if (msg.event === "media") { | |
| const pcm16 = decodeMuLaw(Buffer.from(msg.media.payload, "base64")); | |
| const rms = calculateRMS(pcm16); | |
| const entry = callLogs.find(l => l.call_control_id === callControlId); | |
| const nowAiPlaying = entry?.is_playing || false; | |
| const timeSincePlayback = entry?.playback_started_at | |
| ? (Date.now() - entry.playback_started_at) : 9999; | |
| const isEchoWindow = timeSincePlayback < ECHO_GRACE_PERIOD; | |
| // ββ When AI starts speaking, hard-reset VAD state ββ | |
| // This prevents accumulated silence/noise frames from causing a phantom filler | |
| if (!wasAiPlaying && nowAiPlaying) { | |
| isSpeaking = false; | |
| silenceFrames = 0; | |
| totalNoiseFrames = 0; | |
| hasPlayedFiller = false; | |
| } | |
| wasAiPlaying = nowAiPlaying; | |
| if (rms > VOLUME_THRESHOLD) { | |
| // Suppress echo: if AI just started talking, ignore inbound spikes | |
| if (isEchoWindow) return; | |
| hasGreeted = true; | |
| silenceFrames = 0; | |
| totalNoiseFrames++; | |
| if (!isSpeaking) { | |
| isSpeaking = true; | |
| hasPlayedFiller = false; | |
| if (entry?.is_playing) { | |
| log("BARGE-IN (VAD)", "User speaking β stopping AI."); | |
| entry.is_playing = false; | |
| await sendTelnyxCmd(callControlId, "playback_stop"); | |
| } | |
| } | |
| } else if (isSpeaking) { | |
| silenceFrames++; | |
| if (silenceFrames > SILENCE_THRESHOLD_FRAMES && !hasPlayedFiller) { | |
| hasPlayedFiller = true; | |
| isSpeaking = false; | |
| // 8-second blackout: don't fire fillers during IVR intro | |
| const callAge = entry?.answered_at | |
| ? Date.now() - new Date(entry.answered_at).getTime() : 0; | |
| if (totalNoiseFrames > 40 && callAge > 8000) { | |
| log("VAD_EARLY_TURN", "Long pause detected β playing filler."); | |
| const fillers = ["Right.", "Got it.", "Okay.", "Yeah.", "Ah, okay."]; | |
| const filler = fillers[Math.floor(Math.random() * fillers.length)]; | |
| sendTelnyxCmd(callControlId, "playback_start", { | |
| audio_url: `${AI_SERVER_URL}/play-audio?voice=${entry?.voice_used || ACTIVE_VOICE}&phrase=${encodeURIComponent(filler)}`, | |
| target_legs: "self" | |
| }).catch(() => {}); | |
| } | |
| totalNoiseFrames = 0; | |
| } | |
| } | |
| } | |
| } catch {} | |
| }); | |
| }); | |
| server.listen(PORT, "0.0.0.0", () => { | |
| log("STARTUP", `Telecom Server running on port ${PORT}`); | |
| }); |