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