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