~singpolyma/sgx-jmp

4acf6c3066768957f979b8c9b3a27ebfbef8d786 — Stephen Paul Weber 1 year, 10 months ago d622bc0
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.
M forms/configure_calls.rb => forms/configure_calls.rb +21 -12
@@ 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",

M lib/configure_calls_form.rb => lib/configure_calls_form.rb +4 -3
@@ 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

M lib/customer_fwd.rb => lib/customer_fwd.rb +49 -6
@@ 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

M lib/registration.rb => lib/registration.rb +1 -1
@@ 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

M sgx_jmp.rb => sgx_jmp.rb +1 -1
@@ 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|

A views/forward.slim => views/forward.slim +3 -0
@@ 0,0 1,3 @@
doctype xml
Response
	Forward to=fwd.to from=from callTimeout=300

A views/hangup.slim => views/hangup.slim +3 -0
@@ 0,0 1,3 @@
doctype xml
Response
	Hangup /

M web.rb => web.rb +5 -1
@@ 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) }