From 6e1007658d303bf4f74ab2fb2e5bb52944108bca Mon Sep 17 00:00:00 2001 From: examples-bot Date: Wed, 1 Apr 2026 00:34:20 +0000 Subject: [PATCH 1/4] fix(examples): register Twilio message handler before Deepgram connect in 020-twilio-media-streams-node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Twilio WebSocket message handler was registered after awaiting dgConnection.waitForOpen(), causing early media events to be lost if the Deepgram connection took any time to establish. Now the handler registers immediately and buffers audio until the Deepgram socket is ready. Also uses sendCloseStream (SDK v5 convention) and wraps waitForOpen in try/catch to prevent unhandled rejections. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/index.js | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/examples/020-twilio-media-streams-node/src/index.js b/examples/020-twilio-media-streams-node/src/index.js index 5585157..d419255 100644 --- a/examples/020-twilio-media-streams-node/src/index.js +++ b/examples/020-twilio-media-streams-node/src/index.js @@ -50,35 +50,12 @@ function createApp() { app.ws('/media', async (twilioWs) => { let dgConnection = null; + let dgReady = false; let streamSid = null; + const mediaBuffer = []; console.log('[media] Twilio WebSocket connected'); - dgConnection = await deepgram.listen.v1.createConnection(DEEPGRAM_LIVE_OPTIONS); - - dgConnection.on('open', () => { - console.log('[deepgram] Connection opened'); - }); - - dgConnection.on('error', (err) => { - console.error('[deepgram] Error:', err.message); - }); - - dgConnection.on('close', () => { - console.log('[deepgram] Connection closed'); - }); - - dgConnection.on('message', (data) => { - const transcript = data?.channel?.alternatives?.[0]?.transcript; - if (transcript) { - const tag = data.is_final ? 'final' : 'interim'; - console.log(`[${tag}] ${transcript}`); - } - }); - - dgConnection.connect(); - await dgConnection.waitForOpen(); - twilioWs.on('message', (raw) => { try { const message = JSON.parse(raw); @@ -94,20 +71,19 @@ function createApp() { break; case 'media': - try { - if (dgConnection) { + if (dgReady && dgConnection) { + try { dgConnection.sendMedia(Buffer.from(message.media.payload, 'base64')); - } - } catch {} - + } catch {} + } else { + mediaBuffer.push(message.media.payload); + } break; case 'stop': console.log('[twilio] Stream stopped'); if (dgConnection) { - try { dgConnection.sendFinalize({ type: 'Finalize' }); } catch {} - try { dgConnection.close(); } catch {} - dgConnection = null; + try { dgConnection.sendCloseStream({ type: 'CloseStream' }); } catch {} } break; @@ -122,9 +98,9 @@ function createApp() { twilioWs.on('close', () => { console.log('[media] Twilio WebSocket closed'); if (dgConnection) { - try { dgConnection.sendFinalize({ type: 'Finalize' }); } catch {} - try { dgConnection.close(); } catch {} + try { dgConnection.sendCloseStream({ type: 'CloseStream' }); } catch {} dgConnection = null; + dgReady = false; } }); @@ -133,8 +109,46 @@ function createApp() { if (dgConnection) { try { dgConnection.close(); } catch {} dgConnection = null; + dgReady = false; } }); + + try { + dgConnection = await deepgram.listen.v1.createConnection(DEEPGRAM_LIVE_OPTIONS); + + dgConnection.on('open', () => { + console.log('[deepgram] Connection opened'); + dgReady = true; + + while (mediaBuffer.length > 0) { + try { + dgConnection.sendMedia(Buffer.from(mediaBuffer.shift(), 'base64')); + } catch {} + } + }); + + dgConnection.on('error', (err) => { + console.error('[deepgram] Error:', err.message); + }); + + dgConnection.on('close', () => { + console.log('[deepgram] Connection closed'); + dgReady = false; + }); + + dgConnection.on('message', (data) => { + const transcript = data?.channel?.alternatives?.[0]?.transcript; + if (transcript) { + const tag = data.is_final ? 'final' : 'interim'; + console.log(`[${tag}] ${transcript}`); + } + }); + + dgConnection.connect(); + await dgConnection.waitForOpen(); + } catch (err) { + console.error('[deepgram] Failed to connect:', err.message || err); + } }); app.get('/', (_req, res) => { From 6ff71f1e2737498bea5194d628b286e9bcf60b10 Mon Sep 17 00:00:00 2001 From: examples-bot Date: Wed, 1 Apr 2026 04:04:28 +0000 Subject: [PATCH 2/4] fix(examples): address review feedback in 020-twilio-media-streams-node - Remove unused twilio import - Close twilioWs after stop event - Update to SDK v5 connect() method (replaces deprecated createConnection) - Fix double sendCloseStream by only sending in stop handler, not close handler - Update test to close WS after stop to match real Twilio behavior --- examples/020-twilio-media-streams-node/src/index.js | 9 ++++++--- examples/020-twilio-media-streams-node/tests/test.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/020-twilio-media-streams-node/src/index.js b/examples/020-twilio-media-streams-node/src/index.js index d419255..ce71a15 100644 --- a/examples/020-twilio-media-streams-node/src/index.js +++ b/examples/020-twilio-media-streams-node/src/index.js @@ -5,7 +5,6 @@ require('dotenv').config(); const express = require('express'); const expressWs = require('express-ws'); const { DeepgramClient } = require('@deepgram/sdk'); -const twilio = require('twilio'); const PORT = process.env.PORT || 3000; @@ -84,7 +83,11 @@ function createApp() { console.log('[twilio] Stream stopped'); if (dgConnection) { try { dgConnection.sendCloseStream({ type: 'CloseStream' }); } catch {} + try { dgConnection.close(); } catch {} + dgConnection = null; + dgReady = false; } + twilioWs.close(); break; default: @@ -98,7 +101,7 @@ function createApp() { twilioWs.on('close', () => { console.log('[media] Twilio WebSocket closed'); if (dgConnection) { - try { dgConnection.sendCloseStream({ type: 'CloseStream' }); } catch {} + try { dgConnection.close(); } catch {} dgConnection = null; dgReady = false; } @@ -114,7 +117,7 @@ function createApp() { }); try { - dgConnection = await deepgram.listen.v1.createConnection(DEEPGRAM_LIVE_OPTIONS); + dgConnection = await deepgram.listen.v1.connect(DEEPGRAM_LIVE_OPTIONS); dgConnection.on('open', () => { console.log('[deepgram] Connection opened'); diff --git a/examples/020-twilio-media-streams-node/tests/test.js b/examples/020-twilio-media-streams-node/tests/test.js index 1a33f42..49e7675 100644 --- a/examples/020-twilio-media-streams-node/tests/test.js +++ b/examples/020-twilio-media-streams-node/tests/test.js @@ -195,8 +195,8 @@ function testMediaStreamFlow(port, audioData) { if (ws.readyState !== WebSocket.OPEN) return; if (offset >= audioData.length || offset >= MAX_BYTES) { - // 4. "stop" — call ended ws.send(JSON.stringify({ event: 'stop', streamSid: 'MZ_ci_test' })); + setTimeout(() => ws.close(), 500); return; } From 9403ddfe45ad68e0418c79384fa191cae4e0161c Mon Sep 17 00:00:00 2001 From: examples-bot Date: Wed, 1 Apr 2026 06:28:17 +0000 Subject: [PATCH 3/4] fix(examples): address review feedback in 020-twilio-media-streams-node - Make WS handler synchronous; wrap Deepgram setup in async IIFE with .catch() - Add sendCloseStream before dgConnection.close() in close handler - Remove unused twilio dependency from package.json --- examples/020-twilio-media-streams-node/package.json | 1 - examples/020-twilio-media-streams-node/src/index.js | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/020-twilio-media-streams-node/package.json b/examples/020-twilio-media-streams-node/package.json index 5251f4c..14152c8 100644 --- a/examples/020-twilio-media-streams-node/package.json +++ b/examples/020-twilio-media-streams-node/package.json @@ -12,7 +12,6 @@ "dotenv": "^16.4.0", "express": "^4.21.0", "express-ws": "^5.0.2", - "twilio": "^5.4.0", "ws": "^8.18.0" }, "engines": { diff --git a/examples/020-twilio-media-streams-node/src/index.js b/examples/020-twilio-media-streams-node/src/index.js index ce71a15..c97e218 100644 --- a/examples/020-twilio-media-streams-node/src/index.js +++ b/examples/020-twilio-media-streams-node/src/index.js @@ -47,7 +47,7 @@ function createApp() { console.log(`[voice] New call → streaming to ${streamUrl}`); }); - app.ws('/media', async (twilioWs) => { + app.ws('/media', (twilioWs) => { let dgConnection = null; let dgReady = false; let streamSid = null; @@ -101,6 +101,7 @@ function createApp() { twilioWs.on('close', () => { console.log('[media] Twilio WebSocket closed'); if (dgConnection) { + try { dgConnection.sendCloseStream({ type: 'CloseStream' }); } catch {} try { dgConnection.close(); } catch {} dgConnection = null; dgReady = false; @@ -116,7 +117,7 @@ function createApp() { } }); - try { + (async () => { dgConnection = await deepgram.listen.v1.connect(DEEPGRAM_LIVE_OPTIONS); dgConnection.on('open', () => { @@ -149,9 +150,9 @@ function createApp() { dgConnection.connect(); await dgConnection.waitForOpen(); - } catch (err) { + })().catch((err) => { console.error('[deepgram] Failed to connect:', err.message || err); - } + }); }); app.get('/', (_req, res) => { From bd39722bc515d2c2a3081ffaf2d9c928e9a4948d Mon Sep 17 00:00:00 2001 From: examples-bot Date: Wed, 1 Apr 2026 12:13:08 +0000 Subject: [PATCH 4/4] fix(examples): restore twilio package and use TwiML builder in 020-twilio-media-streams-node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add twilio import and use twilio.twiml.VoiceResponse for TwiML generation - Restore twilio dependency in package.json 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- examples/020-twilio-media-streams-node/package.json | 1 + examples/020-twilio-media-streams-node/src/index.js | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/020-twilio-media-streams-node/package.json b/examples/020-twilio-media-streams-node/package.json index 14152c8..5251f4c 100644 --- a/examples/020-twilio-media-streams-node/package.json +++ b/examples/020-twilio-media-streams-node/package.json @@ -12,6 +12,7 @@ "dotenv": "^16.4.0", "express": "^4.21.0", "express-ws": "^5.0.2", + "twilio": "^5.4.0", "ws": "^8.18.0" }, "engines": { diff --git a/examples/020-twilio-media-streams-node/src/index.js b/examples/020-twilio-media-streams-node/src/index.js index c97e218..ae84903 100644 --- a/examples/020-twilio-media-streams-node/src/index.js +++ b/examples/020-twilio-media-streams-node/src/index.js @@ -5,6 +5,7 @@ require('dotenv').config(); const express = require('express'); const expressWs = require('express-ws'); const { DeepgramClient } = require('@deepgram/sdk'); +const twilio = require('twilio'); const PORT = process.env.PORT || 3000; @@ -35,15 +36,11 @@ function createApp() { const protocol = req.headers['x-forwarded-proto'] === 'https' ? 'wss' : 'ws'; const streamUrl = `${protocol}://${host}/media`; - const twiml = ` - - This call is being transcribed by Deepgram. - - - -`; + const response = new twilio.twiml.VoiceResponse(); + response.say('This call is being transcribed by Deepgram.'); + response.connect().stream({ url: streamUrl }); - res.type('text/xml').send(twiml); + res.type('text/xml').send(response.toString()); console.log(`[voice] New call → streaming to ${streamUrl}`); });