twilio-rails 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|