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 CHANGED
@@ -2,3 +2,9 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ notifications:
6
+ email:
7
+ recipients:
8
+ - pewniak747@gmail.com
9
+ - bartosz.kopinski@netguru.pl
10
+
data/Gemfile CHANGED
@@ -11,6 +11,3 @@ gem 'daemons'
11
11
 
12
12
  gem 'rspec'
13
13
  gem 'mocha'
14
-
15
- # Plugins
16
- gem 'google-weather'
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.10.0)
27
- rspec-core (~> 2.10.0)
28
- rspec-expectations (~> 2.10.0)
29
- rspec-mocks (~> 2.10.0)
30
- rspec-core (2.10.1)
31
- rspec-expectations (2.10.0)
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.10.1)
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 via RubyGems
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
- # tasks with arguments: '@robot deploy to production pls'
35
- on /deploy to (.*) pls/ do |stage|
36
- reply('deploying to #{stage}!')
37
- post("http://#{stage}.example.com") do |http|
38
- reply("deploy server responded with #{http.response_header.status}")
39
- end
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
- # global messages
43
- on /.*hello everyone.*/, global: true do
44
- reply('hello!')
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
- MyCompanyBot.start!
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
- * add support for custom helpers
62
- * mentions - returns list of @mentions in message
63
- * sender_name - returns sender's first name
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
@@ -2,4 +2,11 @@
2
2
 
3
3
  require 'daemons'
4
4
 
5
- Daemons.run('bot.rb')
5
+ hipbot_file = 'bot.rb'
6
+
7
+ if File.exists?(hipbot_file)
8
+ Daemons.run(hipbot_file)
9
+ else
10
+ puts "File #{hipbot_file} not found!"
11
+ exit 1
12
+ end
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.add_runtime_dependency "google-weather", ["~> 0.3.0"]
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/adapters/hipchat'
10
- require 'hipbot/adapters/telnet'
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