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