M lib/bwmsgsv2_repo.rb => lib/bwmsgsv2_repo.rb +1 -1
@@ 20,7 20,7 @@ class Bwmsgsv2Repo
fetch_raw(sgx.from_jid).then do |(((ogm_url, fwd_time, fwd), trans_d), reg)|
sgx.with({
ogm_url: ogm_url,
- fwd: CustomerFwd.for(fwd, fwd_time),
+ fwd: CustomerFwd.for(uri: fwd, timeout: fwd_time),
transcription_enabled: !trans_d,
registered?: reg
}.compact)
M lib/customer_fwd.rb => lib/customer_fwd.rb +35 -30
@@ 1,17 1,25 @@
# frozen_string_literal: true
+require "value_semantics/monkey_patched"
require "uri"
class CustomerFwd
- def self.for(uri, timeout)
+ def self.for(uri:, timeout:)
timeout = Timeout.new(timeout)
- return if !uri || timeout.zero?
+ return None.new(uri: uri, timeout: timeout) if !uri || timeout.zero?
+ if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
+ uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
+ end
URIS.fetch(uri.split(":", 2).first.to_sym) {
raise "Unknown forward URI: #{uri}"
- }.new(uri, timeout)
+ }.new(uri: uri, timeout: timeout)
end
class Timeout
+ def self.new(s)
+ s.is_a?(self) ? s : super
+ end
+
def initialize(s)
@timeout = s.nil? || s.to_i.negative? ? 300 : s.to_i
end
@@ 25,54 33,51 @@ class CustomerFwd
end
end
- def create_call_request
+ value_semantics do
+ uri Either(String, NilClass)
+ # rubocop:disable Style/RedundantSelf
+ self.timeout Timeout, coerce: Timeout.method(:new)
+ # rubocop:enable Style/RedundantSelf
+ end
+
+ def with(new_attrs)
+ CustomerFwd.for(to_h.merge(new_attrs))
+ end
+
+ def create_call(account)
request = Bandwidth::ApiCreateCallRequest.new.tap do |cc|
cc.to = to
cc.call_timeout = timeout.to_i
+ yield cc if block_given?
end
- yield request if block_given?
- request
+ BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
end
class Tel < CustomerFwd
- attr_reader :timeout
-
- def initialize(uri, timeout)
- @tel = uri.sub(/^tel:/, "")
- @timeout = timeout
- end
-
def to
- @tel
+ uri.sub(/^tel:/, "")
end
end
class SIP < CustomerFwd
- attr_reader :timeout
-
- def initialize(uri, timeout)
- @uri = uri
- @timeout = timeout
- end
-
def to
- @uri
+ uri
end
end
class XMPP < CustomerFwd
- attr_reader :timeout
-
- def initialize(uri, timeout)
- @jid = uri.sub(/^xmpp:/, "")
- @timeout = timeout
- end
-
def to
- "sip:#{ERB::Util.url_encode(@jid)}@sip.cheogram.com"
+ jid = uri.sub(/^xmpp:/, "")
+ "sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
end
end
+ class None < CustomerFwd
+ def create_call; end
+
+ def to; end
+ end
+
URIS = {
tel: Tel,
sip: SIP,
A test/test_customer_fwd.rb => test/test_customer_fwd.rb +51 -0
@@ 0,0 1,51 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "customer_fwd"
+
+class Rantly
+ def jid
+ v = Blather::JID.new(Blather::JID.new(string, string).stripped.to_s)
+ guard !v.to_s.to_s.empty?
+ v
+ end
+end
+
+class CustomerFwdTest < Minitest::Test
+ property(:for_xmpp) { jid }
+ def for_xmpp(jid)
+ sip = "sip:#{ERB::Util.url_encode(jid.to_s)}@sip.cheogram.com"
+ fwd = CustomerFwd.for(uri: "xmpp:#{jid}", timeout: 10)
+ assert_kind_of CustomerFwd::XMPP, fwd
+ assert_equal sip, fwd.to
+ end
+
+ property(:for_xmpp_sip) { jid }
+ def for_xmpp_sip(jid)
+ sip = "sip:#{ERB::Util.url_encode(jid.to_s)}@sip.cheogram.com"
+ fwd = CustomerFwd.for(uri: sip, timeout: 10)
+ assert_kind_of CustomerFwd::XMPP, fwd
+ assert_equal sip, fwd.to
+ end
+
+ property(:for_tel) { "+#{string(:digit)}" }
+ def for_tel(tel)
+ fwd = CustomerFwd.for(uri: "tel:#{tel}", timeout: 10)
+ assert_kind_of CustomerFwd::Tel, fwd
+ assert_equal tel, fwd.to
+ end
+
+ property(:for_sip) { "#{string(:alnum)}@#{string(:alnum)}.example.com" }
+ def for_sip(sip)
+ fwd = CustomerFwd.for(uri: "sip:#{sip}", timeout: 10)
+ assert_kind_of CustomerFwd::SIP, fwd
+ assert_equal "sip:#{sip}", fwd.to
+ end
+
+ property(:for_bogus) { string }
+ def for_bogus(bogus)
+ assert_raises(RuntimeError) do
+ CustomerFwd.for(uri: "bogus:#{bogus}", timeout: 10)
+ end
+ end
+end
M web.rb => web.rb +9 -10
@@ 257,17 257,16 @@ class Web < Roda
CustomerRepo.new(
sgx_repo: Bwmsgsv2Repo.new
).find_by_tel(params["to"]).then(&:fwd).then do |fwd|
- if fwd
+ call = fwd.create_call(CONFIG[:creds][:account]) do |cc|
true_inbound_call[pseudo_call_id] = params["callId"]
- outbound_transfers[pseudo_call_id] = BANDWIDTH_VOICE.create_call(
- CONFIG[:creds][:account],
- body: fwd.create_call_request do |cc|
- cc.from = params["from"]
- cc.application_id = params["applicationId"]
- cc.answer_url = url inbound_calls_path(nil)
- cc.disconnect_url = url inbound_calls_path(:transfer_complete)
- end
- ).data.call_id
+ cc.from = params["from"]
+ cc.application_id = params["applicationId"]
+ cc.answer_url = url inbound_calls_path(nil)
+ cc.disconnect_url = url inbound_calls_path(:transfer_complete)
+ end
+
+ if call
+ outbound_transfers[pseudo_call_id] = call
render :pause, locals: { duration: 300 }
else
render :redirect, locals: { to: inbound_calls_path(:voicemail) }