hipbot 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +0 -3
- data/Gemfile.lock +7 -10
- data/README.md +129 -21
- data/bin/hipbot +8 -1
- data/hipbot.gemspec +2 -1
- data/lib/hipbot.rb +8 -7
- data/lib/hipbot/adapters/hipchat/connection.rb +123 -0
- data/lib/hipbot/adapters/hipchat/hipchat.rb +20 -0
- data/lib/hipbot/adapters/{telnet.rb → telnet/connection.rb} +0 -10
- data/lib/hipbot/adapters/telnet/telnet.rb +16 -0
- data/lib/hipbot/bot.rb +10 -9
- data/lib/hipbot/configuration.rb +3 -0
- data/lib/hipbot/http_response.rb +15 -0
- data/lib/hipbot/message.rb +9 -1
- data/lib/hipbot/{encoding.rb → patches/encoding.rb} +0 -0
- data/lib/hipbot/patches/mucclient.rb +147 -0
- data/lib/hipbot/reaction.rb +14 -6
- data/lib/hipbot/response.rb +10 -18
- data/lib/hipbot/room.rb +2 -5
- data/lib/hipbot/version.rb +1 -1
- data/spec/integration/hipbot_spec.rb +33 -4
- data/spec/unit/hipbot_spec.rb +75 -3
- metadata +29 -12
- data/bot.rb +0 -92
- data/lib/hipbot/adapters/hipchat.rb +0 -86
- data/lib/hipbot/mucclient.rb +0 -60
- data/test_server.rb +0 -23
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -14,8 +14,6 @@ GEM
|
|
14
14
|
eventmachine (>= 0.12.9)
|
15
15
|
escape_utils (0.2.4)
|
16
16
|
eventmachine (0.12.10)
|
17
|
-
google-weather (0.3.0)
|
18
|
-
httparty (~> 0.5.0)
|
19
17
|
httparty (0.5.2)
|
20
18
|
crack (= 0.1.6)
|
21
19
|
i18n (0.6.0)
|
@@ -23,14 +21,14 @@ GEM
|
|
23
21
|
mocha (0.11.4)
|
24
22
|
metaclass (~> 0.0.1)
|
25
23
|
rake (0.9.2.2)
|
26
|
-
rspec (2.
|
27
|
-
rspec-core (~> 2.
|
28
|
-
rspec-expectations (~> 2.
|
29
|
-
rspec-mocks (~> 2.
|
30
|
-
rspec-core (2.
|
31
|
-
rspec-expectations (2.
|
24
|
+
rspec (2.11.0)
|
25
|
+
rspec-core (~> 2.11.0)
|
26
|
+
rspec-expectations (~> 2.11.0)
|
27
|
+
rspec-mocks (~> 2.11.0)
|
28
|
+
rspec-core (2.11.1)
|
29
|
+
rspec-expectations (2.11.2)
|
32
30
|
diff-lcs (~> 1.1.3)
|
33
|
-
rspec-mocks (2.
|
31
|
+
rspec-mocks (2.11.1)
|
34
32
|
xmpp4r (0.5)
|
35
33
|
|
36
34
|
PLATFORMS
|
@@ -41,7 +39,6 @@ DEPENDENCIES
|
|
41
39
|
daemons
|
42
40
|
em-http-request
|
43
41
|
eventmachine
|
44
|
-
google-weather
|
45
42
|
httparty
|
46
43
|
i18n
|
47
44
|
mocha
|
data/README.md
CHANGED
@@ -4,16 +4,16 @@
|
|
4
4
|
|
5
5
|
Hipbot is a bot for HipChat, written in ruby & eventmachine.
|
6
6
|
|
7
|
-
This is a work in progress.
|
8
|
-
|
9
7
|
## Usage
|
10
8
|
|
11
|
-
Install
|
9
|
+
### Install
|
12
10
|
|
13
11
|
```
|
14
12
|
gem install hipbot
|
15
13
|
```
|
16
14
|
|
15
|
+
### Customize
|
16
|
+
|
17
17
|
In bot.rb file, subclass Hipbot::Bot and customize robot responses.
|
18
18
|
|
19
19
|
``` ruby
|
@@ -26,28 +26,114 @@ class MyCompanyBot < Hipbot::Bot
|
|
26
26
|
c.password = 'secret'
|
27
27
|
end
|
28
28
|
|
29
|
-
# simple reply to '@robot hello!'
|
30
29
|
on /^hello.*/ do
|
31
30
|
reply('hello!')
|
32
31
|
end
|
32
|
+
end
|
33
|
+
|
34
|
+
MyCompanyBot.start!
|
35
|
+
```
|
36
|
+
|
37
|
+
You can create a response by providing simple regexp:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
on /^hello.*/ do
|
41
|
+
reply('hello!')
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Responses can pass arguments from regexps:
|
46
|
+
|
47
|
+
``` ruby
|
48
|
+
on /my name is (.*)/ do |user_name|
|
49
|
+
reply('hello #{user_name}!')
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Define multiple regexps for a response:
|
54
|
+
|
55
|
+
``` ruby
|
56
|
+
on /my name is (.*)/, /I am (.*)/ do |name|
|
57
|
+
reply('hello #{user_name}!')
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
Use :from to only match messages from certain users
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
on /status report/, :from => ['tom', 'dave'] do
|
65
|
+
reply('all clear')
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
Use :room to only match messages in certain hipchat rooms
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
on /hello/, :room => ['public'] do
|
73
|
+
reply('hello!')
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
Use :global to react to messages that are not sent directly to @robot
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
on /hey I just met you/, :global => true do
|
81
|
+
reply('and this is crazy...')
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
(Use with caution!)
|
86
|
+
|
87
|
+
#### Response helpers
|
88
|
+
|
89
|
+
Use http helpers (`get`, `post`, `put`, `delete`) to preform a http request:
|
33
90
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
91
|
+
``` ruby
|
92
|
+
on /curl (\S+)/ do |url|
|
93
|
+
get url do |response|
|
94
|
+
reply(response.code)
|
95
|
+
reply(response.headers)
|
96
|
+
reply(response.body)
|
40
97
|
end
|
98
|
+
end
|
99
|
+
```
|
41
100
|
|
42
|
-
|
43
|
-
|
44
|
-
|
101
|
+
``` ruby
|
102
|
+
on /ping site/ do
|
103
|
+
get 'http://example.com', :ping => "1" # issues http://example.com?ping=1
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Inside response you have access to following variables:
|
108
|
+
|
109
|
+
* `message.body` - sent message
|
110
|
+
* `message.sender` - user who sent message
|
111
|
+
* `message.mentions` - array of @mentions inside message, without bot
|
112
|
+
* `room.name` - name of the current room
|
113
|
+
|
114
|
+
You can define your own helpers and use them inside responses like this:
|
115
|
+
|
116
|
+
``` ruby
|
117
|
+
module HipbotHelpers
|
118
|
+
def project_name
|
119
|
+
"#{room.name}-project"
|
45
120
|
end
|
46
121
|
end
|
47
122
|
|
48
|
-
|
123
|
+
class Bot < Hipbot::Bot
|
124
|
+
configure do |c|
|
125
|
+
c.helpers = HipbotHelpers
|
126
|
+
# rest of configuration
|
127
|
+
end
|
128
|
+
|
129
|
+
on /what's the project called\?/ do
|
130
|
+
reply(project_name)
|
131
|
+
end
|
132
|
+
end
|
49
133
|
```
|
50
134
|
|
135
|
+
### Run
|
136
|
+
|
51
137
|
Run hipbot as daemon by saying:
|
52
138
|
|
53
139
|
```
|
@@ -56,13 +142,35 @@ hipbot start
|
|
56
142
|
|
57
143
|
Run `hipbot` to see all available commands.
|
58
144
|
|
145
|
+
## Deploying to Heroku
|
146
|
+
|
147
|
+
Create a Procfile & add it to your repo:
|
148
|
+
|
149
|
+
```
|
150
|
+
worker: bundle exec hipbot run
|
151
|
+
```
|
152
|
+
|
153
|
+
```
|
154
|
+
heroku create
|
155
|
+
git push heroku master
|
156
|
+
heroku ps:scale web=0
|
157
|
+
heroku ps:scale worker=1
|
158
|
+
```
|
159
|
+
|
59
160
|
## TODO:
|
60
161
|
|
61
|
-
*
|
62
|
-
|
63
|
-
|
64
|
-
* allow injecting custom module to response object, adding arbitrary methods
|
65
|
-
* handle reconnecting after disconnect/failure
|
66
|
-
* add support for multiple regexps for one response
|
67
|
-
* add support for responses in particular room (`on //, :room => ['public'] do ...`)
|
162
|
+
* handle auto joining on room invite
|
163
|
+
* add database storage with postgresql adapter
|
164
|
+
* rewrite SimpleMUCClient
|
68
165
|
* add extended logging
|
166
|
+
* handle private messages callbacks in the same way
|
167
|
+
|
168
|
+
### Done:
|
169
|
+
|
170
|
+
* ~~add support for custom helpers~~
|
171
|
+
* ~~mentions - returns list of @mentions in message~~
|
172
|
+
* ~~sender_name - returns sender's first name~~
|
173
|
+
* ~~allow injecting custom module to response object, adding arbitrary methods~~
|
174
|
+
* ~~handle reconnecting after disconnect/failure~~
|
175
|
+
* ~~add support for multiple regexps for one response~~
|
176
|
+
* ~~add support for responses in particular room (`on //, :room => ['public'] do ...`)~~
|
data/bin/hipbot
CHANGED
data/hipbot.gemspec
CHANGED
@@ -20,5 +20,6 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_runtime_dependency "eventmachine", ["~> 0.12.10"]
|
21
21
|
gem.add_runtime_dependency "em-http-request", ["~> 0.3.0"]
|
22
22
|
gem.add_runtime_dependency "xmpp4r", ["~> 0.5"]
|
23
|
-
gem.
|
23
|
+
gem.add_development_dependency "rspec", ['~> 2.11.0']
|
24
|
+
gem.add_development_dependency "mocha", ['~> 0.11.4']
|
24
25
|
end
|
data/lib/hipbot.rb
CHANGED
@@ -3,18 +3,19 @@ require 'eventmachine'
|
|
3
3
|
require 'em-http-request'
|
4
4
|
require 'xmpp4r'
|
5
5
|
require 'xmpp4r/muc'
|
6
|
-
require 'hipbot/mucclient'
|
7
|
-
require 'hipbot/encoding'
|
8
6
|
|
9
|
-
require 'hipbot/
|
10
|
-
require 'hipbot/
|
7
|
+
require 'hipbot/patches/mucclient'
|
8
|
+
require 'hipbot/patches/encoding'
|
9
|
+
|
10
|
+
require 'hipbot/adapters/hipchat/hipchat'
|
11
|
+
require 'hipbot/adapters/hipchat/connection'
|
12
|
+
require 'hipbot/adapters/telnet/telnet'
|
13
|
+
require 'hipbot/adapters/telnet/connection'
|
11
14
|
require 'hipbot/bot'
|
12
15
|
require 'hipbot/configuration'
|
13
16
|
require 'hipbot/message'
|
14
17
|
require 'hipbot/reaction'
|
15
18
|
require 'hipbot/response'
|
19
|
+
require 'hipbot/http_response'
|
16
20
|
require 'hipbot/room'
|
17
21
|
require 'hipbot/version'
|
18
|
-
|
19
|
-
# Plugins
|
20
|
-
require 'google_weather'
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Hipbot
|
2
|
+
module Adapters
|
3
|
+
module Hipchat
|
4
|
+
class Connection
|
5
|
+
def initialize bot
|
6
|
+
initialize_bot(bot)
|
7
|
+
initialize_jabber
|
8
|
+
initialize_rooms
|
9
|
+
join_rooms
|
10
|
+
setup_timers
|
11
|
+
end
|
12
|
+
|
13
|
+
def reply room, message
|
14
|
+
send_message(room, message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def restart!
|
18
|
+
leave_rooms
|
19
|
+
initialize_jabber
|
20
|
+
initialize_rooms
|
21
|
+
join_rooms
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def initialize_rooms
|
27
|
+
@muc_browser = Jabber::MUC::MUCBrowser.new(@jabber)
|
28
|
+
@rooms = @muc_browser.muc_rooms('conf.hipchat.com').map { |jid, name|
|
29
|
+
::Hipbot::Room.new(jid, name)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_bot bot
|
34
|
+
@bot = bot
|
35
|
+
@bot.connection = self
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize_jabber
|
39
|
+
@jabber ||= ::Jabber::Client.new(@bot.jid)
|
40
|
+
@jabber.connect
|
41
|
+
@jabber.auth(@bot.password)
|
42
|
+
@jabber.send(::Jabber::Presence.new.set_type(:available))
|
43
|
+
end
|
44
|
+
|
45
|
+
def join_rooms
|
46
|
+
rooms.each do |room|
|
47
|
+
puts "Joining #{room.name}"
|
48
|
+
|
49
|
+
# TODO rewrite (Simple)MUCClient to handle many rooms from one object
|
50
|
+
# as there is no need to create distinct objects and callback for each
|
51
|
+
# room since all of them have to process same data from @jabber stream.
|
52
|
+
# We probably should be able to do something like this:
|
53
|
+
# @jabber.set_presence([room1, room2], :available)
|
54
|
+
# @jabber.on_event do |time, jid, message| # JID includes sender and room/chat
|
55
|
+
# @jabber.send(jid, message)
|
56
|
+
room.connection = ::Jabber::MUC::SimpleMUCClient.new(@jabber)
|
57
|
+
room.connection.on_message do |time, sender, message|
|
58
|
+
puts "#{room.name} > #{time} <#{sender}> #{message}"
|
59
|
+
begin
|
60
|
+
@bot.tell(sender, room, message)
|
61
|
+
rescue => e
|
62
|
+
puts e.inspect
|
63
|
+
e.backtrace.each do |line|
|
64
|
+
puts line
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO Get and store all user data from HipChat API
|
70
|
+
room.users = []
|
71
|
+
room.connection.on_join do |time, nick|
|
72
|
+
room.users << nick
|
73
|
+
end
|
74
|
+
room.connection.on_leave do |time, nick|
|
75
|
+
room.users.delete(nick)
|
76
|
+
end
|
77
|
+
room.connection.join("#{room.jid}/#{@bot.name}", nil, :history => false)
|
78
|
+
end
|
79
|
+
|
80
|
+
# TODO handle sending private messages with 'reply'.
|
81
|
+
# Simplest way is to add room object for each private chat with param
|
82
|
+
# to distinguish whether to use conf or chat domain
|
83
|
+
# rooms.first.connection.on_private_message do |time, jid, message|
|
84
|
+
# send_message rooms.first, 'hello!', jid
|
85
|
+
|
86
|
+
## Alternative sending:
|
87
|
+
# msg = ::Jabber::Message.new(jid, 'hello!')
|
88
|
+
# msg.type = :chat
|
89
|
+
# @jabber.send(msg)
|
90
|
+
|
91
|
+
## We can trigger normal message callback but 'reply' won't work since hipchat PM uses
|
92
|
+
## different jid (user_room@chat.hipchat.com/client_name)
|
93
|
+
# rooms.first.connection.message_block.call(time, sender, message)
|
94
|
+
# end
|
95
|
+
end
|
96
|
+
|
97
|
+
def leave_rooms
|
98
|
+
rooms.each do |room|
|
99
|
+
puts "Leaving #{room.name}"
|
100
|
+
room.connection.exit
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def setup_timers
|
105
|
+
::EM::add_periodic_timer(10) {
|
106
|
+
if !@jabber.nil? && @jabber.is_disconnected?
|
107
|
+
initialize_jabber
|
108
|
+
join_rooms
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def send_message room, message, jid = nil
|
114
|
+
room.connection.say(message, jid)
|
115
|
+
end
|
116
|
+
|
117
|
+
def rooms
|
118
|
+
@rooms || []
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Hipbot
|
2
|
+
module Adapters
|
3
|
+
module Hipchat
|
4
|
+
delegate :reply, to: :connection
|
5
|
+
|
6
|
+
def start!
|
7
|
+
::EM::run do
|
8
|
+
::EM.error_handler do |e|
|
9
|
+
puts e.inspect
|
10
|
+
e.backtrace.each do |line|
|
11
|
+
puts line
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Connection.new(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,10 +1,6 @@
|
|
1
1
|
module Hipbot
|
2
2
|
module Adapters
|
3
3
|
module Telnet
|
4
|
-
def reply room, message
|
5
|
-
connection.send_data("#{self}:#{room}:#{message}\n")
|
6
|
-
end
|
7
|
-
|
8
4
|
class Connection < EM::Connection
|
9
5
|
def initialize bot
|
10
6
|
@bot = bot
|
@@ -16,12 +12,6 @@ module Hipbot
|
|
16
12
|
@bot.tell(sender, room, message)
|
17
13
|
end
|
18
14
|
end
|
19
|
-
|
20
|
-
def start!
|
21
|
-
::EM::run do
|
22
|
-
::EM::connect('0.0.0.0', 3001, Connection, self)
|
23
|
-
end
|
24
|
-
end
|
25
15
|
end
|
26
16
|
end
|
27
17
|
end
|