#!/usr/bin/ruby
# frozen_string_literal: true
require "date"
require "dhall"
require "em_promise"
require "pg"
require "ruby-bandwidth-iris"
require "set"
require_relative "../lib/blather_notify"
require_relative "../lib/to_form"
CONFIG = Dhall.load(<<-DHALL).sync
(#{ARGV[0]}) : {
sgx_jmp: Text,
creds: {
account: Text,
username: Text,
password: Text
},
notify_using: {
jid: Text,
password: Text,
target: Text -> Text,
body: Text -> Text -> Text
},
old_port_ins: List Text
}
DHALL
Faraday.default_adapter = :em_synchrony
BandwidthIris::Client.global_options = {
account_id: CONFIG[:creds][:account],
username: CONFIG[:creds][:username],
password: CONFIG[:creds][:password]
}
using ToForm
db = PG.connect(dbname: "jmp")
db.type_map_for_results = PG::BasicTypeMapForResults.new(db)
db.type_map_for_queries = PG::BasicTypeMapForQueries.new(db)
BlatherNotify.start(
CONFIG[:notify_using][:jid],
CONFIG[:notify_using][:password]
)
def format(item)
if item.respond_to?(:note) && item.note && item.note.text != ""
item.note.text
elsif item.respond_to?(:to_xml)
item.to_xml
else
item.inspect
end
end
ported_in_promise = Promise.new
EM.schedule do
Fiber.new {
begin
tns = Set.new(CONFIG[:old_port_ins])
page = BandwidthIris::PortIn.list(
page: 1,
size: 1000,
status: :complete
)
while page
page.each_slice(250) do |orders|
EMPromise.all(
orders.map { |order|
EMPromise.resolve(nil).then { order.tns }
}
).sync.each { |chunk| tns += chunk.map { |tn| "+1#{tn}" } }
end
page = page.next
end
raise "ported_in looks wrong" if tns.length < 250
ported_in_promise.fulfill(tns)
rescue StandardError
ported_in_promise.reject($!)
end
}.resume
end
class ExpiringCustomer
def initialize(customer_id)
@customer_id = customer_id
end
def info
BlatherNotify.execute(
"customer info",
{ q: @customer_id }.to_form(:submit)
).then do |iq|
@sessionid = iq.sessionid
unless iq.form.field("customer_id")
raise "#{@customer_id} not found"
end
@info = iq
end
end
def next
raise "Call info first" unless @sessionid && @info
return EMPromise.reject(:skip) unless @info.form.field("tel")
BlatherNotify.write_with_promise(BlatherNotify.command(
"customer info",
@sessionid
))
end
def cancel_account
raise "Call info first" unless @sessionid
BlatherNotify.write_with_promise(BlatherNotify.command(
"customer info",
@sessionid,
action: :complete,
form: { action: "cancel_account" }.to_form(:submit)
))
end
end
one = Queue.new
ported_in_promise.then { |ported_in|
EM::Iterator.new(db.exec(
<<-SQL
SELECT customer_id, expires_at FROM customer_plans
WHERE expires_at < LOCALTIMESTAMP - INTERVAL '1 month'
SQL
), 3).each(nil, -> { one << :done }) do |row, iter|
customer = ExpiringCustomer.new(row["customer_id"])
customer.info.then { |iq|
if ported_in.include?(iq.form.field("tel")&.value&.to_s) &&
row["expires_at"] > (Date.today << 12).to_time
puts "#{row['customer_id']} ported in, skipping"
EMPromise.reject(:skip)
else
customer.next
end
}.then {
customer.cancel_account
}.then { |result|
puts format(result)
iter.next
}.catch do |err|
next iter.next if err == :skip
one << (err.is_a?(Exception) ? err : RuntimeError.new(format(err)))
end
end
}.catch do |err|
one << (err.is_a?(Exception) ? err : RuntimeError.new(format(err)))
end
result = one.pop
raise result if result.is_a?(Exception)