hipbot 0.2.0 → 1.0.0.rc1
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.
- data/.gitignore +2 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +15 -0
- data/README.md +28 -20
- data/hipbot.gemspec +2 -2
- data/lib/hipbot.rb +1 -0
- data/lib/hipbot/adapters/hipchat/connection.rb +46 -41
- data/lib/hipbot/adapters/hipchat/hipchat.rb +1 -1
- data/lib/hipbot/adapters/telnet/connection.rb +4 -5
- data/lib/hipbot/bot.rb +61 -48
- data/lib/hipbot/collection.rb +34 -9
- data/lib/hipbot/configuration.rb +15 -7
- data/lib/hipbot/helpers.rb +10 -4
- data/lib/hipbot/logger.rb +0 -4
- data/lib/hipbot/match.rb +48 -0
- data/lib/hipbot/message.rb +13 -13
- data/lib/hipbot/patches/hipchat_client.rb +50 -12
- data/lib/hipbot/plugin.rb +24 -5
- data/lib/hipbot/reactable.rb +27 -42
- data/lib/hipbot/reaction.rb +33 -34
- data/lib/hipbot/response.rb +14 -11
- data/lib/hipbot/room.rb +4 -11
- data/lib/hipbot/user.rb +8 -2
- data/lib/hipbot/version.rb +1 -1
- data/spec/integration/hipbot_spec.rb +96 -68
- data/spec/integration/my_hipbot.rb +104 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/unit/hipbot_spec.rb +45 -34
- data/spec/unit/message_spec.rb +17 -18
- metadata +10 -5
data/lib/hipbot/plugin.rb
CHANGED
@@ -1,11 +1,30 @@
|
|
1
1
|
module Hipbot
|
2
|
-
|
3
|
-
|
2
|
+
module Plugin
|
3
|
+
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
5
|
+
included do
|
6
|
+
extend Reactable
|
7
|
+
extend ClassMethods
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
include Singleton
|
10
|
+
include Helpers
|
11
|
+
|
12
|
+
attr_accessor :response
|
13
|
+
|
14
|
+
delegate :sender, :recipients, :body, :room, :to => :response
|
15
|
+
delegate :bot, :to => Hipbot
|
16
|
+
|
17
|
+
Hipbot.plugins.prepend(self.instance)
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def configure
|
22
|
+
yield instance
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_response response
|
26
|
+
instance.tap{ |i| i.response = response }
|
27
|
+
end
|
9
28
|
end
|
10
29
|
end
|
11
30
|
end
|
data/lib/hipbot/reactable.rb
CHANGED
@@ -1,60 +1,45 @@
|
|
1
1
|
module Hipbot
|
2
|
-
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def defined_reactions
|
9
|
-
@defined_reactions ||= []
|
10
|
-
@defined_reactions + class_reactions
|
2
|
+
module Reactable
|
3
|
+
def default *params, &block
|
4
|
+
scope *params do
|
5
|
+
default_reactions << to_reaction(block)
|
6
|
+
end
|
11
7
|
end
|
12
8
|
|
13
9
|
def default_reactions
|
14
|
-
@default_reactions ||=
|
15
|
-
if reaction = self.class.default_reaction
|
16
|
-
[ to_reaction(reaction[0], reaction[-1]) ]
|
17
|
-
else
|
18
|
-
[]
|
19
|
-
end
|
20
|
-
end
|
10
|
+
@default_reactions ||= []
|
21
11
|
end
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@reactions << [regexps, block]
|
27
|
-
end
|
28
|
-
|
29
|
-
def reactions
|
30
|
-
@reactions || []
|
31
|
-
end
|
32
|
-
|
33
|
-
def default &block
|
34
|
-
@default_reaction = [[/(.*)/], block]
|
35
|
-
end
|
13
|
+
def desc text = nil
|
14
|
+
@description.tap{ @description = text }
|
15
|
+
end
|
36
16
|
|
37
|
-
|
38
|
-
|
17
|
+
def on *params, &block
|
18
|
+
scope *params do
|
19
|
+
reactions << to_reaction(block)
|
39
20
|
end
|
40
21
|
end
|
41
22
|
|
42
|
-
|
23
|
+
def reactions
|
24
|
+
@reactions ||= []
|
25
|
+
end
|
43
26
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
27
|
+
def scope *params, &block
|
28
|
+
options = params.last.kind_of?(Hash) ? params.pop : {}
|
29
|
+
options_stack << options.merge({ regexps: params, desc: desc })
|
30
|
+
yield
|
31
|
+
options_stack.pop
|
48
32
|
end
|
49
33
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
34
|
+
protected
|
35
|
+
|
36
|
+
def to_reaction block
|
37
|
+
current_options = options_stack.inject{ |all, h| all.merge(h) }
|
38
|
+
Reaction.new(self, current_options, block)
|
54
39
|
end
|
55
40
|
|
56
|
-
def
|
57
|
-
|
41
|
+
def options_stack
|
42
|
+
@options_stack ||= []
|
58
43
|
end
|
59
44
|
end
|
60
45
|
end
|
data/lib/hipbot/reaction.rb
CHANGED
@@ -1,66 +1,65 @@
|
|
1
1
|
module Hipbot
|
2
|
-
class Reaction < Struct.new(:
|
3
|
-
|
4
|
-
|
5
|
-
message = message_for(message, sender)
|
6
|
-
arguments = arguments_for(message)
|
7
|
-
Response.new(bot, self, room, message).invoke(arguments)
|
2
|
+
class Reaction < Struct.new(:plugin, :options, :block)
|
3
|
+
def any_room?
|
4
|
+
options[:room] == true
|
8
5
|
end
|
9
6
|
|
10
|
-
def
|
11
|
-
|
12
|
-
matches_regexp?(message) && matches_scope?(room, message) && matches_sender?(message) && matches_room?(room)
|
7
|
+
def anything?
|
8
|
+
regexps.blank?
|
13
9
|
end
|
14
10
|
|
15
|
-
def
|
16
|
-
|
11
|
+
def anywhere?
|
12
|
+
options[:room].nil?
|
17
13
|
end
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
def message_for message, sender
|
22
|
-
Message.new(message, sender)
|
15
|
+
def desc
|
16
|
+
options[:desc]
|
23
17
|
end
|
24
18
|
|
25
|
-
def
|
26
|
-
|
19
|
+
def from_all?
|
20
|
+
options[:from].blank?
|
27
21
|
end
|
28
22
|
|
29
|
-
def
|
30
|
-
|
23
|
+
def global?
|
24
|
+
!!options[:global]
|
31
25
|
end
|
32
26
|
|
33
|
-
def
|
34
|
-
|
27
|
+
def inspect
|
28
|
+
"#<Hipbot::Reaction #{options}>"
|
35
29
|
end
|
36
30
|
|
37
|
-
def
|
38
|
-
|
31
|
+
def plugin_name
|
32
|
+
plugin.name.demodulize
|
39
33
|
end
|
40
34
|
|
41
|
-
def
|
42
|
-
|
43
|
-
room.present? && (rooms.include?(room.name) || options[:room] == true)
|
35
|
+
def match_with message
|
36
|
+
Match.new(self, message)
|
44
37
|
end
|
45
38
|
|
46
|
-
def
|
47
|
-
|
39
|
+
def private_message_only?
|
40
|
+
options[:room] == false
|
48
41
|
end
|
49
42
|
|
50
|
-
def
|
51
|
-
|
43
|
+
def readable_command
|
44
|
+
regexps.to_s.gsub(/(?<!\\)(\/|\[|\]|\^|\\z|\$|\\)/, '')
|
52
45
|
end
|
53
46
|
|
54
|
-
def
|
55
|
-
options[:
|
47
|
+
def regexps
|
48
|
+
options[:regexps]
|
56
49
|
end
|
57
50
|
|
58
51
|
def rooms
|
59
|
-
|
52
|
+
replace_symbols options[:room], Hipbot.rooms
|
60
53
|
end
|
61
54
|
|
62
55
|
def users
|
63
|
-
|
56
|
+
replace_symbols options[:from], Hipbot.teams
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def replace_symbols values, replacements_hash
|
62
|
+
Array(values).flat_map{ |v| replacements_hash[v].presence || v.to_s }
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
data/lib/hipbot/response.rb
CHANGED
@@ -1,29 +1,32 @@
|
|
1
1
|
module Hipbot
|
2
|
-
class Response < Struct.new(:
|
3
|
-
delegate :sender, :recipients, :body, :to => :message
|
2
|
+
class Response < Struct.new(:reaction, :message)
|
4
3
|
include Helpers
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
extend(bot.helpers)
|
9
|
-
end
|
5
|
+
delegate :sender, :recipients, :body, :room, :to => :message
|
6
|
+
delegate :bot, :to => Hipbot
|
10
7
|
|
11
8
|
def invoke arguments
|
9
|
+
Hipbot.logger.info("REACTION #{reaction.inspect}")
|
12
10
|
instance_exec(*arguments, &reaction.block)
|
11
|
+
true
|
13
12
|
rescue Exception => e
|
14
13
|
Hipbot.logger.error(e)
|
15
|
-
instance_exec(e, &
|
14
|
+
instance_exec(e, &Hipbot.error_handler)
|
15
|
+
false
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
protected
|
19
19
|
|
20
20
|
def reply message, room = self.room
|
21
|
-
Hipbot.
|
22
|
-
|
21
|
+
room.nil? ? Hipbot.send_to_user(sender, message) : Hipbot.send_to_room(room, message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing method, *args, &block
|
25
|
+
plugin.send(method, *args, &block)
|
23
26
|
end
|
24
27
|
|
25
28
|
def plugin
|
26
|
-
|
29
|
+
reaction.plugin.with_response(self)
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
data/lib/hipbot/room.rb
CHANGED
@@ -1,22 +1,15 @@
|
|
1
1
|
module Hipbot
|
2
|
-
class Room
|
3
|
-
attr_accessor :user_ids
|
4
|
-
|
5
|
-
def initialize *args
|
6
|
-
super
|
7
|
-
self.user_ids = []
|
8
|
-
end
|
9
|
-
|
2
|
+
class Room
|
10
3
|
def set_topic topic
|
11
|
-
|
4
|
+
Hipbot.set_topic(self, topic)
|
12
5
|
end
|
13
6
|
|
14
7
|
def send_message message
|
15
|
-
|
8
|
+
Hipbot.send_to_room(self, message)
|
16
9
|
end
|
17
10
|
|
18
11
|
def users
|
19
|
-
|
12
|
+
@users ||= []
|
20
13
|
end
|
21
14
|
end
|
22
15
|
end
|
data/lib/hipbot/user.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
module Hipbot
|
2
|
-
class User
|
2
|
+
class User
|
3
|
+
include Reactable
|
4
|
+
|
3
5
|
def send_message message
|
4
|
-
|
6
|
+
Hipbot.send_to_user self, message
|
7
|
+
end
|
8
|
+
|
9
|
+
def mention
|
10
|
+
attributes['mention'] || name.delete(' ')
|
5
11
|
end
|
6
12
|
|
7
13
|
def first_name
|
data/lib/hipbot/version.rb
CHANGED
@@ -1,117 +1,145 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require_relative './my_hipbot'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
4
|
+
describe MyHipbot do
|
5
|
+
before(:all) { MyHipbot.instance.setup }
|
6
|
+
subject { MyHipbot.instance }
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
let(:room) { Hipbot::Room.create(id: '1', name: 'Project 1', topic: 'project 1 stuff only') }
|
9
|
+
let(:sender) { Hipbot::User.create(id: '1', name: 'John Doe') }
|
10
|
+
let(:other_room) { Hipbot::Room.create(id: '2', name: 'Hyde Park', topic: 'nice weather today') }
|
11
|
+
let(:other_sender) { Hipbot::User.create(id: '2', name: 'Other Guy') }
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
reply("awesome responded")
|
13
|
+
before do
|
14
|
+
Hipbot.bot.configuration.user = Hipbot::User.create(name: 'robbot')
|
16
15
|
end
|
17
|
-
end
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
17
|
+
describe 'configuration' do
|
18
|
+
it 'should set robot name' do
|
19
|
+
subject.name.should == 'robbot'
|
20
|
+
end
|
24
21
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
config.jid = 'robbot@chat.hipchat.com'
|
29
|
-
config.helpers = HipbotHelpers
|
30
|
-
config.plugins = [ AwesomePlugin, CoolPlugin.new ]
|
22
|
+
it 'should set hipchat token' do
|
23
|
+
subject.jid.should == 'robbot@chat.hipchat.com'
|
24
|
+
end
|
31
25
|
end
|
32
26
|
|
33
|
-
|
34
|
-
reply
|
35
|
-
|
27
|
+
describe 'replying' do
|
28
|
+
it 'should reply to hello' do
|
29
|
+
subject.expects(:send_to_room).with(room, 'hello!')
|
30
|
+
subject.react(sender, room, '@robbot hello hipbot!')
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
it 'should reply with argument' do
|
34
|
+
subject.expects(:send_to_room).with(room, "I know I'm cool")
|
35
|
+
subject.react(sender, room, '@robbot you\'re cool, robot')
|
36
|
+
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
it 'should reply to global message' do
|
39
|
+
subject.expects(:send_to_room).with(room, 'hello!')
|
40
|
+
subject.react(sender, room, 'hi everyone!')
|
41
|
+
end
|
44
42
|
|
45
|
-
|
46
|
-
|
43
|
+
it 'should respond with default reply' do
|
44
|
+
subject.expects(:send_to_room).with(room, "I didn't understand you")
|
45
|
+
subject.react(sender, room, '@robbot blahlblah')
|
46
|
+
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
describe '"from" option' do
|
50
|
+
it 'reacts to sender from required team' do
|
51
|
+
subject.expects(:send_to_room).with(room, 'restarting')
|
52
|
+
subject.react(sender, room, '@robbot restart')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'ignores sender when not in team' do
|
56
|
+
subject.expects(:send_to_room).with(room, 'What do you mean, Other Guy?')
|
57
|
+
subject.react(other_sender, room, '@robbot restart')
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
|
-
|
54
|
-
|
61
|
+
describe '"room" option' do
|
62
|
+
it 'reacts in required room' do
|
63
|
+
subject.expects(:send_to_room).with(room, 'deploying')
|
64
|
+
subject.react(sender, room, '@robbot deploy')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'ignores other rooms' do
|
68
|
+
subject.expects(:send_to_room).with(other_room, "I didn't understand you")
|
69
|
+
subject.react(sender, other_room, '@robbot deploy')
|
70
|
+
end
|
55
71
|
end
|
56
|
-
end
|
57
72
|
|
58
|
-
describe
|
59
|
-
|
73
|
+
describe 'room=true' do
|
74
|
+
it 'reacts in any room' do
|
75
|
+
subject.expects(:send_to_room).with(room, 'doing room thing')
|
76
|
+
subject.react(sender, room, '@robbot room thing')
|
77
|
+
end
|
60
78
|
|
61
|
-
|
62
|
-
|
79
|
+
it 'ignores room commands if not in room' do
|
80
|
+
subject.expects(:send_to_user).with(sender, "I didn't understand you")
|
81
|
+
subject.react(sender, nil, 'room thing')
|
82
|
+
end
|
83
|
+
end
|
63
84
|
|
64
|
-
describe
|
65
|
-
it
|
66
|
-
subject.
|
85
|
+
describe 'room=false' do
|
86
|
+
it 'ignores private command in room' do
|
87
|
+
subject.expects(:send_to_room).with(room, "I didn't understand you")
|
88
|
+
subject.react(sender, room, '@robbot private thing')
|
67
89
|
end
|
68
90
|
|
69
|
-
it
|
70
|
-
subject.
|
91
|
+
it 'allows private command if not in room' do
|
92
|
+
subject.expects(:send_to_user).with(sender, 'doing private thing')
|
93
|
+
subject.react(sender, nil, 'private thing')
|
71
94
|
end
|
72
95
|
end
|
73
96
|
|
74
|
-
describe
|
75
|
-
it
|
76
|
-
subject.expects(:send_to_room).with(room, '
|
77
|
-
subject.react(sender, room, '@robbot
|
97
|
+
describe 'scope' do
|
98
|
+
it 'sets its attributes to every reaction inside' do
|
99
|
+
subject.expects(:send_to_room).with(room, 'doing John Doe thing')
|
100
|
+
subject.react(sender, room, '@robbot John Doe thing')
|
78
101
|
end
|
79
102
|
|
80
|
-
it
|
81
|
-
subject.expects(:send_to_room).with(room,
|
82
|
-
subject.react(
|
103
|
+
it 'does not match other senders' do
|
104
|
+
subject.expects(:send_to_room).with(room, 'What do you mean, Other Guy?')
|
105
|
+
subject.react(other_sender, room, '@robbot John Doe thing')
|
83
106
|
end
|
84
107
|
|
85
|
-
it
|
86
|
-
subject.expects(:send_to_room).with(room,
|
87
|
-
subject.react(sender, room,
|
108
|
+
it 'merges params if embedded' do
|
109
|
+
subject.expects(:send_to_room).with(room, 'doing John Doe project thing')
|
110
|
+
subject.react(sender, room, '@robbot John Doe project thing')
|
88
111
|
end
|
89
112
|
|
90
|
-
it
|
91
|
-
subject.expects(:send_to_room).with(
|
92
|
-
subject.react(sender,
|
113
|
+
it 'ignores message from same sander in other room' do
|
114
|
+
subject.expects(:send_to_room).with(other_room, "I didn't understand you")
|
115
|
+
subject.react(sender, other_room, '@robbot John Doe project thing')
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'ignores message from other sender in same room' do
|
119
|
+
subject.expects(:send_to_room).with(room, 'What do you mean, Other Guy?')
|
120
|
+
subject.react(other_sender, room, '@robbot John Doe project thing')
|
93
121
|
end
|
94
122
|
end
|
95
123
|
|
96
|
-
describe
|
97
|
-
it
|
98
|
-
subject.expects(:send_to_room).with(room, '
|
124
|
+
describe 'custom helpers' do
|
125
|
+
it 'should have access to room variable' do
|
126
|
+
subject.expects(:send_to_room).with(room, 'Project: Project 1')
|
99
127
|
subject.react(sender, room, '@robbot tell me the project name')
|
100
128
|
end
|
101
129
|
|
102
|
-
it
|
130
|
+
it 'should have access to message variable' do
|
103
131
|
subject.expects(:send_to_room).with(room, 'you are John')
|
104
132
|
subject.react(sender, room, '@robbot tell me my name')
|
105
133
|
end
|
106
134
|
end
|
107
135
|
|
108
|
-
describe
|
109
|
-
it
|
136
|
+
describe 'plugins' do
|
137
|
+
it 'should reply to reaction defined in plugin' do
|
110
138
|
subject.expects(:send_to_room).with(room, 'awesome responded')
|
111
139
|
subject.react(sender, room, '@robbot respond awesome')
|
112
140
|
end
|
113
141
|
|
114
|
-
it
|
142
|
+
it 'should reply to reaction defined in second plugin' do
|
115
143
|
subject.expects(:send_to_room).with(room, 'cool responded')
|
116
144
|
subject.react(sender, room, '@robbot respond cool')
|
117
145
|
end
|