customer_fwd.rb

  1# frozen_string_literal: true
  2
  3require "bandwidth"
  4require "value_semantics/monkey_patched"
  5require "uri"
  6
  7class CustomerFwd
  8	class InfiniteTimeout < StandardError
  9		attr_reader :fwd
 10
 11		def initialize(fwd)
 12			super "Infinite timeout"
 13			@fwd = fwd
 14		end
 15	end
 16
 17	def self.for(uri:, timeout: nil, voicemail_enabled: :default)
 18		timeout = Timeout.for(timeout, voicemail_enabled: voicemail_enabled)
 19
 20		fwd = if uri
 21			if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
 22				uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
 23			end
 24
 25			URIS.fetch(uri.split(":", 2).first&.to_sym) {
 26				raise "Unknown forward URI: #{uri}"
 27			}.new(uri: uri, timeout: timeout)
 28		end
 29
 30		fwd && !timeout.zero? ? fwd : None.new(uri: uri, timeout: timeout)
 31	end
 32
 33	class Timeout
 34		def self.for(s, voicemail_enabled: :default)
 35			return Infinite.new unless voicemail_enabled
 36
 37			if s.nil? || s.is_a?(Infinite) || s.to_i.negative?
 38				return new(25) if voicemail_enabled == true # ~5s / ring, 5 rings
 39
 40				return Infinite.new
 41			end
 42
 43			return s if s.is_a?(self)
 44
 45			new(s)
 46		end
 47
 48		def initialize(s)
 49			@timeout = [s.to_i, 300].min
 50		end
 51
 52		def zero?
 53			@timeout.zero?
 54		end
 55
 56		def infinite?
 57			false
 58		end
 59
 60		def to_i
 61			@timeout
 62		end
 63
 64		def to_s
 65			to_i.to_s
 66		end
 67
 68		def as_json(*)
 69			to_i
 70		end
 71
 72		def to_json(*args)
 73			as_json.to_json(*args)
 74		end
 75
 76		class Infinite < Timeout
 77			def initialize; end
 78
 79			def zero?
 80				false
 81			end
 82
 83			def infinite?
 84				1
 85			end
 86
 87			def to_i; end
 88
 89			def as_json(*)
 90				nil
 91			end
 92		end
 93	end
 94
 95	value_semantics do
 96		uri Either(/:/, NilClass)
 97		def_attr :timeout, Timeout, coerce: Timeout.method(:for)
 98	end
 99
100	def with(new_attrs)
101		CustomerFwd.for(to_h.merge(new_attrs))
102	end
103
104	def voicemail_enabled?
105		!timeout.infinite?
106	end
107
108	def create_call(account)
109		raise InfiniteTimeout, self if timeout.infinite?
110
111		request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
112			cc.to = to
113			cc.call_timeout = timeout.to_i
114			yield cc if block_given?
115		}
116		log_call(account, request)
117	end
118
119	def log_call(account, request)
120		result = BANDWIDTH_VOICE.create_call(account, body: request)
121		log.info "Create Bandwidth Call", { request: request, result: result }
122		result.data.call_id
123	end
124
125	def as_json(*)
126		{ timeout: timeout, uris: [uri] }
127	end
128
129	def to_json(*args)
130		as_json.to_json(*args)
131	end
132
133	class Tel < CustomerFwd
134		def initialize(values)
135			super
136			raise "Bad tel format: #{uri}" unless uri.match?(/\Atel:\+1\d{10}\Z/)
137		end
138
139		def to
140			uri.sub(/^tel:/, "")
141		end
142	end
143
144	class SIP < CustomerFwd
145		def to
146			uri
147		end
148	end
149
150	class XMPP < CustomerFwd
151		def to
152			jid = uri.sub(/^xmpp:/, "")
153			"sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
154		end
155	end
156
157	class None < CustomerFwd
158		def create_call(*); end
159
160		def to; end
161
162		def as_json(*)
163			[]
164		end
165	end
166
167	URIS = {
168		tel: Tel,
169		sip: SIP,
170		xmpp: XMPP
171	}.freeze
172end