diff --git a/Gemfile b/Gemfile index 310c23f7856721d1ab35a1f6b74b2a8ccb497e4b..ca692e20b542b76b5f530430e3e4d4765de0bc2b 100644 --- a/Gemfile +++ b/Gemfile @@ -5,12 +5,14 @@ source "https://rubygems.org" gem "blather" gem "braintree" gem "dhall", ">= 0.5.3.fixed" +gem "em-http-request" gem "em_promise.rb" +gem "em-synchrony" gem "money-open-exchange-rates" gem "pg" gem "redis" gem "roda" -gem "ruby-bandwidth-iris" +gem "ruby-bandwidth-iris", git: "https://github.com/singpolyma/ruby-bandwidth-iris", branch: "list-port-ins" gem "sentry-ruby" gem "slim" diff --git a/bin/cancel_expired_customers b/bin/cancel_expired_customers new file mode 100755 index 0000000000000000000000000000000000000000..9d61d45d006f07eedfe625e21801c53738043c40 --- /dev/null +++ b/bin/cancel_expired_customers @@ -0,0 +1,161 @@ +#!/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 + } + } +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 + 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 + 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 + + iq + end + end + + def next + raise "Call info first" unless @sessionid + + 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) + 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)