I am building a Ruby app using Sinatra and the Twilio api.
Over a phone call to my assigned Twilio number, the user gets prompted to record an audio message. Once that message is recored the user gets redirected to the following route where if the user dials 1 (or anything else), they should get redirected to their feed of msgs, but if the user dials 2, then the user's message gets deleted and should get redirected to a route where they can record a new message.
Here is my route:
get '/playback/handle-recording/:recordingSID' do
if params['Digits'] = '2'
delete(params['recordingSID'])
deletedMsg = "Audio deleted."
getRecord(deletedMsg)
else
getFeed()
end
end
helper methods:
helpers do
def delete(recording)
recording = client().account.recordings.get(recording)
recording.delete
end
def getFeed()
redirect '/feed'
end
def getRecord(appendMsg)
Twilio::TwiML::Response.new do |response|
if appendMsg
response.Say appendMsg
end
response.Say "Record your message."
response.Record :maxLength => '5', :trim => "trim-silence", :playBeep => "true", :action => '/playback', :method => 'get'
end.text
end
end
My issue is that whether the user lands in the "if" or the "else" the first method, getRecord(deletedMsg) is the one that gets called, and not the getFeed().
How can I fix my route so that if the user lands in the else he does get redirected to his feed page and not to the getRecords method.
Are you sure they're actually making it into the else? Ruby won't just randomly not execute what's in there for no good reason.
One thing you may want to look at is you are assigning, not comparing the params value:
if params['Digits'] = '2'
You'll want to do:
if params['Digits'] == '2'
That could definitely lead to some strange behavior in an if statement, like, for instance always executing one path.
Related
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...
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']
I'm trying to integrate our app with MailChimp, so that new users get added to our mailing list and new paid subscribers get updated on the mailing list as paid subscribers (we have a MERGE VARS for that). In order to test that the list is updating properly when the customer pays for a subscription, I'm trying to run the code with the test server for Stripe. (I inherited the code after we had tested it, so I haven't done this before.)
I'm running into an undefined method error, and I can't figure out where it's coming from, since it is working just fine on the live site, and I haven't changed that part of the code.
in the initializer file stripe.rb, I changed Stripe.api_key
="the live secret key" to the test secret key, just subbing in the other key.
I did the same in our layout/application.html.erb file (where we put the initial call to Stripe per Stripe's instructions), swapping out the public live key for the public test key.
In the account_controller processing method (which is called for payment processing on the Stripe token and updating our server with the type of subscription.):
def processing
begin
if params[:coupon][0] != "_"
#coupon = params[:coupon]
else
#coupon = nil
end
customer = Stripe::Customer.create(
:email => current_user.email,
:card => params[:stripeToken],
:plan => params[:plan],
:coupon => #coupon
)
current_user.subscription = params[:plan]
current_user.stripe_id = customer.id
current_user.canceled = false
current_user.save!
rescue Stripe::CardError => e
flash[:error] = e.message
end
#only this code is new, from here:
#for gibbon updating subscription status
gb=Gibbon::API.new
email=current_user.email
subscription=user.subscription
updated_date=user.updated_at.strftime('%B %d, %Y')
gb.lists.subscribe({:id => "our list id", :email => {:email => "#{email}"}, :merge_vars => {:SUB => "test", :UPDATE => "#{updated_date}"}, :update_existing=>true , :double_optin => false, :send_welcome=> false})
#to here is new code
current_user
respond_to do |format|
format.html { redirect_to account_confirmation_path }
end
end
I'm getting a "NoMethodError in AccountController#processing
undefined method 'canceled=' for #User:an-id-here" (note the #User: an id here is in pointy brackets, but those aren't showing up here.)
error. This code works on the live site, so that makes me think it must be something I've done here. Canceled is a column on our User model, which is a boolean indicating whether they've canceled. It is not set as attr_accesible or attr_accessor, but as I said, that code hasn't changed and works on the live site.
I'm not sure what the problem is and I feel like I don't have enough information to start figuring it out. The server is only recording the same error message as shows up in the browser. (Oh, and I'm running the test code only on my local server, not the live server).
What might be causing the undefined method error? I'm not seeing anything in the code I changed that should do it.
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)
I've stumbled across a bit of problem when it comes to redirects behind a protected set of URLs (admin section) within a Sinatra app. It most likely a silly mistake but I haven't found anything online that helps.
This is for a password protected area as the helpers show, where the user can create new events. The first time a user tries to access the admin, they are prompted for a password, then subsequent pages are left. The problem I have is that when the app attempts to redirect after a successful new event is made, the user has to re-auth themselves ... which seems bit redundant.
This also applies for the deletion and editing process, the user always gets prompted when a redirect is attempted. I've tried passing 303 at the second parameter to for a different HTTP code, but to no avail
Anyway, here's the code, any questions/help would be appreciated
helpers do
def protected!
unless authorized?
response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
throw(:halt, [401, "Not authorized\n"])
end
end
def authorized?
#auth ||= Rack::Auth::Basic::Request.new(request.env)
#auth.provided? && #auth.basic? && #auth.credentials && #auth.credentials == ['admin', 'admin']
end
end
...
get "/admin/events/:id" do
protected!
conf = Conference.where(:_id => params[:id]).first
not_found unless conf
haml :admin_event_edit, :layout => :admin_layout, :locals => { :event => conf }
end
post "/admin/events/new/" do
protected!
conf = Conference.new(params[:event])
if conf.save!
redirect "/admin/events/"
else
"Something went horribly wrong creating the new event, heres the form contents #{params.inspect}"
end
end
get "/admin/events/" do
protected!
haml :admin_events, :layout => :admin_layout, :locals => { :our_events => Conference.where(:made => true).order_by(:start_date.asc).limit(15), :other_events => Conference.where(:made => false).order_by(:start_date.asc).limit(15)}
end
Is this only happening in Safari?
I've used the code above and it only re-auths in Safari, Chrome, and FireFox work as expected.
It seems that if you unless you check the "remember my username/password" Safari will send each subsequent request without the Authorization in the header (a great tool for watching headers etc is Charles). If you do check it then Apple sends the Auth in the header correctly and even if you quit out of Safari it will continue to remember to send the Auth on relaunch.
So it's Apple being silly not you :)