jimmycuadra/lita-hipchat

View on GitHub
lib/lita/adapters/hipchat/connector.rb

Summary

Maintainability
A
0 mins
Test Coverage
require "lita/adapters/hipchat/callback"

require "xmpp4r"
require "xmpp4r/roster/helper/roster"
require "xmpp4r/muc/helper/simplemucclient"
require "xmpp4r/muc/helper/mucbrowser"

module Lita
  module Adapters
    class HipChat < Adapter
      class Connector
        attr_reader :robot, :client, :roster

        def initialize(robot, jid, password, server, debug: false)
          @robot = robot
          @jid = normalized_jid(jid, "chat.hipchat.com", "bot")
          @password = password
          @server = server
          @client = Jabber::Client.new(@jid)
          if debug
            Lita.logger.info("Enabling Jabber log.")
            Jabber.debug = true
          end
        end

        def jid
          @jid.to_s
        end

        def connect
          register_exception_handler
          client_connect
          load_roster
          register_message_callback
          send_presence
        end

        def join(muc_domain, room)
          room_jid = normalized_jid(room, muc_domain, robot.name)
          if mucs[room_jid.bare.to_s]
            Lita.logger.debug "Already in room with JID #{room_jid.bare.to_s}"
            return
          end

          muc = Jabber::MUC::SimpleMUCClient.new(client)
          mucs[room_jid.bare.to_s] = muc

          register_muc_message_callback(muc)

          Lita.logger.info("Joining room: #{room_jid}.")
          muc.join(room_jid)
        end

        def join_rooms(muc_domain, rooms)
          rooms.each { |room| join(muc_domain, room) }
        end

        def list_rooms(muc_domain)
          Lita.logger.debug("Querying server for list of rooms.")
          browser = Jabber::MUC::MUCBrowser.new(client)
          browser.muc_rooms(muc_domain).map { |jid, name| jid.to_s }
        end

        def message_jid(user_jid, strings)
          strings.each do |s|
            Lita.logger.debug("Sending message to JID #{user_jid}: #{s}")
            message = Jabber::Message.new(user_jid, encode_string(s))
            message.type = :chat
            client.send(message)
          end
        end

        def message_muc(room_jid, strings)
          muc = mucs[room_jid]
          strings.each do |s|
            Lita.logger.debug("Sending message to MUC #{room_jid}: #{s}")
            muc.say(encode_string(s))
          end if muc
        end

        def mucs
          @mucs ||= {}
        end

        def part(muc_domain, room)
          room_jid = normalized_jid(room, muc_domain, robot.name)
          muc = mucs.delete(room_jid.bare.to_s)
          muc.exit if muc
        end

        def set_topic(room_jid, topic)
          muc = mucs[room_jid]
          if muc
            Lita.logger.debug("Setting topic for MUC #{room_jid}: #{topic}")
            muc.subject = topic
          end
        end

        def shut_down
          Lita.logger.info("Disconnecting from HipChat.")
          client.close
        rescue IOError, SystemCallError => e
          Lita.logger.warn("Encountered error during disconnect: #{e}")
        end

        private

        def send_presence
          Lita.logger.debug("Sending initial XMPP presence.")
          client.send(Jabber::Presence.new(:chat))
        end

        def client_connect
          Lita.logger.info("Connecting to HipChat.")
          client.connect(@server)
          sleep 0.0001 until client.is_connected?
          Lita.logger.debug("Authenticating with HipChat.")
          client.auth(@password)
        end

        def register_exception_handler
          client.on_exception do |error, connection, error_source|
            robot.shut_down
          end
        end

        def register_message_callback
          Callback.new(robot, roster).private_message(client)
        end

        def register_muc_message_callback(muc)
          Callback.new(robot, roster).muc_message(muc)
        end

        def load_roster
          Lita.logger.debug("Loading roster.")
          @roster = Jabber::Roster::Helper.new(client, false)
          Callback.new(robot, roster).roster_update
          roster.get_roster
          roster.wait_for_roster
          robot.mention_name = roster[jid].attributes["mention_name"]
        end

        def normalized_jid(jid, domain, resource)
          jid = Jabber::JID.new(jid)
          jid.resource = resource
          unless jid.node
            jid.node = jid.domain
            jid.domain = domain
          end
          jid
        end

        def encode_string(s)
          s.encode('UTF-8', :invalid => :replace, :undef => :replace)
        end
      end
    end
  end
end