lita-hipchat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/README.md +52 -0
- data/Rakefile +1 -0
- data/lib/lita/adapters/hipchat/callback.rb +67 -0
- data/lib/lita/adapters/hipchat/connector.rb +131 -0
- data/lib/lita/adapters/hipchat.rb +70 -0
- data/lib/lita/encoding_patches.rb +27 -0
- data/lib/lita-hipchat.rb +1 -0
- data/lita-hipchat.gemspec +24 -0
- data/spec/lita/adapters/hipchat/callback_spec.rb +110 -0
- data/spec/lita/adapters/hipchat/connector_spec.rb +200 -0
- data/spec/lita/adapters/hipchat_spec.rb +119 -0
- data/spec/spec_helper.rb +18 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 161a81fd8834b27403dfd7e86aa4b0b733e099bd
|
4
|
+
data.tar.gz: 7904f55d5dcc2d0731673f93ba787c9db8f5d1be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a2c21e123f3b466bc7ebc355bcc3fc91d47c2d775b10729543e12a0f0ab5635cee9ab3c34e820897d34b8f66052b513e09928a07fbb08b05afb339d0bab4a986
|
7
|
+
data.tar.gz: fd312cce8cd98d536fbf8d009d7613b5c85daaa56611023d32293a40f111c8095eb36ce937f1eaafc83b706d17eb239fb5aa6d10c937588e32df0b1065041abd
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# lita-hipchat
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/jimmycuadra/lita-hipchat.png)](https://travis-ci.org/jimmycuadra/lita-hipchat)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/jimmycuadra/lita-hipchat.png)](https://codeclimate.com/github/jimmycuadra/lita-hipchat)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/jimmycuadra/lita-hipchat/badge.png)](https://coveralls.io/r/jimmycuadra/lita-hipchat)
|
6
|
+
|
7
|
+
**lita-hipchat** is an adapter for [Lita](https://github.com/jimmycuadra/lita) that allows you to use the robot with [HipChat](https://www.hipchat.com/).
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add lita-hipchat to your Lita instance's Gemfile:
|
12
|
+
|
13
|
+
``` ruby
|
14
|
+
gem "lita-hipchat"
|
15
|
+
```
|
16
|
+
|
17
|
+
## Configuration
|
18
|
+
|
19
|
+
Values for all of the following attributes can be found on the "XMPP/Jabber info" page of the account settings on the HipChat website. A JID (Jabber ID) looks like "12345_123456@chat.hipchat.com".
|
20
|
+
|
21
|
+
### Required attributes
|
22
|
+
|
23
|
+
* `jid` (String) - The JID of your robot's HipChat account. Default: `nil`.
|
24
|
+
* `password` (String) - The password for your robot's HipChat account. Default: `nil`.
|
25
|
+
|
26
|
+
### Optional attributes
|
27
|
+
|
28
|
+
* `debug` (Boolean) - If `true`, turns on the underlying Jabber library's (xmpp4r) logger, which is fairly verbose. Default: `false`.
|
29
|
+
* `rooms` (Symbol, Array<String>) - An array of room JIDs that Lita should join upon connection. Can also be the symbol `:all`, which will cause Lita to discover and join all rooms. Default: `nil` (no rooms).
|
30
|
+
* `muc_domain` (String) - The XMPP Multi-User Chat domain to use. Default: `"conf.hipchat.com"`.
|
31
|
+
|
32
|
+
**Note: You must set the robot's name to the value shown as "Room nickname" on the XMPP settings page.**
|
33
|
+
|
34
|
+
There's no need to set `config.robot.mention_name` manually. The adapter will load the proper mention name from the XMPP roster upon connection.
|
35
|
+
|
36
|
+
### Example
|
37
|
+
|
38
|
+
``` ruby
|
39
|
+
Lita.configure do |config|
|
40
|
+
config.robot.name = "Lita Bot"
|
41
|
+
config.robot.adapter = :hipchat
|
42
|
+
config.adapter.jid = "12345_123456@chat.hipchat.com"
|
43
|
+
config.adapter.password = "secret"
|
44
|
+
config.adapter.debug = false
|
45
|
+
config.adapter.rooms = :all
|
46
|
+
config.adapter.muc_domain = "conf.hipchat.com"
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
[MIT](http://opensource.org/licenses/MIT)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Lita
|
2
|
+
module Adapters
|
3
|
+
class HipChat < Adapter
|
4
|
+
class Callback
|
5
|
+
attr_reader :robot, :roster
|
6
|
+
|
7
|
+
def initialize(robot, roster)
|
8
|
+
@robot = robot
|
9
|
+
@roster = roster
|
10
|
+
end
|
11
|
+
|
12
|
+
def private_message(client)
|
13
|
+
client.add_message_callback do |m|
|
14
|
+
next if m.type == :error || m.body.nil?
|
15
|
+
user = user_by_jid(m.from)
|
16
|
+
source = Source.new(user)
|
17
|
+
message = Message.new(robot, m.body, source)
|
18
|
+
message.command!
|
19
|
+
Lita.logger.debug("Dispatching PM to Lita from #{user.id}.")
|
20
|
+
robot.receive(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def muc_message(muc)
|
25
|
+
muc.on_message do |time, nick, text|
|
26
|
+
user = user_by_name(nick)
|
27
|
+
source = Source.new(user, muc.jid.bare.to_s)
|
28
|
+
message = Message.new(robot, text, source)
|
29
|
+
Lita.logger.debug(
|
30
|
+
"Dispatching message to Lita from #{user.id} in MUC #{muc.jid}."
|
31
|
+
)
|
32
|
+
robot.receive(message)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def roster_update
|
37
|
+
roster.add_update_callback do |old_item, item|
|
38
|
+
jid = item.attributes["jid"]
|
39
|
+
Lita.logger.debug("Updating record for user with ID: #{jid}.")
|
40
|
+
create_user(item.attributes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def create_user(user_data)
|
47
|
+
User.create(
|
48
|
+
user_data["jid"],
|
49
|
+
name: user_data["name"],
|
50
|
+
mention_name: user_data["mention_name"]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def user_by_jid(jid)
|
55
|
+
Lita.logger.debug("Looking up user with JID: #{jid}.")
|
56
|
+
create_user(roster[jid].attributes)
|
57
|
+
end
|
58
|
+
|
59
|
+
def user_by_name(name)
|
60
|
+
Lita.logger.debug("Looking up user with name: #{name}.")
|
61
|
+
jid = roster.items.detect { |jid, item| item.iname == name }.first
|
62
|
+
user_by_jid(jid)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "lita/encoding_patches"
|
2
|
+
require "lita/adapters/hipchat/callback"
|
3
|
+
|
4
|
+
require "xmpp4r"
|
5
|
+
require "xmpp4r/roster/helper/roster"
|
6
|
+
require "xmpp4r/muc/helper/simplemucclient"
|
7
|
+
require "xmpp4r/muc/helper/mucbrowser"
|
8
|
+
|
9
|
+
module Lita
|
10
|
+
module Adapters
|
11
|
+
class HipChat < Adapter
|
12
|
+
class Connector
|
13
|
+
attr_reader :robot, :client, :roster
|
14
|
+
|
15
|
+
def initialize(robot, jid, password, debug: false)
|
16
|
+
@robot = robot
|
17
|
+
@jid = normalized_jid(jid, "chat.hipchat.com", "bot")
|
18
|
+
@password = password
|
19
|
+
@client = Jabber::Client.new(@jid)
|
20
|
+
if debug
|
21
|
+
Lita.logger.info("Enabling Jabber log.")
|
22
|
+
Jabber.debug = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def jid
|
27
|
+
@jid.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def connect
|
31
|
+
client_connect
|
32
|
+
load_roster
|
33
|
+
register_message_callback
|
34
|
+
send_presence
|
35
|
+
end
|
36
|
+
|
37
|
+
def join_rooms(muc_domain, rooms)
|
38
|
+
rooms.each do |room_name|
|
39
|
+
muc = Jabber::MUC::SimpleMUCClient.new(client)
|
40
|
+
room_jid = normalized_jid(room_name, muc_domain, robot.name)
|
41
|
+
mucs[room_jid.bare.to_s] = muc
|
42
|
+
register_muc_message_callback(muc)
|
43
|
+
Lita.logger.info("Joining room: #{room_jid}.")
|
44
|
+
muc.join(room_jid)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def list_rooms(muc_domain)
|
49
|
+
Lita.logger.debug("Querying server for list of rooms.")
|
50
|
+
browser = Jabber::MUC::MUCBrowser.new(client)
|
51
|
+
browser.muc_rooms(muc_domain).map { |jid, name| jid.to_s }
|
52
|
+
end
|
53
|
+
|
54
|
+
def message_jid(user_jid, strings)
|
55
|
+
strings.each do |s|
|
56
|
+
Lita.logger.debug("Sending message to JID #{user_jid}: #{s}")
|
57
|
+
message = Jabber::Message.new(user_jid, s)
|
58
|
+
message.type = :chat
|
59
|
+
client.send(message)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def message_muc(room_jid, strings)
|
64
|
+
muc = mucs[room_jid]
|
65
|
+
strings.each do |s|
|
66
|
+
Lita.logger.debug("Sending message to MUC #{room_jid}: #{s}")
|
67
|
+
muc.say(s)
|
68
|
+
end if muc
|
69
|
+
end
|
70
|
+
|
71
|
+
def mucs
|
72
|
+
@mucs ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_topic(room_jid, topic)
|
76
|
+
muc = mucs[room_jid]
|
77
|
+
if muc
|
78
|
+
Lita.logger.debug("Setting topic for MUC #{room_jid}: #{topic}")
|
79
|
+
muc.subject = topic
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def shut_down
|
84
|
+
Lita.logger.info("Disconnecting from HipChat.")
|
85
|
+
client.close
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def send_presence
|
91
|
+
Lita.logger.debug("Sending initial XMPP presence.")
|
92
|
+
client.send(Jabber::Presence.new(:chat))
|
93
|
+
end
|
94
|
+
|
95
|
+
def client_connect
|
96
|
+
Lita.logger.info("Connecting to HipChat.")
|
97
|
+
client.connect
|
98
|
+
Lita.logger.debug("Authenticating with HipChat.")
|
99
|
+
client.auth(@password)
|
100
|
+
end
|
101
|
+
|
102
|
+
def register_message_callback
|
103
|
+
Callback.new(robot, roster).private_message(client)
|
104
|
+
end
|
105
|
+
|
106
|
+
def register_muc_message_callback(muc)
|
107
|
+
Callback.new(robot, roster).muc_message(muc)
|
108
|
+
end
|
109
|
+
|
110
|
+
def load_roster
|
111
|
+
Lita.logger.debug("Loading roster.")
|
112
|
+
@roster = Jabber::Roster::Helper.new(client, false)
|
113
|
+
Callback.new(robot, roster).roster_update
|
114
|
+
roster.get_roster
|
115
|
+
roster.wait_for_roster
|
116
|
+
robot.mention_name = roster[jid].attributes["mention_name"]
|
117
|
+
end
|
118
|
+
|
119
|
+
def normalized_jid(jid, domain, resource)
|
120
|
+
jid = Jabber::JID.new(jid)
|
121
|
+
jid.resource = resource
|
122
|
+
unless jid.node
|
123
|
+
jid.node = jid.domain
|
124
|
+
jid.domain = domain
|
125
|
+
end
|
126
|
+
jid
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "lita"
|
2
|
+
require "lita/adapters/hipchat/connector"
|
3
|
+
|
4
|
+
module Lita
|
5
|
+
module Adapters
|
6
|
+
class HipChat < Adapter
|
7
|
+
require_configs :jid, :password
|
8
|
+
|
9
|
+
attr_reader :connector
|
10
|
+
|
11
|
+
def initialize(robot)
|
12
|
+
super
|
13
|
+
|
14
|
+
set_default_config_values
|
15
|
+
|
16
|
+
@connector = Connector.new(
|
17
|
+
robot,
|
18
|
+
config.jid,
|
19
|
+
config.password,
|
20
|
+
debug: config.debug
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
connector.connect
|
26
|
+
connector.join_rooms(config.muc_domain, rooms)
|
27
|
+
sleep
|
28
|
+
rescue Interrupt
|
29
|
+
shut_down
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_messages(target, strings)
|
33
|
+
if target.room
|
34
|
+
connector.message_muc(target.room, strings)
|
35
|
+
else
|
36
|
+
connector.message_jid(target.user.id, strings)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_topic(target, topic)
|
41
|
+
connector.set_topic(target.room, topic)
|
42
|
+
end
|
43
|
+
|
44
|
+
def shut_down
|
45
|
+
connector.shut_down
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def config
|
51
|
+
Lita.config.adapter
|
52
|
+
end
|
53
|
+
|
54
|
+
def rooms
|
55
|
+
if config.rooms == :all
|
56
|
+
connector.list_rooms(config.muc_domain)
|
57
|
+
else
|
58
|
+
Array(config.rooms)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_default_config_values
|
63
|
+
config.debug = false if config.debug.nil?
|
64
|
+
config.muc_domain = "conf.hipchat.com" if config.muc_domain.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Lita.register_adapter(:hipchat, HipChat)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# xmpp4r and REXML are bad and should feel bad.
|
2
|
+
# https://github.com/ln/xmpp4r/issues/3#issuecomment-1739952
|
3
|
+
require 'socket'
|
4
|
+
class TCPSocket
|
5
|
+
def external_encoding
|
6
|
+
Encoding::BINARY
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rexml/source'
|
11
|
+
class REXML::IOSource
|
12
|
+
alias_method :encoding_assign, :encoding=
|
13
|
+
def encoding=(value)
|
14
|
+
encoding_assign(value) if value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
# OpenSSL is optional and can be missing
|
20
|
+
require 'openssl'
|
21
|
+
class OpenSSL::SSL::SSLSocket
|
22
|
+
def external_encoding
|
23
|
+
Encoding::BINARY
|
24
|
+
end
|
25
|
+
end
|
26
|
+
rescue
|
27
|
+
end
|
data/lib/lita-hipchat.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "lita/adapters/hipchat"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "lita-hipchat"
|
3
|
+
spec.version = "0.0.1"
|
4
|
+
spec.authors = ["Jimmy Cuadra"]
|
5
|
+
spec.email = ["jimmy@jimmycuadra.com"]
|
6
|
+
spec.description = %q{A HipChat adapter for Lita.}
|
7
|
+
spec.summary = %q{A HipChat adapter for the Lita chat robot.}
|
8
|
+
spec.homepage = "https://github.com/jimmycuadra/lita-hipchat"
|
9
|
+
spec.license = "MIT"
|
10
|
+
|
11
|
+
spec.files = `git ls-files`.split($/)
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
|
16
|
+
spec.add_runtime_dependency "lita", "~> 1.1"
|
17
|
+
spec.add_runtime_dependency "xmpp4r", "~> 0.5"
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
20
|
+
spec.add_development_dependency "rake"
|
21
|
+
spec.add_development_dependency "rspec", ">= 2.14.0.rc1"
|
22
|
+
spec.add_development_dependency "simplecov"
|
23
|
+
spec.add_development_dependency "coveralls"
|
24
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Lita::Adapters::HipChat::Callback do
|
4
|
+
subject { described_class.new(robot, roster) }
|
5
|
+
|
6
|
+
let(:robot) { double("Lita::Robot") }
|
7
|
+
let(:roster) do
|
8
|
+
double("Jabber::Roster::Helper", items: { "user_id" => roster_item })
|
9
|
+
end
|
10
|
+
let(:user) { double("Lita::User", id: "user_id") }
|
11
|
+
let(:source) { double("Lita::Source") }
|
12
|
+
let(:message) { double("Lita::Message") }
|
13
|
+
let(:roster_item) do
|
14
|
+
double("Jabber::Roster::RosterItem", attributes: {
|
15
|
+
"jid" => "user_id",
|
16
|
+
"name" => "Carl",
|
17
|
+
"mention_name" => "@Carl"
|
18
|
+
}, iname: "Carl")
|
19
|
+
end
|
20
|
+
|
21
|
+
before do
|
22
|
+
allow(roster).to receive(:[]).with("user_id").and_return(roster_item)
|
23
|
+
allow(Lita::User).to receive(:create).with(
|
24
|
+
"user_id",
|
25
|
+
name: "Carl",
|
26
|
+
mention_name: "@Carl"
|
27
|
+
).and_return(user)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has a robot" do
|
31
|
+
expect(subject.robot).to eq(robot)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "has a roster" do
|
35
|
+
expect(subject.roster).to eq(roster)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#private_message" do
|
39
|
+
let(:client) { double("Jabber::Client") }
|
40
|
+
let(:jabber_message) do
|
41
|
+
double("Jabber::Message", type: :chat, from: "user_id", body: "foo")
|
42
|
+
end
|
43
|
+
|
44
|
+
before do
|
45
|
+
allow(client).to receive(:add_message_callback).and_yield(jabber_message)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "sends the message to the robot with the proper source and body" do
|
49
|
+
allow(Lita::Source).to receive(:new).with(user).and_return(source)
|
50
|
+
allow(Lita::Message).to receive(:new).with(
|
51
|
+
robot,
|
52
|
+
"foo",
|
53
|
+
source
|
54
|
+
).and_return(message)
|
55
|
+
expect(message).to receive(:command!)
|
56
|
+
expect(robot).to receive(:receive).with(message)
|
57
|
+
subject.private_message(client)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "skips the message if it's an error type" do
|
61
|
+
allow(jabber_message).to receive(:type).and_return(:error)
|
62
|
+
expect(robot).not_to receive(:receive)
|
63
|
+
subject.private_message(client)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "skips the message if the body is nil" do
|
67
|
+
allow(jabber_message).to receive(:body).and_return(nil)
|
68
|
+
expect(robot).not_to receive(:receive)
|
69
|
+
subject.private_message(client)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#muc_message" do
|
74
|
+
let(:jid) { double("Jabber::JID", bare: "room_id") }
|
75
|
+
let(:muc) { double("Jabber::MUC::SimpleMUCClient", jid: jid) }
|
76
|
+
|
77
|
+
before do
|
78
|
+
allow(muc).to receive(:on_message).and_yield(nil, "Carl", "foo")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sends the message to the robot with the proper source and body" do
|
82
|
+
allow(Lita::Source).to receive(:new).with(
|
83
|
+
user,
|
84
|
+
"room_id"
|
85
|
+
).and_return(source)
|
86
|
+
allow(Lita::Message).to receive(:new).with(
|
87
|
+
robot,
|
88
|
+
"foo",
|
89
|
+
source
|
90
|
+
).and_return(message)
|
91
|
+
expect(robot).to receive(:receive).with(message)
|
92
|
+
subject.muc_message(muc)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#roster_update" do
|
97
|
+
before do
|
98
|
+
allow(roster).to receive(:add_update_callback).and_yield(nil, roster_item)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "finds/creates a user object for the roster item" do
|
102
|
+
expect(Lita::User).to receive(:create).with(
|
103
|
+
"user_id",
|
104
|
+
name: "Carl",
|
105
|
+
mention_name: "@Carl"
|
106
|
+
)
|
107
|
+
subject.roster_update
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Lita::Adapters::HipChat::Connector do
|
4
|
+
subject { described_class.new(robot, "user", "secret") }
|
5
|
+
|
6
|
+
let(:client) { double("Jabber::Client").as_null_object }
|
7
|
+
let(:robot) { double("Lita::Robot", name: "Lita" ) }
|
8
|
+
|
9
|
+
before { allow(subject).to receive(:client).and_return(client) }
|
10
|
+
|
11
|
+
it "sets the JID properly when only a node is supplied" do
|
12
|
+
subject = described_class.new(robot, "user", "secret")
|
13
|
+
expect(subject.jid).to eq("user@chat.hipchat.com/bot")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets the JID properly when a node and domain are supplied" do
|
17
|
+
subject = described_class.new(robot, "user@example.com", "secret")
|
18
|
+
expect(subject.jid).to eq("user@example.com/bot")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "sets the JID properly when a resource is supplied" do
|
22
|
+
subject = described_class.new(robot, "user@example.com/wrong", "secret")
|
23
|
+
expect(subject.jid).to eq("user@example.com/bot")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "turns on the xmpp4r logger if debug: true is supplied" do
|
27
|
+
expect(Jabber).to receive(:debug=).with(true)
|
28
|
+
subject = described_class.new(robot, "user", "secret", debug: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#connect" do
|
32
|
+
let(:presence) { double("Jabber::Presence") }
|
33
|
+
let(:roster) { double("Jabber::Roster::Helper").as_null_object }
|
34
|
+
let(:callback) { double("Lita::Adapters::HipChat::Callback") }
|
35
|
+
|
36
|
+
before do
|
37
|
+
allow(Jabber::Presence).to receive(:new).and_return(presence)
|
38
|
+
allow(Jabber::Roster::Helper).to receive(:new).with(
|
39
|
+
client,
|
40
|
+
false
|
41
|
+
).and_return(roster)
|
42
|
+
allow(Lita::Adapters::HipChat::Callback).to receive(:new).and_return(
|
43
|
+
callback
|
44
|
+
)
|
45
|
+
allow(callback).to receive(:private_message)
|
46
|
+
allow(callback).to receive(:roster_update)
|
47
|
+
allow(robot).to receive(:mention_name=)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "connects to HipChat" do
|
51
|
+
expect(subject.client).to receive(:connect)
|
52
|
+
subject.connect
|
53
|
+
end
|
54
|
+
|
55
|
+
it "authenticates with the supplied password" do
|
56
|
+
expect(subject.client).to receive(:auth).with("secret")
|
57
|
+
subject.connect
|
58
|
+
end
|
59
|
+
|
60
|
+
it "sends an initial presence of :chat" do
|
61
|
+
expect(Jabber::Presence).to receive(:new).with(:chat).and_return(presence)
|
62
|
+
expect(subject.client).to receive(:send).with(presence)
|
63
|
+
subject.connect
|
64
|
+
end
|
65
|
+
|
66
|
+
it "registers a message callback" do
|
67
|
+
expect(Lita::Adapters::HipChat::Callback).to receive(:new).with(
|
68
|
+
robot,
|
69
|
+
roster
|
70
|
+
).and_return(callback)
|
71
|
+
expect(callback).to receive(:private_message).with(client)
|
72
|
+
subject.connect
|
73
|
+
end
|
74
|
+
|
75
|
+
it "loads a roster" do
|
76
|
+
expect(roster).to receive(:wait_for_roster)
|
77
|
+
subject.connect
|
78
|
+
end
|
79
|
+
|
80
|
+
it "assigns the robot's mention_name with info from the roster" do
|
81
|
+
allow(roster).to receive(:[]).with(subject.jid).and_return(
|
82
|
+
double("Jabber::Roster::RosterItem", attributes: {
|
83
|
+
"mention_name" => "LitaBot"
|
84
|
+
})
|
85
|
+
)
|
86
|
+
expect(robot).to receive(:mention_name=).with("LitaBot")
|
87
|
+
subject.connect
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#join rooms" do
|
92
|
+
let(:muc_domain) { "conf.hipchat.com" }
|
93
|
+
let(:rooms) { ["muc_1", "muc_2"] }
|
94
|
+
let(:muc_1) { double("Jabber::MUC::SimpleMUCClient").as_null_object }
|
95
|
+
let(:muc_2) { double("Jabber::MUC::SimpleMUCClient").as_null_object }
|
96
|
+
let(:callback) { double("Lita::Adapters::HipChat::Callback") }
|
97
|
+
let(:roster) { double("Jabber::Roster::Helper") }
|
98
|
+
|
99
|
+
before do
|
100
|
+
allow(Jabber::MUC::SimpleMUCClient).to receive(:new).with(
|
101
|
+
client
|
102
|
+
).and_return(muc_1, muc_2)
|
103
|
+
allow(Lita::Adapters::HipChat::Callback).to receive(:new).and_return(
|
104
|
+
callback
|
105
|
+
)
|
106
|
+
allow(callback).to receive(:muc_message)
|
107
|
+
allow(subject).to receive(:roster).and_return(roster)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "creates a SimpleMUCClient for each room" do
|
111
|
+
subject.join_rooms(muc_domain, rooms)
|
112
|
+
expect(subject.mucs).to eq(
|
113
|
+
"muc_1@conf.hipchat.com" => muc_1,
|
114
|
+
"muc_2@conf.hipchat.com" => muc_2,
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "registers a message callback for each room" do
|
119
|
+
expect(Lita::Adapters::HipChat::Callback).to receive(:new).with(
|
120
|
+
robot,
|
121
|
+
roster
|
122
|
+
).and_return(callback)
|
123
|
+
expect(callback).to receive(:muc_message).with(muc_1)
|
124
|
+
expect(callback).to receive(:muc_message).with(muc_2)
|
125
|
+
subject.join_rooms(muc_domain, rooms)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "joins each room" do
|
129
|
+
expect(muc_1).to receive(:join)
|
130
|
+
expect(muc_2).to receive(:join)
|
131
|
+
subject.join_rooms(muc_domain, rooms)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#list_rooms" do
|
136
|
+
let(:browser) { double("Jabber::MUC::MUCBrowser") }
|
137
|
+
|
138
|
+
before do
|
139
|
+
allow(Jabber::MUC::MUCBrowser).to receive(:new).with(client).and_return(
|
140
|
+
browser
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns an array of room JIDs for the MUC domain" do
|
145
|
+
allow(browser).to receive(:muc_rooms).with("conf.hipchat.com").and_return(
|
146
|
+
"123_456@conf.hipchat.com" => "Room 1",
|
147
|
+
"789_012@conf.hipchat.com" => "Room 2"
|
148
|
+
)
|
149
|
+
expect(subject.list_rooms("conf.hipchat.com")).to eq([
|
150
|
+
"123_456@conf.hipchat.com",
|
151
|
+
"789_012@conf.hipchat.com"
|
152
|
+
])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#message_jid" do
|
157
|
+
let(:message_1) { double("Jabber::Message") }
|
158
|
+
let(:message_2) { double("Jabber::Message") }
|
159
|
+
|
160
|
+
it "sends the messages to the user" do
|
161
|
+
allow(Jabber::Message).to receive(:new).with("jid", "foo").and_return(
|
162
|
+
message_1
|
163
|
+
)
|
164
|
+
allow(Jabber::Message).to receive(:new).with("jid", "bar").and_return(
|
165
|
+
message_2
|
166
|
+
)
|
167
|
+
expect(message_1).to receive(:type=).with(:chat)
|
168
|
+
expect(message_2).to receive(:type=).with(:chat)
|
169
|
+
expect(client).to receive(:send).with(message_1)
|
170
|
+
expect(client).to receive(:send).with(message_2)
|
171
|
+
subject.message_jid("jid", ["foo", "bar"])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#message_muc" do
|
176
|
+
it "sends the messages to the room" do
|
177
|
+
muc = double("Jabber::MUC::SimpleMUCClient")
|
178
|
+
allow(subject).to receive(:mucs).and_return("jid" => muc)
|
179
|
+
expect(muc).to receive(:say).with("foo")
|
180
|
+
expect(muc).to receive(:say).with("bar")
|
181
|
+
subject.message_muc("jid", ["foo", "bar"])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#set_topic" do
|
186
|
+
it "sets the room's topic to the supplied message" do
|
187
|
+
muc = double("Jabber::MUC::SimpleMUCClient")
|
188
|
+
allow(subject).to receive(:mucs).and_return("jid" => muc)
|
189
|
+
expect(muc).to receive(:subject=).with("New topic")
|
190
|
+
subject.set_topic("jid", "New topic")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#shut_down" do
|
195
|
+
it "closes the client connection" do
|
196
|
+
expect(subject.client).to receive(:close)
|
197
|
+
subject.shut_down
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Lita::Adapters::HipChat do
|
4
|
+
before do
|
5
|
+
Lita.config.adapter.jid = "jid"
|
6
|
+
Lita.config.adapter.password = "secret"
|
7
|
+
Lita.config.adapter.rooms = nil
|
8
|
+
Lita.config.adapter.muc_domain = nil
|
9
|
+
allow(Lita).to receive(:logger).and_return(double("Logger").as_null_object)
|
10
|
+
allow(described_class::Connector).to receive(:new).and_return(connector)
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { described_class.new(robot) }
|
14
|
+
|
15
|
+
let(:robot) { double("Lita::Robot") }
|
16
|
+
let(:connector) { double("Lita::Adapters::HipChat::Connector") }
|
17
|
+
|
18
|
+
it "registers with Lita" do
|
19
|
+
expect(Lita.adapters[:hipchat]).to eql(described_class)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "requires config.jid and config.password" do
|
23
|
+
Lita.instance_variable_set(:@config, nil)
|
24
|
+
expect(Lita.logger).to receive(:fatal).with(/jid, password/)
|
25
|
+
expect { subject }.to raise_error(SystemExit)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#run" do
|
29
|
+
before do
|
30
|
+
allow(subject.connector).to receive(:connect)
|
31
|
+
allow(subject.connector).to receive(:join_rooms)
|
32
|
+
allow(subject).to receive(:sleep)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "connects to HipChat" do
|
36
|
+
expect(subject.connector).to receive(:connect)
|
37
|
+
subject.run
|
38
|
+
end
|
39
|
+
|
40
|
+
it "joins rooms with a custom muc_domain" do
|
41
|
+
Lita.config.adapter.muc_domain = "foo.bar.com"
|
42
|
+
expect(subject.connector).to receive(:join_rooms).with(
|
43
|
+
"foo.bar.com",
|
44
|
+
anything
|
45
|
+
)
|
46
|
+
subject.run
|
47
|
+
end
|
48
|
+
|
49
|
+
it "joins all rooms when config.rooms is :all" do
|
50
|
+
all_rooms = ["room_1_id", "room_2_id"]
|
51
|
+
Lita.config.adapter.rooms = :all
|
52
|
+
allow(subject.connector).to receive(:list_rooms).with(
|
53
|
+
"conf.hipchat.com"
|
54
|
+
).and_return(all_rooms)
|
55
|
+
expect(subject.connector).to receive(:join_rooms).with(
|
56
|
+
"conf.hipchat.com",
|
57
|
+
all_rooms
|
58
|
+
)
|
59
|
+
subject.run
|
60
|
+
end
|
61
|
+
|
62
|
+
it "joins rooms specified by config.rooms" do
|
63
|
+
custom_rooms = ["my_room_1_id", "my_room_2_id"]
|
64
|
+
Lita.config.adapter.rooms = custom_rooms
|
65
|
+
expect(subject.connector).to receive(:join_rooms).with(
|
66
|
+
"conf.hipchat.com",
|
67
|
+
custom_rooms
|
68
|
+
)
|
69
|
+
subject.run
|
70
|
+
end
|
71
|
+
|
72
|
+
it "sleeps the main thread" do
|
73
|
+
expect(subject).to receive(:sleep)
|
74
|
+
subject.run
|
75
|
+
end
|
76
|
+
|
77
|
+
it "disconnects gracefully on interrupt" do
|
78
|
+
expect(subject).to receive(:shut_down)
|
79
|
+
allow(subject).to receive(:sleep).and_raise(Interrupt)
|
80
|
+
subject.run
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#send_messages" do
|
85
|
+
it "sends messages to rooms" do
|
86
|
+
source = double("Lita::Source", room: "room_id")
|
87
|
+
expect(subject.connector).to receive(:message_muc).with(
|
88
|
+
"room_id",
|
89
|
+
["Hello!"]
|
90
|
+
)
|
91
|
+
subject.send_messages(source, ["Hello!"])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "sends private messages to users" do
|
95
|
+
user = double("Lita::User", id: "user_id")
|
96
|
+
source = double("Lita::Source", room: nil, user: user)
|
97
|
+
expect(subject.connector).to receive(:message_jid).with(
|
98
|
+
"user_id",
|
99
|
+
["Hello!"]
|
100
|
+
)
|
101
|
+
subject.send_messages(source, ["Hello!"])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#set_topic" do
|
106
|
+
it "sets a new topic for a room" do
|
107
|
+
source = double("Lita::Source", room: "room_id")
|
108
|
+
expect(subject.connector).to receive(:set_topic).with("room_id", "Topic")
|
109
|
+
subject.set_topic(source, "Topic")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#shut_down" do
|
114
|
+
it "shuts down the connector" do
|
115
|
+
expect(subject.connector).to receive(:shut_down)
|
116
|
+
subject.shut_down
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
require "coveralls"
|
3
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
4
|
+
SimpleCov::Formatter::HTMLFormatter,
|
5
|
+
Coveralls::SimpleCov::Formatter
|
6
|
+
]
|
7
|
+
SimpleCov.start
|
8
|
+
|
9
|
+
require "lita-hipchat"
|
10
|
+
require "lita/rspec"
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.include Lita::RSpec
|
14
|
+
|
15
|
+
config.before do
|
16
|
+
allow(Lita).to receive(:logger).and_return(double("Logger").as_null_object)
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lita-hipchat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jimmy Cuadra
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: lita
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: xmpp4r
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.14.0.rc1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.14.0.rc1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A HipChat adapter for Lita.
|
112
|
+
email:
|
113
|
+
- jimmy@jimmycuadra.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .travis.yml
|
120
|
+
- Gemfile
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- lib/lita-hipchat.rb
|
124
|
+
- lib/lita/adapters/hipchat.rb
|
125
|
+
- lib/lita/adapters/hipchat/callback.rb
|
126
|
+
- lib/lita/adapters/hipchat/connector.rb
|
127
|
+
- lib/lita/encoding_patches.rb
|
128
|
+
- lita-hipchat.gemspec
|
129
|
+
- spec/lita/adapters/hipchat/callback_spec.rb
|
130
|
+
- spec/lita/adapters/hipchat/connector_spec.rb
|
131
|
+
- spec/lita/adapters/hipchat_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
homepage: https://github.com/jimmycuadra/lita-hipchat
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata: {}
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - '>='
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
requirements: []
|
152
|
+
rubyforge_project:
|
153
|
+
rubygems_version: 2.0.3
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: A HipChat adapter for the Lita chat robot.
|
157
|
+
test_files:
|
158
|
+
- spec/lita/adapters/hipchat/callback_spec.rb
|
159
|
+
- spec/lita/adapters/hipchat/connector_spec.rb
|
160
|
+
- spec/lita/adapters/hipchat_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
has_rdoc:
|