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