boty 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +18 -1
- data/README.md +39 -3
- data/Rakefile +1 -1
- data/boty.gemspec +9 -5
- data/lib/boty.rb +2 -1
- data/lib/boty/action.rb +26 -35
- data/lib/boty/action_description.rb +24 -0
- data/lib/boty/bot.rb +36 -25
- data/lib/boty/dsl.rb +3 -3
- data/lib/boty/eventable.rb +4 -3
- data/lib/boty/http.rb +3 -4
- data/lib/boty/locale.rb +12 -13
- data/lib/boty/logger.rb +14 -3
- data/lib/boty/rspec.rb +15 -16
- data/lib/boty/session.rb +7 -8
- data/lib/boty/slack.rb +11 -3
- data/lib/boty/slack/channel.rb +2 -1
- data/lib/boty/slack/chat.rb +5 -1
- data/lib/boty/slack/rtm.rb +1 -1
- data/lib/boty/slack/url.rb +10 -5
- data/lib/boty/slack/user.rb +2 -1
- data/lib/boty/slack/users.rb +1 -1
- data/lib/boty/version.rb +1 -1
- data/script/knows.rb +10 -9
- data/script/pug.rb +5 -1
- data/spec/boty/bot_spec.rb +89 -72
- data/spec/boty/cli_spec.rb +5 -5
- data/spec/boty/dsl_spec.rb +12 -13
- data/spec/boty/rspec_spec.rb +2 -1
- data/spec/boty/script_loader_spec.rb +8 -6
- data/spec/boty/session_spec.rb +2 -2
- data/spec/boty/slack/chat_spec.rb +18 -18
- data/spec/boty/slack/im_spec.rb +4 -4
- data/spec/boty/slack/message_spec.rb +11 -9
- data/spec/boty/slack/rtm_spec.rb +3 -3
- data/spec/boty/slack/url_spec.rb +5 -5
- data/spec/boty/slack/users_spec.rb +23 -23
- data/spec/happy_path_spec.rb +19 -18
- data/spec/script/i18n_spec.rb +18 -10
- data/spec/script/knows_spec.rb +8 -7
- data/spec/script/pug_spec.rb +27 -10
- data/spec/script/remember_spec.rb +27 -21
- data/spec/spec_helper.rb +4 -3
- data/spec/support/em_support.rb +1 -3
- data/spec/support/faraday_support.rb +0 -1
- data/spec/support/faye_support.rb +9 -6
- data/spec/support/file_system_matchers.rb +4 -4
- data/spec/support/logger_support.rb +9 -12
- data/spec/support/session_support.rb +26 -15
- data/spec/support/slack_support.rb +9 -6
- data/spec/support/template_project_support.rb +24 -23
- data/spec/support/thor_support.rb +3 -3
- data/template/project/%bot_name%.rb +4 -1
- data/template/project/spec/spec_helper.rb +4 -2
- metadata +21 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7102960a8cdaa6f3a18a7015c1338f2c62a28b7
|
4
|
+
data.tar.gz: dfd326ea4c467506825ac473f47636682e366bab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bab25a32347e1c4e6cb9cc3b8f995fc4d971f094072ef6c5e54018e0d50b5fe37d8c86c9be5e4291a8e4c823cad802ffc2d8f86603a36141eed1ac4adfa7b0da
|
7
|
+
data.tar.gz: fb8d9bfa67bcbf7f624b69dc4f7866e7280ccb3be1a655c7117a124f26147ab7002bec1ec1f6736154ac0a57d33173a0f1a4a50e81d707ac703655586328c007
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
boty (0.1.
|
4
|
+
boty (0.1.1)
|
5
5
|
eventmachine
|
6
6
|
faraday
|
7
7
|
faye-websocket
|
@@ -11,6 +11,9 @@ PATH
|
|
11
11
|
GEM
|
12
12
|
remote: https://rubygems.org/
|
13
13
|
specs:
|
14
|
+
ast (2.1.0)
|
15
|
+
astrolabe (1.3.1)
|
16
|
+
parser (~> 2.2)
|
14
17
|
byebug (8.2.0)
|
15
18
|
diff-lcs (1.2.5)
|
16
19
|
dotenv (2.0.2)
|
@@ -23,6 +26,10 @@ GEM
|
|
23
26
|
websocket-driver (>= 0.5.1)
|
24
27
|
i18n (0.7.0)
|
25
28
|
multipart-post (2.0.0)
|
29
|
+
parser (2.2.3.0)
|
30
|
+
ast (>= 1.1, < 3.0)
|
31
|
+
powerpack (0.1.1)
|
32
|
+
rainbow (2.0.0)
|
26
33
|
rake (10.4.2)
|
27
34
|
rspec (3.4.0)
|
28
35
|
rspec-core (~> 3.4.0)
|
@@ -37,7 +44,16 @@ GEM
|
|
37
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
38
45
|
rspec-support (~> 3.4.0)
|
39
46
|
rspec-support (3.4.0)
|
47
|
+
rubocop (0.35.1)
|
48
|
+
astrolabe (~> 1.3)
|
49
|
+
parser (>= 2.2.3.0, < 3.0)
|
50
|
+
powerpack (~> 0.1)
|
51
|
+
rainbow (>= 1.99.1, < 3.0)
|
52
|
+
ruby-progressbar (~> 1.7)
|
53
|
+
tins (<= 1.6.0)
|
54
|
+
ruby-progressbar (1.7.5)
|
40
55
|
thor (0.19.1)
|
56
|
+
tins (1.6.0)
|
41
57
|
websocket-driver (0.6.3)
|
42
58
|
websocket-extensions (>= 0.1.0)
|
43
59
|
websocket-extensions (0.1.2)
|
@@ -53,6 +69,7 @@ DEPENDENCIES
|
|
53
69
|
fakefs
|
54
70
|
rake (~> 10.0)
|
55
71
|
rspec
|
72
|
+
rubocop
|
56
73
|
|
57
74
|
BUNDLED WITH
|
58
75
|
1.10.6
|
data/README.md
CHANGED
@@ -642,7 +642,7 @@ implementation is fairly simple. Let's use it as an example of how to write your
|
|
642
642
|
own adapter.
|
643
643
|
|
644
644
|
You can extend the ruby _Logger_ class and worry yourself on write the `#add`
|
645
|
-
|
645
|
+
override:
|
646
646
|
|
647
647
|
```ruby
|
648
648
|
class Multi < ::Logger
|
@@ -667,7 +667,7 @@ adapters passed as parameters for the constructor.
|
|
667
667
|
For more information on the `#add` parameters, [check the ruby doc](http://ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html#method-i-add).
|
668
668
|
|
669
669
|
_Multi_ also allows you to change the level for all the underlying adapters at
|
670
|
-
once, the `#level=`
|
670
|
+
once, the `#level=` overriten implementation is like this:
|
671
671
|
|
672
672
|
```ruby
|
673
673
|
def level=(level)
|
@@ -679,7 +679,34 @@ end
|
|
679
679
|
|
680
680
|
### I18n
|
681
681
|
|
682
|
-
|
682
|
+
The descriptions for the commands and listeners that ship with `Boty` can be
|
683
|
+
customized via the [I18n gem mechanics](https://github.com/svenfuchs/i18n).
|
684
|
+
`Boty` ships with translations for `:en` and `:pt-br`. If you want to override
|
685
|
+
those messages, you can take a look at the files on [`locale`](https://github.com/ricardovaleriano/boty/tree/master/locale).
|
686
|
+
|
687
|
+
I'll recomend that you just copy and paste the file that you want to customize
|
688
|
+
on the `locale` dir of your project, and them make the editions that you want.
|
689
|
+
|
690
|
+
Of course, you can add translations for any language that you want on your
|
691
|
+
`locale` dir. And even add any translation that you want for your own
|
692
|
+
command descriptions.
|
693
|
+
|
694
|
+
To instruct your bot about which language to use, set the `Boty.locale`
|
695
|
+
configuration and you're done.
|
696
|
+
|
697
|
+
The `bot` executable created on your project generated by `boty new` have the
|
698
|
+
following line before the session start:
|
699
|
+
|
700
|
+
```ruby
|
701
|
+
Boty.locale = ARGV.pop || :en
|
702
|
+
```
|
703
|
+
|
704
|
+
This means that you can start your bot with a command line argument to tell
|
705
|
+
which is the idiom that this session should use. This allows you to have the
|
706
|
+
same bot running in different sessions with different idioms. The following
|
707
|
+
usage is totally fine:
|
708
|
+
|
709
|
+
./bot pt-br
|
683
710
|
|
684
711
|
## Development
|
685
712
|
|
@@ -691,8 +718,17 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
691
718
|
|
692
719
|
### The local ./bin/bot
|
693
720
|
|
721
|
+
The project have a `./bin/bot` executable that should be the product of
|
722
|
+
compiling the `template/project/bot.tt` erb file.
|
723
|
+
This is an easy way to just run the bot using the same logic that a project
|
724
|
+
generated by `bot new` will use to run the bot.
|
725
|
+
It's highly recomendable that you test your changes using this approach before
|
726
|
+
submit a `PR` or generate a new release. =)
|
727
|
+
|
694
728
|
### Code guidelines
|
695
729
|
|
730
|
+
**todo: add some**
|
731
|
+
|
696
732
|
## Contributing<a name="contributing" />
|
697
733
|
|
698
734
|
Bug reports and pull requests are very welcome on GitHub at
|
data/Rakefile
CHANGED
data/boty.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "boty/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "boty"
|
@@ -9,12 +9,15 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Ricardo Valeriano"]
|
10
10
|
spec.email = ["ricardo.valeriano@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary = %
|
13
|
-
spec.description = %
|
12
|
+
spec.summary = %(Boty is a pretty bot specially tailored for Slack.)
|
13
|
+
spec.description = %(Boty is intendted to be a framework for construction of
|
14
|
+
automated Slack Bots for your needs.)
|
14
15
|
spec.homepage = "http://github.com/ricardovaleriano/boty"
|
15
16
|
spec.license = "MIT"
|
16
17
|
|
17
|
-
spec.files = `git ls-files -z`.split("\x0").reject {
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject {|f|
|
19
|
+
f.match(%r{^(features)/})
|
20
|
+
}
|
18
21
|
spec.bindir = "exe"
|
19
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
23
|
spec.require_paths = ["lib"]
|
@@ -32,4 +35,5 @@ Gem::Specification.new do |spec|
|
|
32
35
|
spec.add_development_dependency "rspec"
|
33
36
|
spec.add_development_dependency "dotenv"
|
34
37
|
spec.add_development_dependency "fakefs"
|
38
|
+
spec.add_development_dependency "rubocop"
|
35
39
|
end
|
data/lib/boty.rb
CHANGED
@@ -13,7 +13,7 @@ require "faye/websocket"
|
|
13
13
|
require "i18n"
|
14
14
|
require "faraday"
|
15
15
|
|
16
|
-
|
16
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
17
17
|
|
18
18
|
module Boty
|
19
19
|
def self.locale=(lang)
|
@@ -29,6 +29,7 @@ require "boty/version"
|
|
29
29
|
require "boty/logger"
|
30
30
|
require "boty/slack"
|
31
31
|
require "boty/session"
|
32
|
+
require "boty/action_description"
|
32
33
|
require "boty/action"
|
33
34
|
require "boty/script_loader"
|
34
35
|
require "boty/dsl"
|
data/lib/boty/action.rb
CHANGED
@@ -1,53 +1,44 @@
|
|
1
1
|
module Boty
|
2
|
+
# Public: Wrap the idea of something that should happen when some regex is
|
3
|
+
# matched.
|
4
|
+
#
|
5
|
+
#
|
2
6
|
class Action
|
3
7
|
include Boty::Logger
|
4
8
|
attr_reader :regex, :desc, :action
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
if description.nil?
|
12
|
-
@description = command
|
13
|
-
else
|
14
|
-
@command, @description = command, description
|
15
|
-
end
|
16
|
-
|
17
|
-
@regex = regex
|
18
|
-
end
|
19
|
-
|
20
|
-
def command
|
21
|
-
return @command if @command
|
22
|
-
|
23
|
-
match = /\?(i?)-(mx|mix):(.*)\)/.match @regex.to_s
|
24
|
-
"/#{match[3]}/#{match[1]}"
|
10
|
+
def initialize(bot, regex, description, &action)
|
11
|
+
if description
|
12
|
+
description.regex = regex
|
13
|
+
else
|
14
|
+
description = ActionDescription.new(nil, regex: regex)
|
25
15
|
end
|
26
|
-
end
|
27
16
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
regex: regex)
|
33
|
-
@regex, @desc, @action = regex, description, action
|
17
|
+
@dsl = DSL.new bot
|
18
|
+
@regex = regex
|
19
|
+
@desc = description
|
20
|
+
@action = action
|
34
21
|
end
|
35
22
|
|
36
|
-
def
|
23
|
+
def this?(regex, block)
|
37
24
|
same_regex = regex == self.regex
|
38
|
-
block ? same_regex && block ==
|
25
|
+
block ? same_regex && block == action : same_regex
|
39
26
|
end
|
40
27
|
|
41
|
-
def execute(
|
42
|
-
|
43
|
-
if match = message.match!(regex)
|
44
|
-
matches = Array(match)
|
45
|
-
matches.shift
|
46
|
-
dsl.instance_exec(*matches, &action)
|
47
|
-
end
|
28
|
+
def execute(message)
|
29
|
+
action_call message.match!(regex)
|
48
30
|
rescue => e
|
49
31
|
logger.error e.message
|
50
32
|
raise e
|
51
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def action_call(match)
|
38
|
+
return unless match
|
39
|
+
matches = Array(match)
|
40
|
+
matches.shift
|
41
|
+
@dsl.instance_exec(*matches, &action)
|
42
|
+
end
|
52
43
|
end
|
53
44
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Boty
|
2
|
+
class ActionDescription
|
3
|
+
attr_reader :command, :description
|
4
|
+
attr_writer :regex
|
5
|
+
|
6
|
+
def initialize(command, description: nil, regex: nil)
|
7
|
+
if description.nil?
|
8
|
+
@description = command
|
9
|
+
else
|
10
|
+
@command = command
|
11
|
+
@description = description
|
12
|
+
end
|
13
|
+
|
14
|
+
@regex = regex
|
15
|
+
end
|
16
|
+
|
17
|
+
def command
|
18
|
+
return @command if @command
|
19
|
+
return unless @regex
|
20
|
+
match = /\?(i?)-(mx|mix):(.*)\)/.match @regex.to_s
|
21
|
+
@command = "/#{match[3]}/#{match[1]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/boty/bot.rb
CHANGED
@@ -8,10 +8,14 @@ module Boty
|
|
8
8
|
|
9
9
|
def initialize(bot_info)
|
10
10
|
Locale.reload
|
11
|
-
@raw_info
|
11
|
+
@raw_info = bot_info
|
12
|
+
@id = bot_info["id"]
|
13
|
+
@name = bot_info["name"]
|
14
|
+
|
12
15
|
@listeners ||= []
|
13
16
|
@commands ||= []
|
14
17
|
@brain ||= {}
|
18
|
+
|
15
19
|
on :message, &method(:message_handler)
|
16
20
|
ScriptLoader.new(self).load
|
17
21
|
end
|
@@ -38,7 +42,8 @@ module Boty
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def desc(command, description = nil)
|
41
|
-
@current_desc =
|
45
|
+
@current_desc = ActionDescription.new command,
|
46
|
+
description: description
|
42
47
|
end
|
43
48
|
|
44
49
|
def say(message, api_parameters = {})
|
@@ -49,33 +54,33 @@ module Boty
|
|
49
54
|
end
|
50
55
|
|
51
56
|
def im(text, destiny: nil, to: nil, user_id: nil)
|
52
|
-
|
53
|
-
|
57
|
+
destiny = User(user_id) || user_by_name(destiny, to)
|
58
|
+
if destiny
|
59
|
+
logger.debug do "Sending #{text} to #{destiny.name}." end
|
54
60
|
Slack.chat.post_im destiny.id, text
|
55
61
|
else
|
56
|
-
logger.debug
|
62
|
+
logger.debug do "User not found, refusing to send im." end
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
66
|
# TODO: return an Action object instead of a hash
|
61
67
|
def know_how
|
62
|
-
|
63
|
-
|
64
|
-
hsh.merge!(desc.command => desc.description)
|
65
|
-
}
|
68
|
+
actions = Array(@commands) + Array(@listeners)
|
69
|
+
actions.sort_by { |action| action.desc.command || "_" }
|
66
70
|
end
|
67
71
|
|
68
72
|
private
|
69
73
|
|
70
74
|
def remove_action(collection, regex = nil, block = nil, command: nil)
|
71
75
|
collection.delete_if do |action|
|
72
|
-
action.desc.command == command || action.
|
76
|
+
action.desc.command == command || action.this?(regex, block)
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
76
80
|
def user_by_name(destiny, to)
|
77
|
-
|
78
|
-
|
81
|
+
to ||= destiny
|
82
|
+
if to
|
83
|
+
Slack.users.by_name to
|
79
84
|
else
|
80
85
|
@trigger_message.user
|
81
86
|
end
|
@@ -83,34 +88,40 @@ module Boty
|
|
83
88
|
|
84
89
|
def create_action(regex, &block)
|
85
90
|
regex = Regexp.new(regex) if regex.is_a? String
|
86
|
-
Boty::Action.new(regex, @current_desc, &block).tap {
|
91
|
+
Boty::Action.new(self, regex, @current_desc, &block).tap {
|
87
92
|
@current_desc = nil
|
88
93
|
}
|
89
94
|
end
|
90
95
|
|
91
96
|
def message_from_bot_itself?(data)
|
92
|
-
data["user"] ==
|
97
|
+
data["user"] == id || data["user"] == name
|
93
98
|
end
|
94
99
|
|
95
|
-
def
|
100
|
+
def valid_mention?(data)
|
96
101
|
return false if message_from_bot_itself? data
|
97
|
-
|
102
|
+
|
103
|
+
if /(<@#{id}|#{name}>)/ =~ data["text"]
|
104
|
+
true
|
105
|
+
else
|
106
|
+
false
|
107
|
+
end
|
98
108
|
end
|
99
109
|
|
100
110
|
def message_handler(data)
|
101
111
|
unless data["text"]
|
102
|
-
return logger.debug "Non text message, just ignoring."
|
112
|
+
return logger.debug do "Non text message, just ignoring." end
|
103
113
|
end
|
114
|
+
actions = valid_mention?(data) ? @commands : @listeners
|
115
|
+
execute_all Message.new(data), actions
|
116
|
+
end
|
104
117
|
|
105
|
-
|
106
|
-
@trigger_message =
|
107
|
-
|
108
|
-
|
109
|
-
action.execute self, @trigger_message
|
110
|
-
end
|
111
|
-
ensure
|
112
|
-
@trigger_message = nil
|
118
|
+
def execute_all(message, actions)
|
119
|
+
@trigger_message = message
|
120
|
+
Array(actions).each do |action|
|
121
|
+
action.execute @trigger_message
|
113
122
|
end
|
123
|
+
ensure
|
124
|
+
@trigger_message = nil
|
114
125
|
end
|
115
126
|
end
|
116
127
|
end
|
data/lib/boty/dsl.rb
CHANGED
@@ -7,14 +7,14 @@ module Boty
|
|
7
7
|
INSTANCES = {}
|
8
8
|
private_constant :INSTANCES
|
9
9
|
class << self
|
10
|
-
|
10
|
+
alias_method :original_constructor, :new
|
11
11
|
end
|
12
12
|
|
13
13
|
attr_accessor :bot
|
14
14
|
|
15
15
|
extend Forwardable
|
16
|
-
def_delegators :bot,
|
17
|
-
|
16
|
+
def_delegators :bot,
|
17
|
+
:desc, :respond, :name, :brain, :know_how, :im, :say, :match
|
18
18
|
def_delegators :message, :user, :channel
|
19
19
|
|
20
20
|
def self.new(bot)
|