hipbot 1.0.0.rc2 → 1.0.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.
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
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
1
  require 'rspec/core/rake_task'
2
2
  RSpec::Core::RakeTask.new(:spec)
3
3
 
4
- task :default => :spec
4
+ task default: :spec
data/bin/hipbot CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  require 'daemons'
4
4
 
5
- hipbot_file = 'bot.rb'
5
+ HIPBOT_FILE = 'bot.rb'
6
6
 
7
- if File.exists?(hipbot_file)
8
- Daemons.run(hipbot_file)
7
+ if File.exists?(HIPBOT_FILE)
8
+ Daemons.run(HIPBOT_FILE)
9
9
  else
10
- puts "File #{hipbot_file} not found!"
10
+ puts "File #{HIPBOT_FILE} not found!"
11
11
  exit 1
12
12
  end
@@ -1,12 +1,13 @@
1
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
2
2
  require File.expand_path('../lib/hipbot/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Bartosz Kopiński", "Tomasz Pewiński"]
6
6
  gem.email = ["bartosz.kopinski@netguru.pl", "pewniak747@gmail.com"]
7
- gem.description = "Hipbot is a bot for HipChat, written in ruby & eventmachine."
8
- gem.summary = "Hipbot is a bot for HipChat, written in ruby & eventmachine."
7
+ gem.description = "Hipbot is a XMPP bot for HipChat, written in Ruby with EventMachine."
8
+ gem.summary = "Hipbot is a XMPP bot for HipChat, written in Ruby with EventMachine."
9
9
  gem.homepage = "http://github.com/pewniak747/hipbot"
10
+ gem.license = "MIT"
10
11
 
11
12
  gem.files = `git ls-files`.split($\)
12
13
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -14,12 +15,11 @@ Gem::Specification.new do |gem|
14
15
  gem.name = "hipbot"
15
16
  gem.require_paths = ["lib"]
16
17
  gem.version = Hipbot::VERSION
17
- gem.add_runtime_dependency "daemons", ["~> 1.1.8"]
18
- gem.add_runtime_dependency "activesupport", ["~> 3.2.12"]
19
- gem.add_runtime_dependency "i18n", ["~> 0.6.0"]
20
- gem.add_runtime_dependency "eventmachine", ["~> 1.0.3"]
21
- gem.add_runtime_dependency "em-http-request", ["~> 1.0.3"]
22
- gem.add_runtime_dependency "xmpp4r", ["~> 0.5"]
18
+ gem.add_runtime_dependency "xmpp4r-hipchat", [">= 0.0.3"]
19
+ gem.add_runtime_dependency "daemons", [">= 1.1.8"]
20
+ gem.add_runtime_dependency "activesupport", [">= 3.2.12"]
21
+ gem.add_runtime_dependency "eventmachine", [">= 1.0.3"]
22
+ gem.add_runtime_dependency "em-http-request", [">= 1.0.3"]
23
23
  gem.add_development_dependency "rspec", ['~> 2.13.0']
24
24
  gem.add_development_dependency "guard-rspec", ['~> 2.5.1']
25
25
  gem.add_development_dependency "mocha", ['~> 0.13.3']
@@ -1,23 +1,40 @@
1
- require 'active_support/all'
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/module/delegation'
3
+ require 'active_support/core_ext/array/extract_options'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'active_support/core_ext/string/inflections'
2
6
  require 'ostruct'
7
+ require 'json'
3
8
  require 'eventmachine'
4
9
  require 'em-http-request'
5
- require 'xmpp4r'
6
- require 'xmpp4r/muc'
10
+ require 'xmpp4r-hipchat'
7
11
 
8
- require 'hipbot/patches/hipchat_client'
12
+ require 'hipbot/cache'
9
13
  require 'hipbot/patches/encoding'
10
-
11
14
  require 'hipbot/logger'
12
- require 'hipbot/adapters/hipchat/hipchat'
13
- require 'hipbot/adapters/hipchat/connection'
14
- require 'hipbot/adapters/telnet/telnet'
15
- require 'hipbot/adapters/telnet/connection'
15
+ require 'hipbot/callbacks/base'
16
+ require 'hipbot/callbacks/message'
17
+ require 'hipbot/callbacks/private_message'
18
+ require 'hipbot/callbacks/room_message'
19
+ require 'hipbot/callbacks/presence'
20
+ require 'hipbot/callbacks/lobby_presence'
21
+ require 'hipbot/callbacks/room_presence'
22
+ require 'hipbot/callbacks/invite'
23
+ require 'hipbot/adapter'
24
+ require 'hipbot/adapters/hipchat/initializer'
25
+ require 'hipbot/adapters/hipchat'
26
+ require 'hipbot/adapters/telnet'
27
+ require 'hipbot/adapters/shell'
28
+ require 'hipbot/reaction_factory'
16
29
  require 'hipbot/reactable'
30
+ require 'hipbot/matchable'
31
+ require 'hipbot/configuration'
32
+ require 'hipbot/configurable'
17
33
  require 'hipbot/bot'
18
34
  require 'hipbot/plugin'
19
- require 'hipbot/configuration'
20
- require 'hipbot/collection'
35
+ require 'hipbot/storages/base'
36
+ require 'hipbot/storages/hash'
37
+ require 'hipbot/http'
21
38
  require 'hipbot/helpers'
22
39
  require 'hipbot/match'
23
40
  require 'hipbot/message'
@@ -0,0 +1,80 @@
1
+ module Hipbot
2
+ module Adapter
3
+ attr_accessor :connection
4
+
5
+ def start!
6
+ logger.info("STARTING")
7
+ self.connection = adapter.new
8
+ connection.start!
9
+ set_presence(status)
10
+ join_rooms(join)
11
+ end
12
+
13
+ def restart!
14
+ logger.info("RESTARTING")
15
+ leave_all_rooms
16
+ connection.restart!
17
+ end
18
+
19
+ def join_room(room)
20
+ logger.info("JOINING #{room}")
21
+ connection.join_room(room)
22
+ end
23
+
24
+ def leave_room(room)
25
+ logger.info("LEAVING #{room}")
26
+ connection.leave_room(room)
27
+ end
28
+
29
+ def invite_to_room(room, users)
30
+ logger.info("INVITING to #{room}: #{users}")
31
+ connection.invite_to_room(room, users)
32
+ end
33
+
34
+ def kick_from_room(room, users)
35
+ logger.info("KICKING from #{room}: #{users}")
36
+ connection.kick_from_room(room, users)
37
+ end
38
+
39
+ def send_to_room(room, message)
40
+ logger.info("REPLY in #{room}")
41
+ connection.send_to_room(room, message)
42
+ end
43
+
44
+ def send_to_user(user, message)
45
+ logger.info("REPLY to #{user}")
46
+ connection.send_to_user(user, message)
47
+ end
48
+
49
+ def set_topic(room, topic)
50
+ logger.info("TOPIC seting in #{room} to '#{topic}'")
51
+ connection.set_topic(room, topic)
52
+ end
53
+
54
+ def set_presence(status, type = :available)
55
+ logger.info("PRESENCE set to #{type} with '#{status}'")
56
+ connection.set_presence(status, type)
57
+ end
58
+
59
+ protected
60
+
61
+ def join_rooms criteria
62
+ rooms_to_join(criteria).each(&:join)
63
+ end
64
+
65
+ def rooms_to_join criteria
66
+ case criteria
67
+ when :all then Room.all
68
+ when :private then Room.where(privacy: 'private')
69
+ when :public then Room.where(privacy: 'public')
70
+ when Array then criteria.flat_map{ |c| rooms_to_join(c) }.uniq
71
+ when String then Room.where(name: criteria)
72
+ else []
73
+ end.reject(&:archived?)
74
+ end
75
+
76
+ def leave_all_rooms
77
+ Room.all.each(&:leave)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,53 @@
1
+ module Hipbot
2
+ module Adapters
3
+ class Hipchat
4
+ attr_accessor :client
5
+
6
+ def start!
7
+ self.client = Initializer.new.client
8
+ end
9
+
10
+ def restart!
11
+ start!
12
+ end
13
+
14
+ def invite_to_room(room, users)
15
+ client.invite(user_ids(users), room.id)
16
+ end
17
+
18
+ def kick_from_room(room, users)
19
+ client.kick(user_ids(users), room.id)
20
+ end
21
+
22
+ def send_to_room(room, message)
23
+ client.send_message(:groupchat, room.id, message)
24
+ end
25
+
26
+ def send_to_user(user, message)
27
+ client.send_message(:chat, user.id, message)
28
+ end
29
+
30
+ def set_topic(room, topic)
31
+ client.send_message(:groupchat, room.id, nil, topic)
32
+ end
33
+
34
+ def set_presence(status, type)
35
+ client.set_presence(type, nil, status)
36
+ end
37
+
38
+ def join_room(room)
39
+ client.join(room.id)
40
+ end
41
+
42
+ def leave_room(room, reason = '')
43
+ client.exit(room.id, reason)
44
+ end
45
+
46
+ protected
47
+
48
+ def user_ids users
49
+ Array(users).map(&:id)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,83 @@
1
+ module Hipbot
2
+ module Adapters
3
+ class Hipchat
4
+ class Initializer
5
+ attr_accessor :client
6
+
7
+ KEEP_ALIVE_INTERVAL = 60
8
+
9
+ def initialize
10
+ Jabber.debug = true
11
+ Jabber.logger = Hipbot.logger
12
+ initialize_client do
13
+ initialize_rooms
14
+ initialize_users
15
+ initialize_bot_user
16
+ initialize_callbacks
17
+ initialize_keep_alive
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def initialize_client
24
+ self.client = ::Jabber::MUC::HipchatClient.new(Hipbot.jid)
25
+ yield if client.connect(Hipbot.password)
26
+ end
27
+
28
+ def initialize_rooms
29
+ room_ids = client.get_rooms.map do |room_data|
30
+ room = Room.find_or_create_by(id: room_data[:item].jid.to_s)
31
+ room.update_attributes(
32
+ name: room_data[:item].iname,
33
+ topic: room_data[:details]['topic'],
34
+ privacy: room_data[:details]['privacy'],
35
+ hipchat_id: room_data[:details]['id'],
36
+ archived: room_data[:details].has_key?('is_archived'),
37
+ guest_url: room_data[:details]['guest_url'],
38
+ )
39
+ room.id
40
+ end
41
+ clean_other_objects(Room, room_ids) if room_ids.any?
42
+ end
43
+
44
+ def initialize_users
45
+ user_ids = client.get_users.map do |user_data|
46
+ user = User.find_or_create_by(id: user_data.delete(:jid))
47
+ user.update_attributes(user_data)
48
+
49
+ if user.attributes['email'].nil?
50
+ # user.update_attributes(client.get_user_details(user.id))
51
+ end
52
+ user.id
53
+ end
54
+ clean_other_objects(User, user_ids) if user_ids.any?
55
+ end
56
+
57
+ def clean_other_objects klass, object_ids
58
+ klass.all.select{ |r| !object_ids.include?(r.id) }.each(&:destroy)
59
+ end
60
+
61
+ def initialize_bot_user
62
+ Hipbot.configuration.user = User.find(Hipbot.jid)
63
+ client.name = Hipbot.user.name
64
+ end
65
+
66
+ def initialize_callbacks
67
+ client.on_room_message{ |*args| Callbacks::RoomMessage.new(*args) }
68
+ client.on_private_message{ |*args| Callbacks::PrivateMessage.new(*args) }
69
+ client.on_invite{ |*args| Callbacks::Invite.new(*args) }
70
+ client.on_lobby_presence{ |*args| Callbacks::LobbyPresence.new(*args) }
71
+ client.on_room_presence{ |*args| Callbacks::RoomPresence.new(*args) }
72
+ client.activate_callbacks
73
+ end
74
+
75
+ def initialize_keep_alive
76
+ ::EM::add_periodic_timer(KEEP_ALIVE_INTERVAL) do
77
+ client.keep_alive(Hipbot.password) unless client.nil?
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,37 @@
1
+ module Hipbot
2
+ module Adapters
3
+ class Shell
4
+ attr_accessor :connection
5
+
6
+ def start!
7
+ EM.open_keyboard(KeyboardHandler, self)
8
+ end
9
+
10
+ def invite_to_room(*); end
11
+ def join_room(*); end
12
+ def kick_from_room(*); end
13
+ def leave_room(*); end
14
+ def restart!; end
15
+ def send_to_room(*); end
16
+ def set_presence(*); end
17
+ def set_topic(*); end
18
+
19
+ module KeyboardHandler
20
+ include EM::Protocols::LineText2
21
+ include Cache
22
+
23
+ def initialize adapter
24
+ adapter.connection = self
25
+ end
26
+
27
+ attr_cache :user do
28
+ Hipbot::User.find_or_create_by(name: 'Shell User')
29
+ end
30
+
31
+ def receive_line data
32
+ Hipbot.react(user, nil, data.strip)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ module Hipbot
2
+ module Adapters
3
+ class Telnet
4
+ attr_accessor :connection
5
+
6
+ def start!
7
+ ::EM::start_server('0.0.0.0', 3001, Connection, self)
8
+ end
9
+
10
+ def send_to_user user, message
11
+ connection.send_data("#{self}:#{user}:#{message}\n")
12
+ end
13
+
14
+ def invite_to_room(*); end
15
+ def join_room(*); end
16
+ def kick_from_room(*); end
17
+ def leave_room(*); end
18
+ def restart!; end
19
+ def send_to_room(*); end
20
+ def set_presence(*); end
21
+ def set_topic(*); end
22
+
23
+ class Connection < EM::Connection
24
+ include Cache
25
+
26
+ def initialize adapter
27
+ adapter.connection = self
28
+ end
29
+
30
+ attr_cache :user do
31
+ Hipbot::User.find_or_create_by(name: 'Telnet User')
32
+ end
33
+
34
+ def receive_data data
35
+ message = data.strip
36
+ Hipbot.react(user, nil, message)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -13,27 +13,15 @@ module Hipbot
13
13
  end
14
14
 
15
15
  class Bot
16
- extend Reactable
16
+ include Adapter
17
+ include Configurable
17
18
  include Singleton
19
+ include Matchable
20
+ extend Reactable
18
21
 
19
- attr_accessor :configuration, :connection
20
-
21
- CONFIGURABLE_OPTIONS = [:adapter, :error_handler, :helpers, :jid, :logger, :password, :plugins, :preloader, :rooms, :storage, :teams, :user]
22
- delegate *CONFIGURABLE_OPTIONS, to: :configuration
23
- delegate :name, to: :user
24
- alias_method :to_s, :name
25
-
26
- def initialize
27
- self.configuration ||= Configuration.new
28
- end
29
-
30
- def react sender, room, message
31
- message = Message.new(message, room, sender)
32
- matching_reactions(message, sender.reactions, plugin_reactions, default_reactions).each(&:invoke)
33
- end
22
+ delegate :name, :to_s, to: :user
34
23
 
35
24
  def setup
36
- extend adapter
37
25
  Hipbot.bot = self
38
26
 
39
27
  User.send(:include, storage)
@@ -41,54 +29,29 @@ module Hipbot
41
29
  Response.send(:include, helpers)
42
30
 
43
31
  helpers.module_exec(&preloader)
44
- plugins.append(self)
45
- Jabber.debug = true
46
- Jabber.logger = logger
47
- end
48
-
49
- def plugin_reactions
50
- plugins.flat_map{ |p| p.class.reactions }
51
- end
52
-
53
- def default_reactions
54
- plugins.flat_map{ |p| p.class.default_reactions }
32
+ plugins << self
55
33
  end
56
34
 
57
35
  class << self
58
- def configure &block
59
- instance.configuration = Configuration.new.tap(&block)
60
- end
61
-
62
36
  def on_preload &block
63
37
  instance.configuration.preloader = block
64
38
  end
65
39
 
66
- def on_error &block
67
- instance.configuration.error_handler = block
40
+ def on_exception &block
41
+ instance.configuration.exception_handler = block
68
42
  end
69
43
 
70
44
  def start!
71
- ::EM::run do
45
+ ::EM.error_handler(&instance.configuration.exception_handler)
46
+ ::EM.run do
72
47
  instance.setup
73
- instance.start!
48
+ begin
49
+ instance.start!
50
+ rescue Exception => e
51
+ instance_exec(e, &instance.configuration.exception_handler)
52
+ end
74
53
  end
75
54
  end
76
55
  end
77
-
78
- protected
79
-
80
- def matching_reactions message, *reaction_sets
81
- reaction_sets.each do |reactions|
82
- matches = reactions.map{ |reaction| matching_rection(message, reaction) }.compact
83
- return matches if matches.any?
84
- end
85
- []
86
- end
87
-
88
- def matching_rection message, reaction
89
- match = reaction.match_with(message)
90
- match.matches? ? match : nil
91
- end
92
-
93
56
  end
94
57
  end