11var express = require ( 'express' ) ;
2+ const retry = require ( 'async-await-retry' ) ;
3+ const request = require ( 'request' ) ;
24var client = require ( "twilio" ) ( process . env . TWIL_FLEX_ACCOUNT_SID , process . env . TWIL_FLEX_ACCOUNT_KEY ) ;
35
46const router = express . Router ( ) ;
57const numberPool = JSON . parse ( process . env . NUMBER_POOL ) . sort ( ) ;
68
9+ async function timeout ( data ) {
10+ const interval = data . exponential ? data . interval * data . factor : data . interval ;
11+ // if interval is set to zero, do not use setTimeout, gain 1 event loop tick
12+ if ( interval ) await new Promise ( r => setTimeout ( r , interval + data . jitter ) ) ;
13+ }
14+
15+ async function doCleanupConversation ( conversationSid ) {
16+ await new Promise ( ( a ) => { setTimeout ( a , Math . random ( 10000 ) + 100 ) } ) ;
17+ if ( ! conversationSid ) {
18+ console . error ( 'Underfined conversationSid' ) ;
19+ }
20+ const conversationInst = client . conversations . conversations ( conversationSid ) ;
21+ const res = await retry ( async ( ) => {
22+ return conversationInst . remove ( ) ;
23+ } ,
24+ null ,
25+ {
26+ onAttemptFail : async ( data ) => {
27+ console . warn ( `Got error response while cleaning up convo ${ conversationSid } : ${ JSON . stringify ( data ) } ` ) ;
28+ if ( data . error . status === 429 ) {
29+ await timeout ( data ) ;
30+ return true ;
31+ } else {
32+ console . error ( `Got error response while cleaning up convo, not retrying ${ conversationSid } : ${ JSON . stringify ( data ) } ` ) ;
33+ return false ; // dont retry
34+ }
35+ } ,
36+ retriesMax : 4 ,
37+ interval : 1000 ,
38+ exponential : true ,
39+ factor : 3 ,
40+ jitter : 10000
41+ } ) ;
42+ return res ;
43+ }
44+
745async function cleanupConversation ( conversationSid ) {
846 try {
9- await client . conversations . conversations ( conversationSid ) . remove ( ) ;
47+ await doCleanupConversation ( conversationSid ) ;
1048 console . log ( `Removed new conversation successfully: ${ conversationSid } ` )
1149 } catch ( removeError ) {
12- console . log ( `Error occurred while removing ${ conversationSid } : ${ removeError } ` ) ;
50+ console . error ( `Error occurred while removing ${ conversationSid } : ${ removeError } ` ) ;
1351 }
1452}
1553
@@ -74,17 +112,64 @@ async function fetchProxyAddressesInOpenConversationsForAddress(address) {
74112 return proxyAddresses ;
75113}
76114
77- // Helper function for create /Conversations endpoint
115+
116+
117+
118+ // Helper function for create /Conversations endpoint with retry handling
78119async function createConversation ( sessionOpts ) {
79- return client . conversations . conversations . create ( sessionOpts ) ;
120+ const res = await retry ( async ( ) => {
121+ return client . conversations . conversations . create ( sessionOpts ) ;
122+ } ,
123+ null ,
124+ {
125+ onAttemptFail : async ( data ) => {
126+ console . warn ( `Got error response when creating conversation ${ JSON . stringify ( data ) } ` ) ;
127+ if ( data . error . status === 429 ) {
128+ await timeout ( data ) ;
129+ return true ;
130+ } else {
131+ return false ; // dont retry
132+ }
133+ } ,
134+ retriesMax : 4 ,
135+ interval : 1000 ,
136+ exponential : true ,
137+ factor : 3 ,
138+ jitter : 1000
139+ } ) ;
140+ return res ;
141+ }
142+
143+ async function doAddParticipantToConversation ( conversationSid , address , proxyAddress ) {
144+ const res = await retry ( async ( ) => {
145+ return client . conversations . conversations ( conversationSid ) . participants . create ( {
146+ 'messagingBinding.address' : address ,
147+ 'messagingBinding.proxyAddress' : proxyAddress
148+ } ) ;
149+ } ,
150+ null ,
151+ {
152+ onAttemptFail : async ( data ) => {
153+ console . warn ( `Got error response when adding participant ${ address } :${ proxyAddress } to ${ conversationSid } : ${ JSON . stringify ( data ) } ` ) ;
154+ if ( data . error . status === 429 ) {
155+ timeout ( data ) ;
156+ return true ;
157+ } else {
158+ return false ; // dont retry
159+ }
160+ } ,
161+ retriesMax : 4 ,
162+ interval : 1000 ,
163+ exponential : true ,
164+ factor : 3 ,
165+ jitter : 1000
166+ } ) ;
167+ return res ;
80168}
81169
82170// Helper function for Conversations create /Participants endpoint
83171async function addParticipantToConversation ( conversationSid , address , proxyAddress ) {
84- return client . conversations . conversations ( conversationSid ) . participants . create ( {
85- 'messagingBinding.address' : address ,
86- 'messagingBinding.proxyAddress' : proxyAddress
87- } )
172+ return doAddParticipantToConversation ( conversationSid , address , proxyAddress ) ;
88173}
89174
90175// Get the numbers in the numberPool that are not in activeSessionNumbers
@@ -102,31 +187,36 @@ function getSetOfAvailableNumbers(numberPool, activeSessionNumbers) {
102187}
103188
104189async function handleAddParticipant ( conversationSid , address ) {
105-
190+ console . log ( `handleAddParticipant called with ${ conversationSid } and ${ address } ` ) ;
191+
106192 const openConversationsProxyAddresses = await fetchProxyAddressesInOpenConversationsForAddress ( address ) ;
107193 const availableNumbers = getSetOfAvailableNumbers ( numberPool , openConversationsProxyAddresses ) ;
108- console . log ( `Found proxy number candidates for ${ address } : ${ availableNumbers } ` ) ;
109194
110195 if ( availableNumbers . length === 0 ) {
196+ console . error ( `No Proxy numbers to add ${ address } to ${ conversationSid } ` ) ;
111197 throw 'No proxy numbers available' ;
112- }
198+ } else {
199+ console . log ( `Found proxy number candidates for ${ address } : ${ availableNumbers } ` ) ;
200+ }
113201
114202 // We need to iterate over the list of available numbers until we add the participant successfully
115203 // This is needed since we may issue more than one request for a given number to add to a session
116204 // for e.g. a driver has multiple deliveries to make
205+ let lastError ;
117206 for ( let i = 0 ; i < availableNumbers . length ; ++ i ) {
118207 try {
119208 console . log ( `Try add ${ address } with proxy_address ${ availableNumbers [ i ] } to conversation ${ conversationSid } ` ) ;
120209 const participant = await addParticipantToConversation ( conversationSid , address , availableNumbers [ i ] ) ;
121210 console . log ( `Added participant ${ address } successfully: ${ participant . sid } to ${ conversationSid } ` ) ;
122211 return participant ;
123212 } catch ( e ) {
124- console . log ( `Failed to add participant ${ address } with proxy_address ${ availableNumbers [ i ] } : ${ JSON . stringify ( e ) } ` )
213+ console . error ( `Failed to add participant ${ address } with proxy_address ${ availableNumbers [ i ] } : ${ JSON . stringify ( e ) } ` )
214+ lastError = e ;
125215 }
126216 }
127217
128- // if we get here, it means we couldnt find a suitable number
129- throw 'No proxy numbers available' ;
218+ // if we get here, it means we couldnt find a suitable number
219+ throw lastError ;
130220}
131221
132222/*
@@ -190,14 +280,14 @@ router.use('/inboundCall', async function(req, res, next) {
190280 res . set ( 'Content-Type' , 'text/xml' ) ;
191281 res . send ( twiml . toString ( ) )
192282 } catch ( e ) {
193- console . log ( `Something went wrong when handling inbound call ${ e } ` )
283+ console . error ( `Something went wrong when handling inbound call ${ e } ` )
194284 res . send ( 500 , e ) ;
195285 }
196286} ) ;
197287
198288// Returns the Conversations reverse chrono order
199289router . get ( '/sessions' , async function ( req , res , next ) {
200- const conversations = await client . conversations . conversations . list ( { limit : req . query . limit ?parseInt ( req . query . limit ) :20 } ) ;
290+ const conversations = await client . conversations . conversations . list ( { limit : req . query . limit ?parseInt ( req . query . limit ) :200 } ) ;
201291 conversations . sort ( ( a , b ) => {
202292 if ( a . state === b . state ) {
203293 return b . dateCreated - a . dateCreated ;
@@ -243,7 +333,7 @@ router.get('/participantsessions', async function(req, res, next) {
243333 return res . render ( 'participantConversations' , { title : 'Conversations' , conversations } ) ;
244334 }
245335
246- const conversations = await client . conversations . conversations . list ( { limit : 10 } ) ;
336+ const conversations = await client . conversations . conversations . list ( { limit : 100 } ) ;
247337 conversations . sort ( ( a , b ) => {
248338 return b . dateCreated - a . dateCreated ;
249339 } ) ;
@@ -289,7 +379,7 @@ async function handleCreateSession(sessionOpts, addresses) {
289379 newConversation = await createConversation ( sessionOpts ) ;
290380 console . log ( `Created new conversation successfully: ${ newConversation . sid } ` )
291381 } catch ( e ) {
292- console . log ( `Couldnt create a new session for ${ JSON . stringify ( addresses ) } : ${ e } ` )
382+ console . error ( `Couldnt create a new session for ${ JSON . stringify ( addresses ) } : ${ JSON . stringify ( e ) } ` )
293383 const error = {
294384 message : 'Could not create session' ,
295385 raw_message : JSON . stringify ( e ) ,
@@ -303,7 +393,7 @@ async function handleCreateSession(sessionOpts, addresses) {
303393 for ( let i = 0 ; i < addresses . length ; ++ i ) {
304394 participants [ i ] = await handleAddParticipant ( newConversation . sid , addresses [ i ] ) ;
305395 }
306-
396+
307397 const result = {
308398 sid : newConversation . sid ,
309399 participants,
@@ -312,11 +402,16 @@ async function handleCreateSession(sessionOpts, addresses) {
312402 return result ;
313403
314404 } catch ( e ) {
315- console . log ( `Couldnt add participants to a new session for ${ JSON . stringify ( addresses ) } : ${ e } ` )
405+ console . error ( `Couldnt add participants to a new session for ${ JSON . stringify ( addresses ) } : ${ e } ` )
316406 if ( newConversation ) {
317- cleanupConversation ( newConversation . sid ) ;
407+ try {
408+ await cleanupConversation ( newConversation . sid ) ;
409+ } catch ( ce ) {
410+ console . log ( `Couldnt clean up conversation ${ newConversation . sid } : ${ JSON . stringify ( ce ) } ` ) ;
411+ }
318412 }
319413 const error = {
414+ sid : newConversation . sid ,
320415 message : 'Could not add participants session' ,
321416 raw_message : JSON . stringify ( e ) ,
322417 }
@@ -354,7 +449,7 @@ router.post('/sessions', async function(req, res, next) {
354449 return res . status ( 200 ) . send ( `${ JSON . stringify ( result ) } ` ) ;
355450
356451 } catch ( e ) {
357- console . log ( `Couldnt create a new session for ${ JSON . stringify ( addresses ) } : ${ JSON . stringify ( e ) } ` )
452+ console . error ( `Couldnt create a new session for ${ JSON . stringify ( addresses ) } : ${ JSON . stringify ( e ) } ` )
358453 return res . send ( 500 , JSON . stringify ( e ) ) ;
359454 } finally {
360455 console . timeEnd ( 'sessionCreate' ) ;
0 commit comments