I am having problems with Twilio, while trying to send parameters to a webSocket that is being called from a webhook. I am using: node.js, express, ws (JS websocket library).
The webhook is not the actual problem, since it only calls an endpoint from my end.
But in that endpoint, I am creating a VoiceResponse
instance of Twilio, and using it to connect to a webSocket. In the process, I am trying to send it some parameters, because I want to access the user data (or whatever data) in the webSocket. Only that, this is not happening :))
Tried multiple things. This would be the endpoint:
app.post("/incoming", async (req, res) => {
const ttsService = new TextToSpeechService({
deepgramApiKey: process.env.DEEPGRAM_API_KEY,
model: "aura-asteria-en",
});
const greetingText =
"Hi! You have called Gigi Bianco, my name is Alice, how can I help you?";
const twiml = new VoiceResponse();
ttsService.generate(
{ partialResponseIndex: null, partialResponse: greetingText },
1
);
twiml.say("Welcome, please hold while we connect you.");
const connect = twiml.connect();
const stream = connect.stream({
url: `wss://${process.env.SERVER}/connection?userid=1233`,
});
stream.parameter({ name: "FirstName", value: "Jane" });
stream.parameter({ name: "LastName", value: "Doe" });
res.type("text/xml");
res.send(twiml.toString());
});
And this would be creating the request to the webSocket:
server.on("upgrade", (request, socket, head) => {
socket.on("error", console.error);
// Parse cookies
cookieParser(COOKIE_SECRET)(request, {}, () => {
const req = {
headers: request.headers,
url: request.url,
cookies: request.cookies,
signedCookies: request.signedCookies,
};
console.log("req upgrade", req);
const token = req.signedCookies[TOKEN_KEY];
console.log("Token from signed cookies:", token);
wss.handleUpgrade(request, socket, head, (ws) => {
ws.req = req; // Attach the request to the WebSocket
socket.removeListener("error", console.error);
wss.emit("connection", ws, req);
});
});
});
wss.on("connection", async (ws, req, res) => {
try {
console.log("req connection", req);
ws.path = req.url;
console.log(
`WebSocket connection established for ${req.url} with userId }`
);
} catch () {
.....
}
}
I did put only the necessary information. So this is what I want to achieve:
Right now, I am having problem with the second one.
Another thing I have tried was to console the twiml.toString(), and I get something from it:
<?xml version="1.0" encoding="UTF-8"?><Response><Connect><Stream _propertyName="stream" response="<Response/>" stream="<Stream url="wss://1e00-5-14-143-178.ngrok-free.app/connection/connection?userid=1233"><Parameter name="FirstName" value="Jane"/><Parameter name="LastName" value="Doe"/></Stream>"/></Connect><Say>Welcome, please hold while we connect you.</Say><Start><Stream url="wss://1e00-5-14-143-178.ngrok-free.app/connection/connection?userid=1233"><Parameter name="FirstName" value="Jane"/><Parameter name="LastName" value="Doe"/></Stream></Start></Response>
But that's it. And I do not get anything else in the request. This is what I get, the url that is always "/connection", no matter how many parameters you're trying to add:
req connection {
headers: {
host: '1e00-5-14-143-178.ngrok-free.app',
'user-agent': 'Twilio.TmeWs/1.0',
connection: 'Upgrade',
'sec-websocket-key': 'a+k0PlJzVcfYSQiY2za6ng==',
'sec-websocket-version': '13',
upgrade: 'websocket',
'x-forwarded-for': '34.207.130.200',
'x-forwarded-host': '1e00-5-14-143-178.ngrok-free.app',
'x-forwarded-proto': 'https',
'x-twilio-signature': '1epKcUPY/a9wq/IeS4UiuPUzyyM=',
'accept-encoding': 'gzip'
},
url: '/connection',
cookies: [Object: null prototype] {},
signedCookies: [Object: null prototype] {}
}
I have also tried to put the JWT token in the signedCookies, and then access it in the webSocket, and using that to access the user data. If the call is being made from my end, I mean, from my app, then this works.
But given the fact that the request is made from a webHook in Twilio, I get undefined for that.
If anyone would know a solution for this, to be able to send data inside a webSocket, I would be grateful.
According to the docs, Twilio sends these values to your WebSocket server in the Start message.
So you need to have a part in your code that follows this structure:
wss.on('connection', async (ws) => {
console.log('Twilio media stream WebSocket connected')
ws.on('message', async (message) => {
const msg = JSON.parse(message);
switch (msg.event) {
case 'connected':
console.info('Twilio media stream connected');
break;
case 'start':
console.info('Twilio media stream started and param are not readable');
break;
...
}
});
});