boty 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/Gemfile.lock +3 -1
- data/README.md +25 -1
- data/Rakefile +5 -0
- data/boty.gemspec +1 -0
- data/lib/boty.rb +18 -15
- data/lib/boty/action.rb +88 -10
- data/lib/boty/action_description.rb +22 -0
- data/lib/boty/bot.rb +13 -55
- data/lib/boty/eventable.rb +1 -0
- data/lib/boty/match_handler.rb +79 -0
- data/lib/boty/version.rb +1 -1
- data/spec/boty/action_spec.rb +23 -0
- data/spec/boty/bot_spec.rb +3 -100
- data/spec/boty/eventable_spec.rb +57 -0
- data/spec/boty/match_handler_spec.rb +119 -0
- data/spec/script/i18n_spec.rb +2 -4
- data/spec/script/knows_spec.rb +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9959fb9fd1218a64dc671442aef786311c03f10
|
4
|
+
data.tar.gz: bad2921130ae92b79ef775529861fb4c6a204cdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0052d938808382105c9f4b2e242ca357a01cb7e83c5eead10f05a3225b6a021cf08c24d0de7dfe7d69b3945015f1595ff20291dbc33d41b1e93b758965023fb5
|
7
|
+
data.tar.gz: 2b31a41cb5443d4693728b2a8689b321371424c7a90b84c058a721a545c2cdf17a1feb952e2404b0f302fb403bc13936e3b8f1a957e3a6d4bfc715d7318782d3
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boty (0.
|
4
|
+
boty (0.2.0)
|
5
5
|
eventmachine
|
6
6
|
faraday
|
7
7
|
faye-websocket
|
@@ -57,6 +57,7 @@ GEM
|
|
57
57
|
websocket-driver (0.6.3)
|
58
58
|
websocket-extensions (>= 0.1.0)
|
59
59
|
websocket-extensions (0.1.2)
|
60
|
+
yard (0.8.7.6)
|
60
61
|
|
61
62
|
PLATFORMS
|
62
63
|
ruby
|
@@ -70,6 +71,7 @@ DEPENDENCIES
|
|
70
71
|
rake (~> 10.0)
|
71
72
|
rspec
|
72
73
|
rubocop
|
74
|
+
yard
|
73
75
|
|
74
76
|
BUNDLED WITH
|
75
77
|
1.10.6
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Boty
|
2
2
|
[<img src="https://travis-ci.org/ricardovaleriano/boty.svg?branch=master" />](https://travis-ci.org/ricardovaleriano/boty)
|
3
|
+
[<img src="http://img.shields.io/badge/yard-docs-blue.svg" />](http://www.rubydoc.info/github/ricardovaleriano/boty)
|
3
4
|
|
4
5
|
`Boty` is a utilitary to create bots (at this time, specificaly Slack bots).
|
5
6
|
|
@@ -291,7 +292,7 @@ Before we start to study the "script" way of configuring `command` and
|
|
291
292
|
`listener` binds, let's see how we can use ruby regexps to capture message
|
292
293
|
parameters.
|
293
294
|
|
294
|
-
|
295
|
+
#### Regexes - a pattern that allow capture parameters within a message<a name="regexes" />
|
295
296
|
|
296
297
|
What is a bot if it can't annoy people when we want it to? So let's teach our
|
297
298
|
bot to send private messages to people in the Slack room.
|
@@ -316,6 +317,29 @@ _command_.
|
|
316
317
|
|
317
318
|
And this is it! :tada:
|
318
319
|
|
320
|
+
##### Commands, Listenners and aliases
|
321
|
+
|
322
|
+
Both *commands* and *listeners* can be aliased, which means that the same
|
323
|
+
`Action` can be triggered by different regexes:
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
command /hi/i, /hello/i do
|
327
|
+
say "Ohay #{user.name}! Hello there."
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
valeriano 12:04 PM
|
332
|
+
@jeeba: hi
|
333
|
+
|
334
|
+
jeeba BOT 12:04 PM
|
335
|
+
Ohay valeriano! Hello there.
|
336
|
+
|
337
|
+
valeriano 12:05 PM
|
338
|
+
@jeeba: hello
|
339
|
+
|
340
|
+
jeeba BOT 12:05 PM
|
341
|
+
Ohay valeriano! Hello there.
|
342
|
+
|
319
343
|
### Adding custom scripts<a name="custom_scripts" />
|
320
344
|
|
321
345
|
Now you already know what are [listeners](#listeners) and
|
data/Rakefile
CHANGED
data/boty.gemspec
CHANGED
data/lib/boty.rb
CHANGED
@@ -1,28 +1,30 @@
|
|
1
|
-
begin
|
2
|
-
require "pp"
|
3
|
-
|
4
|
-
require "dotenv"
|
5
|
-
if defined? Dotenv
|
6
|
-
Dotenv.load
|
7
|
-
Dotenv.overload ".env.local"
|
8
|
-
end
|
9
|
-
rescue LoadError
|
10
|
-
end
|
11
|
-
|
12
1
|
require "faye/websocket"
|
13
2
|
require "i18n"
|
14
3
|
require "faraday"
|
4
|
+
require "dotenv"
|
5
|
+
require "pp"
|
15
6
|
|
16
7
|
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
17
8
|
|
9
|
+
Dotenv.load
|
10
|
+
Dotenv.overload ".env.local"
|
11
|
+
|
12
|
+
# Namespace for all Boty code.
|
18
13
|
module Boty
|
14
|
+
# Public: Reloads the available locale configuration files and set the current
|
15
|
+
# language as the one passed in the paramenter `lang`.
|
16
|
+
#
|
17
|
+
# lang - String or Symbol representing the language that should be set for the
|
18
|
+
# current execution.
|
19
|
+
#
|
20
|
+
# Examples:
|
21
|
+
#
|
22
|
+
# Boty.locale = :en
|
23
|
+
#
|
24
|
+
# Returns nothing.
|
19
25
|
def self.locale=(lang)
|
20
26
|
Locale.reload lang
|
21
27
|
end
|
22
|
-
|
23
|
-
def self.locale
|
24
|
-
I18n.locale ||= :en
|
25
|
-
end
|
26
28
|
end
|
27
29
|
|
28
30
|
require "boty/version"
|
@@ -35,5 +37,6 @@ require "boty/script_loader"
|
|
35
37
|
require "boty/dsl"
|
36
38
|
require "boty/locale"
|
37
39
|
require "boty/eventable"
|
40
|
+
require "boty/match_handler"
|
38
41
|
require "boty/bot"
|
39
42
|
require "boty/http"
|
data/lib/boty/action.rb
CHANGED
@@ -1,32 +1,73 @@
|
|
1
1
|
module Boty
|
2
|
-
# Public: Wrap the idea of something that should happen when
|
3
|
-
#
|
2
|
+
# Public: Wrap the idea of something that should happen when a regex matches.
|
3
|
+
# Actions are the underlying mecanism for store and execute the blocks passed
|
4
|
+
# to `Bot#match` and `Bot#respond` invocations.
|
4
5
|
#
|
6
|
+
# Maybe one of the more important things to remember here is that an Action is
|
7
|
+
# executed within the scope of a `Boty::DSL` instance already binded with the
|
8
|
+
# `Bot` of the current `Session` (via `#instance_exec`).
|
5
9
|
#
|
10
|
+
# Examples:
|
11
|
+
#
|
12
|
+
# # some_script.rb
|
13
|
+
# desc "omg! whoo!"
|
14
|
+
# hear(/omg/i) do
|
15
|
+
# im "whoo!"
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Will generate an `Action` like this:
|
19
|
+
# action = Action.new(bot, /omg/i, "omg! whoo!") { im "whoo!" }
|
20
|
+
#
|
21
|
+
# # Then this can be executed, by `#execute`.
|
22
|
+
# action.execute "omg! this string triggers the hear block."
|
23
|
+
#
|
24
|
+
# # If the regex matches the parameter passed to execute, it'll invoke the
|
25
|
+
# # block passed to `#hear`.
|
6
26
|
class Action
|
7
27
|
include Boty::Logger
|
8
28
|
attr_reader :regex, :desc, :action
|
9
29
|
|
30
|
+
# Public: Initialize an `Action` associated with a specific `Bot`.
|
31
|
+
#
|
32
|
+
# bot - A Bot that will react to the matches on the regex.
|
33
|
+
# regex - A Regexp to match against messages received in the bot session.
|
34
|
+
# action - A Proc that will be executed everytime the regex matches.
|
10
35
|
def initialize(bot, regex, description, &action)
|
11
|
-
if description
|
12
|
-
description.regex = regex
|
13
|
-
else
|
14
|
-
description = ActionDescription.new(nil, regex: regex)
|
15
|
-
end
|
16
|
-
|
17
36
|
@dsl = DSL.new bot
|
18
37
|
@regex = regex
|
19
|
-
@desc = description
|
20
38
|
@action = action
|
39
|
+
|
40
|
+
self.desc = description
|
21
41
|
end
|
22
42
|
|
43
|
+
# Public: Check if a given Regexp and an optional block were the same used
|
44
|
+
# to build this Action.
|
45
|
+
#
|
46
|
+
# Examples:
|
47
|
+
#
|
48
|
+
# send_im = -> { im "whoo!" }
|
49
|
+
# action = Action.new(bot, /omg/i, "omg! whoo!", &send_im)
|
50
|
+
# action.this? /omg/i, send_im
|
51
|
+
# # => return true
|
52
|
+
#
|
53
|
+
# regex - A Regexp to be compared against `regex`.
|
54
|
+
# block - An optional Proc to be checked against `action`.
|
55
|
+
#
|
56
|
+
# Returns True or False.
|
23
57
|
def this?(regex, block)
|
24
58
|
same_regex = regex == self.regex
|
25
59
|
block ? same_regex && block == action : same_regex
|
26
60
|
end
|
27
61
|
|
62
|
+
# Public: Creates a `Regexp::Match` based on the internal `regex` against a
|
63
|
+
# Slack::Message.
|
64
|
+
# Then pass this match to be used as argument to the action per se.
|
65
|
+
#
|
66
|
+
# message - A Slack::Message object to be matched against the `regex`.
|
67
|
+
#
|
68
|
+
# Returns nothing.
|
28
69
|
def execute(message)
|
29
|
-
action_call
|
70
|
+
action_call regex.match(message.text)
|
30
71
|
rescue => e
|
31
72
|
logger.error e.message
|
32
73
|
raise e
|
@@ -34,6 +75,43 @@ module Boty
|
|
34
75
|
|
35
76
|
private
|
36
77
|
|
78
|
+
# Internal: Store the ActionDescription for this action (create a new for it
|
79
|
+
# if a nil value is the parameter).
|
80
|
+
#
|
81
|
+
# To make it possible to create handlers (that will became actions) without
|
82
|
+
# a description, this method can work with a nil parameter. If a nil value
|
83
|
+
# is passed, it creates an `ActionDescription` with only a regex (without a
|
84
|
+
# description text).
|
85
|
+
#
|
86
|
+
# Returns an ActionDescription with the regex associated with this Action.
|
87
|
+
def desc=(description)
|
88
|
+
if description
|
89
|
+
description.regex = regex
|
90
|
+
else
|
91
|
+
description = ActionDescription.new(nil, regex: regex)
|
92
|
+
end
|
93
|
+
@desc = description
|
94
|
+
end
|
95
|
+
|
96
|
+
# Internal: Effectively executes the action.
|
97
|
+
# The regex match created on the `#execute` method will be exploded, so the
|
98
|
+
# matches can be passed as arguments to the block.
|
99
|
+
# Important to remember: the block passed to `Bot#match` or `Bot#respond`
|
100
|
+
# will be executed within the scope of a `Boty::DSL`.
|
101
|
+
#
|
102
|
+
# Examples:
|
103
|
+
#
|
104
|
+
# # some_script.rb
|
105
|
+
# hear(/(omg)\s(lol)/i) do |first_match, second_match|
|
106
|
+
# im "whoo!"
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# # will cause an invocation like this:
|
110
|
+
# @dsl.instance_exec("omg", "lol") do |first_match, second_match|
|
111
|
+
# im "whoo!"
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Returns nothing.
|
37
115
|
def action_call(match)
|
38
116
|
return unless match
|
39
117
|
matches = Array(match)
|
@@ -1,4 +1,26 @@
|
|
1
1
|
module Boty
|
2
|
+
# Public: contains de descriptive information that was associated with an
|
3
|
+
# action via the `Bot#desc` method.
|
4
|
+
#
|
5
|
+
# Besides that also keep tracks of the regex for the associated Action, it is
|
6
|
+
# used to describe the handler in case no command name and/or description was
|
7
|
+
# passed via `Bot#desc`.
|
8
|
+
#
|
9
|
+
# The `ActionDescription` object will be stored inside the `Action#desc`. A
|
10
|
+
# good usage example can be found in the `scripts/knows.rb` script.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# # scripts/omg.rb
|
15
|
+
# desc "X omg!", "Make the bot scream X omgs."
|
16
|
+
# hear(/(\d+ )?omg!/i) do |how_many|
|
17
|
+
# how_many.times do say "LOL!" end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # => generates an action with a description like this:
|
21
|
+
# ActionDescription.new "X omg!",
|
22
|
+
# description: "Make the bot scream X omgs."
|
23
|
+
# regex: /(\d+ )?omg!/i
|
2
24
|
class ActionDescription
|
3
25
|
attr_reader :command, :description
|
4
26
|
attr_writer :regex
|
data/lib/boty/bot.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Boty
|
2
2
|
class Bot
|
3
3
|
include Boty::Eventable
|
4
|
+
include Boty::MatchHandler
|
4
5
|
include Boty::Logger
|
5
6
|
include Slack
|
6
7
|
|
7
|
-
attr_reader :id, :name, :
|
8
|
+
attr_reader :id, :name, :brain
|
8
9
|
|
9
10
|
def initialize(bot_info)
|
10
11
|
Locale.reload
|
@@ -12,42 +13,14 @@ module Boty
|
|
12
13
|
@id = bot_info["id"]
|
13
14
|
@name = bot_info["name"]
|
14
15
|
|
15
|
-
@listeners ||= []
|
16
|
-
@commands ||= []
|
17
16
|
@brain ||= {}
|
18
17
|
|
19
18
|
on :message, &method(:message_handler)
|
20
19
|
ScriptLoader.new(self).load
|
21
20
|
end
|
22
21
|
|
23
|
-
def match(regex, &block)
|
24
|
-
@listeners << create_action(regex, &block)
|
25
|
-
end
|
26
|
-
|
27
|
-
def respond(regex, &block)
|
28
|
-
@commands << create_action(regex, &block)
|
29
|
-
end
|
30
|
-
|
31
|
-
def no_match(regex, &block)
|
32
|
-
remove_action @listeners, regex, block
|
33
|
-
end
|
34
|
-
|
35
|
-
def no_respond(regex, &block)
|
36
|
-
remove_action @commands, regex, block
|
37
|
-
end
|
38
|
-
|
39
|
-
def no(command)
|
40
|
-
remove_action @listeners, command: command
|
41
|
-
remove_action @commands, command: command
|
42
|
-
end
|
43
|
-
|
44
|
-
def desc(command, description = nil)
|
45
|
-
@current_desc = ActionDescription.new command,
|
46
|
-
description: description
|
47
|
-
end
|
48
|
-
|
49
22
|
def say(message, api_parameters = {})
|
50
|
-
channel = (
|
23
|
+
channel = (trigger_message && trigger_message.channel) || "#general"
|
51
24
|
options = { channel: channel }.merge api_parameters
|
52
25
|
post_response = Slack.chat.post_message message, options
|
53
26
|
logger.debug { "Post response: #{post_response}." }
|
@@ -63,41 +36,27 @@ module Boty
|
|
63
36
|
end
|
64
37
|
end
|
65
38
|
|
66
|
-
# TODO: return an Action object instead of a hash
|
67
39
|
def know_how
|
68
|
-
actions = Array(
|
40
|
+
actions = Array(commands) + Array(listeners)
|
69
41
|
actions.sort_by { |action| action.desc.command || "_" }
|
70
42
|
end
|
71
43
|
|
72
44
|
private
|
73
45
|
|
74
|
-
def remove_action(collection, regex = nil, block = nil, command: nil)
|
75
|
-
collection.delete_if do |action|
|
76
|
-
action.desc.command == command || action.this?(regex, block)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
46
|
def user_by_name(destiny, to)
|
81
47
|
to ||= destiny
|
82
48
|
if to
|
83
49
|
Slack.users.by_name to
|
84
50
|
else
|
85
|
-
|
51
|
+
trigger_message.user
|
86
52
|
end
|
87
53
|
end
|
88
54
|
|
89
|
-
def create_action(regex, &block)
|
90
|
-
regex = Regexp.new(regex) if regex.is_a? String
|
91
|
-
Boty::Action.new(self, regex, @current_desc, &block).tap {
|
92
|
-
@current_desc = nil
|
93
|
-
}
|
94
|
-
end
|
95
|
-
|
96
55
|
def message_from_bot_itself?(data)
|
97
56
|
data["user"] == id || data["user"] == name
|
98
57
|
end
|
99
58
|
|
100
|
-
def
|
59
|
+
def command?(data)
|
101
60
|
return false if message_from_bot_itself? data
|
102
61
|
|
103
62
|
if /(<@#{id}|#{name}>)/ =~ data["text"]
|
@@ -111,17 +70,16 @@ module Boty
|
|
111
70
|
unless data["text"]
|
112
71
|
return logger.debug do "Non text message, just ignoring." end
|
113
72
|
end
|
114
|
-
|
115
|
-
execute_all Message.new(data), actions
|
73
|
+
execute data
|
116
74
|
end
|
117
75
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
121
|
-
|
76
|
+
def execute(data)
|
77
|
+
message = Message.new data
|
78
|
+
if command?(data)
|
79
|
+
execute_commands message
|
80
|
+
else
|
81
|
+
execute_matches message
|
122
82
|
end
|
123
|
-
ensure
|
124
|
-
@trigger_message = nil
|
125
83
|
end
|
126
84
|
end
|
127
85
|
end
|
data/lib/boty/eventable.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Boty
|
2
|
+
module MatchHandler
|
3
|
+
include Boty::Logger
|
4
|
+
|
5
|
+
attr_reader :trigger_message
|
6
|
+
|
7
|
+
def listeners
|
8
|
+
@listeners ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def commands
|
12
|
+
@commands ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def desc(command, description = nil)
|
16
|
+
@current_desc = ActionDescription.new command,
|
17
|
+
description: description
|
18
|
+
end
|
19
|
+
|
20
|
+
def match(*regexes, &block)
|
21
|
+
create_action_on_collection listeners, regexes, &block
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond(*regexes, &block)
|
25
|
+
create_action_on_collection commands, regexes, &block
|
26
|
+
end
|
27
|
+
|
28
|
+
def no_match(regex, &block)
|
29
|
+
remove_action listeners, regex, block
|
30
|
+
end
|
31
|
+
|
32
|
+
def no_respond(regex, &block)
|
33
|
+
remove_action commands, regex, block
|
34
|
+
end
|
35
|
+
|
36
|
+
def no(command)
|
37
|
+
remove_action listeners, command: command
|
38
|
+
remove_action commands, command: command
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute_matches(message)
|
42
|
+
execute_actions message, listeners
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute_commands(message)
|
46
|
+
execute_actions message, commands
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def create_action_on_collection(collection, regexes, &block)
|
52
|
+
regexes.each do |regex|
|
53
|
+
collection << create_action(regex, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute_actions(message, actions)
|
58
|
+
@trigger_message = message
|
59
|
+
Array(actions).each do |action|
|
60
|
+
action.execute @trigger_message
|
61
|
+
end
|
62
|
+
ensure
|
63
|
+
@trigger_message = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_action(collection, regex = nil, block = nil, command: nil)
|
67
|
+
collection.delete_if do |action|
|
68
|
+
action.desc.command == command || action.this?(regex, block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_action(regex, &block)
|
73
|
+
regex = Regexp.new(regex) if regex.is_a? String
|
74
|
+
Boty::Action.new(self, regex, @current_desc, &block).tap {
|
75
|
+
@current_desc = nil
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/boty/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Boty
|
2
|
+
RSpec.describe Action, :users do
|
3
|
+
let(:bot) { Bot.new("id" => "U1234", "name" => "boty") }
|
4
|
+
let(:event_data) {
|
5
|
+
{
|
6
|
+
"type" => "message",
|
7
|
+
"text" => "omg lol bbq"
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
subject(:action) {
|
12
|
+
Action.new(bot, /omg/, nil) do nil end
|
13
|
+
}
|
14
|
+
|
15
|
+
it "evals the block in the context of the bot" do
|
16
|
+
dsl = nil
|
17
|
+
bot.match(/omg/) do dsl = self end
|
18
|
+
bot.event event_data
|
19
|
+
|
20
|
+
expect(dsl.bot).to eq bot
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/boty/bot_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Boty
|
2
2
|
RSpec.describe Bot do
|
3
|
-
include Boty
|
4
3
|
subject(:bot) { described_class.new bot_info }
|
5
4
|
|
6
5
|
let(:bot_info) do
|
@@ -10,105 +9,9 @@ module Boty
|
|
10
9
|
}
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
"type" => "omg",
|
17
|
-
"text" => "lol"
|
18
|
-
}
|
19
|
-
|
20
|
-
expect { |b|
|
21
|
-
bot.on :omg, &b
|
22
|
-
bot.event data
|
23
|
-
}.to yield_with_args data
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "#off" do
|
28
|
-
let(:data) do
|
29
|
-
{
|
30
|
-
"type" => "bbq",
|
31
|
-
"text" => "omg. wtf."
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
it "unbounds an event by type AND block, just if the block is the same" do
|
36
|
-
@permanent_executed = false
|
37
|
-
permanent_block = ->(_) do @permanent_executed = true end
|
38
|
-
bot.on :bbq, &permanent_block
|
39
|
-
|
40
|
-
@ephemeral_executed = false
|
41
|
-
ephemeral_block = ->(_) do @ephemeral_executed = true end
|
42
|
-
bot.on :bbq, &ephemeral_block
|
43
|
-
bot.off :bbq, &ephemeral_block
|
44
|
-
|
45
|
-
bot.event data
|
46
|
-
|
47
|
-
expect(@permanent_executed).to eq true
|
48
|
-
expect(@ephemeral_executed).to eq false
|
49
|
-
end
|
50
|
-
|
51
|
-
it "unbounds all events by type" do
|
52
|
-
@first_block = @second_block = @third_block = false
|
53
|
-
bot.on(:bbq) do |_| @first_block = true end
|
54
|
-
bot.on(:bbq) do |_| @second_block = true end
|
55
|
-
bot.on(:bbq) do |_| @third_block = true end
|
56
|
-
bot.off(:bbq)
|
57
|
-
|
58
|
-
bot.event data
|
59
|
-
|
60
|
-
expect(@first_block).to eq false
|
61
|
-
expect(@second_block).to eq false
|
62
|
-
expect(@third_block).to eq false
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "#match", :users do
|
67
|
-
let(:data) do
|
68
|
-
{
|
69
|
-
"type" => "message",
|
70
|
-
"text" => "bbq omg lol"
|
71
|
-
}
|
72
|
-
end
|
73
|
-
|
74
|
-
it "binds a regex to events of `type => message`" do
|
75
|
-
message = nil
|
76
|
-
bot.match(/omg/i) do
|
77
|
-
message = self.message
|
78
|
-
end
|
79
|
-
bot.event data
|
80
|
-
|
81
|
-
expect(message).to be_a Boty::Slack::Message
|
82
|
-
expect(message.text).to eq "bbq omg lol"
|
83
|
-
expect(message.match[0]).to eq "omg"
|
84
|
-
end
|
85
|
-
|
86
|
-
it "binds various actions to events of type message" do
|
87
|
-
iterations = []
|
88
|
-
bot.match(/omg/i) do iterations << "first" end
|
89
|
-
bot.match(/omg/i) do iterations << "second" end
|
90
|
-
bot.event data
|
91
|
-
|
92
|
-
expect(iterations).to eq %w(first second)
|
93
|
-
end
|
94
|
-
|
95
|
-
it "passes the matched strings as parameters to block action" do
|
96
|
-
rspec = self
|
97
|
-
bot.match(/(bbq) omg (lol)/i) do |bbq, lol|
|
98
|
-
rspec.expect(bbq).to rspec.eq "bbq"
|
99
|
-
rspec.expect(lol).to rspec.eq "lol"
|
100
|
-
end
|
101
|
-
|
102
|
-
bot.event data
|
103
|
-
end
|
104
|
-
|
105
|
-
it "evals the block in the context of the bot" do
|
106
|
-
dsl = nil
|
107
|
-
bot.match(/omg/) do dsl = self end
|
108
|
-
bot.event data
|
109
|
-
|
110
|
-
expect(dsl.bot).to eq bot
|
111
|
-
end
|
12
|
+
context "handling message events" do
|
13
|
+
# on message, check if the event is binded, or something...
|
14
|
+
# call execute_actions with a message object
|
112
15
|
end
|
113
16
|
|
114
17
|
describe "#respond", :users do
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Boty
|
2
|
+
RSpec.describe Boty::Eventable do
|
3
|
+
class Client
|
4
|
+
include Eventable
|
5
|
+
end
|
6
|
+
|
7
|
+
subject(:eventable) { Client.new }
|
8
|
+
|
9
|
+
describe "#on" do
|
10
|
+
it "binds an event `'type'` to a block" do
|
11
|
+
expect { |b|
|
12
|
+
eventable.on :omg, &b
|
13
|
+
eventable.event "type" => "omg", "other_field" => "lol"
|
14
|
+
}.to yield_with_args "type" => "omg", "other_field" => "lol"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#off" do
|
19
|
+
let(:data) do
|
20
|
+
{
|
21
|
+
"type" => "bbq",
|
22
|
+
"text" => "omg. wtf."
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "unbounds an event by type AND block, just if the block is the same" do
|
27
|
+
@permanent_executed = false
|
28
|
+
permanent_block = ->(_) do @permanent_executed = true end
|
29
|
+
eventable.on :bbq, &permanent_block
|
30
|
+
|
31
|
+
@ephemeral_executed = false
|
32
|
+
ephemeral_block = ->(_) do @ephemeral_executed = true end
|
33
|
+
eventable.on :bbq, &ephemeral_block
|
34
|
+
eventable.off :bbq, &ephemeral_block
|
35
|
+
|
36
|
+
eventable.event data
|
37
|
+
|
38
|
+
expect(@permanent_executed).to eq true
|
39
|
+
expect(@ephemeral_executed).to eq false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "unbounds all events by type" do
|
43
|
+
@first_block = @second_block = @third_block = false
|
44
|
+
eventable.on(:bbq) do |_| @first_block = true end
|
45
|
+
eventable.on(:bbq) do |_| @second_block = true end
|
46
|
+
eventable.on(:bbq) do |_| @third_block = true end
|
47
|
+
eventable.off(:bbq)
|
48
|
+
|
49
|
+
eventable.event data
|
50
|
+
|
51
|
+
expect(@first_block).to eq false
|
52
|
+
expect(@second_block).to eq false
|
53
|
+
expect(@third_block).to eq false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Boty
|
2
|
+
RSpec.describe MatchHandler do
|
3
|
+
class Handler
|
4
|
+
include MatchHandler
|
5
|
+
end
|
6
|
+
|
7
|
+
subject(:handler) { Handler.new }
|
8
|
+
|
9
|
+
describe "#match", :users do
|
10
|
+
it "binds a regex to be executed by an Action object" do
|
11
|
+
action_block = ->(*) { "omg" }
|
12
|
+
expect(Boty::Action).to receive(:new)
|
13
|
+
.with(handler, /omg/i, nil, &action_block)
|
14
|
+
|
15
|
+
handler.match(/omg/i, &action_block)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows to bind more than one regex with the same action" do
|
19
|
+
counter = 0
|
20
|
+
action_block = ->(*) { counter += 1 }
|
21
|
+
handler.match(/omg/i, /lol/i, /bbq/i, &action_block)
|
22
|
+
|
23
|
+
handler.execute_matches OpenStruct.new(text: "omg")
|
24
|
+
handler.execute_matches OpenStruct.new(text: "lol")
|
25
|
+
handler.execute_matches OpenStruct.new(text: "bbq")
|
26
|
+
|
27
|
+
expect(counter).to eq 3
|
28
|
+
end
|
29
|
+
|
30
|
+
it "passes the matched strings as parameters to the block" do
|
31
|
+
first_match = nil
|
32
|
+
second_match = nil
|
33
|
+
handler.match(/(bbq) omg (lol)/i) do |bbq, lol|
|
34
|
+
first_match = bbq
|
35
|
+
second_match = lol
|
36
|
+
end
|
37
|
+
handler.execute_matches OpenStruct.new(text: "bbq omg lol")
|
38
|
+
|
39
|
+
expect(first_match).to eq "bbq"
|
40
|
+
expect(second_match).to eq "lol"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#respond" do
|
45
|
+
it "allows to bind more than one regex with the same action" do
|
46
|
+
counter = 0
|
47
|
+
action_block = ->(*) { counter += 1 }
|
48
|
+
handler.respond(/omg/i, /lol/i, /bbq/i, &action_block)
|
49
|
+
|
50
|
+
handler.execute_commands OpenStruct.new(text: "omg")
|
51
|
+
handler.execute_commands OpenStruct.new(text: "lol")
|
52
|
+
handler.execute_commands OpenStruct.new(text: "bbq")
|
53
|
+
|
54
|
+
expect(counter).to eq 3
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#execute_matches" do
|
59
|
+
it "executes handler binded to the regex" do
|
60
|
+
executed = false
|
61
|
+
action_block = -> { executed = true }
|
62
|
+
handler.match(/omg/i, &action_block)
|
63
|
+
handler.execute_matches OpenStruct.new(text: "omg")
|
64
|
+
|
65
|
+
expect(executed).to eq true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "repasses the text message holder as a parameter" do
|
69
|
+
message = nil
|
70
|
+
holder = OpenStruct.new(text: "omg")
|
71
|
+
handler.match(/omg/i) do message = self.message end
|
72
|
+
handler.execute_matches holder
|
73
|
+
|
74
|
+
expect(message).to be holder
|
75
|
+
end
|
76
|
+
|
77
|
+
it "executes all handlers when more than one are found" do
|
78
|
+
first_executed = false
|
79
|
+
first_block = -> { first_executed = true }
|
80
|
+
handler.match(/omg/i, &first_block)
|
81
|
+
|
82
|
+
second_executed = false
|
83
|
+
second_block = -> { second_executed = true }
|
84
|
+
handler.match(/omg/i, &second_block)
|
85
|
+
|
86
|
+
handler.execute_matches OpenStruct.new(text: "omg")
|
87
|
+
|
88
|
+
expect(first_executed).to eq true
|
89
|
+
expect(second_executed).to eq true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#execute_commands" do
|
94
|
+
it "executes the command binded to the regex" do
|
95
|
+
executed = false
|
96
|
+
action_block = -> { executed = true }
|
97
|
+
handler.respond(/lol/i, &action_block)
|
98
|
+
handler.execute_commands OpenStruct.new(text: "lol")
|
99
|
+
|
100
|
+
expect(executed).to eq true
|
101
|
+
end
|
102
|
+
|
103
|
+
it "executes all commands when more than one are found" do
|
104
|
+
first_executed = false
|
105
|
+
first_block = -> { first_executed = true }
|
106
|
+
handler.respond(/lol/i, &first_block)
|
107
|
+
|
108
|
+
second_executed = false
|
109
|
+
second_block = -> { second_executed = true }
|
110
|
+
handler.respond(/lol/i, &second_block)
|
111
|
+
|
112
|
+
handler.execute_commands OpenStruct.new(text: "lol")
|
113
|
+
|
114
|
+
expect(first_executed).to eq true
|
115
|
+
expect(second_executed).to eq true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/spec/script/i18n_spec.rb
CHANGED
@@ -10,9 +10,8 @@ RSpec.describe "I18n on default scripts", :session do
|
|
10
10
|
know_how = self.know_how
|
11
11
|
end
|
12
12
|
|
13
|
-
actions = know_how.
|
13
|
+
actions = know_how.each_with_object({}) { |action, hsh|
|
14
14
|
hsh[action.desc.command] = action.desc.description
|
15
|
-
hsh
|
16
15
|
}
|
17
16
|
|
18
17
|
expect(actions["knows"]).to eq "List all the commands known by this bot."
|
@@ -26,9 +25,8 @@ RSpec.describe "I18n on default scripts", :session do
|
|
26
25
|
know_how = self.know_how
|
27
26
|
end
|
28
27
|
|
29
|
-
actions = know_how.
|
28
|
+
actions = know_how.each_with_object({}) { |action, hsh|
|
30
29
|
hsh[action.desc.command] = action.desc.description
|
31
|
-
hsh
|
32
30
|
}
|
33
31
|
|
34
32
|
expect(actions["knows"]).to eq "Lista os comandos conhecidos por esse bot."
|
data/spec/script/knows_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ricardo Valeriano
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: yard
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description: |-
|
168
182
|
Boty is intendted to be a framework for construction of
|
169
183
|
automated Slack Bots for your needs.
|
@@ -204,6 +218,7 @@ files:
|
|
204
218
|
- lib/boty/http.rb
|
205
219
|
- lib/boty/locale.rb
|
206
220
|
- lib/boty/logger.rb
|
221
|
+
- lib/boty/match_handler.rb
|
207
222
|
- lib/boty/rspec.rb
|
208
223
|
- lib/boty/script_loader.rb
|
209
224
|
- lib/boty/session.rb
|
@@ -224,11 +239,14 @@ files:
|
|
224
239
|
- script/knows.rb
|
225
240
|
- script/pug.rb
|
226
241
|
- script/remember.rb
|
242
|
+
- spec/boty/action_spec.rb
|
227
243
|
- spec/boty/bot_spec.rb
|
228
244
|
- spec/boty/cli_spec.rb
|
229
245
|
- spec/boty/dsl_spec.rb
|
246
|
+
- spec/boty/eventable_spec.rb
|
230
247
|
- spec/boty/http_spec.rb
|
231
248
|
- spec/boty/logger_spec.rb
|
249
|
+
- spec/boty/match_handler_spec.rb
|
232
250
|
- spec/boty/rspec_spec.rb
|
233
251
|
- spec/boty/script_loader_spec.rb
|
234
252
|
- spec/boty/session_spec.rb
|
@@ -293,3 +311,4 @@ signing_key:
|
|
293
311
|
specification_version: 4
|
294
312
|
summary: Boty is a pretty bot specially tailored for Slack.
|
295
313
|
test_files: []
|
314
|
+
has_rdoc:
|