hipbot 0.1.0 → 0.2.0

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.
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