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