From a35693b9f499870172fce2d755341568cce64963 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Thu, 16 Mar 2017 19:34:16 -0500 Subject: [PATCH] Refactor the whole chat handler --- Gemfile | 1 + sgx-catapult.rb | 172 +++++++++++++++++++++++------------------------- 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/Gemfile b/Gemfile index 1886af41a6dcc06667a0174497d3bfd85a48418e..9fe5c18de0dd255875a97aced5421f1196938efe 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ gem 'em-http-request' gem 'eventmachine', '1.0.0' gem 'promise.rb' +gem 'em-hiredis' gem 'hiredis', '~> 0.6.0' gem 'redis', '>= 3.2.0' diff --git a/sgx-catapult.rb b/sgx-catapult.rb index d90f43ca4319e473b10b57c7b2fe072d2bbd9931..c24f8686775db036a36e24eeb31daf1f64f3748b 100755 --- a/sgx-catapult.rb +++ b/sgx-catapult.rb @@ -19,6 +19,7 @@ # with sgx-catapult. If not, see . require 'blather/client/dsl' +require 'em-hiredis' require 'em-http-request' require 'json' require 'net/http' @@ -58,6 +59,11 @@ def panic(e) EM.stop end +def extract_shortcode(dest) + num, context = dest.split(';', 2) + num if context && context == 'phone-context=ca-us.phone-context.soprani.ca' +end + module SGXcatapult extend Blather::DSL @@ -101,91 +107,40 @@ module SGXcatapult setup ARGV[0], ARGV[1], ARGV[2], ARGV[3] - message :chat?, :body do |m| - begin - num_dest = m.to.to_s.split('@', 2)[0] - - if num_dest[0] != '+' - # check to see if a valid shortcode context is specified - num_and_context = num_dest.split(';', 2) - if num_and_context[1] and num_and_context[1] == - 'phone-context=ca-us.phone-context.soprani.ca' - - # TODO: check if num_dest is fully numeric - num_dest = num_and_context[0] - else - # TODO: text re num not (yet) supportd/implmentd - write_to_stream error_msg( - m.reply, m.body, - :cancel, 'item-not-found' - ) - next - end - end - - bare_jid = m.from.to_s.split('/', 2)[0] - cred_key = "catapult_cred-" + bare_jid - - conn = Hiredis::Connection.new - conn.connect(ARGV[4], ARGV[5].to_i) - - conn.write ["EXISTS", cred_key] - if conn.read == 0 - conn.disconnect - - # TODO: add text re credentials not being registered - write_to_stream error_msg( - m.reply, m.body, :auth, - 'registration-required' - ) - next - end - - conn.write ["LRANGE", cred_key, 0, 3] - user_id, api_token, api_secret, users_num = conn.read - - # if the destination user is in the system just pass on directly - jid_key = "catapult_jid-" + num_dest - conn.write ["EXISTS", jid_key] - if conn.read > 0 - # setup delivery receipt; sort of a reply but not quite - rcpt = ReceiptMessage.new(bare_jid) - rcpt.from = m.to - - # pass on the original message (before sending receipt) - conn.write ["GET", jid_key] - m.to = conn.read - - m.from = users_num + '@' + ARGV[0] + def self.pass_on_message(m, users_num, jid) + # setup delivery receipt; similar to a reply + rcpt = ReceiptMessage.new(m.from.stripped) + rcpt.from = m.to - puts 'XRESPONSE0: ' + m.inspect - write_to_stream m + # pass original message (before sending receipt) + m.to = jid + m.from = "#{users_num}@#{ARGV[0]}" - # send a delivery receipt back to the sender - # TODO: send only when requested per XEP-0184 + puts 'XRESPONSE0: ' + m.inspect + write_to_stream m - # TODO: put in member/instance variable - rcpt['id'] = SecureRandom.uuid - rcvd = Nokogiri::XML::Node.new 'received', rcpt.document - rcvd['xmlns'] = 'urn:xmpp:receipts' - rcvd['id'] = m.id - rcpt.add_child(rcvd) + # send a delivery receipt back to the sender + # TODO: send only when requested per XEP-0184 + # TODO: pass receipts from target if supported - puts 'XRESPONSE1: ' + rcpt.inspect - write_to_stream rcpt + # TODO: put in member/instance variable + rcpt['id'] = SecureRandom.uuid + rcvd = Nokogiri::XML::Node.new 'received', rcpt.document + rcvd['xmlns'] = 'urn:xmpp:receipts' + rcvd['id'] = m.id + rcpt.add_child(rcvd) - conn.disconnect - next - end - - conn.disconnect + puts 'XRESPONSE1: ' + rcpt.inspect + write_to_stream rcpt + end + def self.to_catapult(m, num_dest, user_id, token, secret, users_num) EM::HttpRequest.new( "https://api.catapult.inetwork.com/"\ "v1/users/#{user_id}/messages" ).post( head: { - 'Authorization' => [api_token, api_secret], + 'Authorization' => [token, secret], 'Content-Type' => 'application/json' }, body: JSON.dump( @@ -193,33 +148,68 @@ module SGXcatapult to: num_dest, text: m.body, tag: - # callbacks need both the id and resourcepart - WEBrick::HTTPUtils.escape(m.id.to_s) + ' ' + + # callbacks need id and resourcepart + WEBrick::HTTPUtils.escape(m.id.to_s) + + ' ' + WEBrick::HTTPUtils.escape( - m.from.to_s.split('/', 2)[1].to_s + m.from.resource.to_s ), receiptRequested: 'all', callbackUrl: ARGV[6] ) ).then { |http| - puts "API response to send: #{http.response} with code "\ - "response.code #{http.response_header.status}" + puts "API response to send: #{http.response} with code"\ + " response.code #{http.response_header.status}" if http.response_header.status != 201 - # TODO: add text re unexpected code; mention code number - write_to_stream error_msg( - m.reply, m.body, :cancel, - 'internal-server-error' + # TODO: add text; mention code number + EMPromise.reject( + [:cancel, 'internal-server-error'] ) end - }.catch(&method(:panic)) - - rescue Exception => e - puts 'Shutting down gateway due to exception 001: ' + e.message - SGXcatapult.shutdown - puts 'Gateway has terminated.' - EM.stop + } end + + message :chat?, :body do |m| + EMPromise.resolve(m.to.node.to_s).then { |num_dest| + if num_dest =~ /\A\+?[0-9]+(?:;.*)?\Z/ + next num_dest if num_dest[0] == '+' + shortcode = extract_shortcode(num_dest) + next shortcode if shortcode + end + # TODO: text re num not (yet) supportd/implmentd + EMPromise.reject([:cancel, 'item-not-found']) + }.then { |num_dest| + cred_key = "catapult_cred-#{m.from.stripped}" + REDIS.lrange(cred_key, 0, 3).then { |creds| + [num_dest] + creds + } + }.then { |(num_dest, *creds)| + if creds.length < 4 + # TODO: add text re credentials not registered + EMPromise.reject( + [:auth, 'registration-required'] + ) + else + jid_key = "catapult_jid-#{num_dest}" + REDIS.get(jid_key).then { |jid| + [jid, num_dest] + creds + } + end + }.then { |(jid, num_dest, *creds)| + # if destination user is in the system pass on directly + if jid + pass_on_message(m, creds.last, jid) + else + to_catapult(m, num_dest, *creds) + end + }.catch { |e| + if e.is_a?(Array) && e.length == 2 + write_to_stream error_msg(m.reply, m.body, *e) + else + EMPromise.reject(e) + end + }.catch(&method(:panic)) end def self.user_cap_identities @@ -1172,6 +1162,8 @@ class WebhookHandler < Goliath::API end EM.run do + REDIS = EM::Hiredis.connect("redis://#{ARGV[4]}:#{ARGV[5]}/0") + SGXcatapult.run # required when using Prosody otherwise disconnects on 6-hour inactivity