1# frozen_string_literal: true
2
3require "bandwidth"
4require "value_semantics/monkey_patched"
5require "uri"
6
7class CustomerFwd
8 class InfiniteTimeout < StandardError
9 attr_reader :fwd
10
11 def initialize(fwd)
12 super "Infinite timeout"
13 @fwd = fwd
14 end
15 end
16
17 def self.for(uri:, timeout: nil, voicemail_enabled: :default)
18 timeout = Timeout.for(timeout, voicemail_enabled: voicemail_enabled)
19
20 fwd = if uri
21 if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
22 uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
23 end
24
25 URIS.fetch(uri.split(":", 2).first&.to_sym) {
26 raise "Unknown forward URI: #{uri}"
27 }.new(uri: uri, timeout: timeout)
28 end
29
30 fwd && !timeout.zero? ? fwd : None.new(uri: uri, timeout: timeout)
31 end
32
33 class Timeout
34 def self.for(s, voicemail_enabled: :default)
35 return Infinite.new unless voicemail_enabled
36
37 if s.nil? || s.is_a?(Infinite) || s.to_i.negative?
38 return new(25) if voicemail_enabled == true # ~5s / ring, 5 rings
39
40 return Infinite.new
41 end
42
43 return s if s.is_a?(self)
44
45 new(s)
46 end
47
48 def initialize(s)
49 @timeout = [s.to_i, 300].min
50 end
51
52 def zero?
53 @timeout.zero?
54 end
55
56 def infinite?
57 false
58 end
59
60 def to_i
61 @timeout
62 end
63
64 def to_s
65 to_i.to_s
66 end
67
68 def as_json(*)
69 to_i
70 end
71
72 def to_json(*args)
73 as_json.to_json(*args)
74 end
75
76 class Infinite < Timeout
77 def initialize; end
78
79 def zero?
80 false
81 end
82
83 def infinite?
84 1
85 end
86
87 def to_i; end
88
89 def as_json(*)
90 nil
91 end
92 end
93 end
94
95 value_semantics do
96 uri Either(/:/, NilClass)
97 def_attr :timeout, Timeout, coerce: Timeout.method(:for)
98 end
99
100 def with(new_attrs)
101 CustomerFwd.for(to_h.merge(new_attrs))
102 end
103
104 def voicemail_enabled?
105 !timeout.infinite?
106 end
107
108 def create_call(account)
109 raise InfiniteTimeout, self if timeout.infinite?
110
111 request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
112 cc.to = to
113 cc.call_timeout = timeout.to_i
114 yield cc if block_given?
115 }
116 log_call(account, request)
117 end
118
119 def log_call(account, request)
120 result = BANDWIDTH_VOICE.create_call(account, body: request)
121 log.info "Create Bandwidth Call", { request: request, result: result }
122 result.data.call_id
123 end
124
125 def as_json(*)
126 { timeout: timeout, uris: [uri] }
127 end
128
129 def to_json(*args)
130 as_json.to_json(*args)
131 end
132
133 class Tel < CustomerFwd
134 def initialize(values)
135 super
136 raise "Bad tel format: #{uri}" unless uri.match?(/\Atel:\+1\d{10}\Z/)
137 end
138
139 def to
140 uri.sub(/^tel:/, "")
141 end
142 end
143
144 class SIP < CustomerFwd
145 def to
146 uri
147 end
148 end
149
150 class XMPP < CustomerFwd
151 def to
152 jid = uri.sub(/^xmpp:/, "")
153 "sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
154 end
155 end
156
157 class None < CustomerFwd
158 def create_call(*); end
159
160 def to; end
161
162 def as_json(*)
163 []
164 end
165 end
166
167 URIS = {
168 tel: Tel,
169 sip: SIP,
170 xmpp: XMPP
171 }.freeze
172end