From 4acf6c3066768957f979b8c9b3a27ebfbef8d786 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 31 Aug 2022 14:16:39 -0500 Subject: [PATCH] Allow infinite timeout / disabled voicemail Use Forward to hand control completely to the target call. If something ends up at our voicemail due to error or similar, just hang up. --- forms/configure_calls.rb | 33 ++++++++++++++-------- lib/configure_calls_form.rb | 7 +++-- lib/customer_fwd.rb | 55 +++++++++++++++++++++++++++++++++---- lib/registration.rb | 2 +- sgx_jmp.rb | 2 +- views/forward.slim | 3 ++ views/hangup.slim | 3 ++ web.rb | 6 +++- 8 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 views/forward.slim create mode 100644 views/hangup.slim diff --git a/forms/configure_calls.rb b/forms/configure_calls.rb index a9312e2..b225d71 100644 --- a/forms/configure_calls.rb +++ b/forms/configure_calls.rb @@ -2,21 +2,30 @@ form! title "Configure Calls" field( - var: "fwd[timeout]", - type: "text-single", - datatype: "xs:integer", - label: "Seconds to ring before voicemail", - description: "One ring is ~5 seconds. Negative means ring forever.", - value: @customer.fwd.timeout.to_i.to_s -) - -field( - var: "voicemail_transcription", + var: "fwd[voicemail_enabled]", type: "boolean", - label: "Voicemail transcription", - value: @customer.transcription_enabled.to_s + label: "Voicemail enabled", + value: @customer.fwd.voicemail_enabled? ) +if @customer.fwd.voicemail_enabled? + field( + var: "fwd[timeout]", + type: "text-single", + datatype: "xs:integer", + label: "Seconds to ring before voicemail", + description: "One ring is ~5 seconds. Negative means ring forever.", + value: @customer.fwd.timeout.to_i.to_s + ) + + field( + var: "voicemail_transcription", + type: "boolean", + label: "Voicemail transcription", + value: @customer.transcription_enabled.to_s + ) +end + field( var: "fwd[uri]", type: "list-single", diff --git a/lib/configure_calls_form.rb b/lib/configure_calls_form.rb index 9a3519f..9aab2b7 100644 --- a/lib/configure_calls_form.rb +++ b/lib/configure_calls_form.rb @@ -33,8 +33,9 @@ protected end def parse_fwd(fwd_from_form) - fwd_from_form.reduce(@customer.fwd) do |fwd, (var, val)| - fwd.with(var.to_sym => val.strip) - end + @customer.fwd.with(fwd_from_form.each_with_object({}) { |(var, val), args| + args[var.to_sym] = + var == "voicemail_enabled" ? ["1", "true"].include?(val) : val&.strip + }) end end diff --git a/lib/customer_fwd.rb b/lib/customer_fwd.rb index 22a83c6..65e85a2 100644 --- a/lib/customer_fwd.rb +++ b/lib/customer_fwd.rb @@ -5,8 +5,17 @@ require "value_semantics/monkey_patched" require "uri" class CustomerFwd - def self.for(uri:, timeout:) - timeout = Timeout.new(timeout) + class InfiniteTimeout < StandardError + attr_reader :fwd + + def initialize(fwd) + super "Infinite timeout" + @fwd = fwd + end + end + + def self.for(uri:, timeout: nil, voicemail_enabled: :default) + timeout = Timeout.for(timeout, voicemail_enabled: voicemail_enabled) fwd = if uri if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/ @@ -22,18 +31,32 @@ class CustomerFwd end class Timeout - def self.new(s) - s.is_a?(self) ? s : super + def self.for(s, voicemail_enabled: :default) + return Infinite.new unless voicemail_enabled + + if s.nil? || s.is_a?(Infinite) || s.to_i.negative? + return new(25) if voicemail_enabled == true # ~5s / ring, 5 rings + + return Infinite.new + end + + return s if s.is_a?(self) + + new(s) end def initialize(s) - @timeout = s.nil? || s.to_i.negative? || s.to_i > 300 ? 300 : s.to_i + @timeout = [s.to_i, 300].min end def zero? @timeout.zero? end + def infinite? + false + end + def to_i @timeout end @@ -41,18 +64,38 @@ class CustomerFwd def to_s to_i.to_s end + + class Infinite < Timeout + def initialize; end + + def zero? + false + end + + def infinite? + 1 + end + + def to_i; end + end end value_semantics do uri Either(/:/, NilClass) - def_attr :timeout, Timeout, coerce: Timeout.method(:new) + def_attr :timeout, Timeout, coerce: Timeout.method(:for) end def with(new_attrs) CustomerFwd.for(to_h.merge(new_attrs)) end + def voicemail_enabled? + !timeout.infinite? + end + def create_call(account) + raise InfiniteTimeout, self if timeout.infinite? + request = Bandwidth::ApiCreateCallRequest.new.tap { |cc| cc.to = to cc.call_timeout = timeout.to_i diff --git a/lib/registration.rb b/lib/registration.rb index e08d4eb..01e8820 100644 --- a/lib/registration.rb +++ b/lib/registration.rb @@ -470,7 +470,7 @@ class Registration EMPromise.all([ REDIS.del("pending_tel_for-#{@customer.jid}"), Bwmsgsv2Repo.new.put_fwd(@customer.customer_id, @tel, CustomerFwd.for( - uri: "xmpp:#{@customer.jid}", timeout: 25 # ~5s / ring, 5 rings + uri: "xmpp:#{@customer.jid}", voicemail_enabled: true )) ]) }.then do diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 3ed4c1b..5f53281 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -527,7 +527,7 @@ Command.new( Command.new( "ogm", "Record Voicemail Greeting", - list_for: ->(fwd: nil, **) { !!fwd }, + list_for: ->(fwd: nil, **) { fwd&.voicemail_enabled? }, customer_repo: CustomerRepo.new(sgx_repo: Bwmsgsv2Repo.new) ) { Command.customer.then do |customer| diff --git a/views/forward.slim b/views/forward.slim new file mode 100644 index 0000000..55e6910 --- /dev/null +++ b/views/forward.slim @@ -0,0 +1,3 @@ +doctype xml +Response + Forward to=fwd.to from=from callTimeout=300 diff --git a/views/hangup.slim b/views/hangup.slim new file mode 100644 index 0000000..68e146d --- /dev/null +++ b/views/hangup.slim @@ -0,0 +1,3 @@ +doctype xml +Response + Hangup / diff --git a/web.rb b/web.rb index 35c169a..d79a182 100644 --- a/web.rb +++ b/web.rb @@ -296,8 +296,10 @@ class Web < Roda sgx_repo: Bwmsgsv2Repo.new, ogm_url: nil ).then { |c| - c.ogm(params["from"]) + c.ogm(params["from"]) if c.fwd.voicemail_enabled? }.then { |ogm| + next render :hangup unless ogm + render :voicemail, locals: { ogm: ogm } } end @@ -339,6 +341,8 @@ class Web < Roda outbound_transfers[params["callId"]] = call render :ring, locals: { duration: 300 } + }.catch_only(CustomerFwd::InfiniteTimeout) { |e| + render :forward, locals: { fwd: e.fwd, from: params["from"] } }.catch { |e| log_error(e) unless e == :voicemail render :redirect, locals: { to: inbound_calls_path(:voicemail) } -- 2.45.2