boty 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +18 -1
  5. data/README.md +39 -3
  6. data/Rakefile +1 -1
  7. data/boty.gemspec +9 -5
  8. data/lib/boty.rb +2 -1
  9. data/lib/boty/action.rb +26 -35
  10. data/lib/boty/action_description.rb +24 -0
  11. data/lib/boty/bot.rb +36 -25
  12. data/lib/boty/dsl.rb +3 -3
  13. data/lib/boty/eventable.rb +4 -3
  14. data/lib/boty/http.rb +3 -4
  15. data/lib/boty/locale.rb +12 -13
  16. data/lib/boty/logger.rb +14 -3
  17. data/lib/boty/rspec.rb +15 -16
  18. data/lib/boty/session.rb +7 -8
  19. data/lib/boty/slack.rb +11 -3
  20. data/lib/boty/slack/channel.rb +2 -1
  21. data/lib/boty/slack/chat.rb +5 -1
  22. data/lib/boty/slack/rtm.rb +1 -1
  23. data/lib/boty/slack/url.rb +10 -5
  24. data/lib/boty/slack/user.rb +2 -1
  25. data/lib/boty/slack/users.rb +1 -1
  26. data/lib/boty/version.rb +1 -1
  27. data/script/knows.rb +10 -9
  28. data/script/pug.rb +5 -1
  29. data/spec/boty/bot_spec.rb +89 -72
  30. data/spec/boty/cli_spec.rb +5 -5
  31. data/spec/boty/dsl_spec.rb +12 -13
  32. data/spec/boty/rspec_spec.rb +2 -1
  33. data/spec/boty/script_loader_spec.rb +8 -6
  34. data/spec/boty/session_spec.rb +2 -2
  35. data/spec/boty/slack/chat_spec.rb +18 -18
  36. data/spec/boty/slack/im_spec.rb +4 -4
  37. data/spec/boty/slack/message_spec.rb +11 -9
  38. data/spec/boty/slack/rtm_spec.rb +3 -3
  39. data/spec/boty/slack/url_spec.rb +5 -5
  40. data/spec/boty/slack/users_spec.rb +23 -23
  41. data/spec/happy_path_spec.rb +19 -18
  42. data/spec/script/i18n_spec.rb +18 -10
  43. data/spec/script/knows_spec.rb +8 -7
  44. data/spec/script/pug_spec.rb +27 -10
  45. data/spec/script/remember_spec.rb +27 -21
  46. data/spec/spec_helper.rb +4 -3
  47. data/spec/support/em_support.rb +1 -3
  48. data/spec/support/faraday_support.rb +0 -1
  49. data/spec/support/faye_support.rb +9 -6
  50. data/spec/support/file_system_matchers.rb +4 -4
  51. data/spec/support/logger_support.rb +9 -12
  52. data/spec/support/session_support.rb +26 -15
  53. data/spec/support/slack_support.rb +9 -6
  54. data/spec/support/template_project_support.rb +24 -23
  55. data/spec/support/thor_support.rb +3 -3
  56. data/template/project/%bot_name%.rb +4 -1
  57. data/template/project/spec/spec_helper.rb +4 -2
  58. metadata +21 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b1b80fa6a35d917745e219d705ad008950daef3b
4
- data.tar.gz: 03fbea6b6493b940a63bcb963ad26c83cc62f756
3
+ metadata.gz: a7102960a8cdaa6f3a18a7015c1338f2c62a28b7
4
+ data.tar.gz: dfd326ea4c467506825ac473f47636682e366bab
5
5
  SHA512:
6
- metadata.gz: 5ffbaa33ad222082cda2865606090e2e4a67051cafdd342a1a0851333594e3fe1514ee516b4b536a96105b54106e86da749ab449c87f1e369cf3009fea6852b4
7
- data.tar.gz: af874f7e181697c3fe8e75b0a3f09f963a37c607a4f066655271e3435f6de4fcce479bde4f0f6ae0f15fc540482eb11030ad2fafc183181bc05f4df3e572d262
6
+ metadata.gz: bab25a32347e1c4e6cb9cc3b8f995fc4d971f094072ef6c5e54018e0d50b5fe37d8c86c9be5e4291a8e4c823cad802ffc2d8f86603a36141eed1ac4adfa7b0da
7
+ data.tar.gz: fb8d9bfa67bcbf7f624b69dc4f7866e7280ccb3be1a655c7117a124f26147ab7002bec1ec1f6736154ac0a57d33173a0f1a4a50e81d707ac703655586328c007
@@ -0,0 +1,4 @@
1
+ Style/StringLiterals:
2
+ EnforcedStyle: double_quotes
3
+ Style/BlockDelimiters:
4
+ EnforcedStyle: semantic
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boty (0.1.0)
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
- overrite:
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=` overrite implementation is like this:
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
- **todo: document**
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
@@ -1,5 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
- require 'rspec/core/rake_task'
2
+ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
  task default: :spec
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'boty/version'
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 = %q{Boty is a pretty bot specially tailored for Slack.}
13
- spec.description = %q{Boty is intendted to be a framework for construction of automated Slack Bots for your needs.}
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 { |f| f.match(%r{^(features)/}) }
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
@@ -13,7 +13,7 @@ require "faye/websocket"
13
13
  require "i18n"
14
14
  require "faraday"
15
15
 
16
- $:.unshift File.expand_path("../../lib", __FILE__)
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"
@@ -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
- class Description
7
- attr_reader :command, :description
8
- attr_writer :regex
9
-
10
- def initialize(command, description: nil, regex: nil)
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
- def initialize(regex, desc, &action)
29
- desc = Hash(desc)
30
- description = Description.new(desc[:command],
31
- description: desc[:description],
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 is_this?(regex, block)
23
+ def this?(regex, block)
37
24
  same_regex = regex == self.regex
38
- block ? same_regex && block == self.action : same_regex
25
+ block ? same_regex && block == action : same_regex
39
26
  end
40
27
 
41
- def execute(bot, message)
42
- dsl = DSL.new(bot)
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
@@ -8,10 +8,14 @@ module Boty
8
8
 
9
9
  def initialize(bot_info)
10
10
  Locale.reload
11
- @raw_info, @id, @name = bot_info, bot_info["id"], bot_info["name"]
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 = { command: command, description: description }
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
- if destiny = User(user_id) || user_by_name(destiny, to)
53
- logger.debug { "Sending #{text} to #{destiny.name}." }
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 { "User not found, refusing to send im." }
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
- descriptions = (Array(@commands) + Array(@listeners)).compact.map(&:desc)
63
- descriptions.inject({}) { |hsh, desc|
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.is_this?(regex, block)
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
- if _to = to || destiny
78
- Slack.users.by_name _to
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"] == self.id || data["user"] == self.name
97
+ data["user"] == id || data["user"] == name
93
98
  end
94
99
 
95
- def has_valid_mention?(data)
100
+ def valid_mention?(data)
96
101
  return false if message_from_bot_itself? data
97
- !!(/(<@#{id}|#{name}>)/ =~ data["text"])
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
- actions = has_valid_mention?(data) ? @commands : @listeners
106
- @trigger_message = Message.new data
107
- begin
108
- Array(actions).each do |action|
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
@@ -7,14 +7,14 @@ module Boty
7
7
  INSTANCES = {}
8
8
  private_constant :INSTANCES
9
9
  class << self
10
- alias original_constructor new
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, :desc, :respond, :name, :brain, :know_how, :im, :say,
17
- :match
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)