call_attempt.rb

  1# frozen_string_literal: true
  2
  3require "value_semantics/monkey_patched"
  4
  5require_relative "tts_template"
  6require_relative "low_balance"
  7
  8class CallAttempt
  9	EXPENSIVE_ROUTE = {
 10		"usd_beta_unlimited-v20210223" => 0.9,
 11		"cad_beta_unlimited-v20210223" => 1.1
 12	}.freeze
 13
 14	def self.for(customer, other_tel, rate, usage, direction:, **kwargs)
 15		kwargs.merge!(direction: direction)
 16		included_credit = [customer.minute_limit.to_d - usage, 0].max
 17		if !rate || rate >= EXPENSIVE_ROUTE.fetch(customer.plan_name, 0.1)
 18			Unsupported.new(direction: direction)
 19		elsif included_credit + customer.balance < rate * 10
 20			NoBalance.for(customer, other_tel, rate, usage, **kwargs)
 21		else
 22			for_ask_or_go(customer, other_tel, rate, usage, **kwargs)
 23		end
 24	end
 25
 26	def self.for_ask_or_go(customer, otel, rate, usage, digits: nil, **kwargs)
 27		can_use = customer.minute_limit.to_d + customer.monthly_overage_limit
 28		kwargs.merge!(
 29			customer_id: customer.customer_id,
 30			from: customer.registered?.phone, to: otel
 31		)
 32		if digits != "1" && can_use - usage < rate * 10
 33			AtLimit.new(**kwargs)
 34		else
 35			new(**kwargs)
 36		end
 37	end
 38
 39	value_semantics do
 40		customer_id String
 41		from(/\A\+\d+\Z/)
 42		to(/\A\+\d+\Z/)
 43		call_id String
 44		direction Either(:inbound, :outbound)
 45	end
 46
 47	def to_render
 48		["#{direction}/connect", { locals: to_h }]
 49	end
 50
 51	def create_call(fwd, *args, &block)
 52		fwd.create_call(*args, &block)
 53	end
 54
 55	def as_json(*)
 56		{
 57			from: from,
 58			to: to,
 59			customer_id: customer_id
 60		}
 61	end
 62
 63	def to_json(*args)
 64		as_json.to_json(*args)
 65	end
 66
 67	class Unsupported
 68		value_semantics do
 69			direction Either(:inbound, :outbound)
 70		end
 71
 72		def view
 73			"#{direction}/unsupported"
 74		end
 75
 76		def tts
 77			TTSTemplate.new(view).tts(self)
 78		end
 79
 80		def to_render
 81			[view]
 82		end
 83
 84		def create_call(*); end
 85
 86		def as_json(*)
 87			{ tts: tts }
 88		end
 89
 90		def to_json(*args)
 91			as_json.to_json(*args)
 92		end
 93	end
 94
 95	class NoBalance
 96		def self.for(customer, other_tel, rate, usage, direction:, **kwargs)
 97			LowBalance.for(customer).then(&:notify!).then do |amount|
 98				if amount&.positive?
 99					CallAttempt.for(
100						customer.with_balance(customer.balance + amount),
101						other_tel, rate, usage, direction: direction, **kwargs
102					)
103				else
104					NoBalance.new(balance: customer.balance, direction: direction)
105				end
106			end
107		end
108
109		value_semantics do
110			balance Numeric
111			direction Either(:inbound, :outbound)
112		end
113
114		def view
115			"#{direction}/no_balance"
116		end
117
118		def tts
119			TTSTemplate.new(view).tts(self)
120		end
121
122		def to_render
123			[view, { locals: to_h }]
124		end
125
126		def create_call(*); end
127
128		def as_json(*)
129			{ tts: tts }
130		end
131
132		def to_json(*args)
133			as_json.to_json(*args)
134		end
135	end
136
137	class AtLimit
138		value_semantics do
139			customer_id String
140			from(/\A\+\d+\Z/)
141			to(/\A\+\d+\Z/)
142			call_id String
143			direction Either(:inbound, :outbound)
144		end
145
146		def view
147			"#{direction}/at_limit"
148		end
149
150		def tts
151			TTSTemplate.new(view).tts(self)
152		end
153
154		def to_render
155			[view, { locals: to_h }]
156		end
157
158		def create_call(fwd, *args, &block)
159			fwd.create_call(*args, &block)
160		end
161
162		def as_json(*)
163			{
164				tts: tts,
165				from: from,
166				to: to,
167				customer_id: customer_id
168			}
169		end
170
171		def to_json(*args)
172			as_json.to_json(*args)
173		end
174	end
175end