twilio-rails 1.0.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.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +0 -0
  3. data/LICENSE +21 -0
  4. data/README.md +413 -0
  5. data/Rakefile +8 -0
  6. data/app/assets/config/twilio_rails_manifest.js +1 -0
  7. data/app/assets/stylesheets/twilio/rails/application.css +15 -0
  8. data/app/controllers/twilio/rails/application_controller.rb +6 -0
  9. data/app/controllers/twilio/rails/phone_controller.rb +112 -0
  10. data/app/controllers/twilio/rails/sms_controller.rb +64 -0
  11. data/app/helpers/twilio/rails/application_helper.rb +6 -0
  12. data/app/jobs/twilio/rails/application_job.rb +6 -0
  13. data/app/jobs/twilio/rails/phone/attach_recording_job.rb +15 -0
  14. data/app/jobs/twilio/rails/phone/finished_call_job.rb +15 -0
  15. data/app/jobs/twilio/rails/phone/unanswered_call_job.rb +15 -0
  16. data/app/mailers/twilio/rails/application_mailer.rb +8 -0
  17. data/app/models/twilio/rails/application_record.rb +7 -0
  18. data/app/operations/twilio/rails/application_operation.rb +21 -0
  19. data/app/operations/twilio/rails/find_or_create_phone_caller_operation.rb +29 -0
  20. data/app/operations/twilio/rails/phone/attach_recording_operation.rb +31 -0
  21. data/app/operations/twilio/rails/phone/base_operation.rb +21 -0
  22. data/app/operations/twilio/rails/phone/create_operation.rb +49 -0
  23. data/app/operations/twilio/rails/phone/find_operation.rb +14 -0
  24. data/app/operations/twilio/rails/phone/finished_call_operation.rb +17 -0
  25. data/app/operations/twilio/rails/phone/receive_recording_operation.rb +35 -0
  26. data/app/operations/twilio/rails/phone/start_call_operation.rb +53 -0
  27. data/app/operations/twilio/rails/phone/twiml/after_operation.rb +37 -0
  28. data/app/operations/twilio/rails/phone/twiml/base_operation.rb +50 -0
  29. data/app/operations/twilio/rails/phone/twiml/error_operation.rb +22 -0
  30. data/app/operations/twilio/rails/phone/twiml/greeting_operation.rb +22 -0
  31. data/app/operations/twilio/rails/phone/twiml/prompt_operation.rb +109 -0
  32. data/app/operations/twilio/rails/phone/twiml/prompt_response_operation.rb +29 -0
  33. data/app/operations/twilio/rails/phone/twiml/request_validation_failure_operation.rb +16 -0
  34. data/app/operations/twilio/rails/phone/twiml/timeout_operation.rb +48 -0
  35. data/app/operations/twilio/rails/phone/unanswered_call_operation.rb +22 -0
  36. data/app/operations/twilio/rails/phone/update_operation.rb +26 -0
  37. data/app/operations/twilio/rails/phone/update_response_operation.rb +38 -0
  38. data/app/operations/twilio/rails/sms/base_operation.rb +17 -0
  39. data/app/operations/twilio/rails/sms/create_operation.rb +23 -0
  40. data/app/operations/twilio/rails/sms/find_message_operation.rb +15 -0
  41. data/app/operations/twilio/rails/sms/find_operation.rb +15 -0
  42. data/app/operations/twilio/rails/sms/send_operation.rb +102 -0
  43. data/app/operations/twilio/rails/sms/twiml/base_operation.rb +11 -0
  44. data/app/operations/twilio/rails/sms/twiml/error_operation.rb +15 -0
  45. data/app/operations/twilio/rails/sms/twiml/message_operation.rb +49 -0
  46. data/app/operations/twilio/rails/sms/update_message_operation.rb +27 -0
  47. data/app/views/layouts/twilio/rails/application.html.erb +15 -0
  48. data/config/routes.rb +16 -0
  49. data/lib/generators/twilio/rails/install/USAGE +15 -0
  50. data/lib/generators/twilio/rails/install/install_generator.rb +34 -0
  51. data/lib/generators/twilio/rails/install/templates/initializer.rb +83 -0
  52. data/lib/generators/twilio/rails/install/templates/message.rb +4 -0
  53. data/lib/generators/twilio/rails/install/templates/migration.rb +89 -0
  54. data/lib/generators/twilio/rails/install/templates/phone_call.rb +4 -0
  55. data/lib/generators/twilio/rails/install/templates/phone_caller.rb +4 -0
  56. data/lib/generators/twilio/rails/install/templates/recording.rb +4 -0
  57. data/lib/generators/twilio/rails/install/templates/response.rb +4 -0
  58. data/lib/generators/twilio/rails/install/templates/sms_conversation.rb +4 -0
  59. data/lib/generators/twilio/rails/phone_tree/USAGE +8 -0
  60. data/lib/generators/twilio/rails/phone_tree/phone_tree_generator.rb +12 -0
  61. data/lib/generators/twilio/rails/phone_tree/templates/tree.rb.erb +13 -0
  62. data/lib/generators/twilio/rails/sms_responder/USAGE +8 -0
  63. data/lib/generators/twilio/rails/sms_responder/sms_responder_generator.rb +12 -0
  64. data/lib/generators/twilio/rails/sms_responder/templates/responder.rb.erb +10 -0
  65. data/lib/tasks/rails_tasks.rake +45 -0
  66. data/lib/twilio/rails/client.rb +75 -0
  67. data/lib/twilio/rails/concerns/has_direction.rb +25 -0
  68. data/lib/twilio/rails/concerns/has_phone_number.rb +27 -0
  69. data/lib/twilio/rails/concerns/has_time_scopes.rb +19 -0
  70. data/lib/twilio/rails/configuration.rb +380 -0
  71. data/lib/twilio/rails/engine.rb +11 -0
  72. data/lib/twilio/rails/formatter.rb +93 -0
  73. data/lib/twilio/rails/models/message.rb +21 -0
  74. data/lib/twilio/rails/models/phone_call.rb +132 -0
  75. data/lib/twilio/rails/models/phone_caller.rb +100 -0
  76. data/lib/twilio/rails/models/recording.rb +27 -0
  77. data/lib/twilio/rails/models/response.rb +153 -0
  78. data/lib/twilio/rails/models/sms_conversation.rb +29 -0
  79. data/lib/twilio/rails/phone/base_tree.rb +229 -0
  80. data/lib/twilio/rails/phone/tree.rb +229 -0
  81. data/lib/twilio/rails/phone/tree_macros.rb +147 -0
  82. data/lib/twilio/rails/phone.rb +12 -0
  83. data/lib/twilio/rails/phone_number.rb +29 -0
  84. data/lib/twilio/rails/railtie.rb +17 -0
  85. data/lib/twilio/rails/sms/delegated_responder.rb +97 -0
  86. data/lib/twilio/rails/sms/responder.rb +33 -0
  87. data/lib/twilio/rails/sms.rb +12 -0
  88. data/lib/twilio/rails/version.rb +5 -0
  89. data/lib/twilio/rails.rb +89 -0
  90. metadata +289 -0
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+ module Twilio
3
+ module Rails
4
+ module Models
5
+ # The core identity object, uniquely identifying an individual by their phone number. All ingoing or outgoing
6
+ # phone calls or SMS messages are associated to a phone caller. A phone caller is automatically created when any
7
+ # phone call or SMS message is sent or received.
8
+ module PhoneCaller
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Twilio::Rails::HasPhoneNumber
13
+
14
+ has_many :phone_calls, -> { order(created_at: :asc) }, class_name: Twilio::Rails.config.phone_call_class_name
15
+ has_many :responses, -> { order(created_at: :asc) }, through: :phone_calls, class_name: Twilio::Rails.config.response_class_name
16
+ end
17
+
18
+ class_methods do
19
+ # Finds a phone caller by phone number string or object, regardless of formatting. Returns `nil` if not found.
20
+ #
21
+ # @param phone_number_string [String, Twilio::Rails::PhoneNumber] The phone number to find the record.
22
+ # @return [Twilio::Rails::Models::PhoneCaller, nil] The phone caller record or `nil` if not found.
23
+ def for(phone_number_string)
24
+ phone_number = Twilio::Rails::Formatter.coerce_to_valid_phone_number(phone_number_string)
25
+ find_by(phone_number: phone_number) if phone_number.present?
26
+ end
27
+ end
28
+
29
+ # @return [String] A well formatted string with the city/state/country of the phone caller, if available.
30
+ def location
31
+ phone_calls.inbound.last&.location
32
+ end
33
+
34
+ # @return [Array<Twilio::Rails::Models::PhoneCall>] All inbound phone calls for the given phone tree or tree name.
35
+ def inbound_calls_for(tree)
36
+ tree = tree.is_a?(Twilio::Rails::Phone::Tree) ? tree.name : tree
37
+ phone_calls.inbound.tree(tree)
38
+ end
39
+
40
+ # @return [Array<Twilio::Rails::Models::PhoneCall>] All outbound phone calls for the given phone tree or tree name.
41
+ def outbound_calls_for(tree)
42
+ tree = tree.is_a?(Twilio::Rails::Phone::Tree) ? tree.name : tree
43
+ phone_calls.outbound.tree(tree)
44
+ end
45
+
46
+ # @return [Array<Twilio::Rails::Models::SmsConversation>] All SMS conversations for the phone caller.
47
+ def sms_conversations
48
+ Twilio::Rails.config.sms_conversation_class.phone_number(self.phone_number)
49
+ end
50
+
51
+ # Returns the digits as a `String` as entered through the keypad during a phone call as `gather:`. Returns
52
+ #`nil` if the response is not found, if the response has no digits, or if the response was a timeout. Can
53
+ # include both `*` and `#` characters if the caller pressed them.
54
+ #
55
+ # @param prompt [String, Symbol] The prompt handle to query.
56
+ # @param tree [String, Symbol, Twilio::Rails::Phone::Tree] The tree or name of the tree to query.
57
+ # @return [String, nil] The digits as entered by the caller or `nil` if not found or not present.
58
+ def response_digits(prompt:, tree:)
59
+ response = responses.tree(tree).where(prompt_handle: prompt, timeout: false).last
60
+ return nil unless response
61
+ response.digits
62
+ end
63
+
64
+ # Returns the digits as an `Integer` entered through the keypad during a phone call as `gather:`. Returns `nil`
65
+ # if the response is not found, if the response has no digits, if the response was a timeout, or if the response
66
+ # contains `*` or `#` characters. Useful for doing branching logic within a phone tree, such as "Press 2 for
67
+ # sales..." etc..
68
+ #
69
+ # @param prompt [String, Symbol] The prompt handle to query.
70
+ # @param tree [String, Symbol, Twilio::Rails::Phone::Tree] The tree or name of the tree to query.
71
+ # @return [Integer, nil] The digits as entered by the caller or `nil` if not found or not present.
72
+ def response_integer_digits(prompt:, tree:)
73
+ response = responses.tree(tree).where(prompt_handle: prompt, timeout: false).last
74
+ return nil unless response
75
+ response.integer_digits
76
+ end
77
+
78
+ # Checks if this phone caller has ever reached a response in a given phone tree. This is useful for building
79
+ # phone trees and determining if a phone caller has reached a certain point in the tree before or not.
80
+ #
81
+ # @param prompt [String, Symbol] The prompt handle to query.
82
+ # @param tree [String, Symbol, Twilio::Rails::Phone::Tree] The tree or name of the tree to query.
83
+ # @return [true, false] If the response has been reached or not.
84
+ def response_reached?(prompt:, tree:)
85
+ response_for(prompt: prompt, tree: tree).present?
86
+ end
87
+
88
+ # Finds the most recent {Twilio::Rails::Models::Response} for the given prompt and tree. This is useful for
89
+ # building phone trees and finding previous responses to prompts. Returns `nil` if no response is found.
90
+ #
91
+ # @param prompt [String, Symbol] The prompt handle to find the response for.
92
+ # @param tree [String, Symbol, Twilio::Rails::Phone::Tree] The tree or name of the tree in which to find the response.
93
+ # @return [Twilio::Rails::Models::Response, nil] The response or `nil` if not found.
94
+ def response_for(prompt:, tree:)
95
+ responses.tree(tree).where(prompt_handle: prompt).last
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module Twilio
3
+ module Rails
4
+ module Models
5
+ # A recording of a fragment of a phone call gathered from Twilio. See `gather: { type: :voice }` in the
6
+ # documentation for {Twilio::Rails::Phone::BaseTree}. Is associated to one {Twilio::Rails::Models::Response}.
7
+ # Attaches the audio file as an ActiveStorage attachment.
8
+ module Recording
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ belongs_to :phone_call, class_name: Twilio::Rails.config.phone_call_class_name
13
+
14
+ has_one :response, class_name: Twilio::Rails.config.response_class_name
15
+ has_one_attached :audio
16
+
17
+ scope :sid, ->(sid) { where(recording_sid: sid) }
18
+ end
19
+
20
+ # @return [Integer, nil] The length of the recording in seconds, or nil if unavailable.
21
+ def length_seconds
22
+ duration.to_i if duration.present?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+ module Twilio
3
+ module Rails
4
+ module Models
5
+ # A response object is created for every prompt in a phone call. It is associated to a
6
+ # {Twilio::Rails::Models::PhoneCall} in order, and contains transcriptions, digits, recordings, timestamps, and
7
+ # all other metadata.
8
+ module Response
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Twilio::Rails::HasTimeScopes
13
+
14
+ validates :prompt_handle, presence: true
15
+
16
+ belongs_to :phone_call, class_name: Twilio::Rails.config.phone_call_class_name
17
+ belongs_to :recording, required: false, class_name: Twilio::Rails.config.recording_class_name
18
+
19
+ delegate :phone_caller, to: :phone_call
20
+
21
+ scope :completed, -> { where(timeout: false) }
22
+ scope :recent_transcriptions, ->(number=5) { completed.order(created_at: :desc).where.not(transcription: nil).limit(number) }
23
+ scope :final_timeout_check, ->(count:, prompt_handle:) {
24
+ prompt(prompt_handle).order(created_at: :desc).limit(count)
25
+ }
26
+ scope :tree, ->(name) { joins(:phone_call).where(phone_calls: { tree_name: name }) }
27
+ scope :prompt, ->(prompt_handle) { where(prompt_handle: prompt_handle) }
28
+ scope :in_order, -> { reorder(created_at: :asc) }
29
+ scope :transcribed, -> { where(transcribed: true) }
30
+
31
+ after_commit :recalculate_phone_call_length, on: :create
32
+ end
33
+
34
+ # Checks if the response is for a given prompt or promts, and given tree or trees or tree names.
35
+ #
36
+ # @param tree [Twilio::Rails::Phone::Tree, String, Symbol, Array] The tree or tree name or an array of them.
37
+ # @param prompt [String, Symbol, Array] The prompt handle or an array of them.
38
+ # @return [true, false] true if the response is for the given prompt and tree.
39
+ def is?(tree:, prompt:)
40
+ trees = Array(tree).map { |t| t.is_a?(Twilio::Rails::Phone::Tree) ? t.name : t.to_s }
41
+
42
+ from?(tree: tree) && Array(prompt).map(&:to_s).reject(&:blank?).include?(self.prompt_handle)
43
+ end
44
+
45
+ # Checks if the response is for a given tree or trees or tree names.
46
+ #
47
+ # @param tree [Twilio::Rails::Phone::Tree, String, Symbol, Array] The tree or tree name or an array of them.
48
+ # @return [true, false] true if the response is for the given tree.
49
+ def from?(tree:)
50
+ trees = Array(tree).map { |t| t.is_a?(Twilio::Rails::Phone::Tree) ? t.name : t.to_s }
51
+
52
+ trees.include?(self.phone_call.tree_name)
53
+ end
54
+
55
+ # Returns the digits as an `Integer` entered through the keypad during a phone call as `gather:`. Returns `nil`
56
+ # if the response has no digits, or if the response contains `*` or `#` characters. Useful for doing branching
57
+ # logic within a phone tree, such as "Press 2 for sales..." etc..
58
+ #
59
+ # @return [Integer, nil] The digits as entered by the caller or `nil` if not found or not present.
60
+ def integer_digits
61
+ return nil unless digits.present?
62
+ return nil unless digits =~ /\A[0-9]+\Z/
63
+ digits.to_i
64
+ end
65
+
66
+ # Returns true if the digits entered through the keypad during a phone call as `gather:` contain only `*` or `#`
67
+ #
68
+ # @return [true, false] true if the digits are only `*` or `#`.
69
+ def pound_star?
70
+ !!(digits =~ /\A[#*]+\Z/)
71
+ end
72
+ alias_method :star_pound?, :pound_star?
73
+
74
+ # Checks if any of the passed in patterns match the transcription. Will always return false if the
75
+ # transcription is blank. Patterns can be a `String`, `Symbol`, `Regexp`, or an `Array` of any of those. Will
76
+ # raise `ArgumentError` if no transcriptions are passed in.
77
+ #
78
+ # @param patterns [String, Symbol, Regexp, Array] The patterns to match against.
79
+ # @return [true, false] true if any of the patterns match the transcription.
80
+ def transcription_matches?(*patterns)
81
+ patterns = Array(patterns).flatten
82
+ raise ArgumentError, "transcription must match against at least one pattern" if patterns.blank?
83
+
84
+ return false if transcription.blank?
85
+
86
+ patterns.each do |pattern|
87
+ case pattern
88
+ when Regexp
89
+ return true if pattern.match?(transcription)
90
+ when String, Symbol
91
+ return true if transcription.downcase.include?(pattern.to_s.downcase)
92
+ else
93
+ raise ArgumentError, "can only match a String or Regexp"
94
+ end
95
+ end
96
+
97
+ false
98
+ end
99
+
100
+ # Returns true if the transcription matches any of the configured "yes". Will return false if the transcription
101
+ # is blank. See {Twilio::Rails::Configuration#yes_responses} for the default values. It is possible for
102
+ # {#answer_yes?} and {#answer_no?} to both be false.
103
+ #
104
+ # @return [true, false] true if the transcription matches any of the configured "yes" responses.
105
+ def answer_yes?
106
+ transcription_matches?(Twilio::Rails.config.yes_responses)
107
+ end
108
+
109
+ # Returns true if the transcription matches any of the configured "no". Will return false if the transcription
110
+ # is blank. See {Twilio::Rails::Configuration#yes_responses} for the default values. It is possible for
111
+ # {#answer_yes?} and {#answer_no?} to both be false.
112
+ #
113
+ # @return [true, false] true if the transcription matches any of the configured "no" responses.
114
+ def answer_no?
115
+ transcription_matches?(Twilio::Rails.config.no_responses)
116
+ end
117
+
118
+ # Returns true if this response is the first time the caller has encountered the given prompt for this phone
119
+ # call. The parameter `include_timeouts` defaults to true and flags whether or not to include responses that
120
+ # are timeouts. If the response is unsaved it will always return false.
121
+ #
122
+ # @param include_timeouts [true, false] Whether or not to include timeouts responses.
123
+ # @return [true, false] if this is the first time the caller has encountered this prompt in this phone call.
124
+ def first_for_phone_call?(include_timeouts: true)
125
+ return false unless id
126
+ finder = phone_call.responses.prompt(prompt_handle).order(id: :asc)
127
+ finder = finder.where(timeout: false) if !include_timeouts
128
+ finder.first&.id == id
129
+ end
130
+
131
+ # Returns true if this response is the first time the caller has encountered the given prompt across *any* phone
132
+ # call. The parameter `include_timeouts` defaults to true and flags whether or not to include responses that
133
+ # are timeouts. If the response is unsaved it will always return false.
134
+ #
135
+ # @param include_timeouts [true, false] Whether or not to include timeouts responses.
136
+ # @return [true, false] if this is the first time the caller has encountered this prompt in any phone call.
137
+ def first_for_phone_caller?(include_timeouts: true)
138
+ return false unless id
139
+ finder = phone_caller.responses.prompt(prompt_handle).tree(phone_call.tree_name).order(id: :asc)
140
+ finder = finder.where(timeout: false) if !include_timeouts
141
+ finder.first&.id == id
142
+ end
143
+
144
+ private
145
+
146
+ def recalculate_phone_call_length
147
+ phone_call.recalculate_length
148
+ true
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Twilio
3
+ module Rails
4
+ module Models
5
+ # A conversation via SMS. Has many {Twilio::Rails::Models::Message}s. Each message has a direction and can be
6
+ # unrolled into a full conversation.
7
+ module SMSConversation
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ has_many :messages, -> { order(created_at: :asc) }, dependent: :destroy, class_name: Twilio::Rails.config.message_class_name
12
+
13
+ scope :recent, -> { reorder(created_at: :desc).limit(10) }
14
+ scope :phone_number, ->(number) { where(from_number: number) }
15
+ end
16
+
17
+ # @return [Twilio::Rails::Models::PhoneCaller] The phone caller associated with this conversation.
18
+ def phone_caller
19
+ Twilio::Rails.config.phone_caller_class.for(from_number)
20
+ end
21
+
22
+ # @return [String] A well formatted string with the city/state/country of the phone number if available.
23
+ def location
24
+ Twilio::Rails::Formatter.location(city: from_city, country: from_country, province: from_province)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+ module Twilio
3
+ module Rails
4
+ module Phone
5
+ # Base class for all phone trees which provides the DSL to define a tree. To define a phone tree start by
6
+ # generating a sublcass.
7
+ #
8
+ # rails generate twilio:rails:phone_tree LeaveFeedback
9
+ #
10
+ # This will create a new class in `app/phone_trees/leave_feedback_tree.rb` which will subclass this class. It must be
11
+ # registered with the framework in the initializer for it to be available. The generator does this.
12
+ #
13
+ # # config/initializers/twilio_rails.rb
14
+ # config.phone_trees.register { LeaveFeedbackTree }
15
+ #
16
+ # Then define the tree using the DSL methods provided by this class. For example:
17
+ #
18
+ # class LeaveFeedbackTree < Twilio::Rails::Phone::BaseTree
19
+ # voice "Polly.Matthew-Neural"
20
+ # final_timeout_message "Sorry, you don't appear to be there. Goodbye."
21
+ # unanswered_call ->(phone_call) { MyMailer.send_followup(phone_call).deliver_later }
22
+ # finished_call ->(phone_call) { MyMailer.send_followup(phone_call).deliver_later }
23
+ # invalid_phone_number "Sorry, we can only accept calls from North America. Goodbye."
24
+ #
25
+ # greeting message: "Hello, and thank you for calling.",
26
+ # prompt: :leave_feedback
27
+ #
28
+ # prompt :leave_feedback,
29
+ # message: "Please leave your feedback after the tone, and press pound when you are finished.",
30
+ # gather: {
31
+ # type: :voice,
32
+ # timeout: 30,
33
+ # transcribe: true,
34
+ # },
35
+ # after: ->(response) {
36
+ # if MyServiceObject.new(response.phone_caller).has_followup_message?
37
+ # { prompt: :followup_message }
38
+ # else
39
+ # {
40
+ # message: "Thank you for your feedback. Have a nice day.",
41
+ # hangup: true,
42
+ # }
43
+ # end
44
+ # }
45
+ #
46
+ # prompt :followup_message,
47
+ # message: ->(response) {
48
+ # [
49
+ # { play: "http://example.com/followup_message_sound.mp3" },
50
+ # { say: MyServiceObject.new(response.phone_caller).followup_message_text },
51
+ # ]
52
+ # },
53
+ # after: {
54
+ # message: "Thank you. Have a nice day.",
55
+ # hangup: true,
56
+ # }
57
+ # end
58
+ class BaseTree
59
+ class << self
60
+ # Accepts a string with the voice parameter to be used throughout the phone tree, unless overridden for a
61
+ # given message. See the Twilio documentation for a list of available voices. The voices are dependent on
62
+ # locale, and can also accept Amazon Polly voices. The default is "male".
63
+ # https://www.twilio.com/docs/voice/twiml/say/text-speech
64
+ #
65
+ # @param voice_name [String] the name of the voice to use.
66
+ # @return [nil]
67
+ def voice(voice_name)
68
+ tree.config[:voice] = voice_name
69
+ nil
70
+ end
71
+
72
+ # The `message:` object that is played to the caller if the phone tree is expecting input but none is
73
+ # received. The default number of attempts before this is called is 3, configured in `final_timeout_attempts`.
74
+ # The default value is a simple "Goodbye."
75
+ def final_timeout_message(message)
76
+ tree.config[:final_timeout_message] = message
77
+ nil
78
+ end
79
+
80
+ # The entrypoint and first call for any incoming or outgoing phone call. It should only be called once as
81
+ # there is only one greeting. Subsequent calls will overwrite the earlier ones. It accepts an optional
82
+ # `message:` object which will be played to the caller. See the documentation for {.prompt} for what a message
83
+ # can contain. It then accepts a required `prompt:` which is the next prompt in the flow of the call.
84
+ #
85
+ # @param message [String, Hash, Array, Proc] The message to play to the caller.
86
+ # @param prompt [Symbol, Hash, Proc] The name of the next prompt.
87
+ # @return [nil]
88
+ def greeting(message: nil, prompt:)
89
+ tree.greeting = Twilio::Rails::Phone::Tree::After.new(message: message, prompt: prompt)
90
+ nil
91
+ end
92
+
93
+ # Defines a prompt in the phone tree. It accepts a required `name:` which must be unique within the tree.
94
+ #
95
+ # It accepts an optional `message:` object which will be played to the caller. A message must be one of:
96
+ # * `nil`: No message will be played.
97
+ # * `String`: A string that will be read to the caller using text-to-speech. This is the equivalent of
98
+ # `{ say: "a string" }`.
99
+ # * `Hash`: A hash that contain only the following keys:
100
+ # * `{ say: "hello" }`: A string that will be read to the caller using text-to-speech. Optionally also
101
+ # accepts a `voice:` key which will override the default voice for this message.
102
+ # * `{ play: "https://example.com/sound.mp3" }`: A URL to a "wav" or "mp3" file that will be played to the
103
+ # caller via Twilio.
104
+ # * `{ pause: 1 }`: Pause in silence for the given number of seconds.
105
+ # * `Array`: An array that contains any number of the above.
106
+ # * `Proc`: A proc that will be called when the prompt is reached. The prompt will receive the previous
107
+ # {Twilio::Rails::Models::Response} instance as an argument. The proc must return one of the above.
108
+ #
109
+ # It accepts an optional `gather:` object which, if present, will be used to gather input from the caller.
110
+ # After the optional message completes, the gather will collect the input. The gather object must be a hash
111
+ # with one of the following types:
112
+ # * `{ type: :digits }`: Collects one or more integer digits from the caller's keypad. Those digits will be
113
+ # stored in the `digits` field of the {Twilio::Rails::Models::Response} instance. Digits accepts the
114
+ # following configuration keys:
115
+ # * `:timeout`: The number of seconds to wait for input before timing out and falling through to the
116
+ # `after:`. The default is 5.
117
+ # * `:number`: The number of digits to collect. The default is 1.
118
+ # * `:interrupt`: Weather pressing a key will interrupt the message, or if the gather will not start
119
+ # until the message is complete. The default is `false`.
120
+ # * `{ type: :voice }`: Records and collects the phone caller's voice as audio. The framework handles
121
+ # updating the `url` and fetching the audio file as a {Twilio::Rails::Models::Recording} attached to the
122
+ # response instance. However, this all happens asynchronously with no guarantee of time or success. Voice
123
+ # accepts the following configuration keys:
124
+ # * `:length`: The number of seconds to record. The default is 10.
125
+ # * `:beep`: A boolean if the gather is preceeded by a beep. The default is `true`.
126
+ # * `:transcribe`: A boolean if Twilio should attempt to transcribe the audio and send it back as text. The
127
+ # framework handles this all asynchronously and will update the `transcription` field. Default is `false`.
128
+ # * `:profanity_filter`: Replaces any profanity in the transcription with ***. Default is `false`.
129
+ # * `{ type: :speech }`: Collects speech from the caller as text using a specialzed model designed to better
130
+ # identify utterances of digits, commands, conversations, etc.. This does not collect audio files, and is
131
+ # more expensive, but returns the `response.transcription` in realtime which can be immediately used in
132
+ # the call flow. Speech accepts the following configuration keys:
133
+ # * `:language`: The language of the caller. The default is "en-US".
134
+ # * `:speech_model`: The model to use for the speech recognition. Accepts "default", "numbers_and_commands",
135
+ # "phone_call", "experimental_conversations", and "experimental_utterances". The default is "default".
136
+ # See the Twilio documentation for details. https://www.twilio.com/docs/voice/twiml/gather#speechmodel
137
+ # * `:enhanced`: A boolean if the enhanced model should be used. Results are better but the cost is higher.
138
+ # The default is `false`.
139
+ # * `:timeout`: The number of seconds to wait for input before timing out and falling through to the
140
+ # `after:`. The default is 5.
141
+ # * `:speech_timeout`: Accepts an interger or "auto". If both this and `timeout` is set, Twilio will use
142
+ # `timeout` for digits and this value for voice or speech.
143
+ # * `:profanity_filter`: Replaces any profanity in the transcription with ***. Default is `false`.
144
+ #
145
+ # It accepts an required `after:` object which, will be used to determine the next prompt in the call flow.
146
+ # The after object must be one of:
147
+ # * `Symbol`: The name of the next prompt in the flow.
148
+ # * `Hash`: A hash that contains:
149
+ # * `:message`: An optional message to play before the next prompt. See the above documentation for what
150
+ # a message can contain.
151
+ # * `:prompt`: The name of the next prompt in the flow.
152
+ # * `:hangup`: A boolean if the call should be hung up after the message. Only one of prompt and hangup can
153
+ # be present.
154
+ # * `Proc`: A proc that will be called after the message and gather have been called. The proc will receive
155
+ # the current {Twilio::Rails::Models::Response} instance as an argument. The proc must return one of the
156
+ # above.
157
+ def prompt(prompt_name, message: nil, gather: nil, after:)
158
+ tree.prompts[prompt_name] = Twilio::Rails::Phone::Tree::Prompt.new(name: prompt_name, message: message, gather: gather, after: after)
159
+ nil
160
+ end
161
+
162
+ # Accepts a proc which will be called when a call goes unanswered, or is answered by an answering machine.
163
+ # The proc will be called asynchronously in a job. The proc will be passed the
164
+ # {Twilio::Rails::Models::PhoneCall} instance for the call. It is called after the call has been completed
165
+ # so cannot control the flow of the call. It is intended to be used as a hook to handle application logic for
166
+ # unanswered calls. The default is `nil` and no action is taken.
167
+ #
168
+ # @param proc [Proc] the proc to call when a call goes unanswered, must accept a phone call instance.
169
+ # @return [nil]
170
+ def unanswered_call(proc)
171
+ tree.unanswered_call = proc
172
+ nil
173
+ end
174
+
175
+ # Accepts a proc which will be called when a call is completed, unanswered, or any state not in progress.
176
+ # The proc will be called asynchronously in a job. The proc will be passed the
177
+ # {Twilio::Rails::Models::PhoneCall} instance for the call. It is called after the call has been completed
178
+ # so cannot control the flow of the call. It is intended to be used as a hook to handle application logic for
179
+ # when a call finishes and is no longer in progress. The default is `nil` and no action is taken.
180
+ #
181
+ # @param proc [Proc] the proc to call when a call is finished, must accept a phone call instance.
182
+ # @return [nil]
183
+ def finished_call(proc)
184
+ tree.finished_call = proc
185
+ nil
186
+ end
187
+
188
+ # The `message:` object that played to the caller if a call from an invalid phone number is received. The
189
+ # important case here is a number from outside of North America. This is currently a limitation of the
190
+ # framework. The default is `nil` and no action is taken. See the documentation for {.prompt} for what a
191
+ # message object can contain.
192
+ #
193
+ # @param message [String, Hash, Array, Proc] The message to play to the caller.
194
+ def invalid_phone_number(message)
195
+ tree.config[:invalid_phone_number] = message
196
+ nil
197
+ end
198
+
199
+ # The string name of the tree used to look it up and identify it in the registry and used in the routes. It
200
+ # must be unique and use URL safe characters. It defaults to the class name but can be overridden here.
201
+ #
202
+ # @return [String] the name of the tree.
203
+ def tree_name
204
+ self.name.demodulize.underscore.sub(/_tree\z/, "")
205
+ end
206
+
207
+ # The instance of {Twilio::Rails::Phone::Tree} built from the DSL. Should be treated as read-only. Used
208
+ # mostly internally by the framework. It is named according to {.tree_name}.
209
+ #
210
+ # @return [Twilio::Rails::Phone::Tree] the tree instance.
211
+ def tree
212
+ @tree ||= Twilio::Rails::Phone::Tree.new(tree_name)
213
+ end
214
+
215
+ # A module of convenience macros used in prompts to prevent repetition or wordy tasks. See
216
+ # {Twilio::Rails::Phone::TreeMacros} for the available methods. The macros do not have access to any instance
217
+ # information and must be passed any context they require.
218
+ #
219
+ # Additional macros can be added through the application config. See {Twilio::Rails::Configuration#include_phone_macros}.
220
+ #
221
+ # @return [Twilio::Rails::Phone::TreeMacros] the module of macros.
222
+ def macros
223
+ Twilio::Rails::Phone::TreeMacros
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end