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.label}"
50 )
51 }.then(&method(:parse)).then(&:write)
52 end
53
54 def instructions
55 price = @sim_kind.price(@customer)
56 return nil unless price
57
58 <<~I
59 To activate your data SIM, you need to deposit $#{'%.2f' % price} 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.price(@customer),
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(customer)
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.get_processor(@customer).then { |order|
99 # NOTE: cheogram will swallow any stanza
100 # with type `completed`
101 # `can_complete: false` is needed to prevent customer
102 # from (possibly) permanently losing their eSIM QR code
103 order.process(can_complete: false)
104 }
105 end
106 end
107
108 class RegistrationType
109 def self.for(customer, google_play_userid, product)
110 if product.is_a?(SIMKind)
111 return Registration::DataOnly.for(
112 customer, product
113 )
114 end
115
116 reload_customer(customer).then do |reloaded|
117 Registration::FinishOrStartActivation.for(
118 reloaded, google_play_userid, product
119 )
120 end
121 end
122 end
123
124 def self.for(customer, google_play_userid, tel_selections)
125 if (reg = customer.registered?)
126 Registered.for(customer, reg.phone)
127 else
128 tel_selections[customer].then(&:choose_tel_or_data).then do |product|
129 reserve_and_continue(tel_selections, customer, product).then do
130 RegistrationType.for(customer, google_play_userid, product)
131 end
132 end
133 end
134 end
135
136 # @param [TelSelections] tel_selections
137 # @param [Customer] customer
138 # @param [TelSelections::ChooseTel::Tn, SIMKind]
139 def self.reserve_and_continue(tel_selections, customer, product)
140 product.reserve(customer).catch do
141 tel_selections.delete(customer.jid).then {
142 tel_selections[customer]
143 }.then { |choose|
144 choose.choose_tel_or_data(
145 error: "The JMP number #{product} is no longer available."
146 )
147 }.then { |n_tel| reserve_and_continue(tel_selections, customer, n_tel) }
148 end
149 end
150
151 class Registered
152 def self.for(customer, tel)
153 if customer.jid.onboarding?
154 FinishOnboarding.for(customer, tel)
155 else
156 new(tel)
157 end
158 end
159
160 def initialize(tel)
161 @tel = tel
162 end
163
164 def write
165 Command.finish("You are already registered with JMP number #{@tel}")
166 end
167 end
168
169 class FinishOrStartActivation
170 def self.for(customer, google_play_userid, tel)
171 if customer.active?
172 Finish.new(customer, tel)
173 elsif customer.balance >= CONFIG[:activation_amount_accept]
174 BillPlan.new(customer, tel)
175 else
176 new(customer, google_play_userid, tel)
177 end
178 end
179
180 def initialize(customer, google_play_userid, tel)
181 @customer = customer
182 @tel = tel
183 @google_play_userid = google_play_userid
184 end
185
186 def write
187 Command.reply { |reply|
188 reply.allowed_actions = [:next]
189 reply.note_type = :info
190 reply.note_text = File.read("#{__dir__}/../fup.txt")
191 }.then { Activation.for(@customer, @google_play_userid, @tel).write }
192 end
193 end
194
195 class Activation
196 def self.for(customer, google_play_userid, tel)
197 jid = ProxiedJID.new(customer.jid).unproxied
198 if CONFIG[:approved_domains].key?(jid.domain.to_sym)
199 Allow.for(customer, tel, jid)
200 elsif google_play_userid
201 GooglePlay.new(customer, google_play_userid, tel)
202 else
203 new(customer, tel)
204 end
205 end
206
207 def initialize(customer, tel)
208 @customer = customer
209 @tel = tel
210 end
211
212 attr_reader :customer, :tel
213
214 def form
215 FormTemplate.render("registration/activate", tel: tel)
216 end
217
218 def write
219 Command.reply { |reply|
220 reply.allowed_actions = [:next]
221 reply.command << form
222 }.then(&method(:next_step))
223 end
224
225 def next_step(iq)
226 EMPromise.resolve(nil).then do
227 plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
228 @customer = @customer.with_plan(plan.name)
229 Registration::Payment::InviteCode.new(
230 @customer, @tel, finish: Finish, db: DB, redis: REDIS
231 ).parse(iq, force_save_plan: true)
232 .catch_only(InvitesRepo::Invalid) do
233 Payment.for(iq, @customer, @tel).then(&:write)
234 end
235 end
236 end
237
238 class GooglePlay
239 def initialize(customer, google_play_userid, tel)
240 @customer = customer
241 @google_play_userid = google_play_userid
242 @tel = tel
243 @invites = InvitesRepo.new(DB, REDIS)
244 @parent_code_repo = ParentCodeRepo.new(redis: REDIS, db: DB)
245 end
246
247 def used
248 REDIS.sismember("google_play_userids", @google_play_userid)
249 end
250
251 def form
252 FormTemplate.render(
253 "registration/google_play",
254 tel: @tel
255 )
256 end
257
258 def write
259 used.then do |u|
260 next Activation.for(@customer, nil, @tel).write if u.to_s == "1"
261
262 Command.reply { |reply|
263 reply.allowed_actions = [:next]
264 reply.command << form
265 }.then(&method(:activate)).then do
266 Finish.new(@customer, @tel).write
267 end
268 end
269 end
270
271 def activate(iq)
272 plan = Plan.for_registration(iq.form.field("plan_name").value)
273 code = iq.form.field("code")&.value
274 EMPromise.all([
275 @parent_code_repo.find(code),
276 REDIS.sadd("google_play_userids", @google_play_userid)
277 ]).then { |(parent, _)|
278 save_active_plan(plan, parent)
279 }.then do
280 use_referral_code(code)
281 end
282 end
283
284 protected
285
286 def save_bogus_transaction
287 Transaction.new(
288 customer_id: @customer.customer_id,
289 transaction_id: "google_play_#{@customer.customer_id}",
290 amount: 0,
291 note: "Activated via Google Play",
292 bonus_eligible?: false
293 ).insert
294 end
295
296 def save_active_plan(plan, parent)
297 @customer = @customer.with_plan(plan.name, parent_customer_id: parent)
298 save_bogus_transaction.then do
299 @customer.activate_plan_starting_now
300 end
301 end
302
303 def use_referral_code(code)
304 EMPromise.resolve(nil).then {
305 @invites.claim_code(@customer.customer_id, code) {
306 @customer.extend_plan
307 }
308 }.catch_only(InvitesRepo::Invalid) do
309 @invites.stash_code(@customer.customer_id, code)
310 end
311 end
312 end
313
314 class Allow < Activation
315 def self.for(customer, tel, jid)
316 credit_to = CONFIG[:approved_domains][jid.domain.to_sym]
317 new(customer, tel, credit_to)
318 end
319
320 def initialize(customer, tel, credit_to)
321 super(customer, tel)
322 @credit_to = credit_to
323 end
324
325 def form
326 FormTemplate.render(
327 "registration/allow",
328 tel: tel,
329 domain: customer.jid.domain
330 )
331 end
332
333 def next_step(iq)
334 plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
335 @customer = customer.with_plan(plan.name)
336 EMPromise.resolve(nil).then { activate }.then do
337 Finish.new(customer, tel).write
338 end
339 end
340
341 protected
342
343 def activate
344 DB.transaction do
345 if @credit_to
346 InvitesRepo.new(DB, REDIS).create_claimed_code(
347 @credit_to,
348 customer.customer_id
349 )
350 end
351 @customer.activate_plan_starting_now
352 end
353 end
354 end
355 end
356
357 module Payment
358 def self.kinds
359 @kinds ||= {}
360 end
361
362 def self.for(
363 iq, customer, product,
364 finish: Finish, maybe_bill: MaybeBill,
365 price: CONFIG[:activation_amount] + product.price
366 )
367 kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
368 raise "Invalid activation method"
369 }.call(
370 customer, product, finish: finish, maybe_bill: maybe_bill, price: price
371 )
372 end
373
374 class CryptoPaymentMethod
375 def crypto_addrs
376 raise NotImplementedError, "Subclass must implement"
377 end
378
379 def reg_form_name
380 raise NotImplementedError, "Subclass must implement"
381 end
382
383 def sell_prices
384 raise NotImplementedError, "Subclass must implement"
385 end
386
387 def initialize(
388 customer, product,
389 price: CONFIG[:activation_amount] + product.price, **
390 )
391 @customer = customer
392 @customer_id = customer.customer_id
393 @product = product
394 @price = price
395 end
396
397 def save
398 return if product.is_a?(SIMKind)
399
400 TEL_SELECTIONS.set(@customer.jid, @product)
401 end
402
403 attr_reader :customer_id, :product
404
405 def form(rate, addr)
406 FormTemplate.render(
407 reg_form_name,
408 amount: @price / rate,
409 addr: addr
410 )
411 end
412
413 def write
414 EMPromise.all([addr_and_rate, save]).then do |((addr, rate), _)|
415 Command.reply { |reply|
416 reply.allowed_actions = [:prev]
417 reply.status = :canceled
418 reply.command << form(rate, addr)
419 }.then(&method(:handle_possible_prev))
420 end
421 end
422
423 protected
424
425 def handle_possible_prev(iq)
426 raise "Action not allowed" unless iq.prev?
427
428 handle_prev(@customer, nil, @product)
429 end
430
431 def addr_and_rate
432 EMPromise.all([
433 crypto_addrs.then { |addrs|
434 addrs.first || add_crypto_addr
435 },
436
437 sell_prices.public_send(@customer.currency.to_s.downcase)
438 ])
439 end
440 end
441
442 class Bitcoin < CryptoPaymentMethod
443 Payment.kinds[:bitcoin] = method(:new)
444
445 def reg_form_name
446 "registration/btc"
447 end
448
449 def sell_prices
450 BTC_SELL_PRICES
451 end
452
453 def crypto_addrs
454 @customer.btc_addresses
455 end
456
457 def add_crypto_addr
458 @customer.add_btc_address
459 end
460 end
461
462 ## Like Bitcoin
463 class BCH < CryptoPaymentMethod
464 Payment.kinds[:bch] = method(:new)
465
466 def reg_form_name
467 "registration/bch"
468 end
469
470 def sell_prices
471 BCH_SELL_PRICES
472 end
473
474 def crypto_addrs
475 @customer.bch_addresses
476 end
477
478 def add_crypto_addr
479 @customer.add_bch_address
480 end
481 end
482
483 class MaybeBill
484 def initialize(customer, tel, finish: Finish)
485 @customer = customer
486 @tel = tel
487 @finish = finish
488 end
489
490 def call
491 reload_customer(@customer).then do |customer|
492 if customer.balance >= CONFIG[:activation_amount_accept]
493 next BillPlan.new(customer, @tel, finish: @finish)
494 end
495
496 yield customer
497 end
498 end
499 end
500
501 class JustCharge
502 def initialize(customer, *, **)
503 @customer = customer
504 end
505
506 def call; end
507 end
508
509 class CreditCard
510 Payment.kinds[:credit_card] = method(:new)
511
512 def initialize(
513 customer, product,
514 finish: Finish, maybe_bill: MaybeBill,
515 price: CONFIG[:activation_amount] + product.price
516 )
517 @customer = customer
518 @product = product
519 @finish = finish
520 @maybe_bill = maybe_bill.new(customer, product, finish: finish)
521 @price = price
522 end
523
524 def oob(reply)
525 oob = OOB.find_or_create(reply.command)
526 oob.url = CONFIG[:credit_card_url].call(
527 reply.to.stripped.to_s.gsub("\\", "%5C"),
528 @customer.customer_id
529 ) + "&amount=#{@price.ceil}"
530 oob.desc = "Pay by credit card, save, then next here to continue"
531 oob
532 end
533
534 def write
535 Command.reply { |reply|
536 reply.allowed_actions = [:next, :prev]
537 toob = oob(reply)
538 reply.note_type = :info
539 reply.note_text = "#{toob.desc}: #{toob.url}"
540 }.then do |iq|
541 handle_prev(@customer, nil, @product) if iq.prev?
542
543 @maybe_bill.call { self }&.then(&:write)
544 end
545 end
546 end
547
548 class InviteCode
549 Payment.kinds[:code] = method(:new)
550
551 FIELDS = [{
552 var: "code",
553 type: "text-single",
554 label: "Your referral code",
555 required: true
556 }].freeze
557
558 def initialize(
559 customer, tel,
560 error: nil, finish: Finish, db: DB, redis: REDIS, **
561 )
562 @customer = customer
563 @tel = tel
564 @error = error
565 @finish = finish
566 @invites_repo = InvitesRepo.new(db, redis)
567 @parent_code_repo = ParentCodeRepo.new(db: db, redis: redis)
568 end
569
570 def add_form(reply)
571 form = reply.form
572 form.type = :form
573 form.title = "Enter Referral Code"
574 form.instructions = @error if @error
575 form.fields = FIELDS
576 end
577
578 def write
579 Command.reply { |reply|
580 reply.allowed_actions = [:next, :prev]
581 add_form(reply)
582 }.then(&method(:parse)).catch_only(InvitesRepo::Invalid) { |e|
583 invalid_code(e).write
584 }
585 end
586
587 def parse(iq, force_save_plan: false)
588 return Activation.for(@customer, nil, @tel).then(&:write) if iq.prev?
589
590 verify(iq.form.field("code")&.value&.to_s, force_save_plan)
591 .then(&:write)
592 end
593
594 protected
595
596 def invalid_code(e)
597 self.class.new(@customer, @tel, error: e.message, finish: @finish)
598 end
599
600 def customer_id
601 @customer.customer_id
602 end
603
604 def verify(code, force_save_plan)
605 @parent_code_repo.claim_code(@customer, code) {
606 check_parent_balance
607 }.catch_only(ParentCodeRepo::Invalid) {
608 (@customer.save_plan! if force_save_plan).then do
609 @invites_repo.claim_code(customer_id, code) {
610 @customer.activate_plan_starting_now
611 }.then { @finish.new(@customer, @tel) }
612 end
613 }
614 end
615
616 def check_parent_balance
617 MaybeBill.new(@customer, @tel, finish: @finish).call do
618 msg = "Account balance not enough to cover the activation"
619 invalid_code(RuntimeError.new(msg))
620 end
621 end
622 end
623
624 class Mail
625 Payment.kinds[:mail] = method(:new)
626
627 def initialize(
628 customer, tel,
629 price: CONFIG[:activation_amount] + tel.price, **
630 )
631 @customer = customer
632 @tel = tel
633 @price = price
634 end
635
636 def form
637 FormTemplate.render(
638 "registration/mail",
639 currency: @customer.currency,
640 price: @price,
641 **onboarding_extras
642 )
643 end
644
645 def onboarding_extras
646 jid = ProxiedJID.new(@customer.jid).unproxied
647 return {} unless jid.domain == CONFIG[:onboarding_domain]
648
649 {
650 customer_id: @customer.customer_id,
651 in_note: "Customer ID"
652 }
653 end
654
655 def write
656 Command.reply { |reply|
657 reply.allowed_actions = [:prev]
658 reply.status = :canceled
659 reply.command << form
660 }.then { |iq|
661 raise "Action not allowed" unless iq.prev?
662
663 Activation.for(@customer, nil, @tel).then(&:write)
664 }
665 end
666 end
667 end
668
669 class BillPlan
670 def initialize(customer, tel, finish: Finish)
671 @customer = customer
672 @tel = tel
673 @finish = finish
674 end
675
676 def write
677 @customer.bill_plan(note: "Bill #{@tel} for first month").then do
678 updated_customer =
679 @customer.with(balance: @customer.balance - @customer.monthly_price)
680 @finish.new(updated_customer, @tel).write
681 end
682 end
683 end
684
685 class BuyNumber
686 def initialize(customer, tel)
687 @customer = customer
688 @tel = tel
689 end
690
691 def instructions
692 <<~I
693 You've selected #{@tel} as your JMP number.
694 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.
695 (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.)
696 I
697 end
698
699 def write
700 Command.reply { |reply|
701 reply.command << FormTemplate.render(
702 "registration/pay_without_code",
703 product: @tel,
704 instructions: instructions,
705 title: "Purchase Number"
706 )
707 }.then(&method(:parse)).then(&:write)
708 end
709
710 protected
711
712 def parse(iq)
713 Payment.for(
714 iq, @customer, @tel,
715 maybe_bill: ::Registration::Payment::JustCharge,
716 price: @tel.price
717 )
718 end
719 end
720
721 class Finish
722 TN_UNAVAILABLE = "The JMP number %s is no longer available."
723
724 def initialize(customer, tel)
725 @customer = customer
726 @tel = tel
727 @invites = InvitesRepo.new(DB, REDIS)
728 end
729
730 def write
731 return buy_number if @customer.balance < @tel.price
732
733 @tel.order(DB, @customer).then(
734 method(:customer_active_tel_purchased),
735 method(:number_purchase_error)
736 )
737 end
738
739 protected
740
741 def buy_number
742 BuyNumber.new(@customer, @tel).write.then do
743 reload_customer(@customer).then { |customer|
744 Finish.new(customer, @tel)
745 }.then(&:write)
746 end
747 end
748
749 def number_purchase_error(e)
750 Command.log.error "number_purchase_error", e
751 TEL_SELECTIONS.delete(@customer.jid)
752 .then { TEL_SELECTIONS[@customer] }
753 .then { |c|
754 c.choose_tel_or_data(
755 error: TN_UNAVAILABLE % @tel,
756 allow_data_only: false
757 )
758 }
759 .then { |p| Finish.new(@customer, p) }
760 .then(&: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(customer)
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, db:, error: nil, old: nil)
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