In rails 4.2.4, I am using gem 'twilio-ruby' to send SMS. Right now I am calling this twilio method for particular action, eg: send otp message & it is working fine. But the issue is same message will send when I am calling other actions.
In development.rb
config.middleware.use Rack::TwilioWebhookAuthentication, Rails.application.secrets.twilio_auth_token, '/voice'
In user model,
def send_otp_notification
begin
client = Twilio::REST::Client.new Rails.application.secrets.twilio_account_sid, Rails.application.secrets.twilio_auth_token
message = client.messages.create :from=> '+12015598867', :to=>"+#{self.mobile_number}", :body=> "Verify your otp: #{self.otp}"
rescue Twilio::REST::RequestError => e
puts "e = #{e}".red
end
end
In mobile/api/user.rb,
user = User.new(params[:user])
user.save
user.send_otp_notification
Here, send_otp_notification method will call only once when user registers, right now when a user creates any other object(eg: create 'posts' table entry) then also otp message will send, how can I avoid this?
Using Active Record new_record
user = User.new(params[:user])
user.save
tmp = true
user.send_otp_notification if tmp.present?
Active Record Persistence new_record
hope this solve your issue!!!
ActiveRecord has two callback method after_save or after_create.You can use any one or both to call your function dynamically.You need to set those method in your model.No need to set condition or call at anywhere.
after_save :send_otp_notification
after_create :send_otp_notification
You can modify your model to contain a boolean field that tells if a notification has already been sent for this user
class AddOtpSentToUsers < ActiveRecord::Migration
def up
add_column :users, :otp_sent, :boolean, default: false
end
def down
remove_column :users, :otp_sent
end
end
And modify your function to be flexible in sending otp if it is requested to be sent again or for the first time
def send_otp_notification send_again = false
if !self.otp_sent || send_again
begin
client = Twilio::REST::Client.new Rails.application.secrets.twilio_account_sid, Rails.application.secrets.twilio_auth_token
message = client.messages.create :from=> '+12015598867', :to=>"+#{self.mobile_number}", :body=> "Verify your otp: #{self.otp}"
rescue Twilio::REST::RequestError => e
puts "e = #{e}".red
end
end
end
Related
How can I handle errors in the Twilio API in regards to creating an SMS message?
Every time an invalid phone number gets entered, I get a message and a 500 error:
Unable to create record: The 'To' number is not a valid phone number.
How can I have it redirect back to the home page and simply flash an error notice?
class Messager
def initialize
#account_sid = ENV['ACCOUNT_SID']
#auth_token = ENV['AUTH_TOKEN']
#twilio_number = ENV['TWILIO_NUMBER']
#client = Twilio::REST::Client.new #account_sid, #auth_token
end
def send_message(phone_number, movies, username)
text_message = #client.api.account.messages.create(
from: #twilio_number,
to: phone_number,
body: "Hello movie lover, #{username}!\nHere is your current watch list:\n#{movies}"
)
puts text_message.to
end
end
I don't need anything fancy, just a redirect to the main page and a quick error message saying their phone number is invalid, not a 500 error page. I'm new to the Twilio API, and I've been troubleshooting this issue for hours.
Twilio developer evangelist here.
When you make a request to the Twilio API using the Ruby gem and something goes wrong, the library will throw an error of type Twilio::REST::RestError.
In order to avoid a 500 error for your user you should rescue from this error and do something else with your controller. I'm not sure what your controller looks like, so I'll take a guess at what you should do.
First, I'd update the Messager class to store the error message if you get one. Also, the send_message function should return a boolean to describe whether the message was successfully sent.
class Messager
attr_reader :error
def initialize
#account_sid = ENV['ACCOUNT_SID']
#auth_token = ENV['AUTH_TOKEN']
#twilio_number = ENV['TWILIO_NUMBER']
#client = Twilio::REST::Client.new #account_sid, #auth_token
end
def send_message(phone_number, movies, username)
begin
text_message = #client.api.account.messages.create(
from: #twilio_number,
to: phone_number,
body: "Hello movie lover, #{username}!\nHere is your current watch list:\n#{movies}"
)
return true
rescue Twilio::REST::RestError => error
#error = error
return false
end
end
end
Then, in your controller you can call send_message and do different things if the message is successful or otherwise.
class MessagingController < ApplicationController
def create
messager = Messager.new
if messager.send_message(params[:phone_number], current_user.movies, current_user.username)
# Success! Message sent!
# Now do what you were going to do with the successfull result
else
# Boo, something happened.
Rails.logger.warn("#{messager.error.code}: #{messager.error.message}")
# Redirect or render an action with an error here.
end
end
end
Let me know if that helps at all.
I am building a simple web app that sends SMS messages to cell phones using Twilio. I want to ensure that the user has entered a full 10 digit phone number before it will allow a message to attempt to be sent.
When I test it with a less-than or greater-than 10 digit number, in heroku logs, I see Twilio::REST::RequestError (The 'To' number 1234567890 is not a valid phone number.).
I have tried to use a begin/rescue wrapper and am telling it to render text: "Try again with a valid number." and tried a variety of if statements to try to avoid the error.
I am pretty new to Ruby and Rails and Twilio, but I promise i have been through every guide I have found. Any help is greatly appreciated. Full code of my UserController below:
require 'twilio-ruby'
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
account_sid = '...'
auth_token = '...'
if #user.save
render text: "Wasn't that fun? Hit the back button in your browser to give it another go!"
begin
client = Twilio::REST::Client.new account_sid, auth_token
client.account.sms.messages.create(
from: '+16035093259',
to: #user.phone,
body: #user.message
)
rescue Twilio::REST::RequestError
render text: "Try again with a valid number."
end
else
render :new
end
end
end
I'd extract the SMS sending logic into a separate model/controller and use a background job to process the submitting. The UserController should only handle, well, user creation/modification.
Scaffolding:
$ rails g model sms_job user:references message:text phone submitted_at:datetime
$ rake db:migrate
Model:
class SmsJob < AR::Base
attr_accessible :user_id, :message, :phone
belongs_to :user
validates_presence_of :message, :phone, :user_id
validates :phone,
length: { min: 10 },
format: { with: /\+?\d+/ }
scope :unsubmitted, where(submitted_at: nil)
TWILIO = {
from_no: '...',
account_sid: '...',
auth_token: '...'
}
# find a way to call this method separately from user request
def self.process!
unsubmitted.find_each do |job|
begin
client = Twilio::REST::Client.new TWILIO[:account_sid], TWILIO[:auth_token]
client.account.sms.messages.create(
from: TWILIO[:from_no],
to: job.phone,
body: job.message
)
job.submitted_at = Time.zone.now
job.save
rescue Twilio::REST::RequestError
# maybe set update a tries counter
# or delete job record
# or just ignore this error
end
end
end
end
The controller then should just provide the information that the SMS is going to be send:
# don't forget the 'resources :sms_jobs' in your routes.rb
class SmsJobsController < ApplicationController
# index, update, destroy only for only admin?
def new
#sms_job = SmsJobs.new
end
def create
#sms_job = current_user.sms_jobs.build params[:sms_job]
if #sms_job.save
redirect_to root_url, notice: "Your message is being send!"
else
render :new
end
end
end
For the background processing, have a look at these excellent Railscasts :-) You probably need to workaround some concurrency problems if you have to process many messages and/or Twilio has a long response time (didn't use that service yet).
In our custom sales app our users are able to send emails based on text partials they choose. We need to record the sent mails in an activity model. How do I get the mailer result as a string in order to save it?
Instead of calling the deliver method to send the mail, you can capture the email message by calling to_s. For example, if you have a mailer:
class MyMailer < ActionMailer::Base
default :from => "sender#me.com"
def my_email
mail(:to => "destination#you.com", :subject => "Mail Subject")
end
end
you would do
mail_content = MyMailer.my_email.to_s
May be you can using a mail observer like in the following example :
class MailObserver
def self.delivered_email(message)
test = Activty.create do |activity|
# etc.
end
end
end
Find here
I have a member model with a reset_token method (which assigns a user a new token in order to send them an email to reset their password). But update_attribute never saves anything in the database. I have :new_password_token assigned to attr_accessible and attr_accessor. The log picks up the salt and token but it always returns nil
def self.reset_token(email)
member = find_by_email(email)
if member
#Reset token, and then send email
salt = BCrypt::Engine.generate_salt
logger.error "Salt is #{salt}"
token = BCrypt::Engine.hash_secret(email, salt)
logger.error "token is #{token}"
if member.update_attribute(:new_password_token, token)
member
end
end
nil
end
Controller method in which it is called:
def reset_password
#member = Member.reset_token(params[:email])
if #member
redirect_to(root_url, :notice => "Please check your email for instructions")
else
redirect_to(root_url, :notice => "Sorry we have no record of your account")
end
end
Try removing attr_accessor from your model. attr_accessor is creating reader and writer methods for new_password_token. The writer method is equivalent to:
def new_password_token=(new_password_token)
#new_password_token = new_password_token
end
So when you update_attribute it is just setting an instance variable on your object and bypassing the database altogether.
in devise invitable, you can invite a new user by performing:
User.invite!(:email => "new_user#example.com", :name => "John Doe")
What I would like to do is (sometimes) prevent devise invitable from sending out an email. I found the following code in the library:
def invite!
if new_record? || invited?
self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
generate_invitation_token if self.invitation_token.nil?
self.invitation_sent_at = Time.now.utc
save(:validate => false)
::Devise.mailer.invitation_instructions(self).deliver
end
end
Any ideas on how to best update that to not send out the email on the last line? I'm not familiar with the ::
thanks
you can use:
User.invite!(:email => "new_user#example.com", :name => "John Doe") do |u|
u.skip_invitation = true
end
or
User.invite!(:email => "new_user#example.com", :name => "John Doe", :skip_invitation => true)
this will skip invitation email.
In your invitations_controller (there should already be one that inherits from Devise::InvitationsController), you can add the following
# this is called when creating invitation
# should return an instance of resource class
def invite_resource
if new_record? || invited?
self.skip_confirmation! if self.new_record? && self.respond_to?(:skip_confirmation!)
super
end
end
This will override Devise's method for inviting, and then call the original Devise method (super) only if the condition is met. Devise should then handle the token generation and send the invite. You may also want to setup what the app does if the condition is false, in my case that looks like this:
def invite_resource
if user_params[:is_free] == "true"
super
else
# skip sending emails on invite
super { |user| user.skip_invitation = true }
end
end
when params[:is_free] is set to ''true'' the invitation is sent, otherwise the resource is created, but no invitation is sent.
After some digging I found this solution here: https://github-wiki-see.page/m/thuy-econsys/rails_app/wiki/customize-DeviseInvitable