1# frozen_string_literal: true
2
3require "blather"
4require "blather/stanza/iq/command"
5require "value_semantics/monkey_patched"
6
7require_relative "customer_fwd"
8require_relative "not_loaded"
9require_relative "blather_notify"
10require_relative "form_to_h"
11
12class BackendSgx
13 COMMANDS_DISCO_NODE = "http://jabber.org/protocol/commands"
14
15 using FormToH
16
17 value_semantics do
18 jid Blather::JID
19 creds HashOf(Symbol => String)
20 from_jid Blather::JID
21 ogm_url Either(String, nil, NotLoaded)
22 fwd Either(CustomerFwd, nil, NotLoaded)
23 transcription_enabled Either(Bool(), NotLoaded)
24 registered? Either(Blather::Stanza::Iq::IBR, FalseClass, NotLoaded)
25 end
26
27 def register!(tel)
28 iq = ibr
29 iq.phone = tel
30 IQ_MANAGER.write(iq).then do
31 REDIS.set("catapult_jid-#{tel}", from_jid.to_s)
32 end
33 end
34
35 def deregister!
36 ibr = Blather::Stanza::Iq::IBR.new(:set, @jid)
37 ibr.from = from_jid
38 ibr.remove!
39 IQ_MANAGER.write(ibr)
40 end
41
42 def stanza(s)
43 s.dup.tap do |stanza|
44 stanza.to = stanza.to.with(
45 domain: jid.domain,
46 node: jid.node || stanza.to.node
47 )
48 stanza.from = from_jid.with(resource: stanza.from&.resource)
49 end
50 end
51
52 def set_ogm_url(url)
53 REDIS.set("catapult_ogm_url-#{from_jid}", url)
54 end
55
56 def tn_portable?
57 iq = Blather::Stanza::DiscoItems.new(:get, COMMANDS_DISCO_NODE)
58 iq.to = jid
59 iq.from = from_jid
60 IQ_MANAGER.write(iq).then { |reply|
61 reply.items.any? { |item| item.node == "set-port-out-pin" }
62 }.catch { |e|
63 Sentry.capture_exception(e)
64 false
65 }
66 end
67
68 def set_port_out_pin(pin)
69 cmd = build_port_out_command(:execute)
70
71 IQ_MANAGER.write(cmd).then { |reply|
72 session_id = reply.command[:sessionid]
73 submit_cmd = build_submit_form(pin, session_id)
74
75 IQ_MANAGER.write(submit_cmd).then { |submit_reply|
76 validate_submit_reply!(submit_reply)
77 }.catch { |e|
78 handle_pin_submission_error(e)
79 }
80 }
81 end
82
83protected
84
85 def ibr
86 s = Blather::Stanza::Iq::IBR.new(:set, jid)
87 s.from = from_jid
88 s.nick = creds[:account]
89 s.username = creds[:username]
90 s.password = creds[:password]
91 s
92 end
93
94 def build_submit_form(pin, session_id)
95 build_port_out_command(:complete, session_id: session_id).tap { |iq|
96 iq.form.type = :submit
97 iq.form.fields = [
98 { var: "pin", value: pin, type: "text-private" },
99 { var: "confirm_pin", value: pin, type: "text-private" }
100 ]
101 }
102 end
103
104 def build_port_out_command(action, session_id: nil)
105 Blather::Stanza::Iq::Command.new.tap { |iq|
106 iq.to = jid
107 iq.from = from_jid
108 iq.node = "set-port-out-pin"
109 iq.action = action
110 iq.sessionid = session_id if session_id
111 }
112 end
113
114 def validate_submit_reply!(submit_reply)
115 sub_text = submit_reply.note&.text
116 case submit_reply.status
117 when :completed
118 raise sub_text if submit_reply.note&.[]("type") == "error"
119 when :canceled
120 raise CanceledError, reply.note&.text
121 else
122 raise sub_text
123 end
124 end
125
126 def handle_pin_submission_error(e)
127 if e.is_a?(Blather::StanzaError) || e.is_a?(RuntimeError)
128 EMPromise.reject(e)
129 else
130 Sentry.capture_exception(e)
131 EMPromise.reject(
132 RuntimeError.new(
133 "Unable to communicate with service. Please try again later."
134 )
135 )
136 end
137 end
138end