jambonz / jambonz-feature-server Goto Github PK
View Code? Open in Web Editor NEWCore telephony feature server for the jambones platform
License: MIT License
Core telephony feature server for the jambones platform
License: MIT License
This was observed on the dial command, but looks to be true any time we get a CANCEL on the A leg
Scenario:
The specific issue, I believe, is that when an inbound call is canceled, we check to see if we have an outdial in progress and if so cancel that leg as well. In this case, when the cancel arrives we have not yet launched the outbound invite, yet have queued it up in such a manner that it is subsequently launched.
To enable linking traces between jambonz and external services, it is necessary to propagate trace context between services. I've seen that you've added a "TraceId" field on the payload when communicating with other services via action hooks over HTTP but it would be great to implement a Trace Context Propagation standard such as the standard W3 (https://w3c.github.io/trace-context/) or B3 from OpenZipkin (https://github.com/openzipkin/b3-propagation).
When remote endpoint responds with 503 for example, logs show error and returns 500 Server error to application
WARNING: NODE_APP_INSTANCE value of '0' did not match any instance config file names.
WARNING: See https://github.com/lorenwest/node-config/wiki/Strict-Mode
TypeError: Cannot read property 'updateCallStatus' of undefined
at new CallSession (/home/deploy/jambonz-feature-server/lib/session/call-session.js:21:50)
at new RestCallSession (/home/deploy/jambonz-feature-server/lib/session/rest-call-session.js:7:5)
at cbRequest (/home/deploy/jambonz-feature-server/lib/http-routes/api/create-call.js:124:16)
at Object._app.request [as callback] (/home/deploy/jambonz-feature-server/node_modules/drachtio-srf/lib/srf.js:400:9)
at DrachtioAgent.obj.pendingRequests.set (/home/deploy/jambonz-feature-server/node_modules/drachtio-srf/lib/drachtio-agent.js:310:16)
at DrachtioAgent._onMsg (/home/deploy/jambonz-feature-server/node_modules/drachtio-srf/lib/drachtio-agent.js:717:11)
at WireProtocol.emit (events.js:198:13)
at WireProtocol.processMessageBuffer (/home/deploy/jambonz-feature-server/node_modules/drachtio-srf/lib/wire-protocol.js:270:12)
at WireProtocol._onData (/home/deploy/jambonz-feature-server/node_modules/drachtio-srf/lib/wire-protocol.js:304:14)
at Socket.emit (events.js:198:13)
at addChunk (_stream_readable.js:287:12)
at readableAddChunk (_stream_readable.js:264:13)
at Socket.Readable.push (_stream_readable.js:223:10)
at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
Currently, the dial verb when used with type 'phone' will send the call out through a configured SIP trunk. If there are multiple SIP trunking providers with outbound trunk, then a least cost routing algorithm will be used (subject to configuration) to determine which SIP trunk to use. However, in many cases it would probably be useful to allow the application to specify the specific sip trunking provider / carrier to use, e.g. below where "trunk" would be a new, optional property on dial:
{
"verb": "dial",
"target" : [
{
"type" : "phone",
"number": "+16173333456",
"trunk": "peerless"
}
]
}
I originate a new call (on Jambonz.us)
/v1/Accounts/myaccount/Calls %{
call_hook: %{
method: "POST",
url: "---"
},
call_status_hook: %{
method: "POST",
url: "...."
},
from: "1111",
to: %{name: ".....", overrideTo: "34", type: "user"}
}
And I get
%{sid: "76ad1a2b-3f0f-4a6f-9141-591ad175c952"}}}
The call progresses and I get notified that it is ringing (SIP state 180)
I try hanging it up:
16:31:00.047 -- REq /v1/Accounts/myaccount/Calls/76ad1a2b-3f0f-4a6f-9141-591ad175c952 %{call_status: "completed"} -> {{:error,
"{\"msg\":\"current call state is incompatible with requested action\"}"}}
16:31:00.185 -- REq /v1/Accounts/myaccount/Calls/76ad1a2b-3f0f-4a6f-9141-591ad175c952 %{call_status: "no-answer"} -> {{:error, "Accepted"}}
But my phone keeps on ringing (and I get no status updates)
If I do it again, I get errors, bu the call stays there
16:31:10.479 --- REq /v1/Accounts/myaccount/Calls/76ad1a2b-3f0f-4a6f-9141-591ad175c952 %{call_status: "completed"} -> {{:error, "{\"msg\":\"Cannot read property 'hasStableDialog' of undefined\"}"}}
16:31:10.599 --- REq /v1/Accounts/myaccount/Calls/76ad1a2b-3f0f-4a6f-9141-591ad175c952 %{call_status: "no-answer"} -> {{:error, "{\"msg\":\"Cannot read property 'direction' of undefined\"}"}}
In the end (after one minute or so) it closes and I get a status back:
{
"call_sid": "76ad1a2b-3f0f-4a6f-9141-591ad175c952",
"direction": "outbound",
"from": "1111",
"to": "....",
"call_id": "e91be8e9-2c7b-123b-9f81-0e2793d0d4c3",
"sip_status": 503,
"call_status": "failed",
"account_sid": "myaccount"
}
Issues have been spotted when we send REGISTERs from one SBC to a carrier for authentication, but then send INVITEs from a different SBC.
Using swagger we get the following response for GET http://jsbc1.viva-eu.net:3000/v1/Accounts
{
"msg": "Unknown column 'acc.device_calling_hook_sid' in 'on clause'"
}
If I place a call using Jambonz and the callee never answers I can see on the SIP traces that eventually I’ll get a 487 Request Terminated from B but that’s not being picked up by Jambonz so the call is not hanged up and hence the action hook not triggered. After the 487 the progress tone is not heard anymore and when A finally hangs up, the action hook is triggered with a dial_call_status of early-media.
When we have a single applicationSid
handling multiple tenants we need to somehow distinguish who "ajukes" belongs to. For example [email protected]
vs [email protected]
Currently the initial payload does not include the domain. Suggest originatingDomainName or something similar if call is initiated from registered handset
direction=inbound,
callSid=5424cfbb-00cf-484a-a3d1-3e9fee159e24,
accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04,
applicationSid=508c3a11-4b6e-41cb-b162-a87dd37a8a42,
from=ajukes,
to=07956697540,
callerName=,
callId=24766c3e-faaf-1238-cca4-02b574cefade,
sipStatus=100,
callStatus=trying,
originatingSipIp=78.33.150.124:5060,
originatingSipTrunkName=null,
...
When a Target or multiple Targets are called and the call completes, the subsequent action hook request does not contain the sip status of the failed (or successful) call leg.
The Action hooks do include the sipStatus
of the A Leg however this does not help in further routing decisions:
Action Hook after 200OK
DrachtioRequest(callSid=64883657-03c3-4987-87a1-316b4b25ca4b, callId=2ea258fb-47cb-123a-a7bf-06a78b9c3549,
parentCallSid=null, accountSid=9351f46a-678c-43f5-b8a6-d4eb58d131af,
from=+441179830003, to=02033888666, callStatus=in-progress, sipStatus=200,
direction=inbound, callerName=null, digits=null, speech=null, customerData=null,
dialCallSid=c2d4aeee-ec3c-45e4-bed7-5658ff0cd492, dialCallStatus=completed,
queueSid=null, queuePosition=null, queueTime=null, queueSize=null, queueResult=null)
Action Hook after 403
DrachtioRequest(callSid=b828ddd9-8c6b-468f-b7d3-2bfd24af0671, callId=3e932dba-47be-123a-efa6-02af80f5881b,
parentCallSid=null, accountSid=9351f46a-678c-43f5-b8a6-d4eb58d131af,
from=01383722538, to=013839, callStatus=trying, sipStatus=100,
direction=inbound, callerName=null, digits=null, speech=null,
customerData=null, dialCallSid=447b9825-f2e2-4ec0-9f80-6042037eec3e,
dialCallStatus=failed, queueSid=null, queuePosition=null, queueTime=null,
queueSize=null, queueResult=null)
dialCallStatus allows us to know if the call completed of failed, suggest dialSipStatus
field to give us the sip code of the dial.
When cancelling a call with PUT Update Call - Live Call Control, you are supposed to set its call_status to completed or not-answered. This is good if you know the current call status, but if you don't you have to try, match on error, and try again. And if you try "completed" and it does not work, you try "non-answered" and it might not work again if the call was connected in the meantime.
I believe the most common case is " just hangup", where it would be up to Jambonz to check the current state and hang up the call accordingly.
When using a Global replicated cache (such as Aws Global Datatore) , each replicated cluster is read only. The only writeable node is the primary cluster endpoint.
This means that every jambones cluster currently has to connect to the primary cluster to read and write, defeating the purpose of a distributed cache.
Ideally a separate read only connection string could be used for retrieving data from the closest replicated cache.
When a write to the cache is needed jambones would connect to the writeable primary cache which would in turn replicate to its slaves.
Websocket Metadata currently has inbound data but needs to include the following:-
After returning 480 response, we would expected to see a drachtio leg a event completed
[
{
"@timestamp": "2020-02-20T14:22:07.427+00:00",
"@version": "1",
"message": "initial-drachtio-request DrachtioInitialRequest(direction=inbound, callSid=41770ea3-c431-4237-ac6e-597c74b99ff7, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, applicationSid=508c3a11-4b6e-41cb-b162-a87dd37a8a42, from=+447956697540, to=+442033885380, callerName=+447956697540, callId=372bdc56-ce8f-1238-fcb3-02b574cefade, sipStatus=100, callStatus=trying, originatingSipIp=99.80.180.248:5060, originatingSipTrunkName=customers, sip=SipPayload(headers={via=SIP/2.0/UDP 34.241.251.115;rport=5060;branch=z9hG4bK44aUc9U0Z0UrS;received=172.31.50.237, max-forwards=70, from=<sip:[email protected]:5060>;tag=g80ByUXK6yt7c, to=<sip:[email protected]:5060>, call-id=372bdc56-ce8f-1238-fcb3-02b574cefade, cseq=16550471 INVITE, contact=<sip:[email protected]:5060>, expires=330, user-agent=Orchid 3.1.7.2, allow=BYE, CANCEL, ACK, INVITE, INFO, OPTIONS, supported=timer, replaces, session-expires=1800;refresher=uas, min-se=90, content-type=application/sdp, content-length=687, X-CID=1b930280-183e4fd2-11230e-7fb2f6210828-de641fac-13e2-7225, X-Forwarded-For=99.80.180.248:5060, X-Originating-Carrier=customers, p-asserted-identity=<sip:[email protected];user=phone>}, payload=[{type=application/sdp, content=v=0\r\no=vibesbc01a 300924929 1582208528 IN IP4 54.76.13.114\r\ns=sip call\r\nc=IN IP4 54.76.13.114\r\nt=0 0\r\nm=audio 50086 RTP/AVP 9 113 8 0 18 112 110 3 101\r\na=silenceSupp:off - - - -\r\na=rtpmap:9 G722/8000\r\na=rtpmap:113 AMR-WB/16000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:110 GSM-EFR/8000\r\na=rtpmap:3 GSM/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:113 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:18 annexb=no\r\na=fmtp:112 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:50087\r\na=ptime:20\r\n}], body=v=0\r\no=vibesbc01a 300924929 1582208528 IN IP4 54.76.13.114\r\ns=sip call\r\nc=IN IP4 54.76.13.114\r\nt=0 0\r\nm=audio 50086 RTP/AVP 9 113 8 0 18 112 110 3 101\r\na=silenceSupp:off - - - -\r\na=rtpmap:9 G722/8000\r\na=rtpmap:113 AMR-WB/16000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:110 GSM-EFR/8000\r\na=rtpmap:3 GSM/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:113 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:18 annexb=no\r\na=fmtp:112 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:50087\r\na=ptime:20\r\n, method=INVITE, version=2.0, uri=sip:[email protected]:5060, raw=INVITE sip:[email protected]:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 34.241.251.115;rport=5060;branch=z9hG4bK44aUc9U0Z0UrS;received=172.31.50.237\r\nMax-Forwards: 70\r\nFrom: <sip:[email protected]:5060>;tag=g80ByUXK6yt7c\r\nTo: <sip:[email protected]:5060>\r\nCall-ID: 372bdc56-ce8f-1238-fcb3-02b574cefade\r\nCSeq: 16550471 INVITE\r\nContact: <sip:[email protected]:5060>\r\nExpires: 330\r\nUser-Agent: Orchid 3.1.7.2\r\nAllow: BYE, CANCEL, ACK, INVITE, INFO, OPTIONS\r\nSupported: timer, replaces\r\nSession-Expires: 1800;refresher=uas\r\nMin-SE: 90\r\nContent-Type: application/sdp\r\nContent-Length: 687\r\nX-CID: 1b930280-183e4fd2-11230e-7fb2f6210828-de641fac-13e2-7225\r\nX-Forwarded-For: 99.80.180.248:5060\r\nX-Originating-Carrier: customers\r\nP-Asserted-Identity: <sip:[email protected];user=phone>\r\n\r\nv=0\r\no=vibesbc01a 300924929 1582208528 IN IP4 54.76.13.114\r\ns=sip call\r\nc=IN IP4 54.76.13.114\r\nt=0 0\r\nm=audio 50086 RTP/AVP 9 113 8 0 18 112 110 3 101\r\na=silenceSupp:off - - - -\r\na=rtpmap:9 G722/8000\r\na=rtpmap:113 AMR-WB/16000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:110 GSM-EFR/8000\r\na=rtpmap:3 GSM/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:113 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:18 annexb=no\r\na=fmtp:112 octet-align=0;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:50087\r\na=ptime:20\r\n))",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-5",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
},
{
"@timestamp": "2020-02-20T14:22:07.540+00:00",
"@version": "1",
"message": "initial-drachtio-response [Verb(gather=null, say=null, play=null, reject=null, hangup=null, redirect=null, dial=null, tag=Tag(data={port=5060, ip=99.80.180.248, privacy=false, pAsserted=+447956697540})), Verb(gather=null, say=null, play=null, reject=null, hangup=null, redirect=null, dial=Dial(actionHook=/ivr/2e0debc1-fb1f-4730-ad9d-261a48f3f8ba, callerId=447956697540, dialMusic=https://vibe-public.s3-eu-west-1.amazonaws.com/en-gb.wav, timeLimit=14400, timeout=60, answerOnBridge=true, target=[Target(type=user, url=null, number=null, sipUri=null, [email protected], auth=null)], listen=Listen(url=/record, mixType=stereo, finishOnKey=#, maxLength=14400, metadata={}, passDtmf=true, playBeep=false, sampleRate=8000, timeout=null, transcribe=null), transcribe=null, dtmfCapture=[*06, *07, *08, *09, *01, *02, *03, *04, *05], dtmfHook=/ivr/dtmf), tag=null)]",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-5",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
},
{
"@timestamp": "2020-02-20T14:22:07.707+00:00",
"@version": "1",
"message": "drachtio-event DrachtioRequest(callSid=41770ea3-c431-4237-ac6e-597c74b99ff7, callId=372bdc56-ce8f-1238-fcb3-02b574cefade, parentCallSid=null, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=+447956697540, to=+442033885380, callStatus=trying, sipStatus=100, direction=inbound, callerName=null, digits=null, speech=null, customerData=null, dialCallSid=null, dialCallStatus=null)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-4",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
},
{
"@timestamp": "2020-02-20T14:22:07.913+00:00",
"@version": "1",
"message": "drachtio-event DrachtioRequest(callSid=5ff6a736-d41a-4843-863c-08584a4d509d, callId=3775fd62-ce8f-1238-4e96-02996c8b2a60, parentCallSid=41770ea3-c431-4237-ac6e-597c74b99ff7, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=447956697540, [email protected], callStatus=trying, sipStatus=100, direction=outbound, callerName=null, digits=null, speech=null, customerData=null, dialCallSid=null, dialCallStatus=null)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-3",
"level": "INFO",
"level_value": 20000,
"pid": "41770ea3-c431-4237-ac6e-597c74b99ff7",
"from": "447956697540",
"to": "[email protected]",
"cid": "5ff6a736-d41a-4843-863c-08584a4d509d"
},
{
"@timestamp": "2020-02-20T14:22:08.105+00:00",
"@version": "1",
"message": "drachtio-event DrachtioRequest(callSid=5ff6a736-d41a-4843-863c-08584a4d509d, callId=3775fd62-ce8f-1238-4e96-02996c8b2a60, parentCallSid=41770ea3-c431-4237-ac6e-597c74b99ff7, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=447956697540, [email protected], callStatus=ringing, sipStatus=180, direction=outbound, callerName=null, digits=null, speech=null, customerData=null, dialCallSid=null, dialCallStatus=null)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-6",
"level": "INFO",
"level_value": 20000,
"pid": "41770ea3-c431-4237-ac6e-597c74b99ff7",
"from": "447956697540",
"to": "[email protected]",
"cid": "5ff6a736-d41a-4843-863c-08584a4d509d"
},
{
"@timestamp": "2020-02-20T14:22:10.008+00:00",
"@version": "1",
"message": "drachtio-request DrachtioRequest(callSid=41770ea3-c431-4237-ac6e-597c74b99ff7, callId=372bdc56-ce8f-1238-fcb3-02b574cefade, parentCallSid=null, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=+447956697540, to=+442033885380, callStatus=trying, sipStatus=100, direction=inbound, callerName=null, digits=null, speech=null, customerData={port=5060, ip=99.80.180.248, privacy=false, pAsserted=+447956697540}, dialCallSid=5ff6a736-d41a-4843-863c-08584a4d509d, dialCallStatus=failed)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-10",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
},
{
"@timestamp": "2020-02-20T14:22:10.084+00:00",
"@version": "1",
"message": "drachtio-request DrachtioRequest(callSid=41770ea3-c431-4237-ac6e-597c74b99ff7, callId=372bdc56-ce8f-1238-fcb3-02b574cefade, parentCallSid=null, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=+447956697540, to=+442033885380, callStatus=trying, sipStatus=100, direction=inbound, callerName=null, digits=null, speech=null, customerData={port=5060, ip=99.80.180.248, privacy=false, pAsserted=+447956697540}, dialCallSid=5ff6a736-d41a-4843-863c-08584a4d509d, dialCallStatus=failed)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-10",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
},
{
"@timestamp": "2020-02-20T14:22:10.095+00:00",
"@version": "1",
"message": "drachtio-event DrachtioRequest(callSid=5ff6a736-d41a-4843-863c-08584a4d509d, callId=3775fd62-ce8f-1238-4e96-02996c8b2a60, parentCallSid=41770ea3-c431-4237-ac6e-597c74b99ff7, accountSid=fef61e75-cec3-496c-a7bc-8368e4d02a04, from=447956697540, [email protected], callStatus=failed, sipStatus=480, direction=outbound, callerName=null, digits=null, speech=null, customerData=null, dialCallSid=null, dialCallStatus=null)",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-9",
"level": "INFO",
"level_value": 20000,
"pid": "41770ea3-c431-4237-ac6e-597c74b99ff7",
"from": "447956697540",
"to": "[email protected]",
"cid": "5ff6a736-d41a-4843-863c-08584a4d509d"
},
{
"@timestamp": "2020-02-20T14:22:10.192+00:00",
"@version": "1",
"message": "drachtio-response [Verb(gather=null, say=null, play=null, reject=Reject(status=480, reason=Temporary Unavailable, headers=null), hangup=null, redirect=null, dial=null, tag=null)]",
"logger_name": "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name": "http-nio-8080-exec-10",
"level": "INFO",
"level_value": 20000,
"from": "+447956697540",
"to": "+442033885380",
"cid": "41770ea3-c431-4237-ac6e-597c74b99ff7"
}
]
Seeing this at the end of a call:
[2021-11-22 13:09:28.410 +0000] INFO (7952 on ip-172-32-32-147): updateCallStatus result: Error: node_redis: The HMSET command contains a invalid argument type.
Only strings, dates and buffers are accepted. Please update your code to use valid argument types.,1
When the "to" field is a Teams object and error is thrown
1|jambonz-api-server | {"level":30, "time": "2020-09-30T09:39:57.285Z","pid":10277,"hostname":"ip-172-31-22-10","msg":"invalid client request","stack":"Error: missing or invalid to property: {\"type\":\"teams\",\"number\":\"+441273652002\",\"tenant\":\"5f51637ce390c45fbc95ace6.customers.viva-eu.net\",\"vmail\":false}\n at validateTo (/home/admin/apps/jambonz-api-server/lib/routes/api/accounts.js:78:9)\n at validateCreateCall (/home/admin/apps/jambonz-api-server/lib/routes/api/accounts.js:88:3)\n at router.post (/home/admin/apps/jambonz-api-server/lib/routes/api/accounts.js:276:11)\n at process._tickCallback (internal/process/next_tick.js:68:7)","type":"Error","v":1}
When the verb dial is finished, if the call is still in progress with other verbs, you will notice that the audio file specified with the dialMusic attribute of the verb dial continues to be played.
To reproduce the bug, you have to reply to the call invitation with the following verbs for example:
[
{
"verb": "dial",
"dialMusic": "http://path_to_audio_file_1",
"timeout": x
...
},
{
"verb": "play",
"url": "http://path_to_audio_file_2"
}
{
"verb": "listen",
"url": "wss://path_to_recorder_server"
}
]
After the specified timeout you will notice that the file (http://path_to_audio_file_1) continues to be played even though the parent dial is finished
Not an issue but here for discussion:
When reading from a central DB/Cache we run the risk of a service affecting outage. Be it MySQL Aurora or Redis Global DS etc. An ideal scenario would be to able to continue call routing even in the event of a MySQL issue, or even a Redis issue.
MySQL being fairly static data could be written to a cache which would mitigate a MySQL outage and also improve performance as Jambones is heavy on reads. Another option could be a local MySQL and replication but this could bring extra overhead and complications?
Redis is a little more difficult as Jambones uses it for more realtime data, If we have a redis issue, then its pretty much game over, however another option would be to replace Redis with IMDG (In memory datagrid) such as Hazelcast, Each FS,SBC & RTP Server would run a Hazelcast instance which joins and become part of a distributed data grid with no spf. As the Jambones cluster grows and shrinks so does the IMDG. Out of box you get Distributed locking, Nearside Cache, Queues, Topics etc. All traffic is local network so any external outage would not affect the running of the cache, each nodes data is backed up on other nodes so you can lose instances and keep data integrity.
For multiple Jambones clusters which are geographically distributed, instead of AWS Redis Global Datastore, AWS SNS could be used to keep distributed Hazelcast clusters in sync with each other such as SIP Client's AOR, Queue status etc.
{"@timestamp":"2020-09-18T09:36:36.135+00:00","@version":"1","message":"Response From IVR","logger_name":"uk.co.viva.vdrachtioservice.services.RouteService","thread_name":"http-nio-8080-exec-7","level":"INFO","level_value":20000,"from":"+447956697540","to":"02033882120","cid":"7f6b4253-2dcf-47af-ac75-ab8ea1085e19","responseFromIVR":[{"dial":{"actionHook":"/ivr/987a7ccd-0b49-4363-9157-7b5944f19bc0","callerId":"+447956697540","dialMusic":"https://vibe-public.s3-eu-west-1.amazonaws.com/en-gb.wav","timeLimit":14400,"timeout":180,"answerOnBridge":true,"target":[{"type":"teams","number":"+442033882121","tenant":"5f51637ce390c45fbc95ace6.customers.viva-uk.net","vmail":false},{"type":"teams","number":"+442033882122","tenant":"5f51637ce390c45fbc95ace6.customers.viva-uk.net","vmail":false}],"listen":{"url":"/record","mixType":"stereo","finishOnKey":"#","maxLength":14400,"metadata":{},"passDtmf":true,"playBeep":false,"sampleRate":8000},"dtmfCapture":["*06","*07","*08","*09","*01","*02","*03","*04","*05"],"dtmfHook":"/ivr/dtmf","headers":{}}}]}
where we have multiple targets, it seems that only the first target is initiating an INVITE
[{"type":"teams","number":"+442033882121","tenant":"5f51637ce390c45fbc95ace6.customers.viva-uk.net","vmail":false},{"type":"teams","number":"+442033882122","tenant":"5f51637ce390c45fbc95ace6.customers.viva-uk.net","vmail":false}]
I checked the drachtio logs and cant see any INVITE for +442033882122
If I do a dial
verb, there is a timeout
limit that can be set, but there is no such option when starting a call.
It would be useful to add, also because cancelling calls #94 is not straightforward.
When a user creates a call through REST, and supplies a 'call_hook' property rather than 'application_sid', a listen verb that uses a relative url fails with the following error
{"level":20,"time":1580832859316,"pid":15381,"hostname":"ip-172-31-3-33","callId":"3be80dce-c20c-1238-6185-06d91d68c9b0","callSid":"3b7607ec-12e9-4c36-b582-e816593ca14b","action":"/ivr/973e4b02-bc61-4fc0-8cdb-3ce4fa4df8d1","callerId":"447956697540","dialMusic":"https://vibe-public.s3-eu-west-1.amazonaws.com/en-gb.wav","timeLimit":14400,"timeout":30,"answerOnBridge":true,"target":[{"type":"user","name":"ajukes"},{"type":"user","name":"daveh"},{"type":"user","name":"brandon"}],"listen":{"url":"/record","mixType":"stereo","finishOnKey":"#","maxLength":14400,"metadata":{"customerId":null,"parentCallSid":"3b7607ec-12e9-4c36-b582-e816593ca14b"},"passDtmf":true,"playBeep":false,"sampleRate":8000},"msg":"makeTask: dial","v":1}
2|jambonz-feature-server | {"level":20,"time":1580832859317,"pid":15381,"hostname":"ip-172-31-3-33","callId":"3be80dce-c20c-1238-6185-06d91d68c9b0","callSid":"3b7607ec-12e9-4c36-b582-e816593ca14b","url":"/record","mixType":"stereo","finishOnKey":"#","maxLength":14400,"metadata":{"customerId":null,"parentCallSid":"3b7607ec-12e9-4c36-b582-e816593ca14b"},"passDtmf":true,"playBeep":false,"sampleRate":8000,"msg":"makeTask: listen","v":1}
2|jambonz-feature-server | TypeError: Cannot read property 'originalRequest' of undefined
2|jambonz-feature-server | at TaskListen.normalizeUrl (/home/deploy/jambonz-feature-server/lib/tasks/task.js:64:35)
2|jambonz-feature-server | at new TaskListen (/home/deploy/jambonz-feature-server/lib/tasks/listen.js:19:22)
2|jambonz-feature-server | at makeTask (/home/deploy/jambonz-feature-server/lib/tasks/make_task.js:41:14)
2|jambonz-feature-server | at new TaskDial (/home/deploy/jambonz-feature-server/lib/tasks/dial.js:59:25)
2|jambonz-feature-server | at makeTask (/home/deploy/jambonz-feature-server/lib/tasks/make_task.js:23:14)
2|jambonz-feature-server | at normalizeJamones.map (/home/deploy/jambonz-feature-server/lib/utils/retrieve-app.js:28:56)
2|jambonz-feature-server | at Array.map (<anonymous>)
2|jambonz-feature-server | at retrieveApp (/home/deploy/jambonz-feature-server/lib/utils/retrieve-app.js:28:41)
2|jambonz-feature-server | at Request.request [as _callback] (/home/deploy/jambonz-feature-server/lib/utils/notifiers.js:35:26)
2|jambonz-feature-server | at Request.self.callback (/home/deploy/jambonz-feature-server/node_modules/request/request.js:185:22)
2|jambonz-feature-server |
Currently, it is possible to send an excessive number of play_audio requests over a listen socket. These should be rate-limited to at most n queued requests
Subsequent Request
"drachtio-request" : {
"callSid" : "5e9c92b6-f18d-4106-a47f-f20be61e82bc",
"callId" : "5863ec22-c904-1238-6185-06d91d68c9b0",
"parentCallSid" : null,
"accountSid" : "fef61e75-cec3-496c-a7bc-8368e4d02a04",
"from" : "+447956697540",
"to" : "+442033885379",
"callStatus" : "in-progress",
"sipStatus" : "200",
"direction" : "inbound",
"callerName" : null,
"digits" : "1",
"speech" : null,
"customerData" : null
}
Call Status Events are ok and still carry the customerData payload
"ivr-event" : {
"callSid" : "5e9c92b6-f18d-4106-a47f-f20be61e82bc",
"callId" : "5863ec22-c904-1238-6185-06d91d68c9b0",
"parentCallSid" : null,
"accountSid" : "fef61e75-cec3-496c-a7bc-8368e4d02a04",
"from" : "+447956697540",
"to" : "+442033885379",
"callStatus" : "in-progress",
"sipStatus" : "200",
"direction" : "inbound",
"callerName" : null,
"digits" : null,
"speech" : null,
"customerData" : {
"port" : "5060",
"ip" : "99.80.180.248",
"privacy" : "false",
"pAsserted" : "+447956697540"
}
}
Provide ability to pass a new parameter queryParams
with the dialogflow
verb, so that they get passed to the initial call to StreamingDetectIntentRequest.
These parameters are needed for the fulfillment webhook, so that it has more context about what to do and which call it is related to.
Initial inbound calls web hook includes invite details in payload.
It would be helpful to include outbound INVITE details on the call_hook for and outbound api call
Not sure if its a bug or just the way its got to be but it takes 10+ seconds for my app to close the websocket connection.
Can you confirm that you close the socket after the listen verb ends?
Hi,
I tried to implement reconnection logic for the websocket conection, however during testing, I noticed that the reconnection itself works, upon losing the WS connection, Jambonz instantly recreates it. However to be able to identify this new WS connection I expected to get the session:reconnect
event, which is never emitted by Jambonz. Instead upon reconnecting the WS connection, the flow just continues as nothing happened before (which is good :)), however since I didn't get the initial session reconnection event, I am not able to match the new connection to any existing call state on my end.
I have attached an example WebSocket server code with a lot of logs which can be used to test the reconnection logic.
It will kill the ws connection after 5 seconds which will trigger a reconnect on Jambonz side.
I can see that the connection is recreated, since I am getting a vebr:hook events on the new connection. However as mentioned before, session:reconnect never reaches my end.
Example code which will send commands back and forth in a loop over the WS connection:
const express = require("express");
const WebSocket = require("ws");
const { WsSession } = require("@jambonz/node-client");
let wsCounter = 0;
const jambonzRedirectLoop = [{say: {
text: "silence_stream://1000"
}}, {
"verb": "redirect",
"actionHook": "/test",
}];
const start = () => {
const app = express();
const port = 8000;
const server = app.listen(port, ()=>{
console.log("server listening at port", port);
});
const websocketServer = new WebSocket.Server({
noServer: true,
path: "/websockets",
});
server.on("upgrade", (request, socket, head) => {
console.log(wsCounter, "received upgrade request", );
websocketServer.handleUpgrade(request, socket, head, (websocket) => {
websocket.counter = wsCounter;
wsCounter += 1;
websocketServer.emit("connection", websocket, request);
});
});
websocketServer.on(
"connection",
function connection(websocketConnection, connectionRequest) {
console.log(websocketConnection.counter, "received ws connection");
// we need to do this for monkeypatchin WS connection instance
// no reference needed for later use
new WsSession({
logger: { info: () => { }, error: () => { }, debug: () => { } },
router: { route: () => true },
ws: websocketConnection,
req: connectionRequest
});
websocketConnection.on("session:new", (data)=>{
console.log(websocketConnection.counter, "received new session", data);
const { msgid } = data;
websocketConnection.ack(msgid, jambonzRedirectLoop);
});
websocketConnection.on("session:reconnect", (data)=>{
console.log(websocketConnection.counter, "received reconnect session", data);
const { msgid } = data;
websocketConnection.ack(msgid, jambonzRedirectLoop);
});
websocketConnection.on("message", (message) => {
const parsedMessage = JSON.parse(message);
console.log(websocketConnection.counter, "received ws message", parsedMessage);
});
websocketConnection.on("close", (data) => console.log(websocketConnection.counter, "ws close", data));
websocketConnection.on("error", (data) => console.log(websocketConnection.counter, "ws error", data));
websocketConnection.on("verb:hook", (data) => {
console.log(websocketConnection.counter, "verb:hook", data);
const { hook, msgid } = data;
websocketConnection.ack(msgid, jambonzRedirectLoop);
});
websocketConnection.on("call:status", (data) => console.log(websocketConnection.counter, "call:status", data));
setTimeout(()=>{
websocketConnection.close();
websocketConnection.terminate();
}, 5000);
});
}
start();
When the timeout value expires on a rest outdial, a CANCEL request is not being sent.
Add the ability to pass an event webhook for the listen verb and to trigger calls to it from the websocket connection.
Hi,
when trying to use a unsecured websocket connection, jambonz-feature-server throws an error that the protocol is not supported, is this expected?
10|jambonz-feature-server | {"level":30, "time": "2022-04-29T08:25:23.236Z","pid":6353,"hostname":"ip-10-0-19-254","callId":"c168a0f1-4238-123b-8f94-060fa1c98f14","callSid":"36eaac50-4e86-454c-9caa-083a104bc9df","accountSid":"9351f46a-678c-43f5-b8a6-d4eb58d131af","callingNumber":"+17812856404","calledNumber":"+441603361413","traceId":"6430db61b090927f85cae6a29a05f75e","url":"ws://grauen-endpoint.eu.ngrok.io/284567ef77424421d9ae479b43df8be2ab68a2fa397ee62868b233eaca2d15a4/voiceGateway","err":{"type":"SyntaxError","message":"The URL's protocol must be one of \"ws:\", \"wss:\", or \"ws+unix:\"","stack":"SyntaxError: The URL's protocol must be one of \"ws:\", \"wss:\", or \"ws+unix:\"\n at initAsClient (/home/admin/apps/jambonz-feature-server/node_modules/ws/lib/websocket.js:692:17)\n at ClientRequest.<anonymous> (/home/admin/apps/jambonz-feature-server/node_modules/ws/lib/websocket.js:850:7)\n at ClientRequest.emit (events.js:400:28)\n at ClientRequest.emit (domain.js:475:12)\n at HTTPParser.parserOnIncomingClient (_http_client.js:647:27)\n at HTTPParser.parserOnHeadersComplete (_http_common.js:127:17)\n at Socket.socketOnData (_http_client.js:515:22)\n at Socket.emit (events.js:400:28)\n at Socket.emit (domain.js:475:12)\n at addChunk (internal/streams/readable.js:293:12)"},"msg":"WsRequestor:request - failed connecting"}
The same URI works fine with a wss://
in front.
Hi,
when sending commands such as redirect via the websocket connection instead of using webhooks as app, it's is kind of complicated to debug them. Especially when queuing commands, its hard to debug them. It would be great to have a feedback look so when we are sending commands to feature server it would provide us some feedback.
My proposal would be the following:
Each command send should contain a id. So for example if we send a list of commands via the redirect command, the payload would look like this:
{
type: "command",
command: "redirect",
queueCommand: true,
data: [
{
gather: {
input: ["speech"],
listenDuringPrompt: false,
bargein: false,
minBargeinWordCount: 0,
dtmfBargein: false,
minDigits: 1,
interDigitTimeout: 0,
recognizer: {
vendor: "default",
language: "default",
vad: { enable: true, mode: 0 }
},
actionHook: "voice",
timeout: 10
},
verbid: "53edc152-3221-475b-8d61-6da7ea3a668c"
}
],
cmdid: "53edc152-3221-475b-8d61-6da7e13a668c"
}
Then if its a redirect with quuing commands. Once the commmand was successfully executed, we get a confirmation back on the ws with the following
{
cmdid : "53edc152-3221-475b-8d61-6da7ea3a668c",
status: "SUCCESSS" | "ERROR",
error: "in case of error send us the error object here (ideally jambonz should have an own one with some general error codes)"
metrics: "we could also think of adding some metrics here such as receivedOnTime, executionTime, queueTime," etc
}
When each verb is executed, FS should send us a confirmation on this as well. So we would know what to do.
{
cmdid : "53edc152-3221-475b-8d61-6da7ea3a668c",
status: "SUCCESSS" | "ABORTED" | "ERROR",
error: "in case of error send us the error object here (ideallyj ambonz should have an own one with some general error codes)"
metrics: "we could also think of adding some metrics here such as receivedOnTime, executionTime, queueTime," etc
}
This would really help debugging and fixing issues faster and also identify wrong send verb configurations without the need of going into the feature-server logs and debugging the error messages there.
Also it would allow us to improve the integration between jambonz and other systems and make them more fault tolerant. For example in a use case where we have jambonz connected with a bot system, if something is not working right and the bot system sends some commands to JB which is not able to execute, or something within jambonz changed so the previously working commands are not valid, we could catch this on the bot side and redirect the caller to an escalation number so the caller is not left without any help but is still helped by some humans.
What do you think about this idea?
I am happy to provide a PR with a first implementation iteration if you think this is something beneficial for the feature server
It looks like the confirmHook
is not an accepted parameter to dequeue
and will actually trigger an error.
{
"beep": true,
"confirmHook": "/jambonz/01/hook/QAgentConnect?acd_queue=10&acd_room=99c11e62-f967-41ad-93fa-10796120d5c2",
"name": "10-99c11e62-f967-41ad-93fa-10796120d5c2",
"timeout": 5,
"verb": "dequeue"
}
I had a look at the source and the only reference to confirmHook is in a TODO:
jambonz-feature-server/lib/tasks/dequeue.js
Line 108 in 8c5cdd3
But it is still documented, so it should be removed from the docs until implemented.
When Listen verb has relative url. for example /record. Jambones throws the following error.
2|jambonz-feature-server | TypeError: Cannot read property 'originalRequest' of undefined
2|jambonz-feature-server | at TaskListen.normalizeUrl (/home/deploy/jambonz-feature-server/lib/tasks/task.js:64:35)
2|jambonz-feature-server | at new TaskListen (/home/deploy/jambonz-feature-server/lib/tasks/listen.js:19:22)
2|jambonz-feature-server | at makeTask (/home/deploy/jambonz-feature-server/lib/tasks/make_task.js:41:14)
2|jambonz-feature-server | at new TaskDial (/home/deploy/jambonz-feature-server/lib/tasks/dial.js:59:25)
2|jambonz-feature-server | at makeTask (/home/deploy/jambonz-feature-server/lib/tasks/make_task.js:23:14)
2|jambonz-feature-server | at normalizeJamones.map (/home/deploy/jambonz-feature-server/lib/utils/retrieve-app.js:28:56)
2|jambonz-feature-server | at Array.map (<anonymous>)
2|jambonz-feature-server | at retrieveApp (/home/deploy/jambonz-feature-server/lib/utils/retrieve-app.js:28:41)
2|jambonz-feature-server | at Request.request [as _callback] (/home/deploy/jambonz-feature-server/lib/utils/notifiers.js:35:26)
2|jambonz-feature-server | at Request.self.callback (/home/deploy/jambonz-feature-server/node_modules/request/request.js:185:22)
I have tried secured and unsecured ws endpoints
Currently there is no way of defining custom sip headers on a specific target. This would be useful when we are dialing multiple targets of different types. For example a dial with 2x Targets
{
"verb": "dial",
"headers" : {
"X-Header-One" : "1234"
},
"target": [
{
"type": "sip",
"sipUri": "sip:[email protected]",
"headers" : {
"X-Header-One" : "2468"
}
},
{
"type": "sip",
"sipUri": "sip:[email protected]",
"headers" : {
"X-Header-Two" : "abcd"
}
},
{
"type": "sip",
"sipUri": "sip:[email protected]"
},
{
"type": "sip",
"sipUri": "sip:[email protected]",
"headers" : {}
}
]
}
I think the simplest way would be if target headers is present then ignore dial headers. The other way would be to merge the two objects with target keys taking precedence. The problem with the latter approach is that there is no way to clear custom headers for a particular target.
WIth the above example and the first option, I would expect to see the following customer headers:
sip111 = "X-Header-One" : "2468"
sip222 = "X-Header-Two" : "abcd"
sip333 = "X-Header-One" : "1234"
sip444 = ""
When Target is not Carrier IP it routes to Carrier instead of intended destination causing a loop.
{
"@timestamp" : "2020-02-06T08:31:10.231+00:00",
"@version" : "1",
"message" : "response",
"logger_name" : "uk.co.viva.vdrachtioservice.controllers.IvrController",
"thread_name" : "http-nio-8080-exec-1",
"level" : "INFO",
"level_value" : 20000,
"activemq.broker" : "localhost",
"drachtio-response" : [ {
"tag" : {
"data" : {
"port" : "5060",
"ip" : "99.80.180.248",
"privacy" : "false",
"pAsserted" : "+447956697540"
}
}
}, {
"dial" : {
"action" : "/ivr/e672997e-9401-46e8-930f-0b8d9ffab60c",
"callerId" : "447956697540",
"dialMusic" : "https://vibe-public.s3-eu-west-1.amazonaws.com/en-gb.wav",
"timeLimit" : 14400,
"timeout" : 60,
"answerOnBridge" : true,
"target" : [ {
"type" : "sip",
"sipUri" : "sip:[email protected]:5060"
} ]
}
} ]
}
Actual INVITE seen was to sip:[email protected]:5060
I’ve been investigating a bit on the synth options available on Jambonz and I’ve realized that it’s not possible to use any neural voices (https://docs.aws.amazon.com/polly/latest/dg/ntts-voices-main.html) from AWS as there is no way to provide the engine parameter to the say action: https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html. By default this parameter is set to standard if none is provided and I’ve checked that it’s not added on the payload:
const synthPolly = (stats, language, voice, text) => {
const polly = new Polly();
const opts = {
OutputFormat: 'mp3',
Text: text,
LanguageCode: language,
TextType: text.startsWith('') ? 'ssml' : 'text',
VoiceId: voice
};
[...]
When INVITE contains no suitable codec, Feature server throws error with 488 Not Acceptable Here
but instead of returning a 488 back to the SBC, it returns a 603.
It would be nice to have the IP address of the entity trying to register, so that one could implement an APIBAN-style protection.
Now we only get:
{
"method": "REGISTER",
"expires": 3600,
"scheme": "digest",
"username": "xxxxx",
"realm": "xxxx.sip.jambonz.us",
"nonce": "164882756524300",
"uri": "sip:xxx.sip.jambonz.us",
"algorithm": "MD5",
"response": "ee14e3daf59ccb3fa80d3e133fbbb861",
"qop": "auth",
"cnonce": "08911326",
"nc": "00004676"
}
Even better it would be to have the "headers" struct where you have it all, like when a call is started.
"sip": {
"headers": {
"via": "SIP/2.0/UDP 52.55.111.178;rport=5060;branch=z9hG4bK3c113XmS7gcvg;received=xxxx",
"max-forwards": "70",
"from": "<sip:31@xxx:5060>;tag=29132U3yS6SZa",
"to": "<sip:[email protected]>",
"call-id": "80d9b6b1-31f3-123b-63a3-0ea24be4d211",
"X-Forwarded-For": "23.88.37.9",
"X-Authenticated-User": "....sip.jambonz.us"
.....
In the below scenario, the FROM field passed to application is incorrect
"from": "<sip:[email protected]:5060>;tag=eN6Fy272e7NvN"
should be sent as {"from" :"07962564063"}
but is incorrectly sent as {"from" :"01273202020"}
which is the PAID
POST https://drachtio-service.viva-uk.net/ivr/start
callId: "c934a2ab-f2ae-1238-f6ae-062ecac8df46"
callSid: "b6ae2aea-b85f-4238-ab3a-255dd354fbc2"
hook: {
"webhook_sid": "faebf58e-f84f-4438-82ff-3dee16a6f184",
"url": "https://drachtio-service.viva-uk.net/ivr/start",
"method": "POST",
"username": "drachtio",
"password": "3298fn238hf9n823hf93h24398gj93n84h"
}
params: {
"sip": {
"headers": {
"via": "SIP/2.0/UDP 35.179.41.104;rport=5060;branch=z9hG4bK64FjjKv3Nrgvg;received=172.31.51.15",
"max-forwards": "70",
"from": "<sip:[email protected]:5060>;tag=eN6Fy272e7NvN",
"to": "<sip:[email protected]>",
"call-id": "c934a2ab-f2ae-1238-f6ae-062ecac8df46",
"cseq": "18536372 INVITE",
"contact": "<sip:35.179.41.104:5060>",
"user-agent": "Orchid 3.1.7.2",
"allow": "BYE, CANCEL, ACK, INVITE, INFO, OPTIONS",
"supported": "timer, replaces",
"session-expires": "1800;refresher=uas",
"min-se": "90",
"content-type": "application/sdp",
"content-length": "257",
"X-CID": "263b84a5-282626ca-4dbde7-7fb2f41ca130-de641fac-13e2-7225",
"X-Forwarded-For": "99.80.180.248:5060",
"X-Originating-Carrier": "customers",
"X-Application-Sid": "99eca0c0-194a-4c66-926d-b62f3cedd948",
"p-asserted-identity": "<sip:[email protected];user=phone>"
},
"body": "v=0\r\no=- 246575 46575 IN IP4 3.10.35.78\r\ns=Cataleya\r\nc=IN IP4 3.10.35.78\r\nt=0 0\r\nm=audio 42772 RTP/AVP 8 18 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:18 annexb=no\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:42773\r\n",
"payload": [
{
"type": "application/sdp",
"content": "v=0\r\no=- 246575 46575 IN IP4 3.10.35.78\r\ns=Cataleya\r\nc=IN IP4 3.10.35.78\r\nt=0 0\r\nm=audio 42772 RTP/AVP 8 18 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:18 annexb=no\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:42773\r\n"
}
],
"method": "INVITE",
"version": "2.0",
"uri": "sip:[email protected]",
"raw": "INVITE sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/UDP 35.179.41.104;rport=5060;branch=z9hG4bK64FjjKv3Nrgvg;received=172.31.51.15\r\nMax-Forwards: 70\r\nFrom: <sip:[email protected]:5060>;tag=eN6Fy272e7NvN\r\nTo: <sip:[email protected]>\r\nCall-ID: c934a2ab-f2ae-1238-f6ae-062ecac8df46\r\nCSeq: 18536372 INVITE\r\nContact: <sip:35.179.41.104:5060>\r\nUser-Agent: Orchid 3.1.7.2\r\nAllow: BYE, CANCEL, ACK, INVITE, INFO, OPTIONS\r\nSupported: timer, replaces\r\nSession-Expires: 1800;refresher=uas\r\nMin-SE: 90\r\nContent-Type: application/sdp\r\nContent-Length: 257\r\nX-CID: 263b84a5-282626ca-4dbde7-7fb2f41ca130-de641fac-13e2-7225\r\nX-Forwarded-For: 99.80.180.248:5060\r\nX-Originating-Carrier: customers\r\nX-Application-Sid: 99eca0c0-194a-4c66-926d-b62f3cedd948\r\nP-Asserted-Identity: <sip:[email protected];user=phone>\r\n\r\nv=0\r\no=- 246575 46575 IN IP4 3.10.35.78\r\ns=Cataleya\r\nc=IN IP4 3.10.35.78\r\nt=0 0\r\nm=audio 42772 RTP/AVP 8 18 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:18 annexb=no\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:42773\r\n"
},
"direction": "inbound",
"callSid": "b6ae2aea-b85f-4238-ab3a-255dd354fbc2",
"accountSid": "06da1b6b-642d-43e5-953a-0c6ab8296314",
"applicationSid": "99eca0c0-194a-4c66-926d-b62f3cedd948",
"from": "01273202020",
"to": "01273202020",
"callerName": "01273202020",
"callId": "c934a2ab-f2ae-1238-f6ae-062ecac8df46",
"sipStatus": 100,
"callStatus": "trying",
"originatingSipIp": "99.80.180.248:5060",
"originatingSipTrunkName": "customers"
}
The following targets cause jambones to ignore sip uri and route to teams tenant.
[{"type":"sip","sipUri":"sip:[email protected]:5060"},{"type":"teams","number":"+441582363701","tenant":"2cc-demo-a.customers.viva-uk.net","vmail":false}]
SIP URI Target INVITE from FS => SBC
INVITE sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 172.31.23.236;rport;branch=z9hG4bK1vp6eepQ2DpNe
Max-Forwards: 70
From: <sip:[email protected]:5060>;tag=r6NpQXv02S5mB
To: <sip:[email protected]:5060>
Call-ID: 6b960d47-7687-1239-5993-0a90b3ff9f3c
CSeq: 25784695 INVITE
Contact: <sip:[email protected]:5060>
Content-Type: application/sdp
Content-Length: 469
X-CID: 6bb8764b-5c5d9e78-12af16d-7fb2f34ba1f8-de641fac-13e2-7225
X-MS-Teams-FQDN: customers.viva-uk.net
X-MS-Teams-Tenant-FQDN: 2cc-demo-a.customers.viva-uk.net
P-Asserted-Identity: "+442033882105" <sip:[email protected]:5060>
v=0
o=VIVA 1600645780 1600645781 IN IP4 172.31.23.236
s=VIVA
c=IN IP4 172.31.23.236
t=0 0
m=audio 31194 RTP/AVP 0 8 102 9 101 103
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:102 opus/48000/2
a=fmtp:102 useinbandfec=1; maxaveragebitrate=30000; maxplaybackrate=48000; ptime=20; minptime=10; maxptime=40
a=rtpmap:9 G722/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=rtpmap:103 telephone-event/48000
a=fmtp:103 0-16
a=ptime:20
a=sendrecv
we got an alert that "tts credentials for aws have not been provisioned" but the actual error in the logs was:
'es-ES-Lucia' at 'voiceId' failed to satisfy constraint: Member must satisfy enum value set: [Nicole, Kevin, Enrique, Tatyana, Russell, Olivia, Lotte, Geraint, Carmen, Ayanda, Mads, Penelope, Mia, Joanna, Matthew, Brian, Seoyeon, Ruben, Ricardo, Maxim, Lea, Giorgio, Carla, Aria, Naja, Maja, Astrid, Ivy, Kimberly, Chantal, Amy, Vicki, Marlene, Ewa, Conchita, Camila, Karl, Zeina, Miguel, Mathieu, Justin, Lucia, Jacek, Bianca, Takumi, Ines, Gwyneth, Cristiano, Mizuki, Celine, Zhiyu, Jan, Liv, Joey, Raveena, Filiz, Dora, Salli, Aditi, Vitoria, Emma, Lupe, Hans, Kendra, Gabrielle]
dialMusic is ignored if the A leg is not an inbound leg - We receive no ringback tone when A leg answers from API call as the A Leg.
[2020-04-15 08:51:02.086 +0000] DEBUG (31802 on ip-172-31-50-11): normalizeJambones: returning document with 1 tasks
document: [
{
"dial": {
"actionHook": "/ivr/hangup",
"callerId": "442033880222",
"dialMusic": "https://vibe-public.s3-eu-west-1.amazonaws.com/en-gb.wav",
"timeLimit": 14400,
"timeout": 180,
"answerOnBridge": true,
"target": [
{
"type": "sip",
"sipUri": "sip:[email protected]:5060"
}
],
"listen": {
"url": "/record",
"mixType": "stereo",
"finishOnKey": "#",
"maxLength": 14400,
"metadata": {},
"passDtmf": true,
"playBeep": false,
"sampleRate": 8000
},
"dtmfCapture": [
"*06",
"*07",
"*08",
"*09",
"*01",
"*02",
"*03",
"*04",
"*05"
],
"dtmfHook": "/ivr/dtmf"
}
}
]
Similar to #54
As per the below, Azure does not return the NBest alternatives in order of confidence decreasing, which causes an issue since in the gather verb we take the first entry in the array, e.g.
{
"Id": "9c6a879181ee4af9801993ca562ff6d7",
"RecognitionStatus": "Success",
"Offset": 106700000,
"Duration": 8300000,
"DisplayText": "",
"NBest": [{
"Confidence": 0.0,
"Lexical": "",
"ITN": "",
"MaskedITN": "",
"Display": ""
}, {
"Confidence": 0.86060256,
"Lexical": "un",
"ITN": "un",
"MaskedITN": "un",
"Display": "un"
}, {
"Confidence": 0.7017915,
"Lexical": "nou",
"ITN": "nou",
"MaskedITN": "nou",
"Display": "nou"
}, {
"Confidence": 0.6646924,
"Lexical": "set",
"ITN": "set",
"MaskedITN": "set",
"Display": "set"
}, {
"Confidence": 0.6564128,
"Lexical": "cinc",
"ITN": "cinc",
"MaskedITN": "cinc",
"Display": "cinc"
}]
}
When running the feature server inside of docker container by itself I receive an error with several files
related to the uuid package. The latest version of uuid does not support Deep Requires and the require
has changed. I have forked the repo and have fixed on my end by replacing the requires.
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './v4' is not defined by "exports" in /usr/src/app/node_modules/uuid/package.json
I can submit a pull request for this.
When returning webhook response like
[{"verb“:“gather“,“actionHook“:“https://my.webhook.com“,“finishOnKey“:“#",“timeout“:30,“input“:[“digits“]}]
Jambonz is expecting there to be STT credentials configured (defaulting to Google):
"TaskGather:exec - ERROR stt using google requested but not creds supplied"
When Jambones ends call instead of called party on outbound api call, the app never receives completed event.
Example API Call
curl -X POST "http://3.10.235.99:3000/v1/Accounts/fef61e75-cec3-496c-a7bc-8368e4d02a04/Calls" -H "accept: application/json" -H "Authorization: Bearer 9604e5f7-9a77-4bcc-b0fa-5665ace28ab3" -H "Content-Type: application/json" -d "{\"from\":\"+442033885379\",\"to\":{\"type\":\"sip\",\"sipUri\":\"sip:[email protected]:5060\"},\"timeout\":10,\"application_sid\":null,\"call_hook\":{\"url\":\"http://drachtio-service-default.viva-api.net/ivr/dml/W3sic2F5Ijp7InRleHQiOiJPb29vaCBFcnJycnIsIE1ycyBCaWxsaWkgQm9uZywgVGhpcyBpcyB0aGUgUmluZ2JhY2sgdGVtcGxhdGVZb3VyIHRheGkgaGFzIGFycml2ZWQhIiwibG9vcCI6MSwic3ludGhlc2l6ZXIiOnsidmVuZG9yIjoiZ29vZ2xlIiwidm9pY2UiOiJlbi1HQi1XYXZlbmV0LUEifX19XQ==\",\"method\":\"POST\",\"username\":\"drachtio\",\"password\":\"3298fn238hf9n823hf93h24398gj93n84h\"},\"call_status_hook\":{\"url\":\"http://drachtio-service-default.viva-api.net/ivr/event/api\",\"method\":\"POST\",\"username\":\"drachtio\",\"password\":\"3298fn238hf9n823hf93h24398gj93n84h\"}}"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.