# frozen_string_literal: true
require "ruby-bandwidth-iris"
Faraday.default_adapter = :em_synchrony
require_relative "form_template"
class TelSelections
THIRTY_DAYS = 60 * 60 * 24 * 30
def initialize(redis: REDIS)
@redis = redis
end
def set(jid, tel)
@redis.setex("pending_tel_for-#{jid}", THIRTY_DAYS, tel)
end
def delete(jid)
@redis.del("pending_tel_for-#{jid}")
end
def [](jid)
@redis.get("pending_tel_for-#{jid}").then do |tel|
tel ? HaveTel.new(tel) : ChooseTel.new
end
end
class HaveTel
def initialize(tel)
@tel = tel
end
def choose_tel
EMPromise.resolve(@tel)
end
end
class ChooseTel
def choose_tel(error: nil)
Command.reply { |reply|
reply.allowed_actions = [:next]
reply.command << FormTemplate.render("tn_search", error: error)
}.then { |iq| choose_from_list(AvailableNumber.for(iq.form).tns) }
end
def choose_from_list(tns)
if tns.empty?
choose_tel(error: "No numbers found, try another search.")
else
Command.reply { |reply|
reply.allowed_actions = [:next]
reply.command << FormTemplate.render("tn_list", tns: tns)
}.then { |iq| iq.form.field("tel").value.to_s.strip }
end
end
class AvailableNumber
def self.for(form)
new(
Q
.for(form.field("q").value.to_s.strip).iris_query
.merge(enableTNDetail: true)
.merge(Quantity.for(form).iris_query)
)
end
def initialize(iris_query)
@iris_query = iris_query
end
def tns
Command.log.debug("BandwidthIris::AvailableNumber.list", @iris_query)
BandwidthIris::AvailableNumber.list(@iris_query).map(&Tn.method(:new))
end
class Quantity
def self.for(form)
rsm_max = form.find(
"ns:set/ns:max",
ns: "http://jabber.org/protocol/rsm"
).first
if rsm_max
new(rsm_max.content.to_i)
else
Default.new
end
end
def initialize(quantity)
@quantity = quantity
end
def iris_query
{ quantity: @quantity }
end
# NOTE: Gajim sends back the whole list on submit, so big
# lists can cause issues
class Default
def iris_query
{ quantity: 10 }
end
end
end
end
class Tn
attr_reader :tel
def initialize(full_number:, city:, state:, **)
@tel = "+1#{full_number}"
@locality = city
@region = state
end
def formatted_tel
@tel =~ /\A\+1(\d{3})(\d{3})(\d+)\Z/
"(#{$1}) #{$2}-#{$3}"
end
def option
op = Blather::Stanza::X::Field::Option.new(value: tel, label: to_s)
op << reference
op
end
def reference
Nokogiri::XML::Builder.new { |xml|
xml.reference(
xmlns: "urn:xmpp:reference:0",
begin: 0,
end: formatted_tel.length - 1,
type: "data",
uri: "tel:#{tel}"
)
}.doc.root
end
def to_s
"#{formatted_tel} (#{@locality}, #{@region})"
end
end
class Q
def self.register(regex, &block)
@queries ||= []
@queries << [regex, block]
end
def self.for(q)
@queries.each do |(regex, block)|
match_data = (q =~ regex)
return block.call($1 || $&, *$~.to_a[2..-1]) if match_data
end
raise "Format not recognized: #{q}"
end
def initialize(q)
@q = q
end
{
areaCode: [:AreaCode, /\A[2-9][0-9]{2}\Z/],
npaNxx: [:NpaNxx, /\A(?:[2-9][0-9]{2}){2}\Z/],
npaNxxx: [:NpaNxxx, /\A(?:[2-9][0-9]{2}){2}[0-9]\Z/],
zip: [:PostalCode, /\A\d{5}(?:-\d{4})?\Z/],
localVanity: [:LocalVanity, /\A~(.+)\Z/]
}.each do |k, args|
klass = const_set(
args[0],
Class.new(Q) do
define_method(:iris_query) do
{ k => @q }
end
end
)
args[1..-1].each do |regex|
register(regex) { |q| klass.new(q) }
end
end
class CityState
Q.register(/\A([^,]+)\s*,\s*([A-Z]{2})\Z/, &method(:new))
def initialize(city, state)
@city = city
@state = state
end
def iris_query
{ city: @city, state: @state }
end
end
end
end
end