I'm using Twilio to set up an inbound call center. I'm using SIP softphones as my endpoints. I have virtually everything working the way I want it, but there is major issue when trying to start a "Conference" to one of my sip endpoints.
The service rep does not hear the callers words until about 7 seconds into the call. The inbound caller can hear the service rep just fine. This is only a problem with the "Conference." The code below also illustrates a direct connection via dialing an extension where the connection delay does not occur at all.
// Inbound Call to Twilio Number
routes.post('/twilio/phone/inbound', (req, res) => {
const twilioVoice = new VoiceResponse();
const gather = twilioVoice.gather({
action: '/twilio/phone/enqueue',
numDigits: 3,
timeout: 2
});
gather.say('For general inquiries, press 1. If you know the extension of the person you are trying to reach, please enter it now.', {
loop: 5
});
twilioVoice.say('Sorry, I did not get a response. Good bye.');
twilioVoice.hangup();
res.type('text/xml');
res.send(twilioVoice.toString());
});
// Second endpoint to process response from above
routes.post('/twilio/phone/enqueue', (req, res) => {
var pressedKeys = req.body.Digits;
var twilioVoice = new VoiceResponse();
if (pressedKeys.length === 3) {
console.log("Direct call via extension.")
twilioVoice.say(`Please hold while we attempt to connect you to extension`);
twilioVoice.dial().sip('sip:employee#businessname.sip.us1.twilio.com');
// Once connected, both parties can hear each other immediately.
res.send(twilioVoice.toString());
} else {
twilioVoice.enqueue({
workflowSid: process.env.WORKFLOW_SID,
});
res.type('text/xml');
res.send(twilioVoice.toString());
}
});
// Workflow AssignmentCallbackUrl
routes.post('/twilio/phone/assignworker', (req, res) => {
res.type('application/json');
// The service rep does not hear the callers words until about 7 seconds into the call
res.send({
instruction: "conference",
to: "sip:employee#businessname.sip.us1.twilio.com"
});
});
Related
I am simply trying to answer a phone call through the browser using Node.js and Angular.
I can receive phone calls but I am unsure how to answer them. I would like to answer them through the browser. How do redirect them out of "enqueue" and connect them to a live user on the front end? Can anyone help?
Here is where I am at...
FRONT END
receive-call.component.ts
ngOnInit(): void {
// displays all of the data from the incoming caller
this.webSocketService.listen('incoming call').subscribe(res => {
console.log(res)
})
// shows in the front end that there is somebody waiting to talk with a rep
this.webSocketService.listen('enqueue').subscribe(res => {
console.log(res)
this.callSid = this.incomingCall.CallSid
this.callFrom = this.incomingCall.From
console.log(this.incomingCall.From, 'now in enqueue')
})
}
answerCall(){
console.log('answer call button works')
// here is where I want to connect my device and the incoming phone call
}
}
BACK END
server.js
// this is where I point twilio to on an incoming call
app.get('/incoming-call', (req, res) => {
// tell the front end that I have an incoming call
socket.emit('incoming call', req.query)
const twiml = new VoiceResponse();
twiml.say('Thanks for calling.')
// here I redirect the call to go enqueue
twiml.redirect('https://myurl/enque')
res.type('text/xml');
res.send(twiml.toString())
});
app.post('/enque', (req, res) => {
const twiml = new VoiceResponse();
twiml.enqueue({
url: 'Music URL'
}, 'AMG Customer Service')
socket.emit('enqueue')
res.type('text/xml');
res.send(twiml.toString())
});
I am generating data with a node.js simulator and passing this data to a http route /simulator/data
In the application I am listening broker with MQTT mqtthandler.js file which I share below.
//This is mqtthandler.js file
const mqtt = require("mqtt");
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = "mqtt://localhost:1883";
this.username = "YOUR_USER"; // mqtt credentials if these are needed to connect
this.password = "YOUR_PASSWORD";
}
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, {
username: this.username,
password: this.password,
});
// Mqtt error calback
this.mqttClient.on("error", (err) => {
console.log(err);
this.mqttClient.end();
});
// Connection callback
this.mqttClient.on("connect", () => {
console.log(`mqtt client connected`);
});
// mqtt subscriptions
this.mqttClient.subscribe("value", { qos: 0 });
// When a message arrives, console.log it
this.mqttClient.on("message", function (topic, message) {
console.log(message.toString());
});
this.mqttClient.on("close", () => {
console.log(`mqtt client disconnected`);
});
}
// Sends a mqtt message to topic: mytopic
sendMessage(message) {
this.mqttClient.publish("value", message);
}
}
module.exports = MqttHandler;
When simulator sending the data to the /simulator/data route, I am getting the value and sending the broker with value topic. I share the post request code and output of simulator below.
var mqttHandler = require("../mqtthandler");
module.exports = function (app) {
app.get("/simulator", function (req, res) {
res.render("iot/simulator");
});
// route to display all the data that is generated
app.get("/simulator/data", require("./controllers/data").all);
var mqttClient = new mqttHandler();
mqttClient.connect();
// route to write data to the database
app.post(
"/simulator/data",
require("./controllers/data").write,
(req, res) => {
mqttClient.sendMessage(req.body.value);
res.status(200).send("Message sent to mqtt");
}
);
// delete the data when the stream is stopped or when the app is closed
app.get("/simulator/data/delete", require("./controllers/data").delete);
};
When I send get request to /simulator/data I am able to see generated data, however this data is not being sent to broker.
//This is output of simulator
[
{
"_id": "5ecfadc13cb66f10e4d9d39b",
"value": "1.886768240197795",
"__v": 0,
"categories": []
},
{
"_id": "5ecfadc23cb66f10e4d9d39c",
"value": "7.351404601932272",
"__v": 0,
"categories": []
}
]
PS: Broker is created via node-red
I would like to pass this data to broker and see the result with MQTT subscription. However I can not find where am I making mistake.
Your solution is to fix your development process. Rather than working from failure debugging 2 subsystems (your publisher / simulator and your subscriber), work from success:
1) use publishers that you KNOW work, eg. mosquitto_pub, any simulator that works, etc.
2) use subscribers that you KNOW work, eg. mosquitto_sub
This will solve your problem in minutes, rather than hours or days, and let you focus on the code that you REALLY want to develop.
So a couple of things to look at here.
Your this.mqttClient.subscribe() call is inline with your connect(), on.("error",...), on.("message",...) etc. So the subscribe() could fire before the connect() has finished...and thus you will never subscribe. Put the subscribe() inside the connect() block.
You are subscribing to "value", which is not a proper MQTT topic. If you must, use value/# for the subscribe(), and "value/.." for the publish(). Your class only allows for the single, hard-coded topic, so won't be very useful when you want to reuse that class for other projects. Take the time now to pass the topic string to the class as well.
I'm building an app with node js that must take request from the users, read in mongodb and update the specific user data before taking another request for this specific user. I was able to do this using async queue with this code:
//Create a queue with concurrency of 1
var tasksQueue = async.queue(function (userInfo, callback) {
User.verifyAndUpdateAccount(userInfo, callback)
}, 1);
router.post('/', function (req, res, next) {
res.setHeader('Content-Type', 'application/json');
//Is required datas present?
if (!req.body.username || !req.body.password) {
return res.send(null);
}
var username = req.body.username;
var password = req.body.password;
//Verify account informations and update them
tasksQueue.push({username: username, password:password}, function (err,
updatedUser) {
if (err == 'NO_MORE_REQUESTS_ALLOW') {
// S'il ne lui reste plus de sms
return res.send({
code: 400,
message: 'NO_MORE_REQUESTS_ALLOW'
})
} else if (err) throw err;
//**********************************************************
// Build the response here and respond to user requests
})
The problem is that it doesn't work well when I use the module cluster.js because each thread has his own queue and don't care about other threads. I would like my app to work like this:
Imagine that a user1 made 2 requests and after that user2 made 1 request. Suppose that I have 4 cores, I would like my first thread to start treating the first request of user1 and all the threads wait that the user1 first request has been treated before treating his second request. But the other thread must be able to treat user2 request before the user1 first request has been treated because 1 request only update data of a single user in mongodb and doesn't affect others so we don't need to wait.
Maybe you can refine code to make response processing fast enough. But in case you do need a non-blocking way for heavy computation, you can try 'napajs', which was released by Microsoft that can work with Node.js to enable multithreading JavaScript scenarios in the same process. Here is a quick introduction for your reference.
your code will then look like:
var napa = require('napajs');
// One-time setup.
// You can change number of workers per your requirement.
var zone = napa.zone.create('response-builders', { workers: 4 });
function serveRequest() {
var request = null;
// Get request from queue.
// ...
zone.execute(() => {
// Build the response here
return response;
}, [request]).then((result) => {
// respond to user
console.log(result.value);
});
}
I am still learning node.js basics. My flow is like this,
browser<-->node<-->backend server doing calculation.
node and backend uses socket to communicate.
From the browser there are start/stop buttons to ask backend to start/stop the
calculation.
When node asks backend to start/stop, it must query to see if backend is
alive first.
My code is like this -
app.get('/stopCmd', function(req, res)
{
socketToBackendServer.write("status", function() {
console.log("Sending:", 'Node asking for STATUS');
});
socketToBackendServer.on("data", function() {
if(status is ok) // pseudo code
{
socketToBackendServer.write("stop", function() {
console.log("Sending:", 'Node sending STOP');
});
} else {
console.log("backend server is NOT ready");
}
});
});
app.get('/startCmd', function(req, res)
{
// do similar things as stopCmd
});
/////////////////////////////////////////////////
var socketToBackendServer = net.connect(2899);
function openSocket() {
socketToBackendServer.setKeepAlive(true);
socketToBackendServer.on('connect', onConnect.bind({}, socketToBackendServer));
socketToBackendServer.on('error', onError.bind({}, socketToBackendServer));
}
function onConnect(socket) {
var myData;
console.log('Socket is open!');
socket.on('data', function(data) {
console.log('Received:', data);
io.emit('time', { time: data.toJSON() });
});
}
function onError(socket) {
console.log('Socket error!');
// Kill socket
clearInterval(interval);
socket.destroy();
socket.unref();
// Re-open socket
setTimeout(openSocket, 1e3);
}
openSocket();
server.listen(7778);
if using the same browser, if i go crazy clicking start/stop... for the "
stopCmd", how to make sure when it queries "status", the response is caught
by its function, not "startCmd"'s ?
it's this line
socketToBackendServer.on("data", function()
Thank you again !
You can use multiple connections to the backend server, so one function can freely use one channel, the responses won't mix.
Or you can use a multiplexer function, that you call from both of your functions:
It could work if you can identify your requests, like you send and id with the status, for example socketToBackendServer.write("status 1", ... , and you send the id with the status response back from the backend server (if it yours). In this way you can send multiple requests at the same time, and when the response come, you can identify it, and call the callback function that you stored in an array with the ids.
You only send one request, and you wait for the response before you send another one. You must use a waiting queue, where you store the request, and the callback functions.
i want to send datas in same client (not all clients) with this code;
app.post("/search", function(req, res) {
//some other codes
//if database saved {
io.sockets.emit('preview-post', {title: "blabla" });// io variable was declared as GLOBAL
// } database saved end.
res.send({bla:bla});// response end before database saving process
});
this sample is working ! but it sends to all clients , How can i emit data to same opened browser(same client) ?
Second Question is: Are there any alternative ways to do this scenario?
My algorithm is post method fired > async call to an api > response end and page loaded on client > async call to an api is still continue > if async call is finished > send alert to client . But How? i wanted to do it wiht socket .io , if i use your 3.part , it'll work , can i do this scenario any other way?
This indeed sends to all sockets.
There are a couple ways to achieve what you are looking to do. The first way is to do something like this on the server:
io.on('connection', function(socket) {
socket.on('search', function(*/ client supplied arguments /*){
socket.emit('preview-post', {title: "blabla" });
});
});
If you are insistent on using a post request, and then sending it back to the client, there are two ways to achieve this.
The easiest way, if you only need to respond to this response, is just send a standard response from node, and let the client handle it:
res.send({
event: 'preview-post',
payload: {title: "blabla" }
});
This removes socket.io's event system, so if you are insistent on using socket.io to send this event back to the same client, you are going to need to use cookies. Express and the module cookie-parser make this easy for you.
Once you have this setup, inside your request you could do something like this:
app.post("/search", function(req, res) {
var socket = findSocketByCookie(req.cookies.myUniqueCookie);
socket.emit('preview-post', {title: "blabla" });
});
function findSocketByCookie(cookie) {
for(var i in io.sockets.connected) {
var socket = io.sockets.connected[i];
if(socket.handshake.headers.cookie.indexOf(cookie) !== -1){
return socket;
}
}
}