boty 0.0.17 → 0.0.17.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/boty.gemspec +1 -1
- data/lib/boty/version.rb +1 -1
- data/spec/boty/bot_spec.rb +419 -0
- data/spec/boty/cli_spec.rb +87 -0
- data/spec/boty/logger_spec.rb +32 -0
- data/spec/boty/message_spec.rb +32 -0
- data/spec/boty/rspec_spec.rb +36 -0
- data/spec/boty/session_spec.rb +70 -0
- data/spec/boty/slack/chat_spec.rb +73 -0
- data/spec/boty/slack/im_spec.rb +34 -0
- data/spec/boty/slack/rtm_spec.rb +20 -0
- data/spec/boty/slack/url_spec.rb +71 -0
- data/spec/boty/slack/users_spec.rb +44 -0
- data/spec/happy_path_spec.rb +88 -0
- data/spec/script/i18n_spec.rb +22 -0
- data/spec/script/knows_spec.rb +18 -0
- data/spec/script/pug_spec.rb +53 -0
- data/spec/script/remember_spec.rb +43 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/em_support.rb +13 -0
- data/spec/support/fakefs_support.rb +13 -0
- data/spec/support/faraday_support.rb +15 -0
- data/spec/support/faye_support.rb +34 -0
- data/spec/support/file_system_matchers.rb +19 -0
- data/spec/support/session_support.rb +31 -0
- data/spec/support/slack_support.rb +28 -0
- data/spec/support/template_project_support.rb +44 -0
- data/spec/support/thor_support.rb +21 -0
- metadata +27 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ebb5950dfa8625a01e2aca017fbd5ed259c499d
|
4
|
+
data.tar.gz: 463e6b73167a63d36ec7441c9205784bf74a5520
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ad60851ef2067df1eaafc8a6bf9be654c2494cf1be814a33a920df05f1b702a8f818d4661d4144c5771bb5dfde15b7e6054227ece9d3208fd296b413bab6577
|
7
|
+
data.tar.gz: 9fa330463454c9be3297c034a4189d7f6663d8a5ad6bfff6b474d0f9866ab33fbc2d1bf6a541431e70771545f5ee46bd97cb4356f6111ddcde0d6d8a438976c6
|
data/Gemfile.lock
CHANGED
data/boty.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = "http://github.com/ricardovaleriano/boty"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(features)/}) }
|
18
18
|
spec.bindir = "exe"
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
data/lib/boty/version.rb
CHANGED
@@ -0,0 +1,419 @@
|
|
1
|
+
module Boty
|
2
|
+
RSpec.describe "happy brighty shiny path", :session do
|
3
|
+
before do
|
4
|
+
start_session
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:user_id) { "U5678" }
|
8
|
+
|
9
|
+
before do
|
10
|
+
bot.hear(/jeeba/i) do
|
11
|
+
say "Ohay <@#{user.id}>! I'm here, that's for sure."
|
12
|
+
end
|
13
|
+
|
14
|
+
bot.hear(/anybody there\?/i) do
|
15
|
+
say "Me! Here."
|
16
|
+
end
|
17
|
+
|
18
|
+
bot.match(/with match/i) do
|
19
|
+
say "Ohay <@#{user.id}>! It matched."
|
20
|
+
end
|
21
|
+
|
22
|
+
bot.command(/do stuff/i) do
|
23
|
+
say "Doing some stuff"
|
24
|
+
end
|
25
|
+
|
26
|
+
bot.command(/exec command/i) do
|
27
|
+
say "Command issued."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "responds to a message" do
|
32
|
+
expect(bot).to receive(:say).
|
33
|
+
with "Ohay <@U5678>! I'm here, that's for sure."
|
34
|
+
faye.message "jeeba, are you there?", user: "U5678"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "responds to a match" do
|
38
|
+
expect(bot).to receive(:say).
|
39
|
+
with "Ohay <@U5678>! It matched."
|
40
|
+
faye.message "called with match?", user: "U5678"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does not respond to a message not binded" do
|
44
|
+
expect(bot).to_not receive(:say)
|
45
|
+
faye.message "are you there?"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "understands #hear as a message binding" do
|
49
|
+
expect(bot).to receive(:say).with("Me! Here.")
|
50
|
+
faye.message "anybody there?"
|
51
|
+
end
|
52
|
+
|
53
|
+
context "Direct message" do
|
54
|
+
it "responds when mentioned by name" do
|
55
|
+
expect(bot).to receive(:say).with "Doing some stuff"
|
56
|
+
faye.message "<@jeeba>: do stuff!"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "responds when mentioned by id" do
|
60
|
+
expect(bot).to receive(:say).with "Doing some stuff"
|
61
|
+
faye.message "<@U1234>: do stuff!"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "responds just once" do
|
65
|
+
expect(bot).to receive(:im).once
|
66
|
+
faye.message "<@U1234>: knows"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "responds when mentioned by both name and id in the same message" do
|
70
|
+
expect(bot).to receive(:im).once
|
71
|
+
faye.message "<@U1234|jabber>: knows"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "understands `#command` bindings" do
|
75
|
+
expect(bot).to receive(:say).with "Command issued."
|
76
|
+
faye.message "<@U1234>: exec command xpto"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
RSpec.describe Bot do
|
82
|
+
subject(:bot) { described_class.new bot_info, Session.new }
|
83
|
+
|
84
|
+
let(:bot_info) {{
|
85
|
+
"id" => "666",
|
86
|
+
"name" => "jabber"
|
87
|
+
}}
|
88
|
+
|
89
|
+
describe "#on" do
|
90
|
+
it "binds an event `'type'` to a block" do
|
91
|
+
data = {
|
92
|
+
"type" => "omg",
|
93
|
+
"text" => "lol"
|
94
|
+
}
|
95
|
+
|
96
|
+
expect { |b|
|
97
|
+
bot.on :omg, &b
|
98
|
+
bot.event data
|
99
|
+
}.to yield_with_args data
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#off" do
|
104
|
+
let(:data) {{
|
105
|
+
"type" => "bbq",
|
106
|
+
"text" => "omg. wtf."
|
107
|
+
}}
|
108
|
+
|
109
|
+
it "unbounds an event by type AND block, just if the block is the same" do
|
110
|
+
@permanent_executed = false
|
111
|
+
permanent_block = ->(_){ @permanent_executed = true }
|
112
|
+
bot.on :bbq, &permanent_block
|
113
|
+
|
114
|
+
@ephemeral_executed = false
|
115
|
+
ephemeral_block = ->(_){ @ephemeral_executed = true }
|
116
|
+
bot.on :bbq, &ephemeral_block
|
117
|
+
bot.off :bbq, &ephemeral_block
|
118
|
+
|
119
|
+
bot.event data
|
120
|
+
|
121
|
+
expect(@permanent_executed).to eq true
|
122
|
+
expect(@ephemeral_executed).to eq false
|
123
|
+
end
|
124
|
+
|
125
|
+
it "unbounds all events by type" do
|
126
|
+
@first_block = @second_block = @third_block = false
|
127
|
+
bot.on(:bbq) { |_| @first_block = true }
|
128
|
+
bot.on(:bbq) { |_| @second_block = true }
|
129
|
+
bot.on(:bbq) { |_| @third_block = true }
|
130
|
+
bot.off(:bbq)
|
131
|
+
|
132
|
+
bot.event data
|
133
|
+
|
134
|
+
expect(@first_block).to eq false
|
135
|
+
expect(@second_block).to eq false
|
136
|
+
expect(@third_block).to eq false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#match", :users do
|
141
|
+
let(:data) { {"type" => "message", "text" => "bbq omg lol"} }
|
142
|
+
|
143
|
+
it "binds a regex to events of `type => message`" do
|
144
|
+
_message = nil
|
145
|
+
bot.match(/omg/i) do
|
146
|
+
_message = message
|
147
|
+
end
|
148
|
+
bot.event data
|
149
|
+
|
150
|
+
expect(_message).to be_a Message
|
151
|
+
expect(_message.text).to eq "bbq omg lol"
|
152
|
+
expect(_message.match[0]).to eq "omg"
|
153
|
+
end
|
154
|
+
|
155
|
+
it "binds various actions to events of type message" do
|
156
|
+
iterations = []
|
157
|
+
bot.match(/omg/i) do iterations << "first" end
|
158
|
+
bot.match(/omg/i) do iterations << "second" end
|
159
|
+
bot.event data
|
160
|
+
|
161
|
+
expect(iterations).to eq ["first", "second"]
|
162
|
+
end
|
163
|
+
|
164
|
+
it "passes the matched strings as parameters to block action" do
|
165
|
+
_lol, _bbq = nil
|
166
|
+
bot.match(/(bbq) omg (lol)/i) do |bbq, lol|
|
167
|
+
_lol, _bbq = lol, bbq
|
168
|
+
end
|
169
|
+
bot.event data
|
170
|
+
|
171
|
+
expect(_lol).to eq "lol"
|
172
|
+
expect(_bbq).to eq "bbq"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#respond", :users do
|
177
|
+
let(:data) {{
|
178
|
+
"type" => "message",
|
179
|
+
"text" => "hey <@jabber>, omg me"
|
180
|
+
}}
|
181
|
+
|
182
|
+
it "binds actions for messages only when there is a mention (@bot)" do
|
183
|
+
expect { |b|
|
184
|
+
bot.respond(/omg me/, &b)
|
185
|
+
bot.event data
|
186
|
+
}.to yield_control
|
187
|
+
end
|
188
|
+
|
189
|
+
it "recognizes the id when it's mentioned" do
|
190
|
+
data["text"] = "hey <@666>: omg me"
|
191
|
+
expect { |b|
|
192
|
+
bot.respond(/omg me/, &b)
|
193
|
+
bot.event data
|
194
|
+
}.to yield_control
|
195
|
+
end
|
196
|
+
|
197
|
+
it "does not bind when regex matches but there is no mention" do
|
198
|
+
data["text"] = "hey <@other_bot>, omg me"
|
199
|
+
|
200
|
+
expect { |b|
|
201
|
+
bot.respond(/omg me/, &b)
|
202
|
+
bot.event data
|
203
|
+
}.to_not yield_control
|
204
|
+
end
|
205
|
+
|
206
|
+
it "ignores mentions from itself" do
|
207
|
+
data["user"] = "666"
|
208
|
+
|
209
|
+
expect { |b|
|
210
|
+
bot.respond(/omg me/, &b)
|
211
|
+
bot.event data
|
212
|
+
}.to_not yield_control
|
213
|
+
end
|
214
|
+
|
215
|
+
it "evals the block in the context of the bot" do
|
216
|
+
dsl = nil
|
217
|
+
bot.respond(/omg me/) { dsl = self }
|
218
|
+
bot.event data
|
219
|
+
|
220
|
+
expect(dsl.bot).to eq bot
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "removing binds for messages and responses", :session do
|
225
|
+
before do
|
226
|
+
start_session
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#no_message" do
|
230
|
+
let(:data) { {"type" => "message", "text" => "bbq omg lol"} }
|
231
|
+
|
232
|
+
it "removes a message based on the regex AND the block" do
|
233
|
+
permanent_executed = false
|
234
|
+
permanent_block = ->() { permanent_executed = true }
|
235
|
+
bot.hear(/omg/i, &permanent_block)
|
236
|
+
|
237
|
+
ephemeral_executed = false
|
238
|
+
ephemeral_block = ->() { ephemeral_executed = true }
|
239
|
+
bot.hear(/omg/i, &ephemeral_block)
|
240
|
+
bot.no_match(/omg/i, &ephemeral_block)
|
241
|
+
|
242
|
+
dsl.bot.event data
|
243
|
+
|
244
|
+
expect(permanent_executed).to eq true
|
245
|
+
expect(ephemeral_executed).to eq false
|
246
|
+
end
|
247
|
+
|
248
|
+
it "removes all binds for a specific regex" do
|
249
|
+
first = second = false
|
250
|
+
bot.hear(/omg/i) { first = true }
|
251
|
+
bot.hear(/omg/i) { second = true }
|
252
|
+
bot.no_match(/omg/i)
|
253
|
+
|
254
|
+
dsl.bot.event data
|
255
|
+
|
256
|
+
expect(first).to eq false
|
257
|
+
expect(second).to eq false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "#no_respond" do
|
262
|
+
let(:data) {{
|
263
|
+
"type" => "message",
|
264
|
+
"text" => "hey <@#{bot.name}>, omg me"
|
265
|
+
}}
|
266
|
+
|
267
|
+
it "removes a message based on the regex AND the block" do
|
268
|
+
permanent_executed = false
|
269
|
+
permanent_block = ->() { permanent_executed = true }
|
270
|
+
bot.respond(/omg/i, &permanent_block)
|
271
|
+
|
272
|
+
ephemeral_executed = false
|
273
|
+
ephemeral_block = ->() { ephemeral_executed = true }
|
274
|
+
bot.respond(/omg/i, &ephemeral_block)
|
275
|
+
bot.no_respond(/omg/i, &ephemeral_block)
|
276
|
+
|
277
|
+
dsl.bot.event data
|
278
|
+
|
279
|
+
expect(permanent_executed).to eq true
|
280
|
+
expect(ephemeral_executed).to eq false
|
281
|
+
end
|
282
|
+
|
283
|
+
it "removes all binds for a specific regex" do
|
284
|
+
first = second = false
|
285
|
+
bot.respond(/omg/i) { first = true }
|
286
|
+
bot.respond(/omg/i) { second = true }
|
287
|
+
bot.no_respond(/omg/i)
|
288
|
+
|
289
|
+
dsl.bot.event data
|
290
|
+
|
291
|
+
expect(first).to eq false
|
292
|
+
expect(second).to eq false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "#say", :users do
|
298
|
+
let(:data) {{
|
299
|
+
"type" => "message",
|
300
|
+
"text" => "hey <@jabber>, omg me",
|
301
|
+
"channel" => "omg"
|
302
|
+
}}
|
303
|
+
|
304
|
+
it "sends a string using the slack api to general (default) channel" do
|
305
|
+
expect(Slack.chat).to receive(:post_message).
|
306
|
+
with "something", channel: "general"
|
307
|
+
bot.say "something"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "send a message to the channel specified by a current binded message" do
|
311
|
+
expect(Slack.chat).to receive(:post_message).
|
312
|
+
with "omg pugs!", channel: "omg"
|
313
|
+
|
314
|
+
bot.respond(/omg me/) { say "omg pugs!" }
|
315
|
+
bot.event data
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe "#im", :session, :users do
|
320
|
+
before do
|
321
|
+
start_session
|
322
|
+
end
|
323
|
+
|
324
|
+
let(:user_id) { "U4321" }
|
325
|
+
let(:data) {{
|
326
|
+
"type" => "message",
|
327
|
+
"text" => "<@#{bot.name}>: omg",
|
328
|
+
"user" => "U4321"
|
329
|
+
}}
|
330
|
+
|
331
|
+
it "sends a message in the back channel (particular, im...)" do
|
332
|
+
bot.respond(/omg/) { im "lol" }
|
333
|
+
expect(Boty::Slack.chat).to receive(:post_im).with "U4321", "lol"
|
334
|
+
|
335
|
+
dsl.bot.event data
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe "#brain", :users do
|
340
|
+
it "exposes a hash that lives while bot is alive. =)" do
|
341
|
+
bot.brain[:something] = "going on"
|
342
|
+
expect(bot.brain[:something]).to eq "going on"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
context "loading script/**/*.rb files", :fakefs, :users do
|
347
|
+
let(:data) {{
|
348
|
+
"type" => "message",
|
349
|
+
"text" => "hey <@jabber>, pug me"
|
350
|
+
}}
|
351
|
+
|
352
|
+
before do
|
353
|
+
FileUtils.mkdir "script" unless Dir.exists? "script"
|
354
|
+
File.open "script/omg.rb", "w" do |f|
|
355
|
+
f.write <<-RUBY
|
356
|
+
respond(/pug me/i) do
|
357
|
+
say "omg"
|
358
|
+
end
|
359
|
+
RUBY
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
it "uses the logic in the script located at script/omg.rb" do
|
364
|
+
expect(bot).to receive(:say).with "omg"
|
365
|
+
bot.event data
|
366
|
+
end
|
367
|
+
|
368
|
+
context "describing commands", :fakefs, :users do
|
369
|
+
before do
|
370
|
+
File.open "script/omg.rb", "w" do |f|
|
371
|
+
f.write <<-RUBY
|
372
|
+
desc "pug me", "show some pretty random pug"
|
373
|
+
respond(/pug me/i) do
|
374
|
+
say "omg"
|
375
|
+
end
|
376
|
+
|
377
|
+
desc "just a description"
|
378
|
+
respond(/just desc/i) do
|
379
|
+
say "just description, raw command"
|
380
|
+
end
|
381
|
+
|
382
|
+
respond(/without desc/i) do
|
383
|
+
say "no desc, raw command"
|
384
|
+
end
|
385
|
+
|
386
|
+
respond(/without desc sensitive/) do
|
387
|
+
say "no desc, raw command"
|
388
|
+
end
|
389
|
+
RUBY
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
it "stores the description within command usage" do
|
394
|
+
expect(bot.know_how).to include({
|
395
|
+
"pug me" => "show some pretty random pug"
|
396
|
+
})
|
397
|
+
end
|
398
|
+
|
399
|
+
it "stores the description for a regex when no command usage is given" do
|
400
|
+
expect(bot.know_how).to include({
|
401
|
+
"/just desc/i" => "just a description"
|
402
|
+
})
|
403
|
+
end
|
404
|
+
|
405
|
+
it "presents the commands without description as regexes" do
|
406
|
+
expect(bot.know_how).to include({
|
407
|
+
"/without desc/i" => nil
|
408
|
+
})
|
409
|
+
end
|
410
|
+
|
411
|
+
it "knows when a regex is case sensitive" do
|
412
|
+
expect(bot.know_how).to include({
|
413
|
+
"/without desc sensitive/" => nil
|
414
|
+
})
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|