From b4ee1fac8515272ad57029bd1ae72f578307a719 Mon Sep 17 00:00:00 2001 From: Accessd Date: Tue, 3 May 2016 18:02:39 +0300 Subject: [PATCH] commands helper --- examples/market/marketbot.rb | 4 + examples/minimal/pongbot.rb | 4 + examples/weather/weatherbot.rb | 4 + lib/slack-ruby-bot.rb | 1 + lib/slack-ruby-bot/app.rb | 1 + lib/slack-ruby-bot/commands/base.rb | 14 +++- lib/slack-ruby-bot/commands/commands.rb | 27 ------- lib/slack-ruby-bot/commands/help.rb | 37 ++++++++- lib/slack-ruby-bot/commands/hi.rb | 4 + lib/slack-ruby-bot/config.rb | 7 +- lib/slack-ruby-bot/support/commands_helper.rb | 79 +++++++++++++++++++ 11 files changed, 151 insertions(+), 31 deletions(-) delete mode 100644 lib/slack-ruby-bot/commands/commands.rb create mode 100644 lib/slack-ruby-bot/support/commands_helper.rb diff --git a/examples/market/marketbot.rb b/examples/market/marketbot.rb index 0bd6265..24afadd 100644 --- a/examples/market/marketbot.rb +++ b/examples/market/marketbot.rb @@ -2,6 +2,10 @@ require 'yahoo-finance' class MarketBot < SlackRubyBot::Bot + title 'MarketBot' + desc 'Shows the last finance quotes' + long_desc 'Understands commands like: How\'s AMZN today?' + scan(/([A-Z]{2,5}+)/) do |client, data, stocks| YahooFinance::Client.new.quotes(stocks, [:name, :symbol, :last_trade_price, :change, :change_in_percent]).each do |quote| next if quote.symbol == 'N/A' diff --git a/examples/minimal/pongbot.rb b/examples/minimal/pongbot.rb index f9429b1..ab223cf 100644 --- a/examples/minimal/pongbot.rb +++ b/examples/minimal/pongbot.rb @@ -1,6 +1,10 @@ require 'slack-ruby-bot' class Bot < SlackRubyBot::Bot + title 'ping' + desc 'Sends you pong.' + long_desc '' + command 'ping' do |client, data, _match| client.say(text: 'pong', channel: data.channel) end diff --git a/examples/weather/weatherbot.rb b/examples/weather/weatherbot.rb index 6c655ed..72340bb 100644 --- a/examples/weather/weatherbot.rb +++ b/examples/weather/weatherbot.rb @@ -1,6 +1,10 @@ require 'slack-ruby-bot' class WeatherBot < SlackRubyBot::Bot + title 'WeatherBot' + desc 'Shows how the weather' + long_desc 'Command format: How is the weather in Paris?' + match(/^How is the weather in (?\w*)\?$/i) do |client, data, match| client.say(channel: data.channel, text: "The weather in #{match[:location]} is nice.") end diff --git a/lib/slack-ruby-bot.rb b/lib/slack-ruby-bot.rb index 0a77fee..119df51 100644 --- a/lib/slack-ruby-bot.rb +++ b/lib/slack-ruby-bot.rb @@ -22,5 +22,6 @@ def config require 'slack-ruby-bot/commands' require 'slack-ruby-bot/client' require 'slack-ruby-bot/server' +require 'slack-ruby-bot/support/commands_helper' require 'slack-ruby-bot/app' require 'slack-ruby-bot/bot' diff --git a/lib/slack-ruby-bot/app.rb b/lib/slack-ruby-bot/app.rb index dbdd5b1..020dd8d 100644 --- a/lib/slack-ruby-bot/app.rb +++ b/lib/slack-ruby-bot/app.rb @@ -8,6 +8,7 @@ def initialize(options = {}) Slack.configure do |config| config.token = SlackRubyBot.config.token end + CommandsHelper.validate_attrs if SlackRubyBot.config.help_attrs_required? super end diff --git a/lib/slack-ruby-bot/commands/base.rb b/lib/slack-ruby-bot/commands/base.rb index c29c3c0..8681b6c 100644 --- a/lib/slack-ruby-bot/commands/base.rb +++ b/lib/slack-ruby-bot/commands/base.rb @@ -2,7 +2,7 @@ module SlackRubyBot module Commands class Base include Loggable - class_attribute :routes + class_attribute :routes, :command_name, :command_desc, :command_long_desc class << self def send_message(client, channel, text, options = {}) @@ -78,6 +78,18 @@ def scan(match, &block) self.routes[match] = { match_method: :scan, call: block } end + def title(title) + self.command_name = title + end + + def desc(desc) + self.command_desc = desc + end + + def long_desc(long_desc) + self.command_long_desc = long_desc + end + private def parse(client, data) diff --git a/lib/slack-ruby-bot/commands/commands.rb b/lib/slack-ruby-bot/commands/commands.rb deleted file mode 100644 index e052c9a..0000000 --- a/lib/slack-ruby-bot/commands/commands.rb +++ /dev/null @@ -1,27 +0,0 @@ -module SlackRubyBot - module Commands - class Commands < Base - BUILTIN_COMMAND_CLASSES = [SlackRubyBot::Commands::Help, SlackRubyBot::Commands::Hi].freeze - - class << self - def call(client, data, _match) - commands = (BUILTIN_COMMAND_CLASSES + external_command_classes).map { |c| c.routes.keys }.flatten - client.say(channel: data.channel, text: "*Possible commands:*\n#{commands.join("\n")}") - end - - private - - def command_classes - SlackRubyBot::Commands::Base.descendants - end - - def external_command_classes - command_classes.reject do |k| - k.name && k.name.start_with?('SlackRubyBot::Commands') || k == SlackRubyBot::Bot - end - end - end - - end - end -end diff --git a/lib/slack-ruby-bot/commands/help.rb b/lib/slack-ruby-bot/commands/help.rb index aa1f8a6..fb461cf 100644 --- a/lib/slack-ruby-bot/commands/help.rb +++ b/lib/slack-ruby-bot/commands/help.rb @@ -1,8 +1,41 @@ module SlackRubyBot module Commands class Help < Base - def self.call(client, data, _match) - client.say(channel: data.channel, text: 'See https://github.com/dblock/slack-ruby-bot, please.', gif: 'help') + title 'help' + desc 'Shows help information.' + long_desc '' + + command 'help' do |client, data, match| + command = match[:expression] + + text = if command.present? + CommandsHelper.command_full_desc(command) + else + general_text + end + + client.say(channel: data.channel, text: text, gif: 'help') + end + + class << self + + private + + def general_text + bots_descs = CommandsHelper.all_bots_descs + command_descs = CommandsHelper.all_commands_descs + <* + +For more information see https://github.com/dblock/slack-ruby-bot, please. +TEXT + end + end end end diff --git a/lib/slack-ruby-bot/commands/hi.rb b/lib/slack-ruby-bot/commands/hi.rb index d2afbd8..2083c13 100644 --- a/lib/slack-ruby-bot/commands/hi.rb +++ b/lib/slack-ruby-bot/commands/hi.rb @@ -1,6 +1,10 @@ module SlackRubyBot module Commands class Hi < Base + title 'hi' + desc 'Says hello.' + long_desc 'When a user types "hi" the bot will reply "hello". This helps everyone stay polite.' + def self.call(client, data, _match) client.say(channel: data.channel, text: "Hi <@#{data.user}>!", gif: 'hi') end diff --git a/lib/slack-ruby-bot/config.rb b/lib/slack-ruby-bot/config.rb index cf2b9a3..c9c46fd 100644 --- a/lib/slack-ruby-bot/config.rb +++ b/lib/slack-ruby-bot/config.rb @@ -2,13 +2,18 @@ module SlackRubyBot module Config extend self - ATTRS = [:token, :url, :aliases, :user, :user_id, :team, :team_id, :allow_message_loops, :send_gifs].freeze + ATTRS = [:token, :url, :aliases, :user, :user_id, :team, :team_id, :allow_message_loops, :send_gifs, + :help_attrs_required].freeze attr_accessor(*ATTRS) def allow_message_loops? allow_message_loops end + def help_attrs_required? + help_attrs_required + end + def send_gifs? v = boolean_from_env('SLACK_RUBY_BOT_SEND_GIFS') v.nil? ? (send_gifs.nil? || send_gifs) : v diff --git a/lib/slack-ruby-bot/support/commands_helper.rb b/lib/slack-ruby-bot/support/commands_helper.rb new file mode 100644 index 0000000..973e593 --- /dev/null +++ b/lib/slack-ruby-bot/support/commands_helper.rb @@ -0,0 +1,79 @@ +module SlackRubyBot + class CommandsHelper + BUILTIN_COMMAND_CLASSES = [SlackRubyBot::Commands::Help, SlackRubyBot::Commands::Hi].freeze + HELP_ATTR_METHODS_MAP = { + command_name: :title, + command_desc: :desc, + command_long_desc: :long_desc + }.freeze + + class << self + def validate_attrs + all_command_classes.each do |k| + HELP_ATTR_METHODS_MAP.each do |attr, setter_method| + raise "#{k}: #{setter_method} is not present" if k.public_send(attr).nil? + end + end + end + + def all_bots_descs + commands_info(bot_classes).map do |command_info| + "#{command_name_and_desc(command_info)}\n#{command_info[:long_desc]}" + end + end + + def all_commands_descs + commands_info(command_classes_only).map do |command_info| + command_name_and_desc(command_info) + end + end + + def command_full_desc(name) + info = commands_info(command_classes_only).find { |command_info| command_info[:title] == name } + return "There's no command #{name}" unless info + return "There's no description for command #{name}" if info[:long_desc].blank? + "#{command_name_and_desc(info)}\n\n#{info[:long_desc]}" + end + + private + + def command_name_and_desc(command_info) + desc = command_info[:desc].present? ? "- #{command_info[:desc]}" : '' + "*#{command_info[:title]}* #{desc}" + end + + def commands_info(command_classes) + commands_with_present_names = command_classes.select { |k| k.command_name.present? } + commands_with_present_names.inject([]) do |data, klass| + info = {} + HELP_ATTR_METHODS_MAP.each do |attr, setter_method| + info[setter_method] = klass.public_send(attr) + end + data << info + end + end + + def bot_classes + all_command_classes.select { |k| k.superclass == SlackRubyBot::Bot } + end + + def command_classes_only + all_command_classes.reject { |k| k.superclass == SlackRubyBot::Bot } + end + + def all_command_classes + BUILTIN_COMMAND_CLASSES + external_command_classes + end + + def command_classes + SlackRubyBot::Commands::Base.descendants + end + + def external_command_classes + command_classes.reject do |k| + k.name && k.name.start_with?('SlackRubyBot::Commands') || k == SlackRubyBot::Bot + end + end + end + end +end