sinatra, ruby and twilio [facilitating a CALLBACK mechanism] - ruby

I would like to do the following:
Call my twilio number from my cell phone
My twilio number identifies the incoming number then immediately hangs up
My twilio number calls back the identified number (my cell phone number)
When I pick up, twilio asks me to enter the number I wish to call
Twilio gathers the input of the number I want to call and connects me.
So I can make cheap international calls (or roaming calls) from my cell phone.
So far, taken from the twilio website api docs, I have:
require 'rubygems'
require 'sinatra'
require 'twilio-ruby'
get '/' do
account_sid = 'xxxxxxx'
auth_token = 'zzzzzzz'
to = params['From']
#to = '+447928344246'
#to = '441903773807'
from = '442033222327'
Twilio::TwiML::Response.new do |r|
r.Hangup
end.text
# set up a client to talk to the Twilio REST API
#client = Twilio::REST::Client.new account_sid, auth_token
#call = #client.account.calls.create(
:from => from, # From your Twilio number
:to => to, # To any number
:timeout => "20",
# Fetch instructions from this URL when the call connects
:url => 'https://dl.dropboxusercontent.com/u/85088004/twilio/twilio.xml'
)
end
post '/makecall' do
warn params.inspect
account_sid = ' ACaf2b951ae6f7424da036ea9dcd5b0d91'
auth_token = 'my token'
#client = Twilio::REST::Client.new account_sid, auth_token
call = #client.account.calls.create(:url => "https://dl.dropboxusercontent.com/u/85088004/twilio/callback.xml",
:to => params[:Digits],
:from => "+442033222327")
puts call.to
end
The twilio.xml, in the '/' section url file is:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather action="/makecall" method="POST">
<Say timeout="10">Enter the number you wish to call</Say>
</Gather>
</Response>
I get "Sorry an application error has occurred." Then it just hangs up.
warn params.inspect
does not produce anything when I inspect the heroku logs. So I think (one of) the problem(s) is that the params of the number I am dialling is not passed.
Is there any other problem with logic or scripting that seems obvious?
Does the problem lie with the url in the '/makecall' snippet? It si:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
</Hangup>
</Response>
Many, many thanks!

SOLVED!! Thank you for help from the T evangelist. Here is the solution:
require 'rubygems'
require 'sinatra'
require 'twilio-ruby'
account_sid = 'my sid'
auth_token = 'my token'
get '/' do
from = 'my T number'
to = params[:From]
Twilio::TwiML::Response.new do |r|
r.Hangup
end.text
sleep 10
# set up a client to talk to the Twilio REST API
#client = Twilio::REST::Client.new account_sid, auth_token
#call = #client.account.calls.create(
:from => from, # From your Twilio number
:to => to, # To any number
# Fetch instructions from this URL when the call connects
:url => 'https://dl.dropboxusercontent.com/u/85088004/twilio/twilio.xml'
)
end
# the xml file. It is VERY IMPORTANT that you have the absolute url in the action=""
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather timeout="20" action="http://dry-journey-9071.herokuapp.com/makecall" method="GET">
<Say voice="alice">Enter the destination number, please.</Say>
</Gather>
<Say voice="alice">Input not received. Thank you</Say>
</Response>
#Back to the app:
get '/makecall' do
number = params[:Digits]
Twilio::TwiML::Response.new do |r|
r.Dial number ### Connect the caller to Koko, or your cell
r.Say 'The call failed or the remote party hung up. Goodbye.'
end.text
end
Yay!

Twilio Developer Evangelist here.
When you detect that it is your own number, you want to return some TwiML (XML) to Twilio, this should suffice: <Response> <Hangup/> </Response>
You would then need to make a REST API call to Twilio to make a new outbound call to yourself:
#client.account.calls.create(to: my_number, from: "+sometiwilionumber", url: "http://example.com/make-other-call-twiml")
Then use the URL of some TwiML to <Gather> the number you want to dial, and simply <Dial> to that number...
The thing you want to avoid is having Twilio call you back before the first call ends. As you will need to make the API call first, we need to use threading to get around it. I'm not bad with Ruby/Sinatra, but I'm no expert with threading here, but this should work:
twilio_call_thread = Thread.new{
sleep 3 #3 Seconds should be plenty, but you may want to experiment.
#client.account.calls.create(to: mynumber, from: some_twilio_number, url: "http://example.com/gather-and-dial")
}
#Then return the TwiML to hangup...
"<Response><Hangup/></Response>"
You call your number, it creates a thread, then goes to sleep. It responds with a <Hangup> to disconnect the call. A few seconds later, the thread wakes up and you'll get the return call. You will need some TwiML on that call to do a <Gather> to get the phone number to dial, and then <Dial> to make the actual call.
I wasn't sure about the threading, so before checking on that, I had two other ideas you could use:
Use SMS. Send you Twilio number the other number you want to dial, and just have Twilio make the call directly between your number, and the other number.
Secondly, just use a URL, presuming you have data on your phone you can just open a URL which does the same without the initial call or SMS.
Oh - and a third option, if you don't want to muck about with threads, or that's not working for you: set the Status Callback URL on your Twilio number (click 'Optional Voice Settings'). This is fired after the call completes, and from there you can make the new call to yourself.
Hope you get it sorted, it's going to be awesome.

Related

How to automatically call a (browser) Client from a Twilio Conference Call?

Here is the scenario:
Assume we have a client with a capability to allow incoming calls, named "Roger".
James calls our Twilio number
conferenceName = "conftest"
caller_id = "+15555555555"
response = Twilio::TwiML::Response.new do |r|
r.Dial :callerId => caller_id do |d|
d.Client 'Roger'
end
end
Now we want Roger to get the incoming call on his browser, but we want the call to be a conference call, rather than a phone-to-browser call (not sure if there is a technical name for this). How can I connect James to Roger in a conference call?
Twilio developer evangelist here.
Sadly, this is not as simple as a single TwiML response. What you need to do is drop James into a conference call and, at the same time, initiate a call to Roger's client, which on answering would drop him into the conference call too.
With code (in a pseudo-sinatra format) that would look like:
conference_name = "conftest"
caller_id = "+15555555555"
# Set the Twilio number endpoint URL to /dial, this will drop James into
# the conference and initiate the call to Roger.
post '/dial' do
response = Twilio::TwiML::Response.new do |r|
r.Dial do |d|
d.Conference conference_name
end
end
# create a REST API client with your Account SID and Auth token
client = Twilio::REST::Client.new "AC123...", "XYZ456..."
client.calls.create from: caller_id, to: "Roger", url: "/client_dial"
response.to_xml
end
# This endpoint is the one that Twilio will hit when Roger answers the
# client incoming call. All we need to do is drop him into the same
# conference call.
post "/client_dial" do
response = Twilio::TwiML::Response.new do |r|
r.Dial do |d|
d.Conference conference_name
end
end
response.to_xml
end
Let me know if this helps!

How do I send two SMS messages in a chain to the same receiver with twilio?

I am working on a project that returns SMS messages to a user who has just sent a SMS message to the server.
The process is:
The user sends a SMS message to the server.
The server will send two SMS messages back to this user. Note that these are two separate short messages and will be sent pretty much at the same time.
I've got the sending part working, but just for sending one SMS message, not two. When I add more code to send another message only the second message part works, which means only the second message has been sent out, the first message has been ignored.
The code looks pretty much like:
else
sms = SMS.create(:body => params['Body'], :from => params['From'], :to => params['To'], :created_at => Time.now)
#return a random saved sms
return_secret = SMS.first(:offset => rand(SMS.count))
twiml = Twilio::TwiML::Response.new do |r|
r.Sms return_secret.body
#send another message to remind user for rating
ask_rating = remind_rating
if ask_rating
twiml = Twilio::TwiML::Response.new do |r|
r.Sms ask_rating
end
twiml.text
end
Does anyone know how to send two messages in Twilio?
You've got some variable shadowing going on with twiml. As you wrote it, the second message's code is inside of the first message's block. Yet, you refer to a variable with the same name as one outside of the block. I would try flattening your code so you aren't nesting like that.
I think the issue here is you're instantiating a second TwiML::Response object when you already have one, so you can just references the previous one which you assigned to r in the first block. You also called it r in the second block so you just remove the block that encloses it:
sms = SMS.create(:body => params['Body'], :from => params['From'], :to => params['To'], :created_at => Time.now)
#return a random saved sms
return_secret = SMS.first(:offset => rand(SMS.count))
twiml = Twilio::TwiML::Response.new do |r|
r.Sms return_secret.body
#send another message to remind user for rating
ask_rating = remind_rating
if ask_rating
r.Sms ask_rating
end
end
Also the blocks weren't balanced in the initial code snippet so I stripped out the else to make it syntactically accurate.
Thank you all, really appreciate your replies.
After consulting with twilio team, they gave me an example like this:
require 'rubygems'
require 'twilio-ruby'
require 'sinatra'
get '/sms-quickstart' do
twiml = Twilio::TwiML::Response.new do |r|
r.Message "Hey Monkey. Thanks for the message!"
r.Message "this is the 2nd message"
end
twiml.text
end
I just deleted
if ask_rating
twiml = Twilio::TwiML::Response.new do |r|
everything works...

Retrieving CallSid for incoming call

When my ruby script makes outgoing calls through Twilio, it's a piece of cake for me to find, output, and reuse the Call Sid for later as such :
#client = Twilio::REST:Client.new account_sid, auth_token
call = #client.account.calls.create({ :from=>'INC', :to=>'OUT', :url=>'URL', :method=>'GET'})
puts call.sid
This works fine for outgoing calls that I make myself.
The issue is when I try to get the call SID for incoming calls.
get '/greeting' do
Twilio::TwiML::Response.new do |r|
r.Say 'Hello. Welcome.'
r.Gather :numDigits => '1', :action => '/greeting/handle-gather', :method => 'get' do |g|
g.Say 'For X, press 1. For Y, press 2. For Z, press 3.'
end
end.text
puts Twilio::TwiML::Request.CallSid
CallSid = incoming_Cid
end
The incoming_Cid is then stored in a MYSQL database for later. I'm not sure if Twilio::TwiML::Request.CallSid is the correct way to get the request parameters that Twilio passes to my application.
How would I properly retrieve the CallSid for incoming calls?
Nevermind, the issue was been solved by simply using Ruby's params instead of the Twilio HTTP Request object:
puts params['CallSid']

Using Twilio api how to send the verification code as sms

I am using twillio api to send sms from my application . Its working fine . But I need to send the verification code as sms to verify his phone number and then need to submit registration from in my rails project.
Can any one please provide the sample example using twilio to verify the code.
Thanks
Here is an example of Twilio
require 'rubygems'
require 'twilio-ruby'
account_sid = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
auth_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
client = Twilio::REST::Client.new account_sid, auth_token
from = '+441702806101' # Your Twilio number
friends = {
'+447908232434' => 'David'
}
friends.each do |key, value|
client.account.sms.messages.create(
:from => from,
:to => key,
:body => "#{value} you are working 30/04/2013 - CX1"
)
puts "Sent message to #{value}"
end
See a code complete tutorial in Ruby:
https://www.twilio.com/docs/tutorials/walkthrough/account-verification/ruby/rails
Hope this helps

Why is Net::HTTP timing out when I try to access a Prawn Generated PDF?

I am using Prawn to generate a PDF from my controller, and when accessed directly at the url, it works flawlessly, I.E. localhost:3000/responses/1.pdf
However, when I try to generate this file on the fly for inclusion in a Mailer, everything freezes up and it times out.
I have tried various methods for generating / attaching the file and none have changed the outcome.
I also tried modifying the timeout for Net::HTTP to no avail, it just takes LONGER to time out.
If I run this command on the Rails Console, I receive a PDF data stream.
Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000)
But if I include this code in my controller, it times out.
I have tried two different methods, and both fail repeatedly.
Method 1
Controller:
http = Net::HTTP.new('localhost', 3000)
http.read_timeout = 6000
file = http.get(response_path(#response, :format => 'pdf')) #timeout here
ResponseMailer.confirmComplete(#response,file).deliver #deliver the mail!
Method 1 Mailer:
def confirmComplete(response,file)
email_address = response.supervisor_id
attachments["test.pdf"] = {:mime_type => "application/pdf", :content=> file}
mail to: email_address, subject: 'Thank you for your feedback!'
end
The above code times out.
Method 2 Controller:
ResponseMailer.confirmComplete(#response).deliver #deliver the mail!
Method 2 Mailer:
def confirmComplete(response)
email_address = response.supervisor_id
attachment "application/pdf" do |a|
a.body = Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000) #timeout here
a.filename = "test.pdf"
end
mail to: email_address, subject: 'Thank you for your feedback!'
end
If I switch the a.body and a.filename, it errors out first with
undefined method `filename=' for #<Mail::Part:0x007ff620e05678>
Every example I find has a different syntax or suggestion but none fix the problem that Net::HTTP times out. Rails 3.1, Ruby 1.9.2
The problem is that, in development, you're only running one server process, which is busy generating the email. That process is sending another request (to itself) to generate a PDF and waiting for a response. The request for the PDF is basically standing in line at the server so that it can get it's PDF, but the server is busy generating the email and waiting to get the PDF before it can finish. And thus, you're waiting forever.
What you need to do is start up a second server process...
script/rails server -p 3001
and then get your PDF with something like ...
args = ['127.0.0.1','/responses/1.pdf']
args << 3001 unless Rails.env == 'production'
file = Net::HTTP.get(*args)
As an aside, depending on what server you're running on your production machine, you might run into issues with pointing at 127.0.0.1. You might need to make that dynamic and point to the full domain when in production, but that should be easy.
I agree with https://stackoverflow.com/users/811172/jon-garvin's analysis that you're only running one server process, but I would mention another solution. Refactor your PDF generation so you don't depend on your controller.
If you're using Prawnto, I'm guessing you have a view like
# app/views/response.pdf.prawn
pdf.text "Hello world"
Move this to your Response model: (or somewhere else more appropriate, like a presenter)
# app/models/response.rb
require 'tmpdir'
class Response < ActiveRecord::Base
def pdf_path
return #pdf_path if #pdf_generated == true
#pdf_path = File.join(Dir.tmpdir, rand(1e11).to_s)
Prawn::Document.generate(#pdf_path) do |pdf|
pdf.text "Hello world"
end
#pdf_generated = true
#pdf_path
end
def pdf_cleanup
if #pdf_generated and File.exist?(#pdf_path.to_s)
File.unlink #pdf_path
end
end
end
Then in your ResponsesController you can do:
# app/controllers/responses_controller.rb
def show
#response = Response.find params[:id]
respond_to do |format|
# this sends the PDF to the browser (doesn't email it)
format.pdf { send_file #response.pdf_path, :type => 'application/pdf', :disposition => 'attachment', :filename => 'test.pdf' }
end
end
And in your mailer you can do:
# this sends an email with the PDF attached
def confirm_complete(response)
email_address = response.supervisor_id
attachments['test.pdf'] = {:mime_type => "application/pdf", :content => File.read(response.pdf_path, :binmode => true) }
mail to: email_address, subject: 'Thank you for your feedback!'
end
Since you created it in the tmpdir, it will be automatically deleted when your server restarts. You can also call the cleanup function.
One final note: you might want to use a different model name like SupervisorReport or something - Response might get you in namespacing trouble later)

Resources