twilio-rails 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -14
- data/app/controllers/twilio/rails/phone_controller.rb +6 -2
- data/app/controllers/twilio/rails/sms_controller.rb +6 -5
- data/app/jobs/twilio/rails/phone/attach_recording_job.rb +1 -0
- data/app/jobs/twilio/rails/phone/finished_call_job.rb +1 -0
- data/app/jobs/twilio/rails/phone/unanswered_call_job.rb +1 -0
- data/app/operations/twilio/rails/application_operation.rb +2 -1
- data/app/operations/twilio/rails/find_or_create_phone_caller_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/attach_recording_operation.rb +3 -2
- data/app/operations/twilio/rails/phone/base_operation.rb +1 -0
- data/app/operations/twilio/rails/phone/create_operation.rb +11 -8
- data/app/operations/twilio/rails/phone/find_operation.rb +1 -0
- data/app/operations/twilio/rails/phone/finished_call_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/receive_recording_operation.rb +3 -2
- data/app/operations/twilio/rails/phone/start_call_operation.rb +15 -13
- data/app/operations/twilio/rails/phone/twiml/after_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/twiml/base_operation.rb +11 -5
- data/app/operations/twilio/rails/phone/twiml/error_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/twiml/greeting_operation.rb +3 -2
- data/app/operations/twilio/rails/phone/twiml/invalid_phone_number_operation.rb +25 -0
- data/app/operations/twilio/rails/phone/twiml/prompt_operation.rb +6 -5
- data/app/operations/twilio/rails/phone/twiml/prompt_response_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/twiml/request_validation_failure_operation.rb +1 -0
- data/app/operations/twilio/rails/phone/twiml/timeout_operation.rb +5 -4
- data/app/operations/twilio/rails/phone/unanswered_call_operation.rb +2 -1
- data/app/operations/twilio/rails/phone/update_operation.rb +1 -0
- data/app/operations/twilio/rails/phone/update_response_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/base_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/create_operation.rb +2 -1
- data/app/operations/twilio/rails/sms/find_message_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/find_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/send_operation.rb +12 -12
- data/app/operations/twilio/rails/sms/twiml/base_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/twiml/error_operation.rb +1 -0
- data/app/operations/twilio/rails/sms/twiml/message_operation.rb +4 -3
- data/app/operations/twilio/rails/sms/update_message_operation.rb +1 -0
- data/lib/generators/twilio/rails/install/install_generator.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/initializer.rb +5 -8
- data/lib/generators/twilio/rails/install/templates/message.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/phone_call.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/phone_caller.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/recording.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/response.rb +1 -0
- data/lib/generators/twilio/rails/install/templates/sms_conversation.rb +1 -0
- data/lib/generators/twilio/rails/phone_tree/phone_tree_generator.rb +1 -0
- data/lib/generators/twilio/rails/sms_responder/sms_responder_generator.rb +1 -0
- data/lib/generators/twilio/rails/sms_responder/templates/responder.rb.erb +2 -2
- data/lib/tasks/rails_tasks.rake +6 -5
- data/lib/twilio/rails/client.rb +9 -8
- data/lib/twilio/rails/concerns/has_direction.rb +2 -1
- data/lib/twilio/rails/concerns/has_phone_number.rb +13 -4
- data/lib/twilio/rails/concerns/has_time_scopes.rb +1 -0
- data/lib/twilio/rails/configuration.rb +42 -40
- data/lib/twilio/rails/formatter.rb +35 -29
- data/lib/twilio/rails/models/message.rb +1 -0
- data/lib/twilio/rails/models/phone_call.rb +1 -0
- data/lib/twilio/rails/models/phone_caller.rb +4 -3
- data/lib/twilio/rails/models/recording.rb +1 -0
- data/lib/twilio/rails/models/response.rb +7 -6
- data/lib/twilio/rails/models/sms_conversation.rb +1 -0
- data/lib/twilio/rails/phone/base_tree.rb +8 -7
- data/lib/twilio/rails/phone/tree.rb +8 -7
- data/lib/twilio/rails/phone/tree_macros.rb +9 -9
- data/lib/twilio/rails/phone.rb +3 -2
- data/lib/twilio/rails/phone_number.rb +6 -5
- data/lib/twilio/rails/phone_number_formatter/north_america.rb +52 -0
- data/lib/twilio/rails/phone_number_formatter.rb +20 -0
- data/lib/twilio/rails/railtie.rb +6 -1
- data/lib/twilio/rails/sms/delegated_responder.rb +6 -5
- data/lib/twilio/rails/sms/responder.rb +3 -2
- data/lib/twilio/rails/sms.rb +3 -2
- data/lib/twilio/rails/version.rb +1 -1
- data/lib/twilio/rails.rb +12 -26
- metadata +20 -6
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
module SMS
|
@@ -12,7 +13,7 @@ module Twilio
|
|
12
13
|
from_number: params["From"].presence,
|
13
14
|
from_city: params["FromCity"].presence,
|
14
15
|
from_province: params["FromState"].presence,
|
15
|
-
from_country: params["FromCountry"].presence
|
16
|
+
from_country: params["FromCountry"].presence
|
16
17
|
)
|
17
18
|
conversation.save!
|
18
19
|
conversation
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
module SMS
|
@@ -19,7 +20,7 @@ module Twilio
|
|
19
20
|
input :messages, accepts: Array, type: :keyword, required: true
|
20
21
|
input :from_number, accepts: [String, Twilio::Rails::PhoneNumber], type: :keyword, required: false
|
21
22
|
|
22
|
-
TWILIO_UNSUBSCRIBED_ERROR_CODES = [
|
23
|
+
TWILIO_UNSUBSCRIBED_ERROR_CODES = [21610].freeze
|
23
24
|
|
24
25
|
# @param phone_caller_id [Integer] the id of the phone caller to send the message to.
|
25
26
|
# @param messages [Array<String>] the messages to send to the phone caller. It may be empty.
|
@@ -29,14 +30,14 @@ module Twilio
|
|
29
30
|
# @return [Twilio::Rails::Models::SMSConversation] the SMS conversation that was created and sent.
|
30
31
|
def execute
|
31
32
|
return nil if messages.blank?
|
32
|
-
raise Twilio::Rails::SMS::Error, "from_number=#{
|
33
|
+
raise Twilio::Rails::SMS::Error, "from_number=#{from_number} is not a valid phone number" if from_number.present? && !Twilio::Rails::PhoneNumberFormatter.coerce(from_number)
|
33
34
|
|
34
35
|
conversation = ::Twilio::Rails.config.sms_conversation_class.new(
|
35
36
|
number: calculated_from_number,
|
36
37
|
from_number: calculated_to_number,
|
37
38
|
from_city: phone_call&.from_city,
|
38
39
|
from_province: phone_call&.from_province,
|
39
|
-
from_country: phone_call&.from_country
|
40
|
+
from_country: phone_call&.from_country
|
40
41
|
)
|
41
42
|
conversation.save!
|
42
43
|
|
@@ -46,21 +47,20 @@ module Twilio
|
|
46
47
|
sid = Twilio::Rails::Client.send_message(
|
47
48
|
message: body,
|
48
49
|
to: calculated_to_number,
|
49
|
-
from: calculated_from_number
|
50
|
+
from: calculated_from_number
|
50
51
|
)
|
51
52
|
rescue Twilio::REST::RestError => e
|
52
53
|
if TWILIO_UNSUBSCRIBED_ERROR_CODES.include?(e.code)
|
53
|
-
Twilio::Rails.config.logger.tagged(self.class) { |l| l.warn("tried to send to unsubscribed and got Twilio::REST::RestError code=21610 phone_caller_id=#{
|
54
|
+
Twilio::Rails.config.logger.tagged(self.class) { |l| l.warn("tried to send to unsubscribed and got Twilio::REST::RestError code=21610 phone_caller_id=#{phone_caller.id} phone_number=#{calculated_to_number} message=#{body}") }
|
54
55
|
else
|
55
|
-
|
56
|
-
|
56
|
+
::Rails.error.report(e,
|
57
|
+
handled: false,
|
57
58
|
context: {
|
59
|
+
message: "Failed to send Twilio message. Got REST error response.",
|
58
60
|
to: calculated_to_number,
|
59
61
|
from: calculated_from_number,
|
60
|
-
phone_call_id: phone_call&.id
|
61
|
-
}
|
62
|
-
exception_binding: binding
|
63
|
-
)
|
62
|
+
phone_call_id: phone_call&.id
|
63
|
+
})
|
64
64
|
raise
|
65
65
|
end
|
66
66
|
end
|
@@ -85,7 +85,7 @@ module Twilio
|
|
85
85
|
|
86
86
|
def calculated_from_number
|
87
87
|
if from_number.present?
|
88
|
-
Twilio::Rails::
|
88
|
+
Twilio::Rails::PhoneNumberFormatter.coerce(from_number)
|
89
89
|
elsif phone_call
|
90
90
|
phone_call.number
|
91
91
|
else
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
module SMS
|
@@ -20,7 +21,7 @@ module Twilio
|
|
20
21
|
if body.present?
|
21
22
|
message = conversation.messages.build(
|
22
23
|
direction: "outbound",
|
23
|
-
body: body
|
24
|
+
body: body
|
24
25
|
)
|
25
26
|
|
26
27
|
message.save!
|
@@ -32,11 +33,11 @@ module Twilio
|
|
32
33
|
)
|
33
34
|
end
|
34
35
|
|
35
|
-
Twilio::Rails.config.logger.info("message_twiml: #{twiml_response
|
36
|
+
Twilio::Rails.config.logger.info("message_twiml: #{twiml_response}")
|
36
37
|
twiml_response.to_s
|
37
38
|
else
|
38
39
|
Twilio::Rails.config.logger.info("resply is blank, not sending message in response")
|
39
|
-
Twilio::Rails.config.logger.info("message_twiml: #{twiml_response
|
40
|
+
Twilio::Rails.config.logger.info("message_twiml: #{twiml_response}")
|
40
41
|
|
41
42
|
twiml = Twilio::TwiML::MessagingResponse.new
|
42
43
|
twiml.to_s
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
Twilio::Rails.setup do |config|
|
3
4
|
# These are the Twilio account credentials used to access the Twilio API. These should likely be configured in the
|
4
5
|
# encrypted Rails credentials or loaded from an ENV variable.
|
@@ -45,14 +46,6 @@ Twilio::Rails.setup do |config|
|
|
45
46
|
# [ "Bad text" ].any? { |regex| regex.match?(params["Body"]) }
|
46
47
|
# }
|
47
48
|
|
48
|
-
# A proc that will be called when an exception is raised in certain key points in the framework. This will never
|
49
|
-
# capture the exception, it will raise regardless, but it is a good spot to send an email or notify in chat
|
50
|
-
# if desired. The proc needs to accept `(exception, message, context, exception_binding)` as arguments. The
|
51
|
-
# default is `nil`, which means no action will be taken.
|
52
|
-
# config.exception_notifier = ->(exception, message, context, exception_binding) {
|
53
|
-
# MyChatClient.send_message("Error: #{ message } #{ exception.message } #{ context }")
|
54
|
-
# }
|
55
|
-
|
56
49
|
# Controls if recordings will be downloaded and attached to the `Recording` model in an ActiveStorage attachment.
|
57
50
|
# This is `true` by default, but can be set to `false` to disable all downloads. It can also be set to a `Proc` or
|
58
51
|
# callable that will receive the `Recording` instance and return a boolean for this specific instance. if reordings will be downloaded.
|
@@ -66,6 +59,10 @@ Twilio::Rails.setup do |config|
|
|
66
59
|
# the phone macros. Defaults to a list of common responses.
|
67
60
|
# config.no_responses = ["no"]
|
68
61
|
|
62
|
+
# An instance of class that will be used to format phone numbers. Defaults to `Twilio::Rails::PhoneNumberFormatter::NorthAmerica`
|
63
|
+
# for now but in the next major version it will be set to a formatter that can handle all countries.
|
64
|
+
# config.phone_number_formatter = Twilio::Rails::PhoneNumberFormatter::NorthAmerica.new
|
65
|
+
|
69
66
|
# The name of the model classes, as strings, that this application uses to represent the concepts stored in the DB.
|
70
67
|
# The generators will generate the models with the default names below, but they can be changed as the application
|
71
68
|
# may need.
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
class <%= class_name %>Responder < Twilio::Rails::SMS::DelegatedResponder
|
3
3
|
def handle?
|
4
|
-
raise
|
4
|
+
raise NoMethodError, "#{ self.class }#handle? must be implemented."
|
5
5
|
end
|
6
6
|
|
7
7
|
def reply
|
8
|
-
raise
|
8
|
+
raise NoMethodError, "#{ self.class }#reply must be implemented."
|
9
9
|
end
|
10
10
|
end
|
data/lib/tasks/rails_tasks.rake
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
namespace :twilio do
|
3
4
|
namespace :rails do
|
4
5
|
desc "Show the available values to config in Twilio"
|
@@ -9,7 +10,7 @@ namespace :twilio do
|
|
9
10
|
puts "Twilio::Rails.config.host is set to a test value. Set it in the `config/initializers/twilio_rails.rb` file."
|
10
11
|
else
|
11
12
|
http_methods = if Twilio::Rails.config.controller_http_methods.length == 1
|
12
|
-
"HTTP #{
|
13
|
+
"HTTP #{Twilio::Rails.config.controller_http_methods.first.to_s.upcase}"
|
13
14
|
else
|
14
15
|
"HTTP POST or HTTP GET"
|
15
16
|
end
|
@@ -22,18 +23,18 @@ namespace :twilio do
|
|
22
23
|
puts "You cannot yet configure `Voice & Fax' because There are no phone trees registered in this application."
|
23
24
|
puts "Register them in the `config/initializers/twilio_rails.rb` file if you want to handle phone calls, and run this task again to help configure Twilio."
|
24
25
|
else
|
25
|
-
puts "Under 'Voice & Fax' set 'A CALL COMES IN' to 'Webhook' with #{
|
26
|
+
puts "Under 'Voice & Fax' set 'A CALL COMES IN' to 'Webhook' with #{http_methods} and one of the following URLs:"
|
26
27
|
Twilio::Rails.config.phone_trees.all.each do |name, tree|
|
27
28
|
puts " #{tree.inbound_url}"
|
28
29
|
end
|
29
30
|
puts ""
|
30
31
|
puts "Under 'Voice & Fax' set 'CALL STATUS CHANGES' to following URL:"
|
31
|
-
puts " #{
|
32
|
+
puts " #{::Twilio::Rails.config.host}#{::Twilio::Rails::Engine.routes.url_helpers.phone_status_path(format: :xml)}"
|
32
33
|
end
|
33
34
|
|
34
35
|
puts ""
|
35
|
-
puts "Under 'Messaging' set 'A MESSAGE COMES IN' to 'Webhook' with #{
|
36
|
-
puts " #{
|
36
|
+
puts "Under 'Messaging' set 'A MESSAGE COMES IN' to 'Webhook' with #{http_methods} and the following URL:"
|
37
|
+
puts " #{::Twilio::Rails.config.host}#{::Twilio::Rails::Engine.routes.url_helpers.sms_message_path(format: :xml)}"
|
37
38
|
|
38
39
|
if Twilio::Rails.config.sms_responders.all.length == 0
|
39
40
|
puts "There are no SMS responders registered so they will not be handled."
|
data/lib/twilio/rails/client.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
# An abstraction over top of the `Twilio::REST` API client. Used to send SMS messages and start calls, as well as
|
@@ -10,7 +11,7 @@ module Twilio
|
|
10
11
|
def client
|
11
12
|
@twilio_client ||= Twilio::REST::Client.new(
|
12
13
|
Twilio::Rails.config.account_sid,
|
13
|
-
Twilio::Rails.config.auth_token
|
14
|
+
Twilio::Rails.config.auth_token
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
@@ -23,12 +24,12 @@ module Twilio
|
|
23
24
|
# @param from [String] the phone number to send the message from.
|
24
25
|
# @return [String] the SID returned from Twilio for the sent SMS message.
|
25
26
|
def send_message(message:, to:, from:)
|
26
|
-
Twilio::Rails.config.logger.tagged(self) { |l| l.info("[send_message] to=#{
|
27
|
+
Twilio::Rails.config.logger.tagged(self) { |l| l.info("[send_message] to=#{to} from=#{from} body='#{message}'") }
|
27
28
|
client.messages.create(
|
28
29
|
from: from,
|
29
30
|
to: to,
|
30
31
|
body: message,
|
31
|
-
status_callback: "#{
|
32
|
+
status_callback: "#{Twilio::Rails.config.host}#{::Twilio::Rails::Engine.routes.url_helpers.sms_status_path(format: :xml)}"
|
32
33
|
).sid
|
33
34
|
end
|
34
35
|
|
@@ -55,18 +56,18 @@ module Twilio
|
|
55
56
|
# @param answering_machine_detection [true, false] whether or not to enable answering machine detection.
|
56
57
|
# @return [String] the SID returned from Twilio for the started call.
|
57
58
|
def start_call(url:, to:, from:, answering_machine_detection: true)
|
58
|
-
Twilio::Rails.config.logger.tagged(self) { |l| l.info("[start_call] to=#{
|
59
|
+
Twilio::Rails.config.logger.tagged(self) { |l| l.info("[start_call] to=#{to} from=#{from} url=#{url} answering_machine_detection=#{!!answering_machine_detection}") }
|
59
60
|
client.calls.create(
|
60
61
|
from: from,
|
61
62
|
to: to,
|
62
63
|
url: url,
|
63
|
-
machine_detection: (
|
64
|
+
machine_detection: (answering_machine_detection ? "Enable" : "Disable"),
|
64
65
|
async_amd: true,
|
65
|
-
async_amd_status_callback: "#{
|
66
|
+
async_amd_status_callback: "#{Twilio::Rails.config.host}#{::Twilio::Rails::Engine.routes.url_helpers.phone_status_path(format: :xml, async_amd: "true")}",
|
66
67
|
async_amd_status_callback_method: "POST",
|
67
|
-
status_callback: "#{
|
68
|
+
status_callback: "#{Twilio::Rails.config.host}#{::Twilio::Rails::Engine.routes.url_helpers.phone_status_path(format: :xml)}",
|
68
69
|
status_callback_method: "POST",
|
69
|
-
status_callback_event: ["completed", "no-answer"]
|
70
|
+
status_callback_event: ["completed", "no-answer"]
|
70
71
|
# timeout: 30,
|
71
72
|
).sid
|
72
73
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
# Provides scopes, validations, and convenience methods for a model that has an attribute `direction` with
|
@@ -7,7 +8,7 @@ module Twilio
|
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
included do
|
10
|
-
validates :direction, inclusion: {
|
11
|
+
validates :direction, inclusion: {in: ["outbound", "inbound"]}
|
11
12
|
|
12
13
|
scope :outbound, -> { where(direction: "outbound") }
|
13
14
|
scope :inbound, -> { where(direction: "inbound") }
|
@@ -1,26 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
# Provides validations and reformatting on validation for a model that has an attribute `phone_number` that is
|
5
|
-
# that is unique and that matches {Twilio::Rails::
|
6
|
+
# that is unique and that matches {Twilio::Rails::PhoneNumberFormatter.coerce}.
|
6
7
|
module HasPhoneNumber
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
included do
|
10
|
-
validates :phone_number, uniqueness: {
|
11
|
+
validates :phone_number, uniqueness: {allow_blank: true, message: "already exists"}
|
11
12
|
|
12
13
|
before_validation :reformat_phone_number
|
13
14
|
end
|
14
15
|
|
15
16
|
def reformat_phone_number
|
16
|
-
current = Twilio::Rails::
|
17
|
+
current = Twilio::Rails::PhoneNumberFormatter.coerce(phone_number)
|
17
18
|
self.phone_number = current if current
|
18
19
|
|
19
20
|
true
|
20
21
|
end
|
21
22
|
|
22
23
|
def valid_north_american_phone_number?
|
23
|
-
Twilio::Rails
|
24
|
+
Twilio::Rails.deprecator.warn(<<~DEPRECATION.strip)
|
25
|
+
valid_north_american_phone_number? is deprecated and will be removed in the next major version.
|
26
|
+
Use valid_phone_number? instead. The configured phone_number_formatter can manage the region of the phone number.
|
27
|
+
DEPRECATION
|
28
|
+
Twilio::Rails::PhoneNumberFormatter.valid?(phone_number)
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid_phone_number?
|
32
|
+
Twilio::Rails::PhoneNumberFormatter.valid?(phone_number)
|
24
33
|
end
|
25
34
|
end
|
26
35
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Twilio
|
3
4
|
module Rails
|
4
5
|
class Configuration
|
5
6
|
# Raised in initialization if the configuration is invalid.
|
6
|
-
class Error < StandardError
|
7
|
+
class Error < StandardError; end
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@finalized = false
|
@@ -14,10 +15,9 @@ module Twilio
|
|
14
15
|
@account_sid = nil
|
15
16
|
@auth_token = nil
|
16
17
|
@spam_filter = nil
|
17
|
-
@exception_notifier = nil
|
18
18
|
@attach_recordings = true
|
19
|
-
@yes_responses = [
|
20
|
-
@no_responses = [
|
19
|
+
@yes_responses = ["yes", "accept", "ya", "yeah", "true", "ok", "okay", "yep", "yup", "yes please"]
|
20
|
+
@no_responses = ["no", "naw", "nah", "reject", "decline", "negative", "not", "false", "nope", "no thank you", "know"]
|
21
21
|
@message_class_name = "Message"
|
22
22
|
@message_class = nil
|
23
23
|
@phone_call_class_name = "PhoneCall"
|
@@ -33,12 +33,11 @@ module Twilio
|
|
33
33
|
@phone_trees = PhoneTreeRegistry.new
|
34
34
|
@sms_responders = SMSResponderRegistry.new
|
35
35
|
@host = if ::Rails.configuration&.action_controller&.default_url_options
|
36
|
-
"#{
|
37
|
-
else
|
38
|
-
nil
|
36
|
+
"#{::Rails.configuration.action_controller.default_url_options[:protocol]}://#{::Rails.configuration.action_controller.default_url_options[:host]}"
|
39
37
|
end
|
40
38
|
@controller_http_methods = [:get, :post]
|
41
39
|
@include_phone_macros = []
|
40
|
+
@phone_number_formatter = Twilio::Rails::PhoneNumberFormatter::NorthAmerica.new
|
42
41
|
end
|
43
42
|
|
44
43
|
# This is the phone number that will be used to send SMS messages or start Phone Calls. It must be first configured
|
@@ -75,14 +74,6 @@ module Twilio
|
|
75
74
|
# @return [Proc] a proc that will be called to filter messages, or `nil` if no filter is set.
|
76
75
|
attr_accessor :spam_filter
|
77
76
|
|
78
|
-
# A proc that will be called when an exception is raised in certain key points in the framework. This will never
|
79
|
-
# capture the exception, it will raise regardless, but it is a good spot to send an email or notify in chat
|
80
|
-
# if desired. The proc needs to accept `(exception, message, context, exception_binding)` as arguments. The
|
81
|
-
# default is `nil`, which means no action will be taken.
|
82
|
-
#
|
83
|
-
# @return [Proc] a proc that will be called when an exception is raised in certain key points in the framework.
|
84
|
-
attr_accessor :exception_notifier
|
85
|
-
|
86
77
|
# Controls if recordings will be downloaded and attached to the `Recording` model in an ActiveStorage attachment.
|
87
78
|
# This is `true` by default, but can be set to `false` to disable all downloads. It can also be set to a `Proc` or
|
88
79
|
# callable that will receive the `Recording` instance and return a boolean for this specific instance. A typical
|
@@ -155,8 +146,7 @@ module Twilio
|
|
155
146
|
def host_domain
|
156
147
|
return nil unless host.present?
|
157
148
|
value = host.gsub(/\Ahttps?:\/\//, "")
|
158
|
-
value
|
159
|
-
value
|
149
|
+
value.gsub(/:\d+\z/, "")
|
160
150
|
end
|
161
151
|
|
162
152
|
# The HTTP methods that Twilio will use to call into the app. Defaults to `[:get, :post]` but can be restricted
|
@@ -195,6 +185,19 @@ module Twilio
|
|
195
185
|
end
|
196
186
|
end
|
197
187
|
|
188
|
+
# An instance of the class which will validate and format phone numbers. This is used internally to decide what
|
189
|
+
# phone numbers are valid, how to format them, how to parse them, how to display them, and what country assumptions
|
190
|
+
# to make.
|
191
|
+
#
|
192
|
+
# This class must implement four methods:
|
193
|
+
# * coerce(string)
|
194
|
+
# * valid?(string)
|
195
|
+
# * to_param(string)
|
196
|
+
# * display(string)
|
197
|
+
#
|
198
|
+
# @return [Object]
|
199
|
+
attr_accessor :phone_number_formatter
|
200
|
+
|
198
201
|
# Flags that the configuration has been setup and should be validated and finalized.
|
199
202
|
# If this is not called, the framework will not work, but the Railtie will not prevent
|
200
203
|
# the application from starting.
|
@@ -217,12 +220,12 @@ module Twilio
|
|
217
220
|
raise Error, "`auth_token` must be set" if @auth_token.blank?
|
218
221
|
raise Error, "`logger` must be set" if @logger.blank?
|
219
222
|
raise Error, "`spam_filter` must be callable" if @spam_filter && !@spam_filter.respond_to?(:call)
|
220
|
-
raise Error, "`
|
221
|
-
raise Error,
|
222
|
-
raise Error,
|
223
|
-
raise Error, "`
|
224
|
-
raise Error, "`
|
225
|
-
raise Error, "`
|
223
|
+
raise Error, "`yes_responses` must be an array" unless @yes_responses.is_a?(Array)
|
224
|
+
raise Error, "`no_responses` must be an array" unless @no_responses.is_a?(Array)
|
225
|
+
raise Error, "`host` #{@host.inspect} is not a valid URL of the format https://example.com without the trailing slash" unless /\Ahttps?:\/\/[a-z0-9\-\.:]+\Z/i.match?(@host)
|
226
|
+
raise Error, "`controller_http_methods` must be an array containing one or both of `:get` and `:post` but was #{@controller_http_methods.inspect}" unless [[:get], [:post], [:get, :post], [:post, :get]].any? { |v| @controller_http_methods == v }
|
227
|
+
raise Error, "`include_phone_macros` must be a module, but received #{@include_phone_macros.inspect}" unless @include_phone_macros.all? { |mod| mod.is_a?(Module) }
|
228
|
+
raise Error, "`phone_number_formatter` must be set" unless @phone_number_formatter
|
226
229
|
nil
|
227
230
|
end
|
228
231
|
|
@@ -241,13 +244,13 @@ module Twilio
|
|
241
244
|
:response_class_name,
|
242
245
|
:sms_conversation_class_name,
|
243
246
|
:message_class_name,
|
244
|
-
:recording_class_name
|
247
|
+
:recording_class_name
|
245
248
|
].each do |attribute|
|
246
|
-
value =
|
249
|
+
value = send(attribute)
|
247
250
|
raise Error, "`#{attribute}` must be set to a string name" if value.blank? || !value.is_a?(String)
|
248
251
|
begin
|
249
252
|
klass = value.constantize
|
250
|
-
instance_variable_set("@#{
|
253
|
+
instance_variable_set("@#{attribute.to_s.gsub("_name", "")}", klass)
|
251
254
|
rescue NameError
|
252
255
|
raise Error, "`#{attribute}` must be a valid class name but could not be found or constantized"
|
253
256
|
end
|
@@ -290,7 +293,7 @@ module Twilio
|
|
290
293
|
# @yield [nil] if a block is passed, it will be called and the result will be used as the value.
|
291
294
|
# @yieldreturn [Class, String, Proc] containing the Class to be lazily initialized when {#finalize!} is called.
|
292
295
|
# @return [nil]
|
293
|
-
def register(klass_or_proc=nil, &block)
|
296
|
+
def register(klass_or_proc = nil, &block)
|
294
297
|
raise Error, "Must pass either a param or a block" unless klass_or_proc.present? ^ block.present?
|
295
298
|
value = klass_or_proc || block
|
296
299
|
|
@@ -305,7 +308,7 @@ module Twilio
|
|
305
308
|
# @param [String, Symbol] name of the phone tree or SMS responder to find.
|
306
309
|
# @return [Class] the phone tree or SMS responder class.
|
307
310
|
def for(name)
|
308
|
-
@registry[name.to_s] || raise(error_class, "
|
311
|
+
@registry[name.to_s] || raise(error_class, "Name '#{name}' has not been registered and cannot be found.")
|
309
312
|
end
|
310
313
|
|
311
314
|
# Returns all the phone trees or SMS responders as a read-only hash, keyed by name.
|
@@ -318,7 +321,7 @@ module Twilio
|
|
318
321
|
private
|
319
322
|
|
320
323
|
def add_to_registry(value)
|
321
|
-
raise
|
324
|
+
raise NoMethodError
|
322
325
|
end
|
323
326
|
|
324
327
|
def error_class
|
@@ -335,14 +338,14 @@ module Twilio
|
|
335
338
|
value = value.call if value.respond_to?(:call)
|
336
339
|
begin
|
337
340
|
value = value.constantize if value.is_a?(String)
|
338
|
-
rescue NameError
|
339
|
-
raise(error_class, "Responder class '#{
|
341
|
+
rescue NameError
|
342
|
+
raise(error_class, "Responder class '#{value}' could not be constantized")
|
340
343
|
end
|
341
344
|
raise(error_class, "Responder cannot be blank") unless value.present?
|
342
|
-
raise(error_class, "Responder must be a class but got #{
|
345
|
+
raise(error_class, "Responder must be a class but got #{value.inspect}") unless value.is_a?(Class)
|
343
346
|
name = value.responder_name
|
344
347
|
raise(error_class, "Responder name cannot be blank") unless name.present?
|
345
|
-
raise(error_class, "Responder name '#{
|
348
|
+
raise(error_class, "Responder name '#{name}' is already registered") if @registry[name]
|
346
349
|
@registry[name] = value
|
347
350
|
end
|
348
351
|
|
@@ -360,16 +363,15 @@ module Twilio
|
|
360
363
|
value = value.call if value.respond_to?(:call)
|
361
364
|
begin
|
362
365
|
value = value.constantize if value.is_a?(String)
|
363
|
-
rescue NameError
|
364
|
-
raise(error_class, "Tree class '#{
|
366
|
+
rescue NameError
|
367
|
+
raise(error_class, "Tree class '#{value}' could not be constantized")
|
365
368
|
end
|
366
|
-
raise(error_class, "Tree cannot be blank #{
|
367
|
-
raise(error_class, "Tree is not a Twilio::Rails::Phone::BaseTree class #{
|
368
|
-
raise(error_class, "Tree is not a Twilio::Rails::Phone::BaseTree #{
|
369
|
+
raise(error_class, "Tree cannot be blank #{value}") unless value.present?
|
370
|
+
raise(error_class, "Tree is not a Twilio::Rails::Phone::BaseTree class #{value}") unless value.is_a?(Class)
|
371
|
+
raise(error_class, "Tree is not a Twilio::Rails::Phone::BaseTree #{value}") unless value.ancestors.include?(Twilio::Rails::Phone::BaseTree)
|
369
372
|
name = value.tree_name
|
370
373
|
raise(error_class, "Tree name cannot be blank") unless name.present?
|
371
|
-
raise(error_class, "Tree name '#{
|
372
|
-
klass = klass.constantize if klass.is_a?(String)
|
374
|
+
raise(error_class, "Tree name '#{name}' is already registered") if @registry[name]
|
373
375
|
@registry[name] = value.tree
|
374
376
|
end
|
375
377
|
|