hipbot 1.0.3 → 1.0.4

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -3
  4. data/Gemfile +12 -1
  5. data/README.md +16 -8
  6. data/bin/hipbot +5 -0
  7. data/hipbot.gemspec +2 -1
  8. data/lib/hipbot.rb +7 -10
  9. data/lib/hipbot/adaptable.rb +60 -0
  10. data/lib/hipbot/adapter.rb +6 -6
  11. data/lib/hipbot/adapters/hipchat.rb +5 -48
  12. data/lib/hipbot/adapters/hipchat/client.rb +8 -0
  13. data/lib/hipbot/adapters/shell.rb +2 -9
  14. data/lib/hipbot/adapters/slack.rb +10 -0
  15. data/lib/hipbot/adapters/slack/client.rb +47 -0
  16. data/lib/hipbot/adapters/telnet.rb +2 -9
  17. data/lib/hipbot/adapters/xmpp.rb +59 -0
  18. data/lib/hipbot/adapters/{hipchat/initializer.rb → xmpp/client.rb} +35 -21
  19. data/lib/hipbot/bot.rb +5 -1
  20. data/lib/hipbot/callbacks/base.rb +20 -2
  21. data/lib/hipbot/callbacks/lobby_presence.rb +2 -1
  22. data/lib/hipbot/callbacks/presence.rb +1 -5
  23. data/lib/hipbot/callbacks/room_invite.rb +13 -0
  24. data/lib/hipbot/callbacks/room_message.rb +3 -9
  25. data/lib/hipbot/callbacks/room_presence.rb +10 -8
  26. data/lib/hipbot/callbacks/room_topic.rb +18 -0
  27. data/lib/hipbot/configuration.rb +11 -6
  28. data/lib/hipbot/helpers.rb +2 -0
  29. data/lib/hipbot/{http.rb → helpers/http.rb} +11 -7
  30. data/lib/hipbot/message.rb +2 -2
  31. data/lib/hipbot/presence.rb +0 -6
  32. data/lib/hipbot/reaction.rb +9 -7
  33. data/lib/hipbot/response.rb +1 -1
  34. data/lib/hipbot/room.rb +1 -1
  35. data/lib/hipbot/storages/hash.rb +5 -5
  36. data/lib/hipbot/storages/mongoid.rb +1 -1
  37. data/lib/hipbot/user.rb +4 -0
  38. data/lib/hipbot/{cache.rb → utilities/cache.rb} +0 -0
  39. data/lib/hipbot/{logger.rb → utilities/logger.rb} +0 -0
  40. data/lib/hipbot/version.rb +1 -1
  41. data/spec/integration/my_hipbot_spec.rb +1 -1
  42. data/spec/spec_helper.rb +7 -2
  43. data/spec/unit/match_spec.rb +7 -7
  44. data/spec/unit/message_spec.rb +2 -2
  45. data/spec/unit/reaction_spec.rb +14 -14
  46. metadata +28 -8
  47. data/lib/hipbot/callbacks/invite.rb +0 -11
@@ -0,0 +1,59 @@
1
+ require 'hipbot/adapters/xmpp/client'
2
+
3
+ module Hipbot
4
+ module Adapters
5
+ class XMPP
6
+ include Hipbot::Adaptable
7
+
8
+ add_config_options :jid, :conference_host
9
+
10
+ attr_accessor :client
11
+
12
+ def start!
13
+ self.client = self.class::Client.new.client
14
+ end
15
+
16
+ def restart!
17
+ start!
18
+ end
19
+
20
+ def invite_to_room(room, users)
21
+ client.invite(user_ids(users), room.id)
22
+ end
23
+
24
+ def kick_from_room(room, users)
25
+ client.kick(user_ids(users), room.id)
26
+ end
27
+
28
+ def send_to_room(room, message)
29
+ client.send_message(:groupchat, room.id, message)
30
+ end
31
+
32
+ def send_to_user(user, message)
33
+ client.send_message(:chat, user.id, message)
34
+ end
35
+
36
+ def set_topic(room, topic)
37
+ client.send_message(:groupchat, room.id, nil, topic)
38
+ end
39
+
40
+ def set_presence(status, type)
41
+ client.set_presence(status, type)
42
+ end
43
+
44
+ def join_room(room)
45
+ client.join(room.id)
46
+ end
47
+
48
+ def leave_room(room, reason = '')
49
+ client.exit(room.id, reason)
50
+ end
51
+
52
+ protected
53
+
54
+ def user_ids users
55
+ Array(users).map(&:id)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,41 +1,52 @@
1
+ require 'xmpp4r-hipchat'
2
+
1
3
  module Hipbot
2
4
  module Adapters
3
- class Hipchat
4
- class Initializer
5
+ class XMPP
6
+ class Client
5
7
  attr_accessor :client
6
8
 
7
9
  KEEP_ALIVE_INTERVAL = 60
8
10
 
9
11
  def initialize
10
- Jabber.debug = true
11
- Jabber.logger = Hipbot.logger
12
+ initialize_logger
13
+
14
+ Hipbot.logger.info("INITIALIZE XMPP Client")
12
15
  initialize_client do
16
+ Hipbot.logger.info("INITIALIZE Rooms")
13
17
  initialize_rooms
18
+ Hipbot.logger.info("INITIALIZE Users")
14
19
  initialize_users
20
+ Hipbot.logger.info("INITIALIZE Bot user")
15
21
  initialize_bot_user
22
+ Hipbot.logger.info("INITIALIZE Callbacks")
16
23
  initialize_callbacks
24
+ Hipbot.logger.info("INITIALIZE Keep-alive")
17
25
  initialize_keep_alive
18
26
  end
19
27
  end
20
28
 
21
29
  protected
22
30
 
31
+ def initialize_logger
32
+ Jabber.logger = Hipbot.logger
33
+ if Hipbot.logger.level == Logger::DEBUG
34
+ Jabber.debug = true
35
+ end
36
+ end
37
+
23
38
  def initialize_client
24
- self.client = ::Jabber::MUC::HipchatClient.new(Hipbot.jid)
25
- yield if client.connect(Hipbot.password)
39
+ self.client = ::Jabber::MUC::HipchatClient.new(
40
+ Hipbot.configuration.jid,
41
+ Hipbot.configuration.conference_host,
42
+ )
43
+ yield if client.connect(Hipbot.configuration.password)
26
44
  end
27
45
 
28
46
  def initialize_rooms
29
47
  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
- )
48
+ room = Room.find_or_create_by(id: room_data.id)
49
+ room.update_attributes(room_data.attributes)
39
50
  room.id
40
51
  end
41
52
  clean_other_objects(Room, room_ids) if room_ids.any?
@@ -43,11 +54,11 @@ module Hipbot
43
54
 
44
55
  def initialize_users
45
56
  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)
57
+ user = User.find_or_create_by(id: user_data.id)
58
+ user.update_attributes(user_data.attributes)
48
59
 
49
60
  if user.attributes['email'].nil?
50
- user.update_attributes(client.get_user_details(user.id))
61
+ user.update_attributes(client.get_user_details(user.id).attributes)
51
62
  end
52
63
  user.id
53
64
  end
@@ -55,18 +66,21 @@ module Hipbot
55
66
  end
56
67
 
57
68
  def clean_other_objects klass, object_ids
58
- klass.all.select{ |r| !object_ids.include?(r.id) }.each(&:destroy)
69
+ klass.all.select{ |r| !object_ids.include?(r.id) }.tap{ |o|
70
+ Hipbot.logger.info("REMOVING #{o.count} objects from #{klass}")
71
+ }.each(&:destroy)
59
72
  end
60
73
 
61
74
  def initialize_bot_user
62
- Hipbot.configuration.user = User.find(Hipbot.jid)
75
+ Hipbot.configuration.user = User.find(Jabber::JID.new(Hipbot.configuration.jid).node)
63
76
  client.name = Hipbot.user.name
64
77
  end
65
78
 
66
79
  def initialize_callbacks
67
80
  client.on_room_message{ |*args| Callbacks::RoomMessage.new(*args) }
81
+ client.on_room_topic{ |*args| Callbacks::RoomTopic.new(*args) }
68
82
  client.on_private_message{ |*args| Callbacks::PrivateMessage.new(*args) }
69
- client.on_invite{ |*args| Callbacks::Invite.new(*args) }
83
+ client.on_room_invite{ |*args| Callbacks::RoomInvite.new(*args) }
70
84
  client.on_lobby_presence{ |*args| Callbacks::LobbyPresence.new(*args) }
71
85
  client.on_room_presence{ |*args| Callbacks::RoomPresence.new(*args) }
72
86
  client.activate_callbacks
@@ -1,12 +1,16 @@
1
1
  module Hipbot
2
2
  class << self
3
- attr_accessor :bot, :plugins
3
+ attr_accessor :bot, :plugins, :adapters
4
4
  delegate :name, to: :bot
5
5
 
6
6
  def plugins
7
7
  @plugins ||= []
8
8
  end
9
9
 
10
+ def adapters
11
+ @adapters ||= []
12
+ end
13
+
10
14
  def method_missing name, *params, &block
11
15
  bot.send(name, *params, &block)
12
16
  end
@@ -4,11 +4,29 @@ module Hipbot
4
4
  protected
5
5
 
6
6
  def with_room params
7
- yield Room.find_or_create_by(params)
7
+ yield Room.find_or_create_by(params) if valid_params?(params)
8
8
  end
9
9
 
10
10
  def with_user params
11
- yield User.find_or_create_by(params)
11
+ yield User.find_or_create_by(params) if valid_params?(params)
12
+ end
13
+
14
+ def with_user_by_name_or_mention name_or_mention
15
+ return if !valid_value?(name_or_mention)
16
+
17
+ yield User.where(mention: name_or_mention).first ||
18
+ User.where(name: name_or_mention).first ||
19
+ User.create(name: name_or_mention, mention: name_or_mention)
20
+ end
21
+
22
+ private
23
+
24
+ def valid_params? params
25
+ params.any?{ |_, v| valid_value?(v) }
26
+ end
27
+
28
+ def valid_value? value
29
+ !value.nil? && value.present?
12
30
  end
13
31
  end
14
32
  end
@@ -5,7 +5,8 @@ module Hipbot
5
5
  self.presence = presence
6
6
 
7
7
  with_user(id: user_id) do |user|
8
- user.update_attribute(:is_online, online_presence?)
8
+ Hipbot.logger.info("PRESENCE from #{user}: #{presence}")
9
+ user.update_attribute(:is_online, !offline_presence?)
9
10
  end
10
11
  end
11
12
  end
@@ -5,12 +5,8 @@ module Hipbot
5
5
 
6
6
  protected
7
7
 
8
- def online_presence?
9
- presence.empty?
10
- end
11
-
12
8
  def offline_presence?
13
- presence == 'unavailable'
9
+ presence == :unavailable
14
10
  end
15
11
  end
16
12
  end
@@ -0,0 +1,13 @@
1
+ module Hipbot
2
+ module Callbacks
3
+ class RoomInvite < Base
4
+ def initialize room_id, room_name
5
+ return unless Hipbot.configuration.join_on_invite
6
+
7
+ with_room(id: room_id, name: room_name) do |room|
8
+ Hipbot.join_room(room)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,21 +1,15 @@
1
1
  module Hipbot
2
2
  module Callbacks
3
3
  class RoomMessage < Message
4
- def initialize room_id, user_name, message_body, topic
4
+ def initialize room_id, user_name_or_mention, message_body
5
5
  with_room(id: room_id) do |room|
6
- update_topic(room, topic)
7
- with_user(name: user_name) do |user|
6
+ with_user_by_name_or_mention(user_name_or_mention) do |user|
8
7
  return if ignore_message?(user, message_body)
8
+
9
9
  Hipbot.react(user, room, message_body)
10
10
  end
11
11
  end
12
12
  end
13
-
14
- protected
15
-
16
- def update_topic room, topic
17
- room.update_attribute(:topic, topic) unless topic.empty?
18
- end
19
13
  end
20
14
  end
21
15
  end
@@ -1,12 +1,13 @@
1
1
  module Hipbot
2
2
  module Callbacks
3
3
  class RoomPresence < Presence
4
- attr_accessor :room_id, :user_name
4
+ attr_accessor :room_id, :user_name_or_mention
5
+
6
+ def initialize room_id, user_name_or_mention, presence, role
7
+ self.room_id = room_id
8
+ self.user_name_or_mention = user_name_or_mention
9
+ self.presence = presence
5
10
 
6
- def initialize room_id, user_name, presence
7
- self.presence = presence
8
- self.user_name = user_name
9
- self.room_id = room_id
10
11
  handle_room_presence
11
12
  end
12
13
 
@@ -14,13 +15,14 @@ module Hipbot
14
15
 
15
16
  def handle_room_presence
16
17
  with_room(id: room_id) do |room|
17
- with_user(name: user_name) do |user|
18
+ with_user_by_name_or_mention(user_name_or_mention) do |user|
19
+ Hipbot.logger.info("PRESENCE in ##{room} from #{user}: #{presence}")
18
20
  Hipbot.react_to_presence(user, presence, room)
21
+
19
22
  if offline_presence?
20
23
  room.on_leave(user)
21
- elsif online_presence? && !room.users.include?(user)
22
- room.on_join(user)
23
24
  else
25
+ room.on_join(user) if !room.users.include?(user)
24
26
  # TODO: Availability status change handling
25
27
  end
26
28
  end
@@ -0,0 +1,18 @@
1
+ module Hipbot
2
+ module Callbacks
3
+ class RoomTopic < Message
4
+ def initialize room_id, topic
5
+ with_room(id: room_id) do |room|
6
+ Hipbot.logger.info("TOPIC in ##{room}: #{topic}")
7
+ update_topic(room, topic)
8
+ end
9
+ end
10
+
11
+ protected
12
+
13
+ def update_topic room, topic
14
+ room.update_attribute(:topic, topic)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,24 +1,29 @@
1
1
  module Hipbot
2
2
  class Configuration
3
3
  OPTIONS = [
4
- :adapter, :exception_handler, :helpers, :jid, :join, :logger, :password,
5
- :plugins, :preloader, :rooms, :status, :storage, :teams, :user
4
+ :adapter, :case_insensitive, :exception_handler, :helpers, :join, :join_on_invite,
5
+ :logger, :password, :plugins, :preloader, :rooms, :status, :storage, :teams, :user
6
6
  ]
7
7
  attr_accessor *OPTIONS
8
8
 
9
9
  def initialize
10
- self.adapter = Adapters::Hipchat
11
- self.exception_handler = Proc.new do |e|
10
+ self.class.class_eval do
11
+ attr_accessor *Hipbot.adapters.flat_map(&:options).compact
12
+ end
13
+
14
+ self.adapter = Adapters::Hipchat
15
+ self.case_insensitive = true
16
+ self.exception_handler = proc do |e|
12
17
  Hipbot.logger.error(e.message)
13
18
  e.backtrace.each { |line| Hipbot.logger.error(line) }
14
19
  end
15
20
  self.helpers = Module.new
16
- self.jid = ''
17
21
  self.join = :all
22
+ self.join_on_invite = true
18
23
  self.logger = Logger.new($stdout)
19
24
  self.password = ''
20
25
  self.plugins = Hipbot.plugins
21
- self.preloader = Proc.new{}
26
+ self.preloader = proc {}
22
27
  self.rooms = {}
23
28
  self.status = ''
24
29
  self.storage = Storages::Hash
@@ -1,3 +1,5 @@
1
+ require 'hipbot/helpers/http'
2
+
1
3
  module Hipbot
2
4
  module Helpers
3
5
  [:get, :post, :put, :delete].each do |method|
@@ -1,13 +1,17 @@
1
+ # encoding: utf-8
1
2
  module Hipbot
2
3
  module Http
3
- class Request < Struct.new(:url, :query, :method)
4
+ class Request < Struct.new(:url, :params, :method)
4
5
  DEFAULT_HEADERS = { 'accept-encoding' => 'gzip, compressed' }.freeze
5
6
  CONNECTION_SETTINGS = { connect_timeout: 5, inactivity_timeout: 10 }.freeze
6
7
  ERROR_CALLBACK = ->(error){ Hipbot.logger.error(error) }
7
8
 
8
9
  def initialize *args
9
10
  super
10
- Hipbot.logger.info("HTTP-REQUEST: #{url} #{query}")
11
+ self.params ||= {}
12
+ self.params = params.has_key?(:query) ? params : { query: params }
13
+ self.params = { head: DEFAULT_HEADERS }.merge(params)
14
+ Hipbot.logger.info("HTTP-REQUEST: #{url} #{params}")
11
15
  end
12
16
 
13
17
  def call &success_block
@@ -21,16 +25,16 @@ module Hipbot
21
25
 
22
26
  def success
23
27
  yield Http::Response.new(http)
24
- rescue => e
25
- instance_exec(e, &Hipbot.exception_handler)
26
28
  end
27
29
 
28
- def http
29
- @http ||= connection.send(method, query: query.merge(head: DEFAULT_HEADERS))
30
+ def http options = {}
31
+ @http ||= connection.send(method, params.merge(options))
32
+ rescue => e
33
+ instance_exec(e, &Hipbot.exception_handler)
30
34
  end
31
35
 
32
36
  def connection
33
- EM::HttpRequest.new(url, CONNECTION_SETTINGS)
37
+ @connection ||= EM::HttpRequest.new(url, CONNECTION_SETTINGS)
34
38
  end
35
39
  end
36
40
 
@@ -5,11 +5,11 @@ module Hipbot
5
5
 
6
6
  attr_accessor :body
7
7
 
8
- MENTION_REGEXP = /@(\p{Word}++)/.freeze
8
+ MENTION_REGEXP = /@(\p{Word}++):?/.freeze
9
9
 
10
10
  def initialize *args
11
11
  super
12
- Hipbot.logger.info("MESSAGE from #{sender} in #{room}")
12
+ Hipbot.logger.info("MESSAGE from #{sender} in ##{room}")
13
13
  self.raw_body = raw_body.force_encoding('UTF-8')
14
14
  self.body = strip_bot_mention
15
15
  end
@@ -1,11 +1,5 @@
1
1
  module Hipbot
2
2
  class Presence < Struct.new(:sender, :body, :room)
3
-
4
- def initialize *args
5
- super
6
- Hipbot.logger.info("PRESENCE from #{sender} in #{room}")
7
- end
8
-
9
3
  def for? _
10
4
  true
11
5
  end