~singpolyma/jmp-pay

ref: a45fe0f4d168bb8d3ea3e6cdeb499616de860bcf jmp-pay/bin/cancel_expired_customers -rwxr-xr-x 3.5 KiB
a45fe0f4Stephen Paul Weber Pull old port ins (from v1 days) from config file 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/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)