hipbot 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  *.gem
2
+ test_bot.rb
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
4
  gem 'eventmachine'
@@ -1,25 +1,32 @@
1
1
  GEM
2
- remote: http://rubygems.org/
2
+ remote: https://rubygems.org/
3
3
  specs:
4
4
  active_support (3.0.0)
5
5
  activesupport (= 3.0.0)
6
6
  activesupport (3.0.0)
7
- addressable (2.2.8)
8
- crack (0.1.6)
7
+ addressable (2.3.2)
8
+ cookiejar (0.3.0)
9
9
  daemons (1.1.8)
10
10
  diff-lcs (1.1.3)
11
- em-http-request (0.3.0)
12
- addressable (>= 2.0.0)
13
- escape_utils
14
- eventmachine (>= 0.12.9)
15
- escape_utils (0.2.4)
16
- eventmachine (0.12.10)
17
- httparty (0.5.2)
18
- crack (= 0.1.6)
11
+ em-http-request (1.0.2)
12
+ addressable (>= 2.2.3)
13
+ cookiejar
14
+ em-socksify
15
+ eventmachine (>= 1.0.0.beta.4)
16
+ http_parser.rb (>= 0.5.3)
17
+ em-socksify (0.2.0)
18
+ eventmachine (>= 1.0.0.beta.4)
19
+ eventmachine (1.0.0.rc.4)
20
+ http_parser.rb (0.5.3)
21
+ httparty (0.8.3)
22
+ multi_json (~> 1.0)
23
+ multi_xml
19
24
  i18n (0.6.0)
20
25
  metaclass (0.0.1)
21
- mocha (0.11.4)
26
+ mocha (0.12.1)
22
27
  metaclass (~> 0.0.1)
28
+ multi_json (1.3.6)
29
+ multi_xml (0.5.1)
23
30
  rake (0.9.2.2)
24
31
  rspec (2.11.0)
25
32
  rspec-core (~> 2.11.0)
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Hipbot
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/pewniak747/hipbot.png?branch=master)](http://travis-ci.org/pewniak747/hipbot)
4
+ [![Code Climate](https://codeclimate.com/github/pewniak747/hipbot.png)](https://codeclimate.com/github/pewniak747/hipbot)
4
5
 
5
6
  Hipbot is a bot for HipChat, written in ruby & eventmachine.
6
7
 
@@ -84,6 +85,8 @@ end
84
85
 
85
86
  (Use with caution!)
86
87
 
88
+ For more examples, check out (https://github.com/pewniak747/hipbot/tree/master/examples)
89
+
87
90
  #### Response helpers
88
91
 
89
92
  Use http helpers (`get`, `post`, `put`, `delete`) to preform a http request:
@@ -159,14 +162,14 @@ heroku ps:scale worker=1
159
162
 
160
163
  ## TODO:
161
164
 
162
- * handle auto joining on room invite
163
- * add database storage with postgresql adapter
164
- * rewrite SimpleMUCClient
165
+ * add plugins support
165
166
  * add extended logging
166
- * handle private messages callbacks in the same way
167
167
 
168
168
  ### Done:
169
169
 
170
+ * ~~rewrite SimpleMUCClient~~
171
+ * ~~handle private messages callbacks~~
172
+ * ~~handle auto joining on room invite~~
170
173
  * ~~add support for custom helpers~~
171
174
  * ~~mentions - returns list of @mentions in message~~
172
175
  * ~~sender_name - returns sender's first name~~
@@ -0,0 +1,23 @@
1
+ # example bot that simulates intelligence
2
+ # using http://cleverbot.com/ api
3
+
4
+ require 'hipbot'
5
+ require 'cleverbot'
6
+ require 'htmlentities'
7
+
8
+ class CleverHipbot < Hipbot::Bot
9
+ configure do |c|
10
+ c.jid = ENV['HIPBOT_JID']
11
+ c.password = ENV['HIPBOT_PASSWORD']
12
+ c.name = ENV['HIPBOT_NAME']
13
+ end
14
+
15
+ cleverbot = ::Cleverbot::Client.new
16
+
17
+ on /(.+)/ do |message|
18
+ coder = HTMLEntities.new
19
+ reply(coder.decode(cleverbot.write(message)))
20
+ end
21
+ end
22
+
23
+ CleverHipbot.start!
@@ -0,0 +1,35 @@
1
+ # Pull out a random image from the Google Images API and display it
2
+ # Uses: google_image_api http://rubygems.org/gems/google_image_api
3
+ # Author Rushi Vishavadia <rushi.v@gmail.com>
4
+
5
+ require 'hipbot'
6
+ require 'google_image_api'
7
+
8
+ class SampleBot < Hipbot::Bot
9
+ configure do |c|
10
+ c.jid = ENV['HIPBOT_JID']
11
+ c.password = ENV['HIPBOT_PASSWORD']
12
+ c.name = ENV['HIPBOT_NAME']
13
+ end
14
+
15
+ on /\Aimage (.+)/i do |img_str|
16
+ max = 8 # max number of results you want to pull a random
17
+ puts img_str
18
+ begin
19
+ results = GoogleImageApi.find(img_str, :rsz => max)
20
+ if results.raw_data["responseStatus"] == 200 and results.images.size > 0
21
+ reply(results.images.take(max).sample['unescapedUrl'])
22
+ elsif results.raw_data["responseStatus"] == 200 and results.images.size == 0
23
+ reply("I'm sorry I couldn't find an image for #{img_str}")
24
+ else
25
+ reply("I'm sorry, an error occurred. Try again please") # Most likely a 403
26
+ end
27
+ rescue => e
28
+ reply("I'm sorry, an error occurred trying to find that image")
29
+ p e.message
30
+ p e.backtrace
31
+ end
32
+ end
33
+ end
34
+
35
+ SampleBot.start!
@@ -1,10 +1,11 @@
1
1
  require 'active_support/all'
2
+ require 'ostruct'
2
3
  require 'eventmachine'
3
4
  require 'em-http-request'
4
5
  require 'xmpp4r'
5
6
  require 'xmpp4r/muc'
6
7
 
7
- require 'hipbot/patches/mucclient'
8
+ require 'hipbot/patches/hipchat_client'
8
9
  require 'hipbot/patches/encoding'
9
10
 
10
11
  require 'hipbot/adapters/hipchat/hipchat'
@@ -13,9 +14,11 @@ require 'hipbot/adapters/telnet/telnet'
13
14
  require 'hipbot/adapters/telnet/connection'
14
15
  require 'hipbot/bot'
15
16
  require 'hipbot/configuration'
17
+ require 'hipbot/collection'
18
+ require 'hipbot/http_response'
16
19
  require 'hipbot/message'
17
20
  require 'hipbot/reaction'
18
21
  require 'hipbot/response'
19
- require 'hipbot/http_response'
20
22
  require 'hipbot/room'
23
+ require 'hipbot/user'
21
24
  require 'hipbot/version'
@@ -3,120 +3,153 @@ module Hipbot
3
3
  module Hipchat
4
4
  class Connection
5
5
  def initialize bot
6
- initialize_bot(bot)
7
- initialize_jabber
8
- initialize_rooms
9
- join_rooms
6
+ @bot = bot
7
+ @bot.connection = self
8
+
9
+ return unless setup_bot
10
10
  setup_timers
11
11
  end
12
12
 
13
- def reply room, message
14
- send_message(room, message)
13
+ def restart!
14
+ exit_all_rooms # TODO: Nice quit
15
+ setup_bot
15
16
  end
16
17
 
17
- def restart!
18
- leave_rooms
19
- initialize_jabber
18
+ def send_to_room(room, message)
19
+ @client.send_message(:groupchat, room.id, message)
20
+ end
21
+
22
+ def send_to_user(user, message)
23
+ @client.send_message(:chat, user.id, message)
24
+ end
25
+
26
+ def set_topic(room, topic)
27
+ @client.send_message(:groupchat, room.id, nil, topic)
28
+ end
29
+
30
+ def set_presence(status = nil, type = :available)
31
+ @client.set_presence(type, nil, status)
32
+ end
33
+
34
+ private
35
+
36
+ def setup_bot
37
+ return unless initialize_client
20
38
  initialize_rooms
39
+ initialize_users
40
+ initialize_callbacks
21
41
  join_rooms
42
+ set_presence('Hello humans!')
43
+ true
22
44
  end
23
45
 
24
- private
46
+ def initialize_client
47
+ ::Jabber.debug = true
48
+ @client = ::Jabber::MUC::HipchatClient.new(@bot.jid + '/' + @bot.name)
49
+ @client.connect(@bot.password)
50
+ end
25
51
 
26
52
  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
- }
53
+ Room.bot = @bot
54
+ @client.get_rooms.each do |r|
55
+ Room.create(r[:item].jid, r[:item].iname, topic: r[:details]['topic'])
56
+ end
57
+ true
31
58
  end
32
59
 
33
- def initialize_bot bot
34
- @bot = bot
35
- @bot.connection = self
60
+ def initialize_users
61
+ User.bot = @bot
62
+ @client.get_users.each do |v|
63
+ params = {
64
+ email: v[:vcard]['EMAIL/USERID'],
65
+ mention: v[:item].attributes['mention_name'],
66
+ title: v[:vcard]['TITLE'],
67
+ photo: v[:vcard]['PHOTO'],
68
+ }
69
+ User.create(v[:item].jid, v[:item].iname, params)
70
+ end
71
+ true
36
72
  end
37
73
 
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))
74
+ 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)
81
+ end
82
+ true
43
83
  end
44
84
 
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
85
+ def exit_all_rooms
86
+ Room.each do |room_jid, _|
87
+ @client.exit(room_jid, 'bye bye!')
88
+ end
89
+ end
90
+
91
+ 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
66
105
  end
67
106
  end
107
+ end
68
108
 
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
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)
73
121
  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
122
  end
79
123
 
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
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
96
128
 
97
- def leave_rooms
98
- rooms.each do |room|
99
- puts "Leaving #{room.name}"
100
- room.connection.exit
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]
133
+ if pres == 'unavailable'
134
+ if user_name == @bot.name
135
+ room.delete
136
+ elsif user.present?
137
+ room.user_ids.delete(user.id)
138
+ end
139
+ elsif pres.blank? && user.present? && room.user_ids.exclude?(user.id)
140
+ room.user_ids << user.id
141
+ end
101
142
  end
143
+
144
+ @client.activate_callbacks
102
145
  end
103
146
 
104
147
  def setup_timers
105
- ::EM::add_periodic_timer(10) {
106
- if !@jabber.nil? && @jabber.is_disconnected?
107
- initialize_jabber
108
- join_rooms
109
- end
148
+ ::EM::add_periodic_timer(60) {
149
+ @client.keep_alive(@bot.password) if @client.present?
110
150
  }
111
151
  end
112
152
 
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
153
  end
121
154
  end
122
155
  end
@@ -1,8 +1,6 @@
1
1
  module Hipbot
2
2
  module Adapters
3
3
  module Hipchat
4
- delegate :reply, to: :connection
5
-
6
4
  def start!
7
5
  ::EM::run do
8
6
  ::EM.error_handler do |e|
@@ -15,6 +13,10 @@ module Hipbot
15
13
  Connection.new(self)
16
14
  end
17
15
  end
16
+
17
+ def method_missing(sym, *args, &block)
18
+ connection.send sym, *args, &block
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -9,7 +9,7 @@ module Hipbot
9
9
 
10
10
  def receive_data(data)
11
11
  sender, room, message = *data.strip.split(':')
12
- @bot.tell(sender, room, message)
12
+ @bot.react(sender, room, message)
13
13
  end
14
14
  end
15
15
  end
@@ -1,7 +1,7 @@
1
1
  module Hipbot
2
2
  module Adapters
3
3
  module Telnet
4
- def reply room, message
4
+ def send_to_room room, message
5
5
  connection.send_data("#{self}:#{room}:#{message}\n")
6
6
  end
7
7