Masked Phone Numbers with Ruby and Rails

Download the Code

This Ruby on Rails sample application is modeled after the amazing rental experience created by AirBnB, but with more Klingons.

Host users can offer rental properties which other guest users can reserve. The guest and the host can then anonymously communicate via a disposable Twilio phone number created just for a reservation. In this tutorial, we'll show you the key bits of code to make this work.

To run this sample app yourself, download the code and follow the instructions on GitHub.

Read how Lyft uses masked phone numbers to let customers securely contact drivers

Create a reservation

The first step in connecting a guest and host is creating a reservation. Here, we handle a form submission for a new reservation which contains the guest's name and phone number.

Loading Code Samples...
Language
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
app/controllers/reservations_controller.rb
Reservation Creation Method

app/controllers/reservations_controller.rb

Part of our reservation system is receiving reservation requests from potential renters. However, these reservations need to be confirmed. Let's see how we would handle this step.

Confirm the Reservation

Before the reservation is finalized, the host needs to confirm that the property is still available. Learn how to automate this process in our first AirTNG tutorial, Workflow Automation.

Once the reservation is confirmed, we need to create a Twilio number that the guest and host can use to communicate in the provision_phone_number method.

Loading Code Samples...
Language
class Reservation < ActiveRecord::Base
  validates :name, presence: true
  validates :guest_phone, presence: true

  enum status: [ :pending, :confirmed, :rejected ]

  belongs_to :vacation_property
  belongs_to :user

  def notify_host(force = false)
    # Don't send the message if we have more than one and we aren't being forced
    if self.host.pending_reservations.length > 1 and !force
      return
    else
      message = "You have a new reservation request from #{self.name} for #{self.vacation_property.description}:

      '#{self.message}'

      Reply [accept] or [reject]."

      self.host.send_message_via_sms(message)
    end
  end

  def host
    @host = User.find(self.vacation_property[:user_id])
  end

  def guest
    @guest = User.find_by(phone_number: self.guest_phone)
  end

  def confirm!
    provision_phone_number
    self.update!(status: 1)
  end

  def reject!
    self.update!(status: 0)
  end

  def notify_guest
    if self.status_changed? && (self.status == :confirmed || self.status == :rejected)
      message = "Your recent request to stay at #{self.vacation_property.description} was #{self.status}."
      self.guest.send_message_via_sms(message)
    end
  end

  def send_message_to_guest(message)
    message = "From #{self.host.name}: #{message}"
    self.guest.send_message_via_sms(message, self.phone_number)
  end

  def send_message_to_host(message)
    message = "From guest #{self.guest.name}: #{message}"
    self.host.send_message_via_sms(message, self.phone_number)
  end

  private

  def provision_phone_number
    @client = Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])
    begin
      # Lookup numbers in host area code, if none than lookup from anywhere
      @numbers = @client.api.available_phone_numbers('US').local.list(area_code: self.host.area_code)
      if @numbers.empty?
        @numbers = @client.api.available_phone_numbers('US').local.list()
      end

      # Purchase the number & set the application_sid for voice and sms, will
      # tell the number where to route calls/sms
      @number = @numbers.first.phone_number
      @client.api.incoming_phone_numbers.create(
        phone_number: @number,
        voice_application_sid: ENV['ANONYMOUS_APPLICATION_SID'],
        sms_application_sid: ENV['ANONYMOUS_APPLICATION_SID']
      )

      # Set the reservation.phone_number
      self.update!(phone_number: @number)

    rescue Exception => e
      puts "ERROR: #{e.message}"
    end
  end
end
app/models/reservation.rb
Confirm Reservation Method

app/models/reservation.rb

Once the reservation is confirmed, we need to purchase a Twilio number that the guest and host can use to communicate.

Purchase a Twilio Number

Here we use a Twilio REST API Client to search for and buy a new phone number to associate with the reservation. When we buy the number, we designate a Twilio application that will handle webhook requests when the new number receives an incoming call or text.

We then save the new phone number on our Reservation model, so when our app receives calls or texts to this number, we'll know which reservation the call or text belongs to.

Loading Code Samples...
Language
class Reservation < ActiveRecord::Base
  validates :name, presence: true
  validates :guest_phone, presence: true

  enum status: [ :pending, :confirmed, :rejected ]

  belongs_to :vacation_property
  belongs_to :user

  def notify_host(force = false)
    # Don't send the message if we have more than one and we aren't being forced
    if self.host.pending_reservations.length > 1 and !force
      return
    else
      message = "You have a new reservation request from #{self.name} for #{self.vacation_property.description}:

      '#{self.message}'

      Reply [accept] or [reject]."

      self.host.send_message_via_sms(message)
    end
  end

  def host
    @host = User.find(self.vacation_property[:user_id])
  end

  def guest
    @guest = User.find_by(phone_number: self.guest_phone)
  end

  def confirm!
    provision_phone_number
    self.update!(status: 1)
  end

  def reject!
    self.update!(status: 0)
  end

  def notify_guest
    if self.status_changed? && (self.status == :confirmed || self.status == :rejected)
      message = "Your recent request to stay at #{self.vacation_property.description} was #{self.status}."
      self.guest.send_message_via_sms(message)
    end
  end

  def send_message_to_guest(message)
    message = "From #{self.host.name}: #{message}"
    self.guest.send_message_via_sms(message, self.phone_number)
  end

  def send_message_to_host(message)
    message = "From guest #{self.guest.name}: #{message}"
    self.host.send_message_via_sms(message, self.phone_number)
  end

  private

  def provision_phone_number
    @client = Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])
    begin
      # Lookup numbers in host area code, if none than lookup from anywhere
      @numbers = @client.api.available_phone_numbers('US').local.list(area_code: self.host.area_code)
      if @numbers.empty?
        @numbers = @client.api.available_phone_numbers('US').local.list()
      end

      # Purchase the number & set the application_sid for voice and sms, will
      # tell the number where to route calls/sms
      @number = @numbers.first.phone_number
      @client.api.incoming_phone_numbers.create(
        phone_number: @number,
        voice_application_sid: ENV['ANONYMOUS_APPLICATION_SID'],
        sms_application_sid: ENV['ANONYMOUS_APPLICATION_SID']
      )

      # Set the reservation.phone_number
      self.update!(phone_number: @number)

    rescue Exception => e
      puts "ERROR: #{e.message}"
    end
  end
end
app/models/reservation.rb
Provision Phone Number Method

app/models/reservation.rb

Now that each reservation has a Twilio Phone Number, we can see how the application will look up reservations as guest or host calls come in.

Find a reservation when a guest or host calls

In our controller, we create a filter which gets executed every time Twilio asks our application how to handle an incoming call or text. This filter finds and stores the correct reservation (the one associated with the anonymous number) as an instance variable that will be used as we connect the guest and host via voice or SMS.

Loading Code Samples...
Language
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
app/controllers/reservations_controller.rb
Find a Reservation

app/controllers/reservations_controller.rb

Next, let's see how to connect the guest and the host via SMS.

Connect the Guest and the Host via SMS

Our Twilio application should be configured to send HTTP requests to this controller method on any incoming text message. Our app responds with TwiML to tell Twilio what to do in response to the message.

If the initial message sent to the anonymous number was made by the host, we forward it on to the guest. But if the message was sent by the guest, we forward it to the host.

Loading Code Samples...
Language
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
app/controllers/reservations_controller.rb
Connect Via SMS

app/controllers/reservations_controller.rb

Let's see how to connect the guest and the host via phone call next.

Connect the Guest and Host via Phone Call

Our Twilio application will send HTTP requests to this method on any incoming voice call. Our app responds with TwiML instructions that tell Twilio to Play an introductory MP3 audio file and then Dial either the guest or host, depending on who initiated the call.

Loading Code Samples...
Language
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
app/controllers/reservations_controller.rb
Connect Via Phone Call

app/controllers/reservations_controller.rb

That's it! We've just implemented anonymous communications that allow your customers to connect while protecting their privacy with the help of the Twilio Ruby Helper Library.

Where to Next?

If you're a Ruby developer working with Twilio, you might want to check out these other tutorials.

Part 1 of this Tutorial: Workflow Automation

Increase your rate of response by automating the workflows that are key to your business.

Appointment Reminders

Send your customers a text message when they have an upcoming appointment - this tutorial shows you how to do it from a background job.

Did this help?

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio to let us know what you think.

Jarod Reyes
Kat King
Jose Oliveros
David Prothero
Andrew Baker
Hector Ortega
Agustin Camino

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
class Reservation < ActiveRecord::Base
  validates :name, presence: true
  validates :guest_phone, presence: true

  enum status: [ :pending, :confirmed, :rejected ]

  belongs_to :vacation_property
  belongs_to :user

  def notify_host(force = false)
    # Don't send the message if we have more than one and we aren't being forced
    if self.host.pending_reservations.length > 1 and !force
      return
    else
      message = "You have a new reservation request from #{self.name} for #{self.vacation_property.description}:

      '#{self.message}'

      Reply [accept] or [reject]."

      self.host.send_message_via_sms(message)
    end
  end

  def host
    @host = User.find(self.vacation_property[:user_id])
  end

  def guest
    @guest = User.find_by(phone_number: self.guest_phone)
  end

  def confirm!
    provision_phone_number
    self.update!(status: 1)
  end

  def reject!
    self.update!(status: 0)
  end

  def notify_guest
    if self.status_changed? && (self.status == :confirmed || self.status == :rejected)
      message = "Your recent request to stay at #{self.vacation_property.description} was #{self.status}."
      self.guest.send_message_via_sms(message)
    end
  end

  def send_message_to_guest(message)
    message = "From #{self.host.name}: #{message}"
    self.guest.send_message_via_sms(message, self.phone_number)
  end

  def send_message_to_host(message)
    message = "From guest #{self.guest.name}: #{message}"
    self.host.send_message_via_sms(message, self.phone_number)
  end

  private

  def provision_phone_number
    @client = Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])
    begin
      # Lookup numbers in host area code, if none than lookup from anywhere
      @numbers = @client.api.available_phone_numbers('US').local.list(area_code: self.host.area_code)
      if @numbers.empty?
        @numbers = @client.api.available_phone_numbers('US').local.list()
      end

      # Purchase the number & set the application_sid for voice and sms, will
      # tell the number where to route calls/sms
      @number = @numbers.first.phone_number
      @client.api.incoming_phone_numbers.create(
        phone_number: @number,
        voice_application_sid: ENV['ANONYMOUS_APPLICATION_SID'],
        sms_application_sid: ENV['ANONYMOUS_APPLICATION_SID']
      )

      # Set the reservation.phone_number
      self.update!(phone_number: @number)

    rescue Exception => e
      puts "ERROR: #{e.message}"
    end
  end
end
class Reservation < ActiveRecord::Base
  validates :name, presence: true
  validates :guest_phone, presence: true

  enum status: [ :pending, :confirmed, :rejected ]

  belongs_to :vacation_property
  belongs_to :user

  def notify_host(force = false)
    # Don't send the message if we have more than one and we aren't being forced
    if self.host.pending_reservations.length > 1 and !force
      return
    else
      message = "You have a new reservation request from #{self.name} for #{self.vacation_property.description}:

      '#{self.message}'

      Reply [accept] or [reject]."

      self.host.send_message_via_sms(message)
    end
  end

  def host
    @host = User.find(self.vacation_property[:user_id])
  end

  def guest
    @guest = User.find_by(phone_number: self.guest_phone)
  end

  def confirm!
    provision_phone_number
    self.update!(status: 1)
  end

  def reject!
    self.update!(status: 0)
  end

  def notify_guest
    if self.status_changed? && (self.status == :confirmed || self.status == :rejected)
      message = "Your recent request to stay at #{self.vacation_property.description} was #{self.status}."
      self.guest.send_message_via_sms(message)
    end
  end

  def send_message_to_guest(message)
    message = "From #{self.host.name}: #{message}"
    self.guest.send_message_via_sms(message, self.phone_number)
  end

  def send_message_to_host(message)
    message = "From guest #{self.guest.name}: #{message}"
    self.host.send_message_via_sms(message, self.phone_number)
  end

  private

  def provision_phone_number
    @client = Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])
    begin
      # Lookup numbers in host area code, if none than lookup from anywhere
      @numbers = @client.api.available_phone_numbers('US').local.list(area_code: self.host.area_code)
      if @numbers.empty?
        @numbers = @client.api.available_phone_numbers('US').local.list()
      end

      # Purchase the number & set the application_sid for voice and sms, will
      # tell the number where to route calls/sms
      @number = @numbers.first.phone_number
      @client.api.incoming_phone_numbers.create(
        phone_number: @number,
        voice_application_sid: ENV['ANONYMOUS_APPLICATION_SID'],
        sms_application_sid: ENV['ANONYMOUS_APPLICATION_SID']
      )

      # Set the reservation.phone_number
      self.update!(phone_number: @number)

    rescue Exception => e
      puts "ERROR: #{e.message}"
    end
  end
end
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end
class ReservationsController < ApplicationController
  skip_before_filter  :verify_authenticity_token, only: [:accept_or_reject, :connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_action :set_twilio_params, only: [:connect_guest_to_host_sms, :connect_guest_to_host_voice]
  before_filter :authenticate_user, only: [:index]

  # GET /reservations
  def index
    @reservations = current_user.reservations.all
  end

  # GET /reservations/new
  def new
    @reservation = Reservation.new
  end

  def create
    @vacation_property = VacationProperty.find(params[:reservation][:property_id])
    @reservation = @vacation_property.reservations.create(reservation_params)

    if @reservation.save
      flash[:notice] = "Sending your reservation request now."
      @reservation.host.check_for_reservations_pending
      redirect_to @vacation_property
    else
      flash[:danger] = @reservation.errors
    end
  end

  # webhook for twilio incoming message from host
  def accept_or_reject
    incoming = params[:From]
    sms_input = params[:Body].downcase
    begin
      @host = User.find_by(phone_number: incoming)
      @reservation = @host.pending_reservation
      if sms_input == "accept" || sms_input == "yes"
        @reservation.confirm!
      else
        @reservation.reject!
      end

      @host.check_for_reservations_pending

      sms_reponse = "You have successfully #{@reservation.status} the reservation."
      respond(sms_reponse)
    rescue Exception => e
      puts "ERROR: #{e.message}"
      sms_reponse = "Sorry, it looks like you don't have any reservations to respond to."
      respond(sms_reponse)
    end
  end

  # webhook for twilio to anonymously connect the two parties
  def connect_guest_to_host_sms
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(:body => @message, :to => @outgoing_number)
    render text: response.to_s
  end

  # webhook for twilio -> TwiML for voice calls
  def connect_guest_to_host_voice
    # Guest -> Host
    if @reservation.guest.phone_number == @incoming_phone
      @outgoing_number = @reservation.host.phone_number

    # Host -> Guest
    elsif @reservation.host.phone_number == @incoming_phone
      @outgoing_number = @reservation.guest.phone_number
    end
    response = Twilio::TwiML::VoiceResponse.new
    response.play(url: "http://howtodocs.s3.amazonaws.com/howdy-tng.mp3")
    response.dial(number: @outgoing_number)

    render text: response.to_s
  end


  private
    # Send an SMS back to the Subscriber
    def respond(message)
      response = Twilio::TwiML::MessagingResponse.new
      response.message(body: message)

      render text: response.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def reservation_params
      params.require(:reservation).permit(:name, :guest_phone, :message)
    end

    # Load up Twilio parameters
    def set_twilio_params
      @incoming_phone = params[:From]
      @message = params[:Body]
      anonymous_phone_number = params[:To]
      @reservation = Reservation.where(phone_number: anonymous_phone_number).first
    end

end