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 class Infinite < Timeout
69 def initialize; end
70
71 def zero?
72 false
73 end
74
75 def infinite?
76 1
77 end
78
79 def to_i; end
80 end
81 end
82
83 value_semantics do
84 uri Either(/:/, NilClass)
85 def_attr :timeout, Timeout, coerce: Timeout.method(:for)
86 end
87
88 def with(new_attrs)
89 CustomerFwd.for(to_h.merge(new_attrs))
90 end
91
92 def voicemail_enabled?
93 !timeout.infinite?
94 end
95
96 def create_call(account)
97 raise InfiniteTimeout, self if timeout.infinite?
98
99 request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
100 cc.to = to
101 cc.call_timeout = timeout.to_i
102 yield cc if block_given?
103 }
104 BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
105 end
106
107 class Tel < CustomerFwd
108 def initialize(values)
109 super
110 raise "Bad tel format: #{uri}" unless uri.match?(/\Atel:\+1\d{10}\Z/)
111 end
112
113 def to
114 uri.sub(/^tel:/, "")
115 end
116 end
117
118 class SIP < CustomerFwd
119 def to
120 uri
121 end
122 end
123
124 class XMPP < CustomerFwd
125 def to
126 jid = uri.sub(/^xmpp:/, "")
127 "sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
128 end
129 end
130
131 class None < CustomerFwd
132 def create_call(*); end
133
134 def to; end
135 end
136
137 URIS = {
138 tel: Tel,
139 sip: SIP,
140 xmpp: XMPP
141 }.freeze
142end