hipbot 1.0.0.rc2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +6 -0
  4. data/Gemfile +1 -2
  5. data/README.md +482 -88
  6. data/Rakefile +1 -1
  7. data/bin/hipbot +4 -4
  8. data/hipbot.gemspec +9 -9
  9. data/lib/hipbot.rb +28 -11
  10. data/lib/hipbot/adapter.rb +80 -0
  11. data/lib/hipbot/adapters/hipchat.rb +53 -0
  12. data/lib/hipbot/adapters/hipchat/initializer.rb +83 -0
  13. data/lib/hipbot/adapters/shell.rb +37 -0
  14. data/lib/hipbot/adapters/telnet.rb +41 -0
  15. data/lib/hipbot/bot.rb +15 -52
  16. data/lib/hipbot/cache.rb +23 -0
  17. data/lib/hipbot/callbacks/base.rb +15 -0
  18. data/lib/hipbot/callbacks/invite.rb +11 -0
  19. data/lib/hipbot/callbacks/lobby_presence.rb +13 -0
  20. data/lib/hipbot/callbacks/message.rb +11 -0
  21. data/lib/hipbot/callbacks/presence.rb +17 -0
  22. data/lib/hipbot/callbacks/private_message.rb +12 -0
  23. data/lib/hipbot/callbacks/room_message.rb +21 -0
  24. data/lib/hipbot/callbacks/room_presence.rb +28 -0
  25. data/lib/hipbot/configurable.rb +22 -0
  26. data/lib/hipbot/configuration.rb +9 -3
  27. data/lib/hipbot/helpers.rb +4 -40
  28. data/lib/hipbot/http.rb +65 -0
  29. data/lib/hipbot/match.rb +21 -9
  30. data/lib/hipbot/matchable.rb +38 -0
  31. data/lib/hipbot/message.rb +24 -9
  32. data/lib/hipbot/plugin.rb +3 -3
  33. data/lib/hipbot/reactable.rb +14 -21
  34. data/lib/hipbot/reaction.rb +27 -15
  35. data/lib/hipbot/reaction_factory.rb +38 -0
  36. data/lib/hipbot/response.rb +18 -10
  37. data/lib/hipbot/room.rb +34 -2
  38. data/lib/hipbot/storages/base.rb +78 -0
  39. data/lib/hipbot/storages/hash.rb +89 -0
  40. data/lib/hipbot/storages/mongoid.rb +18 -0
  41. data/lib/hipbot/user.rb +8 -2
  42. data/lib/hipbot/version.rb +1 -1
  43. data/spec/integration/my_hipbot.rb +24 -4
  44. data/spec/integration/{hipbot_spec.rb → my_hipbot_spec.rb} +41 -23
  45. data/spec/spec_helper.rb +14 -2
  46. data/spec/unit/adapters/hipchat_spec.rb +5 -0
  47. data/spec/unit/{hipbot_spec.rb → bot_spec.rb} +13 -12
  48. data/spec/unit/match_spec.rb +148 -0
  49. data/spec/unit/message_spec.rb +14 -7
  50. data/spec/unit/reaction_factory_spec.rb +54 -0
  51. data/spec/unit/reaction_spec.rb +99 -0
  52. data/spec/unit/storages/hash_spec.rb +75 -0
  53. data/spec/unit/user_spec.rb +0 -2
  54. metadata +64 -54
  55. data/Gemfile.lock +0 -90
  56. data/examples/cleverbot.rb +0 -23
  57. data/examples/google_images.rb +0 -35
  58. data/lib/hipbot/adapters/hipchat/connection.rb +0 -166
  59. data/lib/hipbot/adapters/hipchat/hipchat.rb +0 -13
  60. data/lib/hipbot/adapters/telnet/connection.rb +0 -17
  61. data/lib/hipbot/adapters/telnet/telnet.rb +0 -14
  62. data/lib/hipbot/collection.rb +0 -72
  63. data/lib/hipbot/patches/hipchat_client.rb +0 -230
@@ -1,90 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- activesupport (3.2.12)
5
- i18n (~> 0.6)
6
- multi_json (~> 1.0)
7
- addressable (2.3.3)
8
- coderay (1.0.9)
9
- colorize (0.5.8)
10
- cookiejar (0.3.0)
11
- coveralls (0.6.7)
12
- colorize
13
- multi_json (~> 1.3)
14
- rest-client
15
- simplecov (>= 0.7)
16
- thor
17
- daemons (1.1.9)
18
- diff-lcs (1.2.2)
19
- em-http-request (1.0.3)
20
- addressable (>= 2.2.3)
21
- cookiejar
22
- em-socksify
23
- eventmachine (>= 1.0.0.beta.4)
24
- http_parser.rb (>= 0.5.3)
25
- em-socksify (0.2.1)
26
- eventmachine (>= 1.0.0.beta.4)
27
- eventmachine (1.0.3)
28
- formatador (0.2.4)
29
- guard (1.7.0)
30
- formatador (>= 0.2.4)
31
- listen (>= 0.6.0)
32
- lumberjack (>= 1.0.2)
33
- pry (>= 0.9.10)
34
- thor (>= 0.14.6)
35
- guard-rspec (2.5.2)
36
- guard (>= 1.1)
37
- rspec (~> 2.11)
38
- http_parser.rb (0.5.3)
39
- httparty (0.10.2)
40
- multi_json (~> 1.0)
41
- multi_xml (>= 0.5.2)
42
- i18n (0.6.4)
43
- listen (0.7.3)
44
- lumberjack (1.0.3)
45
- metaclass (0.0.1)
46
- method_source (0.8.1)
47
- mime-types (1.23)
48
- mocha (0.13.3)
49
- metaclass (~> 0.0.1)
50
- multi_json (1.7.2)
51
- multi_xml (0.5.3)
52
- pry (0.9.12)
53
- coderay (~> 1.0.5)
54
- method_source (~> 0.8)
55
- slop (~> 3.4)
56
- rake (10.0.4)
57
- rest-client (1.6.7)
58
- mime-types (>= 1.16)
59
- rspec (2.13.0)
60
- rspec-core (~> 2.13.0)
61
- rspec-expectations (~> 2.13.0)
62
- rspec-mocks (~> 2.13.0)
63
- rspec-core (2.13.1)
64
- rspec-expectations (2.13.0)
65
- diff-lcs (>= 1.1.3, < 2.0)
66
- rspec-mocks (2.13.0)
67
- simplecov (0.7.1)
68
- multi_json (~> 1.0)
69
- simplecov-html (~> 0.7.1)
70
- simplecov-html (0.7.1)
71
- slop (3.4.4)
72
- thor (0.18.1)
73
- xmpp4r (0.5)
74
-
75
- PLATFORMS
76
- ruby
77
-
78
- DEPENDENCIES
79
- activesupport
80
- coveralls
81
- daemons
82
- em-http-request
83
- eventmachine
84
- guard-rspec
85
- httparty
86
- i18n
87
- mocha
88
- rake
89
- rspec
90
- xmpp4r
@@ -1,23 +0,0 @@
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!
@@ -1,35 +0,0 @@
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,166 +0,0 @@
1
- module Hipbot
2
- module Adapters
3
- module Hipchat
4
- class Connection
5
- def initialize
6
- Hipbot.connection = self
7
- setup_error_handler && setup_bot && setup_timers
8
- end
9
-
10
- def restart!
11
- exit_all_rooms # TODO: Nice quit
12
- setup_bot
13
- end
14
-
15
- def send_to_room(room, message)
16
- Hipbot.logger.info("REPLY in #{room}: #{message}")
17
- @client.send_message(:groupchat, room.id, message)
18
- end
19
-
20
- def send_to_user(user, message)
21
- Hipbot.logger.info("REPLY to #{user}: #{message}")
22
- @client.send_message(:chat, user.id, message)
23
- end
24
-
25
- def set_topic(room, topic)
26
- Hipbot.logger.info("TOPIC set in #{room} to '#{topic}'")
27
- @client.send_message(:groupchat, room.id, nil, topic)
28
- end
29
-
30
- def set_presence(status = nil, type = :available)
31
- Hipbot.logger.info("PRESENCE set to #{type} with '#{status}'")
32
- @client.set_presence(type, nil, status)
33
- end
34
-
35
- protected
36
-
37
- def setup_bot
38
- initialize_client do
39
- initialize_rooms
40
- initialize_users
41
- initialize_callbacks
42
- set_bot_user
43
- join_rooms
44
- set_presence('Hello humans!')
45
- end
46
- end
47
-
48
- def initialize_client
49
- @client = ::Jabber::MUC::HipchatClient.new(Hipbot.jid)
50
- yield if @client.connect(Hipbot.password)
51
- end
52
-
53
- def initialize_rooms
54
- @client.get_rooms.each do |room_data|
55
- room = Room.find_or_create_by(id: room_data[:item].jid.to_s)
56
- room.update_attributes({
57
- name: room_data[:item].iname,
58
- topic: room_data[:details]['topic'],
59
- })
60
- end
61
- end
62
-
63
- def initialize_users
64
- @client.get_users.each do |user_data|
65
- user = User.find_or_create_by(id: user_data.delete(:jid))
66
- user.update_attributes(user_data)
67
-
68
- if user.attributes['email'].nil?
69
- user.update_attributes(@client.get_user_details(user.id))
70
- end
71
- end
72
- end
73
-
74
- def set_bot_user
75
- Hipbot.configuration.user = User[Hipbot.jid]
76
- @client.name = Hipbot.user
77
- end
78
-
79
- def join_rooms
80
- with_rooms do |room|
81
- @client.join(room.id)
82
- end
83
- end
84
-
85
- def exit_all_rooms
86
- with_rooms do |room|
87
- @client.exit(room.id, 'bye bye!')
88
- end
89
- end
90
-
91
- def initialize_callbacks
92
- @client.on_message{ |*args| message_callback *args }
93
- @client.on_private_message{ |*args| private_message_callback *args }
94
- @client.on_invite{ |*args| invite_callback *args }
95
- @client.on_presence{ |*args| presence_callback *args }
96
- @client.activate_callbacks
97
- end
98
-
99
- def message_callback room_jid, user_name, message
100
- with_sender(room_jid, user_name) do |room, user|
101
- room.update_attribute(:topic, message.subject) if message.subject.present?
102
- return if user_name == Hipbot.name || message.body.blank?
103
- Hipbot.react(user, room, message.body)
104
- end
105
- end
106
-
107
- def invite_callback room_jid, user_name, room_name, topic
108
- Room.create(id: room_jid, name: room_name, topic: topic)
109
- @client.join(room_jid)
110
- end
111
-
112
- def presence_callback room_jid, user_name, pres
113
- with_sender(room_jid, user_name) do |room, user|
114
- if pres == 'unavailable'
115
- if user_name == Hipbot.name
116
- room.delete
117
- elsif user.present?
118
- room.users.delete(user)
119
- end
120
- elsif pres.blank? && room.users.exclude?(user)
121
- room.users << user
122
- end
123
- end
124
- end
125
-
126
- def private_message_callback user_jid, message
127
- with_user(user_jid) do |user|
128
- Hipbot.react(user, nil, message.body) if user.name != Hipbot.name
129
- end if message.body.present?
130
- end
131
-
132
- def with_rooms
133
- return Hipbot.logger.error 'No rooms found' if Room.empty?
134
- Room.each{ |room| yield room }
135
- end
136
-
137
- def with_sender room_id, user_id
138
- room = Room[room_id]
139
- with_user(user_id) do |user|
140
- yield room, user
141
- end if room.present?
142
- end
143
-
144
- def with_user user_id
145
- user = User[user_id]
146
- yield user if user
147
- end
148
-
149
- def setup_timers
150
- ::EM::add_periodic_timer(60) do
151
- @client.keep_alive(Hipbot.password) if @client.present?
152
- end
153
- end
154
-
155
- def setup_error_handler
156
- ::EM.error_handler do |e|
157
- Hipbot.logger.error e.inspect
158
- e.backtrace.each do |line|
159
- Hipbot.logger.error line
160
- end
161
- end
162
- end
163
- end
164
- end
165
- end
166
- end
@@ -1,13 +0,0 @@
1
- module Hipbot
2
- module Adapters
3
- module Hipchat
4
- def start!
5
- connection = Connection.new
6
- end
7
-
8
- def method_missing(sym, *args, &block)
9
- connection.send sym, *args, &block
10
- end
11
- end
12
- end
13
- end
@@ -1,17 +0,0 @@
1
- module Hipbot
2
- module Adapters
3
- module Telnet
4
- class Connection < EM::Connection
5
- def initialize
6
- Hipbot.connection = self
7
- end
8
-
9
- def receive_data(data)
10
- sender, room, message = data.strip.split(':')
11
- Hipbot.react(sender, room, message)
12
- end
13
- end
14
- end
15
- end
16
- end
17
-
@@ -1,14 +0,0 @@
1
- module Hipbot
2
- module Adapters
3
- module Telnet
4
- def send_to_room room, message
5
- connection.send_data("#{self}:#{room}:#{message}\n")
6
- end
7
-
8
- def start!
9
- ::EM::connect('0.0.0.0', 3001, Connection, self)
10
- end
11
- end
12
- end
13
- end
14
-
@@ -1,72 +0,0 @@
1
- module Hipbot
2
- module Collection
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- extend ClassMethods
7
-
8
- attr_accessor :id, :name, :attributes
9
- alias_method :to_s, :name
10
- end
11
-
12
- def initialize params
13
- params = params.with_indifferent_access
14
- self.id = params.delete(:id)
15
- self.name = params.delete(:name)
16
- self.attributes = params
17
- end
18
-
19
- def update_attribute key, value
20
- if key == :name
21
- self.name = value
22
- else
23
- self.attributes[key] = value
24
- end
25
- end
26
-
27
- def update_attributes hash
28
- hash.each do |k, v|
29
- update_attribute k, v
30
- end
31
- end
32
-
33
- def delete
34
- self.class.collection.delete(self.id)
35
- end
36
-
37
- module ClassMethods
38
- def create params, &block
39
- collection[params[:id]] = new(params, &block)
40
- end
41
-
42
- def collection
43
- @collection ||= {}
44
- end
45
-
46
- def [] *items
47
- items.first.is_a?(Array) ? find_many(*items) : find_one(items.first)
48
- end
49
-
50
- def find_one item
51
- collection[item] || find{ |i| i.name == item }
52
- end
53
-
54
- def find_many *items
55
- items.flatten!
56
- items.map{ |i| find_one(i) }.compact.uniq
57
- end
58
-
59
- def find_or_create_by params
60
- find_one(params[:id] || params[:name]) || create(params)
61
- end
62
-
63
- protected
64
-
65
- def method_missing name, *args, &block
66
- return collection.values.public_send(name, *args, &block) if Array.instance_methods.include?(name)
67
- super
68
- end
69
- end
70
- end
71
- end
72
-
@@ -1,230 +0,0 @@
1
- require 'xmpp4r/muc/x/muc'
2
- require 'xmpp4r/muc/iq/mucowner'
3
- require 'xmpp4r/muc/iq/mucadmin'
4
- require 'xmpp4r/dataforms'
5
- require 'xmpp4r/roster'
6
- require 'xmpp4r/vcard'
7
-
8
- module Jabber
9
- module MUC
10
- class HipchatClient
11
-
12
- def initialize(jid)
13
- @my_jid = JID.new(jid)
14
-
15
- @stream = Client.new(@my_jid.strip) # TODO: Error Handling
16
- Jabber::debuglog "Stream initialized"
17
- @chat_domain = @my_jid.domain
18
-
19
- @presence_cbs = CallbackList.new
20
- @message_cbs = CallbackList.new
21
- @private_message_cbs = CallbackList.new
22
- @invite_cbs = CallbackList.new
23
- end
24
-
25
- def join(jid, password = nil, opts = { :history => false })
26
- room_jid = JID.new(jid)
27
- xmuc = XMUC.new
28
- xmuc.password = password
29
-
30
- if !opts[:history]
31
- history = REXML::Element.new('history').tap{ |h| h.add_attribute('maxstanzas','0') }
32
- xmuc.add_element history
33
- end
34
-
35
- room_jid.resource = name
36
- set_presence(:available, room_jid, nil, xmuc) # TODO: Handle all join responses
37
- end
38
-
39
- def exit(jid, reason = nil)
40
- room_jid = JID.new(jid)
41
- Jabber::debuglog "Exiting #{jid}"
42
- set_presence(:unavailable, room_jid, reason)
43
- end
44
-
45
- def keep_alive password
46
- if @stream.is_disconnected?
47
- connect(password)
48
- end
49
- end
50
-
51
- def name
52
- @my_jid.resource
53
- end
54
-
55
- def name= resource
56
- @my_jid.resource = resource
57
- end
58
-
59
- def on_presence(prio = 0, ref = nil, &block)
60
- @presence_cbs.add(prio, ref) do |room_jid, user_name, pres_type|
61
- block.call(room_jid, user_name, pres_type)
62
- false
63
- end
64
- end
65
-
66
- def on_message(prio = 0, ref = nil, &block)
67
- @message_cbs.add(prio, ref) do |room_jid, user_name, message|
68
- block.call(room_jid, user_name, message)
69
- false
70
- end
71
- end
72
-
73
- def on_private_message(prio = 0, ref = nil, &block)
74
- @private_message_cbs.add(prio, ref) do |user_jid, message|
75
- block.call(user_jid, message)
76
- false
77
- end
78
- end
79
-
80
- def on_invite(prio = 0, ref = nil, &block)
81
- @invite_cbs.add(prio, ref) do |room_jid, user_name, room_name, topic|
82
- block.call(room_jid, user_name, room_name, topic)
83
- false
84
- end
85
- end
86
-
87
- def set_presence(type, to = nil, reason = nil, xmuc = nil, &block)
88
- pres = Presence.new(:chat, reason)
89
- pres.type = type
90
- pres.to = to if to
91
- pres.from = @my_jid
92
- pres.add(xmuc) if xmuc
93
- @stream.send(pres) { |r| block.call(r) }
94
- end
95
-
96
- def kick(recipients, room_jid)
97
- iq = Iq.new(:set, room_jid)
98
- iq.from = @my_jid
99
- iq.add(IqQueryMUCAdmin.new)
100
- recipients.each do |recipient|
101
- item = IqQueryMUCAdminItem.new
102
- item.nick = recipient
103
- item.role = :none
104
- iq.query.add(item)
105
- end
106
- @stream.send_with_id(iq)
107
- end
108
-
109
- def invite(recipients, room_jid)
110
- msg = Message.new
111
- msg.from = @my_jid
112
- msg.to = room_jid
113
- x = msg.add(XMUCUser.new)
114
- recipients.each do |jid|
115
- x.add(XMUCUserInvite.new(jid))
116
- end
117
- @stream.send(msg)
118
- end
119
-
120
- def send_message(type, jid, text, subject = nil)
121
- message = Message.new(JID.new(jid), text.to_s)
122
- message.type = type
123
- message.from = @my_jid
124
- message.subject = subject
125
-
126
- @send_thread.join if @send_thread.present? && @send_thread.alive?
127
- @send_thread = Thread.new {
128
- @stream.send(message)
129
- sleep(0.2)
130
- }
131
- end
132
-
133
- def connect password
134
- begin
135
- @stream.connect
136
- Jabber::debuglog "Connected to stream"
137
- @stream.auth(password)
138
- Jabber::debuglog "Authenticated"
139
- @muc_browser = MUCBrowser.new(@stream)
140
- Jabber::debuglog "MUCBrowser initialized"
141
- @conference_domain = @muc_browser.muc_rooms(@chat_domain).keys.first
142
- Jabber::debuglog "No conference domain found" if !@conference_domain.present?
143
- @roster = Roster::Helper.new(@stream) # TODO: Error handling
144
- @vcard = Vcard::Helper.new(@stream) # TODO: Error handling
145
- true
146
- rescue => e
147
- Jabber::debuglog "Connection failed"
148
- false
149
- end
150
- end
151
-
152
- def activate_callbacks
153
- @stream.add_presence_callback(150, self) { |presence|
154
- @presence_cbs.process(presence.from.strip.to_s, presence.from.resource, presence.type.to_s)
155
- }
156
-
157
- @stream.add_message_callback(150, self) { |message|
158
- handle_message(message)
159
- }
160
- Jabber::debuglog "Callbacks activated"
161
- end
162
-
163
- def get_rooms
164
- iq = Iq.new(:get, @conference_domain)
165
- iq.from = @stream.jid
166
- iq.add(Discovery::IqQueryDiscoItems.new)
167
-
168
- rooms = []
169
- @stream.send_with_id(iq) do |answer|
170
- answer.query.each_element('item') do |item|
171
- details = {}
172
- item.first.children.each{ |c| details[c.name] = c.text }
173
- rooms << {
174
- :item => item,
175
- :details => details
176
- }
177
- end
178
- end
179
- rooms
180
- end
181
-
182
- def get_users
183
- @roster.wait_for_roster
184
- @roster.items.map do |jid, item|
185
- {
186
- jid: item.jid.to_s,
187
- name: item.iname,
188
- mention: item.attributes['mention_name'],
189
- }
190
- end
191
- end
192
-
193
- def get_user_details user_jid
194
- vcard = @vcard.get(user_jid)
195
- {
196
- email: vcard['EMAIL/USERID'],
197
- title: vcard['TITLE'],
198
- photo: vcard['PHOTO'],
199
- }
200
- end
201
-
202
- def deactivate_callbacks
203
- @stream.delete_presence_callback(self)
204
- @stream.delete_message_callback(self)
205
- Jabber::debuglog "Callbacks deactivated"
206
- end
207
-
208
- private
209
-
210
- def handle_message(message)
211
- if is_invite?(message)
212
- room_name = message.children.last.first_element_text('name')
213
- topic = message.children.last.first_element_text('topic')
214
- @invite_cbs.process(message.from.strip.to_s, message.from.resource, room_name, topic)
215
- elsif message.type == :chat
216
- @private_message_cbs.process(message.from.strip.to_s, message)
217
- elsif message.type == :groupchat
218
- @message_cbs.process(message.from.strip.to_s, message.from.resource, message)
219
- elsif message.type == :error
220
- false
221
- end
222
- end
223
-
224
- def is_invite?(message)
225
- !message.x.nil? && message.x.kind_of?(XMUCUser) && message.x.first.kind_of?(XMUCUserInvite)
226
- end
227
-
228
- end
229
- end
230
- end