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