1# frozen_string_literal: true
2
3require "erb"
4require "ruby-bandwidth-iris"
5require "securerandom"
6
7require_relative "./alt_top_up_form"
8require_relative "./bandwidth_tn_order"
9require_relative "./bandwidth_tn_reservation_repo"
10require_relative "./command"
11require_relative "./em"
12require_relative "./invites_repo"
13require_relative "./oob"
14require_relative "./parent_code_repo"
15require_relative "./proxied_jid"
16require_relative "./tel_selections"
17require_relative "./welcome_message"
18require_relative "./sim_kind"
19require_relative "./onboarding"
20
21def reload_customer(customer)
22 EMPromise.resolve(nil).then do
23 Command.execution.customer_repo.find(customer.customer_id)
24 end
25end
26
27def handle_prev(customer, googleplay_user_id, product)
28 if product.is_a?(SIMKind)
29 Registration::DataOnly.for(customer, product)
30 else
31 Registration::Activation.for(customer, googleplay_user_id, product)
32 end
33end
34
35class Registration
36 class PayForSim
37 def initialize(customer, sim_kind)
38 @customer = customer
39 @sim_kind = sim_kind
40 end
41
42 def write
43 Command.reply { |reply|
44 reply.command << FormTemplate.render(
45 "registration/pay_without_code",
46 product: @sim_kind,
47 instructions: instructions,
48 currency_required: !@customer.currency,
49 title: "Pay for #{@sim_kind.klass.label}"
50 )
51 }.then(&method(:parse)).then(&:write)
52 end
53
54 def instructions
55 cfg = @sim_kind.cfg(@customer.currency)
56 return nil unless cfg
57
58 <<~I
59 To activate your data SIM, you need to deposit $#{'%.2f' % (cfg[:price] / 100.0)} to your balance.
60 (If you'd like to pay in another cryptocurrency, currently we recommend using a service like simpleswap.io, morphtoken.com, changenow.io, or godex.io.)
61 I
62 end
63
64 def parse(iq)
65 unless @customer.currency
66 plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
67 (@customer = @customer.with_plan(plan.name)).save_plan!
68 end.then { process_payment(iq) }
69 end
70
71 def process_payment(iq)
72 Payment.for(
73 iq, @customer, @sim_kind,
74 price: @sim_kind.cfg(@customer.currency)[:price],
75 maybe_bill: Registration::Payment::JustCharge
76 ).write.then { reload_customer(@customer) }.then { |customer|
77 DataOnly.for(customer, @sim_kind)
78 }
79 end
80 end
81
82 class DataOnly
83 def self.for(customer, sim_kind)
84 cfg = sim_kind.cfg(customer.currency)
85 unless cfg && customer.balance > cfg[:price]
86 return PayForSim.new(customer, sim_kind)
87 end
88
89 new(customer, sim_kind)
90 end
91
92 def initialize(customer, sim_kind)
93 @customer = customer
94 @sim_kind = sim_kind
95 end
96
97 def write
98 @sim_kind.klass.for(
99 @customer,
100 **@sim_kind.cfg(@customer.currency)
101 ).then { |order|
102 # NOTE: cheogram will swallow any stanza
103 # with type `completed`
104 # `can_complete: false` is needed to prevent customer
105 # from (possibly) permanently losing their eSIM QR code
106 order.process(can_complete: false)
107 }
108 end
109 end
110
111 class RegistrationType
112 def self.for(customer, google_play_userid, product)
113 if product.is_a?(SIMKind)
114 return Registration::DataOnly.for(
115 customer, product
116 )
117 end
118
119 Registration::FinishOrStartActivation.for(
120 customer, google_play_userid, product
121 )
122 end
123 end
124
125 def self.for(customer, google_play_userid, tel_selections)
126 if (reg = customer.registered?)
127 Registered.for(customer, reg.phone)
128 else
129 tel_selections[customer.jid].then(&:choose_tel_or_data).then do |product|
130 reserve_and_continue(tel_selections, customer, product).then do
131 RegistrationType.for(customer, google_play_userid, product)
132 end
133 end
134 end
135 end
136
137 def self.reserve_and_continue(tel_selections, customer, tel)
138 tel.reserve(customer).catch do
139 tel_selections.delete(customer.jid).then {
140 tel_selections[customer.jid]
141 }.then { |choose|
142 choose.choose_tel_or_data(
143 error: "The JMP number #{tel} is no longer available."
144 )
145 }.then { |n_tel| reserve_and_continue(tel_selections, customer, n_tel) }
146 end
147 end
148
149 class Registered
150 def self.for(customer, tel)
151 if customer.jid.onboarding?
152 FinishOnboarding.for(customer, tel)
153 else
154 new(tel)
155 end
156 end
157
158 def initialize(tel)
159 @tel = tel
160 end
161
162 def write
163 Command.finish("You are already registered with JMP number #{@tel}")
164 end
165 end
166
167 class FinishOrStartActivation
168 def self.for(customer, google_play_userid, tel)
169 if customer.active?
170 Finish.new(customer, tel)
171 elsif customer.balance >= CONFIG[:activation_amount_accept]
172 BillPlan.new(customer, tel)
173 else
174 new(customer, google_play_userid, tel)
175 end
176 end
177
178 def initialize(customer, google_play_userid, tel)
179 @customer = customer
180 @tel = tel
181 @google_play_userid = google_play_userid
182 end
183
184 def write
185 Command.reply { |reply|
186 reply.allowed_actions = [:next]
187 reply.note_type = :info
188 reply.note_text = File.read("#{__dir__}/../fup.txt")
189 }.then { Activation.for(@customer, @google_play_userid, @tel).write }
190 end
191 end
192
193 class Activation
194 def self.for(customer, google_play_userid, tel)
195 jid = ProxiedJID.new(customer.jid).unproxied
196 if CONFIG[:approved_domains].key?(jid.domain.to_sym)
197 Allow.for(customer, tel, jid)
198 elsif google_play_userid
199 GooglePlay.new(customer, google_play_userid, tel)
200 else
201 new(customer, tel)
202 end
203 end
204
205 def initialize(customer, tel)
206 @customer = customer
207 @tel = tel
208 end
209
210 attr_reader :customer, :tel
211
212 def form
213 FormTemplate.render("registration/activate", tel: tel)
214 end
215
216 def write
217 Command.reply { |reply|
218 reply.allowed_actions = [:next]
219 reply.command << form
220 }.then(&method(:next_step))
221 end
222
223 def next_step(iq)
224 EMPromise.resolve(nil).then do
225 plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
226 @customer = @customer.with_plan(plan.name)
227 Registration::Payment::InviteCode.new(
228 @customer, @tel, finish: Finish, db: DB, redis: REDIS
229 ).parse(iq, force_save_plan: true)
230 .catch_only(InvitesRepo::Invalid) do
231 Payment.for(iq, @customer, @tel).then(&:write)
232 end
233 end
234 end
235
236 class GooglePlay
237 def initialize(customer, google_play_userid, tel)
238 @customer = customer
239 @google_play_userid = google_play_userid
240 @tel = tel
241 @invites = InvitesRepo.new(DB, REDIS)
242 @parent_code_repo = ParentCodeRepo.new(redis: REDIS, db: DB)
243 end
244
245 def used
246 REDIS.sismember("google_play_userids", @google_play_userid)
247 end
248
249 def form
250 FormTemplate.render(
251 "registration/google_play",
252 tel: @tel
253 )
254 end
255
256 def write
257 used.then do |u|
258 next Activation.for(@customer, nil, @tel).write if u.to_s == "1"
259
260 Command.reply { |reply|
261 reply.allowed_actions = [:next]
262 reply.command << form
263 }.then(&method(:activate)).then do
264 Finish.new(@customer, @tel).write
265 end
266 end
267 end
268
269 def activate(iq)
270 plan = Plan.for_registration(iq.form.field("plan_name").value)
271 code = iq.form.field("code")&.value
272 EMPromise.all([
273 @parent_code_repo.find(code),
274 REDIS.sadd("google_play_userids", @google_play_userid)
275 ]).then { |(parent, _)|
276 save_active_plan(plan, parent)
277 }.then do
278 use_referral_code(code)
279 end
280 end
281
282 protected
283
284 def save_bogus_transaction
285 Transaction.new(
286 customer_id: @customer.customer_id,
287 transaction_id: "google_play_#{@customer.customer_id}",
288 amount: 0,
289 note: "Activated via Google Play",
290 bonus_eligible?: false
291 ).insert
292 end
293
294 def save_active_plan(plan, parent)
295 @customer = @customer.with_plan(plan.name, parent_customer_id: parent)
296 save_bogus_transaction.then do
297 @customer.activate_plan_starting_now
298 end
299 end
300
301 def use_referral_code(code)
302 EMPromise.resolve(nil).then {
303 @invites.claim_code(@customer.customer_id, code) {
304 @customer.extend_plan
305 }
306 }.catch_only(InvitesRepo::Invalid) do
307 @invites.stash_code(@customer.customer_id, code)
308 end
309 end
310 end
311
312 class Allow < Activation
313 def self.for(customer, tel, jid)
314 credit_to = CONFIG[:approved_domains][jid.domain.to_sym]
315 new(customer, tel, credit_to)
316 end
317
318 def initialize(customer, tel, credit_to)
319 super(customer, tel)
320 @credit_to = credit_to
321 end
322
323 def form
324 FormTemplate.render(
325 "registration/allow",
326 tel: tel,
327 domain: customer.jid.domain
328 )
329 end
330
331 def next_step(iq)
332 plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
333 @customer = customer.with_plan(plan.name)
334 EMPromise.resolve(nil).then { activate }.then do
335 Finish.new(customer, tel).write
336 end
337 end
338
339 protected
340
341 def activate
342 DB.transaction do
343 if @credit_to
344 InvitesRepo.new(DB, REDIS).create_claimed_code(
345 @credit_to,
346 customer.customer_id
347 )
348 end
349 @customer.activate_plan_starting_now
350 end
351 end
352 end
353 end
354
355 module Payment
356 def self.kinds
357 @kinds ||= {}
358 end
359
360 def self.for(
361 iq, customer, product,
362 finish: Finish, maybe_bill: MaybeBill,
363 price: CONFIG[:activation_amount] + product.price
364 )
365 kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
366 raise "Invalid activation method"
367 }.call(
368 customer, product, finish: finish, maybe_bill: maybe_bill, price: price
369 )
370 end
371
372 class CryptoPaymentMethod
373 def crypto_addrs
374 raise NotImplementedError, "Subclass must implement"
375 end
376
377 def reg_form_name
378 raise NotImplementedError, "Subclass must implement"
379 end
380
381 def sell_prices
382 raise NotImplementedError, "Subclass must implement"
383 end
384
385 def initialize(
386 customer, product,
387 price: CONFIG[:activation_amount] + product.price, **
388 )
389 @customer = customer
390 @customer_id = customer.customer_id
391 @product = product
392 @price = price
393 end
394
395 def save
396 return if product.is_a?(SIMKind)
397
398 TEL_SELECTIONS.set(@customer.jid, @product)
399 end
400
401 attr_reader :customer_id, :product
402
403 def form(rate, addr)
404 FormTemplate.render(
405 reg_form_name,
406 amount: @price / rate,
407 addr: addr
408 )
409 end
410
411 def write
412 EMPromise.all([addr_and_rate, save]).then do |((addr, rate), _)|
413 Command.reply { |reply|
414 reply.allowed_actions = [:prev]
415 reply.status = :canceled
416 reply.command << form(rate, addr)
417 }.then(&method(:handle_possible_prev))
418 end
419 end
420
421 protected
422
423 def handle_possible_prev(iq)
424 raise "Action not allowed" unless iq.prev?
425
426 handle_prev(@customer, nil, @product)
427 end
428
429 def addr_and_rate
430 EMPromise.all([
431 crypto_addrs.then { |addrs|
432 addrs.first || add_crypto_addr
433 },
434
435 sell_prices.public_send(@customer.currency.to_s.downcase)
436 ])
437 end
438 end
439
440 class Bitcoin < CryptoPaymentMethod
441 Payment.kinds[:bitcoin] = method(:new)
442
443 def reg_form_name
444 "registration/btc"
445 end
446
447 def sell_prices
448 BTC_SELL_PRICES
449 end
450
451 def crypto_addrs
452 @customer.btc_addresses
453 end
454
455 def add_crypto_addr
456 @customer.add_btc_address
457 end
458 end
459
460 ## Like Bitcoin
461 class BCH < CryptoPaymentMethod
462 Payment.kinds[:bch] = method(:new)
463
464 def reg_form_name
465 "registration/bch"
466 end
467
468 def sell_prices
469 BCH_SELL_PRICES
470 end
471
472 def crypto_addrs
473 @customer.bch_addresses
474 end
475
476 def add_crypto_addr
477 @customer.add_bch_address
478 end
479 end
480
481 class MaybeBill
482 def initialize(customer, tel, finish: Finish)
483 @customer = customer
484 @tel = tel
485 @finish = finish
486 end
487
488 def call
489 reload_customer(@customer).then do |customer|
490 if customer.balance >= CONFIG[:activation_amount_accept]
491 next BillPlan.new(customer, @tel, finish: @finish)
492 end
493
494 yield customer
495 end
496 end
497 end
498
499 class JustCharge
500 def initialize(customer, *, **)
501 @customer = customer
502 end
503
504 def call; end
505 end
506
507 class CreditCard
508 Payment.kinds[:credit_card] = method(:new)
509
510 def initialize(
511 customer, product,
512 finish: Finish, maybe_bill: MaybeBill,
513 price: CONFIG[:activation_amount] + product.price
514 )
515 @customer = customer
516 @product = product
517 @finish = finish
518 @maybe_bill = maybe_bill.new(customer, product, finish: finish)
519 @price = price
520 end
521
522 def oob(reply)
523 oob = OOB.find_or_create(reply.command)
524 oob.url = CONFIG[:credit_card_url].call(
525 reply.to.stripped.to_s.gsub("\\", "%5C"),
526 @customer.customer_id
527 ) + "&amount=#{@price.ceil}"
528 oob.desc = "Pay by credit card, save, then next here to continue"
529 oob
530 end
531
532 def write
533 Command.reply { |reply|
534 reply.allowed_actions = [:next, :prev]
535 toob = oob(reply)
536 reply.note_type = :info
537 reply.note_text = "#{toob.desc}: #{toob.url}"
538 }.then do |iq|
539 handle_prev(@customer, nil, @product) if iq.prev?
540
541 @maybe_bill.call { self }&.then(&:write)
542 end
543 end
544 end
545
546 class InviteCode
547 Payment.kinds[:code] = method(:new)
548
549 FIELDS = [{
550 var: "code",
551 type: "text-single",
552 label: "Your referral code",
553 required: true
554 }].freeze
555
556 def initialize(
557 customer, tel,
558 error: nil, finish: Finish, db: DB, redis: REDIS, **
559 )
560 @customer = customer
561 @tel = tel
562 @error = error
563 @finish = finish
564 @invites_repo = InvitesRepo.new(db, redis)
565 @parent_code_repo = ParentCodeRepo.new(db: db, redis: redis)
566 end
567
568 def add_form(reply)
569 form = reply.form
570 form.type = :form
571 form.title = "Enter Referral Code"
572 form.instructions = @error if @error
573 form.fields = FIELDS
574 end
575
576 def write
577 Command.reply { |reply|
578 reply.allowed_actions = [:next, :prev]
579 add_form(reply)
580 }.then(&method(:parse)).catch_only(InvitesRepo::Invalid) { |e|
581 invalid_code(e).write
582 }
583 end
584
585 def parse(iq, force_save_plan: false)
586 return Activation.for(@customer, nil, @tel).then(&:write) if iq.prev?
587
588 verify(iq.form.field("code")&.value&.to_s, force_save_plan)
589 .then(&:write)
590 end
591
592 protected
593
594 def invalid_code(e)
595 self.class.new(@customer, @tel, error: e.message, finish: @finish)
596 end
597
598 def customer_id
599 @customer.customer_id
600 end
601
602 def verify(code, force_save_plan)
603 @parent_code_repo.claim_code(@customer, code) {
604 check_parent_balance
605 }.catch_only(ParentCodeRepo::Invalid) {
606 (@customer.save_plan! if force_save_plan).then do
607 @invites_repo.claim_code(customer_id, code) {
608 @customer.activate_plan_starting_now
609 }.then { @finish.new(@customer, @tel) }
610 end
611 }
612 end
613
614 def check_parent_balance
615 MaybeBill.new(@customer, @tel, finish: @finish).call do
616 msg = "Account balance not enough to cover the activation"
617 invalid_code(RuntimeError.new(msg))
618 end
619 end
620 end
621
622 class Mail
623 Payment.kinds[:mail] = method(:new)
624
625 def initialize(
626 customer, tel,
627 price: CONFIG[:activation_amount] + tel.price, **
628 )
629 @customer = customer
630 @tel = tel
631 @price = price
632 end
633
634 def form
635 FormTemplate.render(
636 "registration/mail",
637 currency: @customer.currency,
638 price: @price,
639 **onboarding_extras
640 )
641 end
642
643 def onboarding_extras
644 jid = ProxiedJID.new(@customer.jid).unproxied
645 return {} unless jid.domain == CONFIG[:onboarding_domain]
646
647 {
648 customer_id: @customer.customer_id,
649 in_note: "Customer ID"
650 }
651 end
652
653 def write
654 Command.reply { |reply|
655 reply.allowed_actions = [:prev]
656 reply.status = :canceled
657 reply.command << form
658 }.then { |iq|
659 raise "Action not allowed" unless iq.prev?
660
661 Activation.for(@customer, nil, @tel).then(&:write)
662 }
663 end
664 end
665 end
666
667 class BillPlan
668 def initialize(customer, tel, finish: Finish)
669 @customer = customer
670 @tel = tel
671 @finish = finish
672 end
673
674 def write
675 @customer.bill_plan(note: "Bill #{@tel} for first month").then do
676 updated_customer =
677 @customer.with_balance(@customer.balance - @customer.monthly_price)
678 @finish.new(updated_customer, @tel).write
679 end
680 end
681 end
682
683 class BuyNumber
684 def initialize(customer, tel)
685 @customer = customer
686 @tel = tel
687 end
688
689 def instructions
690 <<~I
691 You've selected #{@tel} as your JMP number.
692 To activate your account, you can either deposit $#{'%.2f' % (CONFIG[:activation_amount] + @tel.price)} to your balance or enter your referral code if you have one.
693 (If you'd like to pay in another cryptocurrency, currently we recommend using a service like simpleswap.io, morphtoken.com, changenow.io, or godex.io.)
694 I
695 end
696
697 def write
698 Command.reply { |reply|
699 reply.command << FormTemplate.render(
700 "registration/pay_without_code",
701 product: @tel,
702 instructions: instructions,
703 title: "Purchase Number"
704 )
705 }.then(&method(:parse)).then(&:write)
706 end
707
708 protected
709
710 def parse(iq)
711 Payment.for(
712 iq, @customer, @tel,
713 maybe_bill: ::Registration::Payment::JustCharge,
714 price: @tel.price
715 )
716 end
717 end
718
719 class Finish
720 def initialize(customer, tel)
721 @customer = customer
722 @tel = tel
723 @invites = InvitesRepo.new(DB, REDIS)
724 end
725
726 def write
727 return buy_number if @customer.balance < @tel.price
728
729 @tel.order(DB, @customer).then(
730 ->(_) { customer_active_tel_purchased },
731 method(:number_purchase_error)
732 )
733 end
734
735 protected
736
737 def buy_number
738 BuyNumber.new(@customer, @tel).write.then do
739 reload_customer(@customer).then { |customer|
740 Finish.new(customer, @tel)
741 }.then(&:write)
742 end
743 end
744
745 def number_purchase_error(e)
746 Command.log.error "number_purchase_error", e
747 TEL_SELECTIONS.delete(@customer.jid).then {
748 TEL_SELECTIONS[@customer.jid]
749 }.then { |choose|
750 choose.choose_tel_or_data(
751 error: "The JMP number #{@tel} is no longer available."
752 )
753 }.then { |tel| Finish.new(@customer, tel).write }
754 end
755
756 def raise_setup_error(e)
757 Command.log.error "@customer.register! failed", e
758 Command.finish(
759 "There was an error setting up your number, " \
760 "please contact JMP support.",
761 type: :error
762 )
763 end
764
765 def put_default_fwd
766 Bwmsgsv2Repo.new.put_fwd(@customer.customer_id, @tel.tel, CustomerFwd.for(
767 uri: "xmpp:#{@customer.jid}",
768 voicemail_enabled: true
769 ))
770 end
771
772 def use_referral_code
773 @invites.use_pending_group_code(@customer.customer_id).then do |credit_to|
774 next unless credit_to
775
776 Transaction.new(
777 customer_id: @customer.customer_id,
778 transaction_id: "referral_#{@customer.customer_id}_#{credit_to}",
779 amount: @customer.monthly_price,
780 note: "Referral Bonus",
781 bonus_eligible?: false
782 ).insert
783 end
784 end
785
786 def customer_active_tel_purchased
787 @customer.register!(@tel.tel).catch(&method(:raise_setup_error)).then {
788 EMPromise.all([
789 TEL_SELECTIONS.delete(@customer.jid),
790 put_default_fwd,
791 use_referral_code,
792 @tel.charge(@customer)
793 ])
794 }.then do
795 FinishOnboarding.for(@customer, @tel).then(&:write)
796 end
797 end
798 end
799
800 module FinishOnboarding
801 def self.for(customer, tel, db: LazyObject.new { DB })
802 jid = ProxiedJID.new(customer.jid).unproxied
803 if jid.domain == CONFIG[:onboarding_domain]
804 Snikket.for(customer, tel, db: db)
805 else
806 NotOnboarding.new(customer, tel)
807 end
808 end
809
810 class Snikket
811 def self.for(customer, tel, db:)
812 ::Snikket::Repo.new(db: db).find_by_customer(customer).then do |is|
813 if is.empty?
814 new(customer, tel, db: db)
815 elsif is[0].bootstrap_token.empty?
816 # This is a need_dns one, try the launch again
817 new(customer, tel, db: db).launch(is[0].domain)
818 else
819 GetInvite.for(customer, is[0], tel, db: db)
820 end
821 end
822 end
823
824 def initialize(customer, tel, error: nil, old: nil, db:)
825 @customer = customer
826 @tel = tel
827 @error = error
828 @db = db
829 @old = old
830 end
831
832 ACTION_VAR = "http://jabber.org/protocol/commands#actions"
833
834 def form
835 FormTemplate.render(
836 "registration/snikket",
837 tel: @tel,
838 error: @error
839 )
840 end
841
842 def write
843 Command.reply { |reply|
844 reply.allowed_actions = [:next]
845 reply.command << form
846 }.then(&method(:next_step))
847 end
848
849 def next_step(iq)
850 subdomain = empty_nil(iq.form.field("subdomain")&.value)
851 domain = "#{subdomain}.snikket.chat"
852 if iq.form.field(ACTION_VAR)&.value == "custom_domain"
853 CustomDomain.new(@customer, @tel, old: @old).write
854 elsif @old && (!subdomain || domain == @old.domain)
855 GetInvite.for(@customer, @old, @tel, db: @db).then(&:write)
856 else
857 launch(domain)
858 end
859 end
860
861 def launch(domain)
862 IQ_MANAGER.write(::Snikket::Launch.new(
863 nil, CONFIG[:snikket_hosting_api], domain: domain
864 )).then { |launched|
865 save_instance_and_wait(domain, launched)
866 }.catch { |e|
867 next EMPromise.reject(e) unless e.respond_to?(:text)
868
869 Snikket.new(@customer, @tel, old: @old, error: e.text, db: @db).write
870 }
871 end
872
873 def save_instance_and_wait(domain, launched)
874 instance = ::Snikket::CustomerInstance.for(@customer, domain, launched)
875 repo = ::Snikket::Repo.new(db: @db)
876 (@old&.domain == domain ? EMPromise.resolve(nil) : repo.del(@old))
877 .then { repo.put(instance) }.then do
878 if launched.status == :needs_dns
879 NeedsDNS.new(@customer, instance, @tel, launched.records).write
880 else
881 GetInvite.for(@customer, instance, @tel, db: @db).then(&:write)
882 end
883 end
884 end
885
886 def empty_nil(s)
887 s.nil? || s.empty? ? nil : s
888 end
889
890 class NeedsDNS < Snikket
891 def initialize(customer, instance, tel, records, db: DB)
892 @customer = customer
893 @instance = instance
894 @tel = tel
895 @records = records
896 @db = db
897 end
898
899 def form
900 FormTemplate.render(
901 "registration/snikket_needs_dns",
902 records: @records
903 )
904 end
905
906 def write
907 Command.reply { |reply|
908 reply.allowed_actions = [:prev, :next]
909 reply.command << form
910 }.then do |iq|
911 if iq.prev?
912 CustomDomain.new(@customer, @tel, old: @instance).write
913 else
914 launch(@instance.domain)
915 end
916 end
917 end
918 end
919
920 class GetInvite
921 def self.for(customer, instance, tel, db: DB)
922 instance.fetch_invite.then do |xmpp_uri|
923 if xmpp_uri
924 GoToInvite.new(xmpp_uri)
925 else
926 new(customer, instance, tel, db: db)
927 end
928 end
929 end
930
931 def initialize(customer, instance, tel, db: DB)
932 @customer = customer
933 @instance = instance
934 @tel = tel
935 @db = db
936 end
937
938 def form
939 FormTemplate.render(
940 "registration/snikket_wait",
941 domain: @instance.domain
942 )
943 end
944
945 def write
946 Command.reply { |reply|
947 reply.allowed_actions = [:prev, :next]
948 reply.command << form
949 }.then do |iq|
950 if iq.prev?
951 Snikket.new(@customer, @tel, old: @instance, db: @db).write
952 else
953 GetInvite.for(@customer, @instance, @tel, db: @db).then(&:write)
954 end
955 end
956 end
957 end
958
959 class GoToInvite
960 def initialize(xmpp_uri)
961 @xmpp_uri = xmpp_uri
962 end
963
964 def write
965 Command.finish do |reply|
966 oob = OOB.find_or_create(reply.command)
967 oob.url = @xmpp_uri
968 end
969 end
970 end
971 end
972
973 class CustomDomain < Snikket
974 def initialize(customer, tel, old: nil, error: nil, db: DB)
975 @customer = customer
976 @tel = tel
977 @error = error
978 @old = old
979 @db = db
980 end
981
982 def form
983 FormTemplate.render(
984 "registration/snikket_custom",
985 tel: @tel,
986 error: @error
987 )
988 end
989
990 def write
991 Command.reply { |reply|
992 reply.allowed_actions = [:prev, :next]
993 reply.command << form
994 }.then do |iq|
995 if iq.prev?
996 Snikket.new(@customer, @tel, db: @db, old: @old).write
997 else
998 launch(empty_nil(iq.form.field("domain")&.value) || @old&.domain)
999 end
1000 end
1001 end
1002 end
1003
1004 class NotOnboarding
1005 def initialize(customer, tel)
1006 @customer = customer
1007 @tel = tel
1008 end
1009
1010 def write
1011 WelcomeMessage.new(@customer, @tel).welcome
1012 Command.finish("Your JMP account has been activated as #{@tel}")
1013 end
1014 end
1015 end
1016
1017 def self.public_onboarding_invite
1018 Command.finish { |reply|
1019 reply.allowed_actions = [:prev]
1020 oob = OOB.find_or_create(reply.command)
1021 oob.url = CONFIG[:public_onboarding_url]
1022 oob.desc = "Get your XMPP account"
1023 }
1024 end
1025end