@@ -5,7 +5,6 @@ require('dotenv').config();
55const express = require ( 'express' ) ;
66const expressWs = require ( 'express-ws' ) ;
77const { DeepgramClient } = require ( '@deepgram/sdk' ) ;
8- const twilio = require ( 'twilio' ) ;
98
109const PORT = process . env . PORT || 3000 ;
1110
@@ -29,7 +28,9 @@ function createApp() {
2928 process . exit ( 1 ) ;
3029 }
3130
32- const deepgram = new DeepgramClient ( { apiKey : process . env . DEEPGRAM_API_KEY } ) ;
31+ const deepgram = new DeepgramClient ( {
32+ apiKey : process . env . DEEPGRAM_API_KEY ,
33+ } ) ;
3334
3435 app . post ( '/voice' , ( req , res ) => {
3536 const host = req . headers . host ;
@@ -48,37 +49,14 @@ function createApp() {
4849 console . log ( `[voice] New call → streaming to ${ streamUrl } ` ) ;
4950 } ) ;
5051
51- app . ws ( '/media' , async ( twilioWs ) => {
52+ app . ws ( '/media' , ( twilioWs ) => {
5253 let dgConnection = null ;
54+ let dgReady = false ;
5355 let streamSid = null ;
56+ const mediaQueue = [ ] ;
5457
5558 console . log ( '[media] Twilio WebSocket connected' ) ;
5659
57- dgConnection = await deepgram . listen . v1 . createConnection ( DEEPGRAM_LIVE_OPTIONS ) ;
58-
59- dgConnection . on ( 'open' , ( ) => {
60- console . log ( '[deepgram] Connection opened' ) ;
61- } ) ;
62-
63- dgConnection . on ( 'error' , ( err ) => {
64- console . error ( '[deepgram] Error:' , err . message ) ;
65- } ) ;
66-
67- dgConnection . on ( 'close' , ( ) => {
68- console . log ( '[deepgram] Connection closed' ) ;
69- } ) ;
70-
71- dgConnection . on ( 'message' , ( data ) => {
72- const transcript = data ?. channel ?. alternatives ?. [ 0 ] ?. transcript ;
73- if ( transcript ) {
74- const tag = data . is_final ? 'final' : 'interim' ;
75- console . log ( `[${ tag } ] ${ transcript } ` ) ;
76- }
77- } ) ;
78-
79- dgConnection . connect ( ) ;
80- await dgConnection . waitForOpen ( ) ;
81-
8260 twilioWs . on ( 'message' , ( raw ) => {
8361 try {
8462 const message = JSON . parse ( raw ) ;
@@ -94,21 +72,23 @@ function createApp() {
9472 break ;
9573
9674 case 'media' :
97- try {
98- if ( dgConnection ) {
75+ if ( dgReady && dgConnection ) {
76+ try {
9977 dgConnection . sendMedia ( Buffer . from ( message . media . payload , 'base64' ) ) ;
100- }
101- } catch { }
102-
78+ } catch { }
79+ } else {
80+ mediaQueue . push ( message . media . payload ) ;
81+ }
10382 break ;
10483
10584 case 'stop' :
10685 console . log ( '[twilio] Stream stopped' ) ;
10786 if ( dgConnection ) {
108- try { dgConnection . sendFinalize ( { type : 'Finalize ' } ) ; } catch { }
87+ try { dgConnection . sendCloseStream ( { type : 'CloseStream ' } ) ; } catch { }
10988 try { dgConnection . close ( ) ; } catch { }
11089 dgConnection = null ;
11190 }
91+ twilioWs . close ( ) ;
11292 break ;
11393
11494 default :
@@ -122,7 +102,7 @@ function createApp() {
122102 twilioWs . on ( 'close' , ( ) => {
123103 console . log ( '[media] Twilio WebSocket closed' ) ;
124104 if ( dgConnection ) {
125- try { dgConnection . sendFinalize ( { type : 'Finalize ' } ) ; } catch { }
105+ try { dgConnection . sendCloseStream ( { type : 'CloseStream ' } ) ; } catch { }
126106 try { dgConnection . close ( ) ; } catch { }
127107 dgConnection = null ;
128108 }
@@ -135,6 +115,43 @@ function createApp() {
135115 dgConnection = null ;
136116 }
137117 } ) ;
118+
119+ ( async ( ) => {
120+ dgConnection = await deepgram . listen . v1 . connect ( DEEPGRAM_LIVE_OPTIONS ) ;
121+
122+ dgConnection . on ( 'open' , ( ) => {
123+ console . log ( '[deepgram] Connection opened' ) ;
124+ } ) ;
125+
126+ dgConnection . on ( 'error' , ( err ) => {
127+ console . error ( '[deepgram] Error:' , err . message ) ;
128+ } ) ;
129+
130+ dgConnection . on ( 'close' , ( ) => {
131+ console . log ( '[deepgram] Connection closed' ) ;
132+ } ) ;
133+
134+ dgConnection . on ( 'message' , ( data ) => {
135+ const transcript = data ?. channel ?. alternatives ?. [ 0 ] ?. transcript ;
136+ if ( transcript ) {
137+ const tag = data . is_final ? 'final' : 'interim' ;
138+ console . log ( `[${ tag } ] ${ transcript } ` ) ;
139+ }
140+ } ) ;
141+
142+ dgConnection . connect ( ) ;
143+ await dgConnection . waitForOpen ( ) ;
144+
145+ dgReady = true ;
146+ for ( const payload of mediaQueue ) {
147+ try {
148+ dgConnection . sendMedia ( Buffer . from ( payload , 'base64' ) ) ;
149+ } catch { }
150+ }
151+ mediaQueue . length = 0 ;
152+ } ) ( ) . catch ( ( err ) => {
153+ console . error ( '[deepgram] Setup failed:' , err . message ) ;
154+ } ) ;
138155 } ) ;
139156
140157 app . get ( '/' , ( _req , res ) => {
0 commit comments