diff --git a/app/controllers/pager_tree/integrations/live_call_routing/twilio/v3_controller.rb b/app/controllers/pager_tree/integrations/live_call_routing/twilio/v3_controller.rb index 852b7e7..813e46b 100644 --- a/app/controllers/pager_tree/integrations/live_call_routing/twilio/v3_controller.rb +++ b/app/controllers/pager_tree/integrations/live_call_routing/twilio/v3_controller.rb @@ -27,6 +27,12 @@ def queue_status head :ok end + def call_status + ::PagerTree::Integrations.deferred_request_class.constantize.perform_later_from_request!(request) + + head :ok + end + def queue_status_deferred(deferred_request) params = deferred_request.params @@ -42,6 +48,20 @@ def queue_status_deferred(deferred_request) @integration.adapter_process_queue_status_deferred end + def call_status_deferred(deferred_request) + params = deferred_request.params + + id = params.dig("id") + @integration = find_integration(id) + + deferred_request.account_id = @integration.account_id + @integration.adapter_source_log = @integration.logs.create!(level: :info, format: :json, message: deferred_request.request) if @integration.log_incoming_requests? + @integration.adapter_incoming_request_params = params + @integration.adapter_incoming_deferred_request = deferred_request + + @integration.adapter_process_call_status_deferred + end + private def set_integration diff --git a/app/models/pager_tree/integrations/live_call_routing/twilio/v3.rb b/app/models/pager_tree/integrations/live_call_routing/twilio/v3.rb index 416f51f..1fd0339 100644 --- a/app/models/pager_tree/integrations/live_call_routing/twilio/v3.rb +++ b/app/models/pager_tree/integrations/live_call_routing/twilio/v3.rb @@ -7,6 +7,7 @@ class LiveCallRouting::Twilio::V3 < Integration {key: :api_region, type: :string, default: "ashburn.us1"}, {key: :force_input, type: :boolean, default: false}, {key: :record, type: :boolean, default: false}, + {key: :send_straight_to_voicemail, type: :boolean, default: false}, {key: :record_email, type: :string, default: ""}, {key: :banned_phone, type: :string, default: ""}, {key: :dial_pause, type: :integer}, @@ -30,6 +31,7 @@ class LiveCallRouting::Twilio::V3 < Integration validates :option_api_region, inclusion: {in: API_REGIONS} validates :option_force_input, inclusion: {in: [true, false]} validates :option_record, inclusion: {in: [true, false]} + validates :option_send_straight_to_voicemail, inclusion: {in: [true, false]} validates :option_max_wait_time, numericality: {greater_than_or_equal_to: 30, less_than_or_equal_to: 3600}, allow_nil: true validate :validate_record_emails @@ -40,6 +42,7 @@ class LiveCallRouting::Twilio::V3 < Integration self.option_api_region ||= "ashburn.us1" self.option_force_input ||= false self.option_record ||= false + self.option_send_straight_to_voicemail ||= false self.option_record_email ||= "" self.option_banned_phone ||= "" end @@ -189,6 +192,33 @@ def adapter_response_incoming return adapter_controller&.render(xml: _twiml.to_xml) end + + if adapter_alert.meta["live_call_status_callback_set"] != true + begin + # give us status updates on the call, so we can clean up if they hang up before leaving a message + _call.update( + status_callback: PagerTree::Integrations::Engine.routes.url_helpers.call_status_live_call_routing_twilio_v3_url(id, thirdparty_id: _thirdparty_id), + status_callback_method: "POST", + url: adapter_controller&.url_for || endpoint + ) + + adapter_alert.meta["live_call_status_callback_set"] = true + adapter_alert.save! + rescue ::Twilio::REST::RestError => e + if e.code == 21220 + # 21220 - Unable to update record. Call is not in-progress. Cannot redirect. + adapter_alert.logs.create!(message: "Updating the call for status callbacks failed. The caller has already hung up.") + else + adapter_alert.logs.create!(message: "Updating the call for status callbacks failed. #{e.message}") + end + + adapter_alert.logs.create!(message: "Marking alert as resolved due to call update failure.") + adapter_alert.resolve!(self, force: true) + end + + return adapter_controller&.render(xml: _twiml.to_xml) + end + # if this was attached to a router if !adapter_alert.meta["live_call_router_team_prefix_ids"].present? && routers.size > 0 && account.subscription_feature_routers? adapter_alert.logs.create!(message: "Routed to router. Attempting to get a list of teams...") @@ -235,6 +265,12 @@ def adapter_response_incoming return adapter_controller&.render(xml: _twiml.to_xml) end + if option_record && option_send_straight_to_voicemail && adapter_alert.meta["live_call_send_straight_to_voicemail"].nil? + # flag this call to send straight to voicemail + adapter_alert.meta["live_call_send_straight_to_voicemail"] = true + adapter_alert.save! + end + if !adapter_alert.meta["live_call_welcome"] && option_welcome_media.present? adapter_alert.logs.create!(message: "Play welcome media to caller.") _twiml.play(url: option_welcome_media.url) @@ -245,6 +281,17 @@ def adapter_response_incoming if selected_team adapter_alert.logs.create!(message: "Caller selected team '#{selected_team.name}'.") if _teams_size > 1 || option_force_input + if adapter_alert.meta["live_call_send_straight_to_voicemail"] == true + adapter_alert.destination_teams = [selected_team] + adapter_alert.save! + + adapter_alert.logs.create!(message: "Send caller straight to voicemail (integration option).") + + _twiml.redirect(PagerTree::Integrations::Engine.routes.url_helpers.dropped_live_call_routing_twilio_v3_url(id, thirdparty_id: _thirdparty_id), method: "POST") + + return adapter_controller&.render(xml: _twiml.to_xml) + end + adapter_alert.logs.create!(message: "Play please wait media to caller.") _twiml.play(url: option_please_wait_media_url) friendly_name = adapter_alert.id @@ -358,11 +405,20 @@ def adapter_response_dropped LiveCallRouting::Twilio::V3Mailer.with(email: email, alert: adapter_alert, from: adapter_incoming_request_params.dig("From"), recording_url: recording_url).call_recording.deliver_later end end + + if adapter_alert.meta["live_call_send_straight_to_voicemail"] == true + adapter_alert.logs.create!(message: "Call was sent straight to voicemail and caller left a message. Routing the alert.") + + # kick off the alert workflow + adapter_alert.route_later + adapter_alert.logs.create!(message: "Successfully enqueued alert team workflow.") + end elsif option_record adapter_alert.logs.create!(message: "No one is available to answer this call. Requesting voicemail recording.") _twiml.play(url: option_no_answer_media_url) _twiml.record(max_length: 60) else + # A friendly goodbye (when the integration has been configured to not record) if option_no_answer_no_record_media_url.present? adapter_alert.logs.create!(message: "No one is available to answer this call. Play media. Hangup on caller.") _twiml.play(url: option_no_answer_no_record_media_url) @@ -383,15 +439,33 @@ def adapter_process_queue_status_deferred if queue_result == "hangup" self.adapter_alert = alerts.find_by(thirdparty_id: _thirdparty_id) adapter_alert.logs.create!(message: "Caller hungup while waiting in queue.") - adapter_alert.resolve!(self) + adapter_alert.resolve!(self, force: true) queue_destroy end adapter_source_log&.save! end + def adapter_process_call_status_deferred + call_status = adapter_incoming_request_params.dig("CallStatus") + adapter_source_log&.sublog("Processing call status #{call_status}") + + if ["completed"].include?(call_status) + self.adapter_alert = alerts.find_by(thirdparty_id: _thirdparty_id) + + if adapter_alert.present? && adapter_alert.meta["live_call_send_straight_to_voicemail"] == true && !adapter_alert.additional_data.any? { |x| x["label"] == "Voicemail" } + adapter_alert.logs.create!(message: "Caller hung up without leaving a message. Marking alert as resolved.") + adapter_alert.resolve!(self, force: true) + elsif adapter_alert.present? && adapter_alert.meta["live_call_send_straight_to_voicemail"] != true && !adapter_alert.meta["live_call_queue_sid"].present? + adapter_alert.logs.create!(message: "Caller hungup before being put in a queue. Marking alert as resolved.") + adapter_alert.resolve!(self, force: true) + end + end + end + def adapter_process_outgoing return unless adapter_alert.source_id == id + return if adapter_alert.meta["live_call_send_straight_to_voicemail"] == true event = adapter_outgoing_event.event_name.to_s if event == "alert_acknowledged" diff --git a/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_form_options.html.erb b/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_form_options.html.erb index a342aff..b51b412 100644 --- a/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_form_options.html.erb +++ b/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_form_options.html.erb @@ -1,4 +1,9 @@ -
<%== t(".option_record_hint_html") %>
<%== t(".option_send_straight_to_voicemail_hint_html") %>
+<%== t(".option_record_emails_list_hint_html") %>
diff --git a/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_show_options.html.erb b/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_show_options.html.erb index c4f81ec..9ecc125 100644 --- a/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_show_options.html.erb +++ b/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_show_options.html.erb @@ -138,6 +138,17 @@- <%= link_to email, "mailto:#{email}" %> +<% if integration.option_record %> +
+ <%= link_to email, "mailto:#{email}" %> +
+ <% end %> ++ (<%= t("pager_tree.integrations.common.none") %>)
- <% end %> -- (<%= t("pager_tree.integrations.common.none") %>) -
-