Reputation: 4378
EDIT: This question is very moot with the rollout of Twilio Proxy! Check that out instead. It basically does what I wanted to do but way better, and with less manual organization on my own end.
I am building an app with a feature that let's you call or text another user you are matched with. However, I don't want to give out user's information, so I am trying to mask the user's numbers with a number I have through Twilio.
I am using Parse as my backend, which allows me to run cloud code and host files on their server. Each match is temporary, so the numbers don't have to be permanently assigned, such as this example which seems to store the map of all users with a max of 100 connections each, a pair only ever has to have one connection, and only at the time the connection needs to be made.
I think I'm going to be storing the phone number string for each user on their Parse User object, and when a user hits the button to call another, have it set that string as the button pressing user's numberToCall attribute. Then, I will save the current user with a saveInBackgroundWithBlock call, and inside of the block, I will prompt a call to my twilio number. I changed the Request URL to [MyApp].parseapps.com/[MyFunction]. There, I will perform a Parse Query to figure out which user the incoming call belongs to, and forward the call to their numberToCall attribute.
I have been able to set up [MyApp].parseapps.com/[MyFunction] to play [Message] when my Twilio number is called using the following code:
// Include Cloud Code module dependencies
var express = require('express'),
twilio = require('twilio');
// Create an Express web app (more info: http://expressjs.com/)
var app = express();
// Create a route that will respond to am HTTP GET request with some
// simple TwiML instructions
app.get('/hello', function(request, response) {
// Create a TwiML response generator object
var twiml = new twilio.TwimlResponse();
// add some instructions
twiml.say('Hello there! Isn\'t Parse cool?', {
voice:'woman'
});
// Render the TwiML XML document
response.type('text/xml');
response.send(twiml.toString());
});
// Start the Express app
app.listen();
I have performed the Parse Query, so I have the number string in the format '+1XXXXXXXXXX'. Now I just need to figure out how to connect the two users. I have tried searching through Twilio's API documentation, but I haven't been able to find the relative info. If anybody could point me in the right direction, I'd really appreciate it.
Upvotes: 0
Views: 397
Reputation: 4378
Just saw this question in my history, and someone favorited it, so I thought I'd share my answer.
My Twilio numbers are configured with the Request URLs "https://[MY APP NAME].parseapp.com/calls" and "https://[MY APP NAME].parseapp.com/texts" In my Parse cloud code file I included the following:
// Include Cloud Code module dependencies
var express = require('express'),
twilio = require('twilio');
// Create an Express web app (more info: http://expressjs.com/)
var app = express();
app.get
('/calls', function(request, response)
{
// Create a TwiML response generator object
var twiml = new twilio.TwimlResponse();
var query1 = new Parse.Query(ActiveJob); //query1 will look to see if a customer made this call
var twilioNumber = request.param('To');
var fromNumber = request.param('From');
query1.equalTo("customerNumber", fromNumber);
query1.equalTo("customerTwilioNumber", twilioNumber);
query1.first
({
success: function(result)
{ //Forward call to the provider that the customer was trying to reach
var Job = result;
var receiverNumber = Job.get("providerNumber");
var receiverTwilioNumber = Job.get("providerTwilioNumber");
twiml.dial({callerId:receiverTwilioNumber}, receiverNumber);
response.type('text/xml');
response.send(twiml.toString(''));
},
error: function(error)
{ //No customer made this call. See if a provider made the call with query2
var query2 = new Parse.Query(ActiveJob);
query2.equalTo("providerNumber", fromNumber);
query2.equalTo("providerTwilioNumber", twilioNumber);
query2.first
({
success: function(result)
{ //Forward the call to the customer the provider was trying to call
var Job = result;
var receiverNumber = Job.get("customerNumber");
var receiverTwilioNumber = Job.get("customerTwilioNumber");
twiml.dial({callerId:receiverTwilioNumber}, receiverNumber);
response.type('text/xml');
response.send(twiml.toString(''));
},
error: function(error)
{ //The phone number used to make this call has not been assigned to this Twilio
//number as a customer nor a provider
response.error("This number has not been assigned to a Job for the current user");
twiml.say('Connection failed. Users must use their Lawn Guru verified number for communication.', {voice:'woman'});
}
});
}
});
//Since queries are ran in background, this will play while/before the call is connected
twiml.say('Connecting.', {voice:'woman'});
});
Basically, each ActiveJob has a customer's phone number, a provider's phone number, and the twilio number that each one will associate with the job. I can only do one thing when a user calls my twilio number, based on their number, so I find the active job where they are either the customer or provider, and the number they have associated with that job is the twilio number that they called. I then dial out to the other user associated with the job, with the caller ID being the number that the second user associates with the job. This means that if customer calls the provider, and they hang up, the provider could call the same number they received the call from and be connected to the customer. I did a similar thing with texting, so that users could seamlessly text each other from their messaging app.
The one downside to this is that as jobs are fulfilled and new ones created, users will be reusing the same old twilio numbers on the new jobs, so 1) I am limiting the number of active jobs users can have based on the number of twilio users I have, and 2) if a user tries to call a number from an old provider after the job is over, they may be connected to a different provider from a current job.
Hope this helps someone.
Upvotes: 3