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