hipbot 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ - 2.0.0
5
6
  notifications:
6
7
  email:
7
8
  recipients:
data/Gemfile CHANGED
@@ -5,9 +5,10 @@ gem 'eventmachine'
5
5
  gem 'em-http-request'
6
6
  gem 'xmpp4r'
7
7
  gem 'httparty'
8
- gem 'active_support'
8
+ gem 'activesupport'
9
9
  gem 'i18n'
10
10
  gem 'daemons'
11
11
 
12
12
  gem 'rspec'
13
+ gem 'guard-rspec'
13
14
  gem 'mocha'
data/Gemfile.lock CHANGED
@@ -1,51 +1,72 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- active_support (3.0.0)
5
- activesupport (= 3.0.0)
6
- activesupport (3.0.0)
7
- addressable (2.3.2)
4
+ activesupport (3.2.12)
5
+ i18n (~> 0.6)
6
+ multi_json (~> 1.0)
7
+ addressable (2.3.3)
8
+ coderay (1.0.9)
8
9
  cookiejar (0.3.0)
9
- daemons (1.1.8)
10
- diff-lcs (1.1.3)
11
- em-http-request (1.0.2)
10
+ daemons (1.1.9)
11
+ diff-lcs (1.2.2)
12
+ em-http-request (1.0.3)
12
13
  addressable (>= 2.2.3)
13
14
  cookiejar
14
15
  em-socksify
15
16
  eventmachine (>= 1.0.0.beta.4)
16
17
  http_parser.rb (>= 0.5.3)
17
- em-socksify (0.2.0)
18
+ em-socksify (0.2.1)
18
19
  eventmachine (>= 1.0.0.beta.4)
19
- eventmachine (1.0.0.rc.4)
20
+ eventmachine (1.0.3)
21
+ formatador (0.2.4)
22
+ guard (1.7.0)
23
+ formatador (>= 0.2.4)
24
+ listen (>= 0.6.0)
25
+ lumberjack (>= 1.0.2)
26
+ pry (>= 0.9.10)
27
+ thor (>= 0.14.6)
28
+ guard-rspec (2.5.2)
29
+ guard (>= 1.1)
30
+ rspec (~> 2.11)
20
31
  http_parser.rb (0.5.3)
21
- httparty (0.8.3)
32
+ httparty (0.10.2)
22
33
  multi_json (~> 1.0)
23
- multi_xml
24
- i18n (0.6.0)
34
+ multi_xml (>= 0.5.2)
35
+ i18n (0.6.4)
36
+ listen (0.7.3)
37
+ lumberjack (1.0.3)
25
38
  metaclass (0.0.1)
26
- mocha (0.12.1)
39
+ method_source (0.8.1)
40
+ mocha (0.13.3)
27
41
  metaclass (~> 0.0.1)
28
- multi_json (1.3.6)
29
- multi_xml (0.5.1)
30
- rake (0.9.2.2)
31
- rspec (2.11.0)
32
- rspec-core (~> 2.11.0)
33
- rspec-expectations (~> 2.11.0)
34
- rspec-mocks (~> 2.11.0)
35
- rspec-core (2.11.1)
36
- rspec-expectations (2.11.2)
37
- diff-lcs (~> 1.1.3)
38
- rspec-mocks (2.11.1)
42
+ multi_json (1.7.2)
43
+ multi_xml (0.5.3)
44
+ pry (0.9.12)
45
+ coderay (~> 1.0.5)
46
+ method_source (~> 0.8)
47
+ slop (~> 3.4)
48
+ rake (10.0.4)
49
+ rspec (2.13.0)
50
+ rspec-core (~> 2.13.0)
51
+ rspec-expectations (~> 2.13.0)
52
+ rspec-mocks (~> 2.13.0)
53
+ rspec-core (2.13.1)
54
+ rspec-expectations (2.13.0)
55
+ diff-lcs (>= 1.1.3, < 2.0)
56
+ rspec-mocks (2.13.0)
57
+ slop (3.4.4)
58
+ thor (0.18.1)
39
59
  xmpp4r (0.5)
40
60
 
41
61
  PLATFORMS
42
62
  ruby
43
63
 
44
64
  DEPENDENCIES
45
- active_support
65
+ activesupport
46
66
  daemons
47
67
  em-http-request
48
68
  eventmachine
69
+ guard-rspec
49
70
  httparty
50
71
  i18n
51
72
  mocha
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build Status](https://secure.travis-ci.org/pewniak747/hipbot.png?branch=master)](http://travis-ci.org/pewniak747/hipbot)
4
4
  [![Code Climate](https://codeclimate.com/github/pewniak747/hipbot.png)](https://codeclimate.com/github/pewniak747/hipbot)
5
5
 
6
- Hipbot is a bot for HipChat, written in ruby & eventmachine.
6
+ Hipbot is a XMPP bot for HipChat, written in ruby & eventmachine.
7
7
 
8
8
  ## Usage
9
9
 
@@ -15,21 +15,44 @@ gem install hipbot
15
15
 
16
16
  ### Customize
17
17
 
18
- In bot.rb file, subclass Hipbot::Bot and customize robot responses.
18
+ Create `bot.rb` file, subclass `Hipbot::Bot` and customize the responses.
19
19
 
20
20
  ``` ruby
21
21
  require 'hipbot'
22
22
 
23
23
  class MyCompanyBot < Hipbot::Bot
24
24
  configure do |c|
25
- c.name = 'robot'
26
- c.jid = 'changeme@chat.hipchat.com'
27
- c.password = 'secret'
25
+ c.name = 'robot' # required
26
+ c.jid = 'changeme@chat.hipchat.com' # required
27
+ c.password = 'secret' # required
28
+ c.teams = { vip: ['John', 'Mike'] }
29
+ c.rooms = { project_rooms: ['Project 1', 'Project 2'] }
30
+ c.plugins = [ PluginClass ]
28
31
  end
29
32
 
30
- on /^hello.*/ do
33
+ on /^hello/ do
31
34
  reply('hello!')
32
35
  end
36
+
37
+ on /^restart/, from: :vip do
38
+ # ...
39
+ reply('restarting...')
40
+ end
41
+
42
+ on /^deploy/, room: :project_rooms do
43
+ # ...
44
+ reply('deploying...')
45
+ end
46
+
47
+ default do
48
+ reply('I don\'t understand you!')
49
+ end
50
+ end
51
+
52
+ class PluginClass < Hipbot::Plugin
53
+ on /^plugin/ do
54
+ reply('this is from plugin!')
55
+ end
33
56
  end
34
57
 
35
58
  MyCompanyBot.start!
@@ -38,7 +61,7 @@ MyCompanyBot.start!
38
61
  You can create a response by providing simple regexp:
39
62
 
40
63
  ``` ruby
41
- on /^hello.*/ do
64
+ on /^hello/ do
42
65
  reply('hello!')
43
66
  end
44
67
  ```
@@ -59,10 +82,15 @@ on /my name is (.*)/, /I am (.*)/ do |name|
59
82
  end
60
83
  ```
61
84
 
62
- Use :from to only match messages from certain users
85
+ Use :from to only match messages from certain users or user groups defined in configuration
63
86
 
64
87
  ``` ruby
65
- on /status report/, :from => ['tom', 'dave'] do
88
+ configure do |c|
89
+ # ...
90
+ c.teams = { vip: ['John', 'Mike'] }
91
+ end
92
+
93
+ on /status report/, from: ['Tom', 'Dave', :vip] do
66
94
  reply('all clear')
67
95
  end
68
96
  ```
@@ -70,7 +98,12 @@ end
70
98
  Use :room to only match messages in certain hipchat rooms
71
99
 
72
100
  ``` ruby
73
- on /hello/, :room => ['public'] do
101
+ configure do |c|
102
+ # ...
103
+ c.rooms = { project_rooms: ['Project 1', 'Project 2'] }
104
+ end
105
+
106
+ on /hello/, room: ['Public Room', :project_rooms] do
74
107
  reply('hello!')
75
108
  end
76
109
  ```
@@ -78,7 +111,7 @@ end
78
111
  Use :global to react to messages that are not sent directly to @robot
79
112
 
80
113
  ``` ruby
81
- on /hey I just met you/, :global => true do
114
+ on /hey I just met you/, global: true do
82
115
  reply('and this is crazy...')
83
116
  end
84
117
  ```
@@ -103,7 +136,7 @@ end
103
136
 
104
137
  ``` ruby
105
138
  on /ping site/ do
106
- get 'http://example.com', :ping => "1" # issues http://example.com?ping=1
139
+ get 'http://example.com', ping: "1" # issues http://example.com?ping=1
107
140
  end
108
141
  ```
109
142
 
@@ -125,8 +158,8 @@ end
125
158
 
126
159
  class Bot < Hipbot::Bot
127
160
  configure do |c|
161
+ # ...
128
162
  c.helpers = HipbotHelpers
129
- # rest of configuration
130
163
  end
131
164
 
132
165
  on /what's the project called\?/ do
@@ -135,6 +168,42 @@ class Bot < Hipbot::Bot
135
168
  end
136
169
  ```
137
170
 
171
+ #### Plugins
172
+
173
+ To define a plugin, subclass `Hipbot::Plugin` and add responses like in bot:
174
+
175
+ ``` ruby
176
+ class GreeterPlugin < Hipbot::Plugin
177
+ on /^hello/ do
178
+ reply('hello there!')
179
+ end
180
+ end
181
+ ```
182
+
183
+ You can gain access to plugin data inside reaction with `plugin` helper:
184
+
185
+ ``` ruby
186
+ class GreeterPlugin < Hipbot::Plugin
187
+ attr_accessor :language
188
+ def initialize(language)
189
+ self.language = language
190
+ end
191
+
192
+ on /^hello/ do
193
+ case plugin.language
194
+ when :en
195
+ reply("hello!")
196
+ when :pl
197
+ reply("cześć!")
198
+ when :jp
199
+ reply("おはよう!")
200
+ end
201
+ end
202
+ end
203
+ ```
204
+
205
+ For a collection of open-source plugins, see https://github.com/netguru/hipbot-plugins
206
+
138
207
  ### Run
139
208
 
140
209
  Run hipbot as daemon by saying:
@@ -162,11 +231,10 @@ heroku ps:scale worker=1
162
231
 
163
232
  ## TODO:
164
233
 
165
- * add plugins support
166
- * add extended logging
167
-
168
234
  ### Done:
169
235
 
236
+ * ~~add extended logging~~
237
+ * ~~add plugins support~~
170
238
  * ~~rewrite SimpleMUCClient~~
171
239
  * ~~handle private messages callbacks~~
172
240
  * ~~handle auto joining on room invite~~
@@ -176,4 +244,4 @@ heroku ps:scale worker=1
176
244
  * ~~allow injecting custom module to response object, adding arbitrary methods~~
177
245
  * ~~handle reconnecting after disconnect/failure~~
178
246
  * ~~add support for multiple regexps for one response~~
179
- * ~~add support for responses in particular room (`on //, :room => ['public'] do ...`)~~
247
+ * ~~add support for responses in particular room (`on //, room: ['public'] do ...`)~~
data/hipbot.gemspec CHANGED
@@ -15,11 +15,12 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Hipbot::VERSION
17
17
  gem.add_runtime_dependency "daemons", ["~> 1.1.8"]
18
- gem.add_runtime_dependency "active_support", ["~> 3.0.0"]
18
+ gem.add_runtime_dependency "activesupport", ["~> 3.2.12"]
19
19
  gem.add_runtime_dependency "i18n", ["~> 0.6.0"]
20
- gem.add_runtime_dependency "eventmachine", ["~> 0.12.10"]
21
- gem.add_runtime_dependency "em-http-request", ["~> 0.3.0"]
20
+ gem.add_runtime_dependency "eventmachine", ["~> 1.0.3"]
21
+ gem.add_runtime_dependency "em-http-request", ["~> 1.0.3"]
22
22
  gem.add_runtime_dependency "xmpp4r", ["~> 0.5"]
23
- gem.add_development_dependency "rspec", ['~> 2.11.0']
24
- gem.add_development_dependency "mocha", ['~> 0.11.4']
23
+ gem.add_development_dependency "rspec", ['~> 2.13.0']
24
+ gem.add_development_dependency "guard-rspec", ['~> 2.5.1']
25
+ gem.add_development_dependency "mocha", ['~> 0.13.3']
25
26
  end
data/lib/hipbot.rb CHANGED
@@ -8,14 +8,17 @@ require 'xmpp4r/muc'
8
8
  require 'hipbot/patches/hipchat_client'
9
9
  require 'hipbot/patches/encoding'
10
10
 
11
+ require 'hipbot/logger'
11
12
  require 'hipbot/adapters/hipchat/hipchat'
12
13
  require 'hipbot/adapters/hipchat/connection'
13
14
  require 'hipbot/adapters/telnet/telnet'
14
15
  require 'hipbot/adapters/telnet/connection'
16
+ require 'hipbot/reactable'
15
17
  require 'hipbot/bot'
18
+ require 'hipbot/plugin'
16
19
  require 'hipbot/configuration'
17
20
  require 'hipbot/collection'
18
- require 'hipbot/http_response'
21
+ require 'hipbot/helpers'
19
22
  require 'hipbot/message'
20
23
  require 'hipbot/reaction'
21
24
  require 'hipbot/response'
@@ -6,8 +6,7 @@ module Hipbot
6
6
  @bot = bot
7
7
  @bot.connection = self
8
8
 
9
- return unless setup_bot
10
- setup_timers
9
+ setup_error_handler && setup_bot && setup_timers
11
10
  end
12
11
 
13
12
  def restart!
@@ -34,23 +33,21 @@ module Hipbot
34
33
  private
35
34
 
36
35
  def setup_bot
37
- return unless initialize_client
38
- initialize_rooms
39
- initialize_users
40
- initialize_callbacks
41
- join_rooms
42
- set_presence('Hello humans!')
43
- true
36
+ initialize_client do
37
+ initialize_rooms
38
+ initialize_users
39
+ initialize_callbacks
40
+ join_rooms
41
+ set_presence('Hello humans!')
42
+ end
44
43
  end
45
44
 
46
45
  def initialize_client
47
- ::Jabber.debug = true
48
46
  @client = ::Jabber::MUC::HipchatClient.new(@bot.jid + '/' + @bot.name)
49
- @client.connect(@bot.password)
47
+ yield if @client.connect(@bot.password)
50
48
  end
51
49
 
52
50
  def initialize_rooms
53
- Room.bot = @bot
54
51
  @client.get_rooms.each do |r|
55
52
  Room.create(r[:item].jid, r[:item].iname, topic: r[:details]['topic'])
56
53
  end
@@ -58,13 +55,12 @@ module Hipbot
58
55
  end
59
56
 
60
57
  def initialize_users
61
- User.bot = @bot
62
58
  @client.get_users.each do |v|
63
59
  params = {
64
- email: v[:vcard]['EMAIL/USERID'],
60
+ email: v[:vcard]['EMAIL/USERID'],
65
61
  mention: v[:item].attributes['mention_name'],
66
- title: v[:vcard]['TITLE'],
67
- photo: v[:vcard]['PHOTO'],
62
+ title: v[:vcard]['TITLE'],
63
+ photo: v[:vcard]['PHOTO'],
68
64
  }
69
65
  User.create(v[:item].jid, v[:item].iname, params)
70
66
  end
@@ -72,84 +68,93 @@ module Hipbot
72
68
  end
73
69
 
74
70
  def join_rooms
75
- if Room.empty?
76
- Jabber::debuglog "No rooms to join"
77
- return false
78
- end
79
- Room.each do |room_jid, _|
80
- @client.join(room_jid)
71
+ with_rooms do |rooms|
72
+ rooms.each do |r|
73
+ @client.join(r.id)
74
+ end
81
75
  end
82
- true
83
76
  end
84
77
 
85
78
  def exit_all_rooms
86
- Room.each do |room_jid, _|
87
- @client.exit(room_jid, 'bye bye!')
79
+ with_rooms do |rooms|
80
+ rooms.each do |r|
81
+ @client.exit(r.id, 'bye bye!')
82
+ end
88
83
  end
89
84
  end
90
85
 
91
86
  def initialize_callbacks
92
- @client.on_message do |room_jid, user_name, message|
93
- room = Room[room_jid]
94
- user = User[user_name]
95
- next if room.nil? && user.nil?
96
- room.params.topic = message.subject if message.subject.present?
97
- next if user_name == @bot.name || message.body.blank?
98
- Jabber::debuglog "[#{Time.now}] <#{room.name}> #{user_name}: #{message.body}"
99
- begin
100
- @bot.react(user, room, message.body)
101
- rescue => e
102
- Jabber::debuglog e.inspect
103
- e.backtrace.each do |line|
104
- Jabber::debuglog line
105
- end
106
- end
107
- end
87
+ @client.on_message{ |*args| message_callback *args }
88
+ @client.on_private_message{ |*args| private_message_callback *args }
89
+ @client.on_invite{ |*args| invite_callback *args }
90
+ @client.on_presence{ |*args| presence_callback *args }
91
+ @client.activate_callbacks
92
+ end
108
93
 
109
- @client.on_private_message do |user_jid, message|
110
- user = User[user_jid]
111
- next if user.blank? || user.name == @bot.name
112
- if message.body.nil?
113
- # if message.active?
114
- # elsif message.inactive?
115
- # elsif message.composing?
116
- # elsif message.gone?
117
- # elsif message.paused?
118
- # end
119
- else
120
- @bot.react(user, nil, message.body)
121
- end
94
+ def message_callback room_jid, user_name, message
95
+ with_sender(room_jid, user_name) do |room, user|
96
+ room.params.topic = message.subject if message.subject.present?
97
+ return if user_name == @bot.name || message.body.blank?
98
+ @bot.react(user, room, message.body)
122
99
  end
100
+ end
123
101
 
124
- @client.on_invite do |room_jid, user_name, room_name, topic|
125
- Room.create(room_jid, room_name, topic: topic)
126
- @client.join(room_jid)
127
- end
102
+ def invite_callback room_jid, user_name, room_name, topic
103
+ Room.create(room_jid, room_name, topic: topic)
104
+ @client.join(room_jid)
105
+ end
128
106
 
129
- @client.on_presence do |room_jid, user_name, pres|
130
- room = Room[room_jid]
131
- next if room.blank? || user_name.blank?
132
- user = User[user_name]
107
+ def presence_callback room_jid, user_name, pres
108
+ with_sender(room_jid, user_name) do |room, user|
133
109
  if pres == 'unavailable'
134
110
  if user_name == @bot.name
135
111
  room.delete
136
112
  elsif user.present?
137
113
  room.user_ids.delete(user.id)
138
114
  end
139
- elsif pres.blank? && user.present? && room.user_ids.exclude?(user.id)
115
+ elsif pres.blank? && room.user_ids.exclude?(user.id)
140
116
  room.user_ids << user.id
141
117
  end
142
118
  end
119
+ end
143
120
 
144
- @client.activate_callbacks
121
+ def private_message_callback user_jid, message
122
+ with_user(user_jid) do |user|
123
+ @bot.react(user, nil, message.body) if user.name != @bot.name
124
+ end if message.body.present?
125
+ end
126
+
127
+ def with_rooms
128
+ return Jabber::debuglog 'No rooms found' if Room.empty?
129
+ yield Room
130
+ end
131
+
132
+ def with_sender room_id, user_id
133
+ room = Room[room_id]
134
+ with_user(user_id) do |user|
135
+ yield room, user
136
+ end if room.present?
137
+ end
138
+
139
+ def with_user user_id
140
+ user = User[user_id]
141
+ yield user if user
145
142
  end
146
143
 
147
144
  def setup_timers
148
- ::EM::add_periodic_timer(60) {
145
+ ::EM::add_periodic_timer(60) do
149
146
  @client.keep_alive(@bot.password) if @client.present?
150
- }
147
+ end
151
148
  end
152
149
 
150
+ def setup_error_handler
151
+ ::EM.error_handler do |e|
152
+ Jabber::debuglog e.inspect
153
+ e.backtrace.each do |line|
154
+ Jabber::debuglog line
155
+ end
156
+ end
157
+ end
153
158
  end
154
159
  end
155
160
  end