lita-hipchat 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://travis-ci.org/jimmycuadra/lita-hipchat)
|
4
|
+
[](https://codeclimate.com/github/jimmycuadra/lita-hipchat)
|
5
|
+
[](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:
|