diff --git a/.rubocop.yml b/.rubocop.yml index 2a65056..242f375 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,7 @@ Style/MixinUsage: Style/ClassVars: Enabled: true Exclude: - - lib/toddlerbot/handlers/base_handler.rb + - lib/slackify/handlers/base.rb AllCops: TargetRubyVersion: 2.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a727e2..2034029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## V0.1.2 + +* Renaming the gem from toddlerbot to slackify. +* Cleanup of `lib/slackify` folder. +* Added a new configuration: unhandled_handler. You can specify a subclass of `Slackify::Handlers::Base` in the config. This class will be called with `#unhandled` when a message has no regex match. + ## V0.1.1 Fix constantize vulnerability in the slack controller by introducing supported handler through `#inherited` or `Toddlerbot::BaseHandler`. diff --git a/Gemfile.lock b/Gemfile.lock index 73ec5f6..252242e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - toddlerbot (0.1.1) + slackify (0.1.2) rails slack-ruby-client @@ -158,7 +158,7 @@ DEPENDENCIES mocha rake rubocop - toddlerbot! + slackify! BUNDLED WITH 1.17.3 diff --git a/README.md b/README.md index 9fe5289..ceb12f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ToddlerBot [![Build Status](https://travis-ci.org/jusleg/toddlerbot.svg?branch=master)](https://travis-ci.org/jusleg/toddlerbot) [![Gem Version](https://badge.fury.io/rb/toddlerbot.svg)](https://badge.fury.io/rb/toddlerbot) +# Slackify [![Build Status](https://travis-ci.org/jusleg/slackify.svg?branch=master)](https://travis-ci.org/jusleg/slackify) [![Gem Version](https://badge.fury.io/rb/slackify.svg)](https://badge.fury.io/rb/slackify) -Toddlerbot is a gem that allows to build slackbots on Rails using the [Event API](https://api.slack.com/events-api) from Slack. +Slackify is a gem that allows to build slackbots on Rails using the [Event API](https://api.slack.com/events-api) from Slack. ## Table of Contents * [How does it work](#how-does-it-work) @@ -9,11 +9,12 @@ Toddlerbot is a gem that allows to build slackbots on Rails using the [Event API * [Interactive messages](#handling-interactive-messages) * [Slash Command](#handling-slash-commands) * [Custom handler for event subtypes](#custom-handler-for-event-subtypes) + * [Custom unhandled handler](#custom-unhandled-handler) * [Slack client](#slack-client) * [Sending a simple message](#sending-a-simple-message) * [Sending an interactive message](#sending-an-interactive-message) * [Slack 3 second reply window](#slack-3-seconds-reply-window) -* [How to run your own toddlerbot](#how-to-run-your-own-toddlerbot) +* [How to run your own slackify](#how-to-run-your-own-slackify) * [Initial Setup](#initial-setup) * [Slack Setup](#slack-setup) @@ -44,7 +45,7 @@ Those handlers are configured via the `config/handlers.yml` configuration file. The ruby class `repeat_handler.rb` would look something like this: ```ruby -class RepeatHandler < Toddlerbot::BaseHandler +class RepeatHandler < Slackify::Handlers::Base class << self def repeat(params) slack_client.chat_postMessage( @@ -67,12 +68,12 @@ To add a new handler, you can add a new file under `app/handlers/` and start add When sending an interactive message to a user, slack let's you define the `callback_id`. The app uses the callback id to select the proper handler for the message that was sent. The callback id must follow the given format: `class_name#method_name`. For instance if you set the callback id to `repeat_handler#repeat`, then `RepeatHandler#repeat` will be called. Adding new handlers does not require to update the `config/handlers.yml` configuration. You only need to update the callback id to define the proper handler to be used when you send an interactive message. ### Handling slash commands -The code also has an example of a slash command and its handler (`slash_handler.rb`). To add a command on the bot, head to you app configuration on https://api.slack.com/apps and navigate to Slack Commands using the sidebar. Create a new one. The important part is to set the path properly. To bind with the demo handler, you would need to setup the path like this: `/toddlerbot/slash/slash_handler/example_slash`. The format is `/toddlerbot/slash/[handler_name]/[action_name]`. An app shouldn't have many slash commands. Keep in mind that adding a slash command means that the whole organization will see it. +The code also has an example of a slash command and its handler (`slash_handler.rb`). To add a command on the bot, head to you app configuration on https://api.slack.com/apps and navigate to Slack Commands using the sidebar. Create a new one. The important part is to set the path properly. To bind with the demo handler, you would need to setup the path like this: `/slackify/slash/slash_handler/example_slash`. The format is `/slackify/slash/[handler_name]/[action_name]`. An app shouldn't have many slash commands. Keep in mind that adding a slash command means that the whole organization will see it. You will need to whitelist the method in the handler to indicate it can be used as a slash command using `allow_slash_method` ```ruby -class DummyHandler < Toddlerbot::BaseHandler +class DummyHandler < Slackify::Handlers::Base allow_slash_method :slash_command class << self @@ -86,18 +87,41 @@ end ### Custom handler for event subtypes -If you wish to add more functionalities to your bot, you can specify define new behaviours for different event subtypes. You can specify a hash with the event subtype as a key and the handler class as the value. Toddlerbot will call `.handle_event` on your class and pass the controller params as parameters. +If you wish to add more functionalities to your bot, you can specify define new behaviours for different event subtypes. You can specify a hash with the event subtype as a key and the handler class as the value. Slackify will call `.handle_event` on your class and pass the controller params as parameters. ```ruby -Toddlerbot.configuration.custom_event_subtype_handlers = { +Slackify.configuration.custom_event_subtype_handlers = { file_share: ImageHandler } ``` In this example, all events of subtype `file_share` will be sent to the `ImageHandler` class. +### Custom unhandled handler + +By default, the Slackify has a handler that will be called if there is no regex match for a plain text message. `Slackify::Handlers::UnhandledHandler#unhandled` is called and it replies to the user by saying "This command is not handled at the moment". You can update this in the configuration by specifying a handler class for `unhandled_handler` that responds to a class method named `unhandled`. If you do not wish to use any handler for unhandled message, you can call `remove_unhandled_handler` + +```ruby +# config/application.rb +Slackify.configure do |config| + # ... + config.unhandled_handler = MyCustomHandlerClass # enables a custom unhandled handler + + config.remove_unhandled_handler # removes the unhandled_handler +end +``` + +```ruby +# app/handlers/my_custom_handler_class.rb +class MyCustomHandlerClass + def self.unhandled(params) + # ... + end +end +``` + ## Slack client -In order to send messages, the [slack ruby client gem](https://github.com/slack-ruby/slack-ruby-client) was used. You can send plain text messages, images and interactive messages. Since the bot was envisioned being more repsonsive than proactive, the client was made available for handlers to call using the `slack_client` method. If you wish to send messages outside of handlers, you can get the slack client by calling `Toddlerbot.configuration.slack_client` +In order to send messages, the [slack ruby client gem](https://github.com/slack-ruby/slack-ruby-client) was used. You can send plain text messages, images and interactive messages. Since the bot was envisioned being more repsonsive than proactive, the client was made available for handlers to call using the `slack_client` method. If you wish to send messages outside of handlers, you can get the slack client by calling `Slackify.configuration.slack_client` ### Sending a simple message ```ruby @@ -134,14 +158,14 @@ slack_client.chat_postMessage( ``` ## Slack 3 seconds reply window -Slack introduced a [3 seconds reply window](https://api.slack.com/messaging/interactivity#response) for interactive messages. That means that if you reply to an interactive message or slash command event with a json, slack will show either update the attachment or send a new one without having to use `chat_postMessage`. If you wish to use this feature with Toddlerbot, you only need to return either a json of an attachment or a plain text string when you handler method is called. **Your method should always return `nil` otherwise**. +Slack introduced a [3 seconds reply window](https://api.slack.com/messaging/interactivity#response) for interactive messages. That means that if you reply to an interactive message or slash command event with a json, slack will show either update the attachment or send a new one without having to use `chat_postMessage`. If you wish to use this feature with Slackify, you only need to return either a json of an attachment or a plain text string when you handler method is called. **Your method should always return `nil` otherwise**. -# How to run your own toddlerbot +# How to run your own slackify ## Initial Setup -1. Install toddlerbot in your app by adding the following line in your `Gemfile`: +1. Install slackify in your app by adding the following line in your `Gemfile`: ```ruby -gem "toddlerbot" +gem "slackify" ``` 2. run the following command in your terminal: @@ -150,7 +174,7 @@ gem "toddlerbot" bundle install ``` -3. Add handlers to your application. Remember to make them extend `Toddlerbot::BaseHandler` +3. Add handlers to your application. Remember to make them extend `Slackify::Handlers::Base` 4. Create a `config/handlers.yml` file and define your triggers for specific commands. @@ -162,7 +186,7 @@ First, you'll need to create a new app on slack. Head over to [slack api](https: 1. **Set Slack Secret Token** - In order to verify that the requets are coming from slack, we'll need to set the slack secret token in toddlerbot. This value can be found as the signing secret in the app credentials section of the basic information page. + In order to verify that the requets are coming from slack, we'll need to set the slack secret token in slackify. This value can be found as the signing secret in the app credentials section of the basic information page. 2. **Add a bot user** @@ -170,19 +194,19 @@ First, you'll need to create a new app on slack. Head over to [slack api](https: 3. **Enable events subscription** - Under the feature section, click "Events subscription". Turn the feature on and use your app url followed by `/toddlerbot/event`. [Ngrok](https://ngrok.com/) can easily get you a public url if you are developing locally. The app needs to be running when you configure this url. After the url is configured, under the section "Subscribe to Bot Events", add the bot user event `message.im`. + Under the feature section, click "Events subscription". Turn the feature on and use your app url followed by `/slackify/event`. [Ngrok](https://ngrok.com/) can easily get you a public url if you are developing locally. The app needs to be running when you configure this url. After the url is configured, under the section "Subscribe to Bot Events", add the bot user event `message.im`. 4. **Activate the interactive components** - Under the feature section, click "interactive components". Turn the feature on and use your ngrok url followed by `/toddlerbot/interactive`. Save the setting. + Under the feature section, click "interactive components". Turn the feature on and use your ngrok url followed by `/slackify/interactive`. Save the setting. 5. **Install the App** Under the setting section, click "install app" and proceed to install the app to the workspace. Once the app is installed, go back to the "install app" page and copy the Bot User OAuth Access Token. -6. **Configure Toddlerbot** +6. **Configure Slackify** ```ruby -Toddlerbot.configure do |config| +Slackify.configure do |config| config.slack_bot_token = "xoxb-sdkjlkjsdflsd..." config.slack_secret_token = "1234dummysecret" end @@ -190,16 +214,16 @@ end 7. **Add an initializer** ```ruby -# config/initializers/toddlerbot.rb -Toddlerbot.load_handlers +# config/initializers/slackify.rb +Slackify.load_handlers ``` 8. **Define handlers specific subtypes** (Optional) ```ruby -# config/initializers/toddlerbot.rb -Toddlerbot.load_handlers -Toddlerbot.configuration.custom_event_subtype_handlers = { +# config/initializers/slackify.rb +Slackify.load_handlers +Slackify.configuration.custom_event_subtype_handlers = { file_share: ImageHandler, channel_join: JoinHandler, ... @@ -210,4 +234,4 @@ Toddlerbot.configuration.custom_event_subtype_handlers = { # LICENSE -Copyright (c) 2019 Justin Léger, Michel Chatmajian. See [LICENSE](https://github.com/jusleg/toddlerbot/blob/master/LICENSE) for further details. +Copyright (c) 2019 Justin Léger, Michel Chatmajian. See [LICENSE](https://github.com/jusleg/slackify/blob/master/LICENSE) for further details. diff --git a/app/controllers/concerns/slack_token_verify.rb b/app/controllers/concerns/slack_token_verify.rb index 0007ea5..cb1bea2 100644 --- a/app/controllers/concerns/slack_token_verify.rb +++ b/app/controllers/concerns/slack_token_verify.rb @@ -27,7 +27,7 @@ def verify_token signature = "v0:#{timestamp}:#{request_body}" calculated_hmac = "v0=" + - OpenSSL::HMAC.hexdigest("sha256", Toddlerbot.configuration.slack_secret_token, signature) + OpenSSL::HMAC.hexdigest("sha256", Slackify.configuration.slack_secret_token, signature) return if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header) diff --git a/app/controllers/toddlerbot/slack_controller.rb b/app/controllers/slackify/slack_controller.rb similarity index 75% rename from app/controllers/toddlerbot/slack_controller.rb rename to app/controllers/slackify/slack_controller.rb index 4fe2523..89b0283 100644 --- a/app/controllers/toddlerbot/slack_controller.rb +++ b/app/controllers/slackify/slack_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Toddlerbot +module Slackify class SlackController < ActionController::API include SlackTokenVerify @@ -51,7 +51,7 @@ def slash_command_callback private def handle_direct_message_event - if handler = Toddlerbot.configuration.custom_event_subtype_handlers[params[:event][:subtype]] + if handler = Slackify.configuration.custom_event_subtype_handlers[params[:event][:subtype]] handler.handle_event(params[:slack]) head :ok return @@ -60,7 +60,7 @@ def handle_direct_message_event return if params[:event][:subtype] == "bot_message" || params[:event].key?(:bot_id) || params[:event][:hidden] command = params[:event][:text] - Toddlerbot.configuration.handlers.call_command(command, params[:slack]) + Slackify.configuration.handlers.call_command(command, params[:slack]) rescue RuntimeError => e raise e unless e.message == "Component not found for a command message" end @@ -69,8 +69,8 @@ def handler_from_callback_id(callback_id) class_name, method_name = callback_id.split('#') class_name = class_name.camelize - raise Toddlerbot::Exceptions::HandlerNotSupported, class_name unless - Toddlerbot::BaseHandler.supported_handlers.include?(class_name) + raise Exceptions::HandlerNotSupported, class_name unless + Handlers::Base.supported_handlers.include?(class_name) class_name.constantize.method(method_name) end @@ -78,12 +78,12 @@ def handler_from_callback_id(callback_id) def verify_handler_slash_permission(handler_class, handler_method) handler_class = handler_class.camelize - raise Toddlerbot::Exceptions::HandlerNotSupported, handler_class unless - Toddlerbot::BaseHandler.supported_handlers.include?(handler_class) + raise Exceptions::HandlerNotSupported, handler_class unless + Handlers::Base.supported_handlers.include?(handler_class) handler = handler_class.constantize - raise Toddlerbot::Exceptions::MissingSlashPermission, "#{handler_class}##{handler_method} is missing slash permission" unless - handler < BaseHandler && handler.allowed_slash_methods.include?(handler_method.to_sym) + raise Exceptions::MissingSlashPermission, "#{handler_class}##{handler_method} is missing slash permission" unless + handler < Handlers::Base && handler.allowed_slash_methods.include?(handler_method.to_sym) end end end diff --git a/config/routes.rb b/config/routes.rb index f41d1b9..96c5a88 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Rails.application.routes.draw do - post '/toddlerbot/event', to: 'toddlerbot/slack#event_callback' - post '/toddlerbot/interactive', to: 'toddlerbot/slack#interactive_callback' - post '/toddlerbot/slash/:handler_class/:handler_method', to: 'toddlerbot/slack#slash_command_callback' + post '/slackify/event', to: 'slackify/slack#event_callback' + post '/slackify/interactive', to: 'slackify/slack#interactive_callback' + post '/slackify/slash/:handler_class/:handler_method', to: 'slackify/slack#slash_command_callback' end diff --git a/lib/slackify.rb b/lib/slackify.rb new file mode 100644 index 0000000..a3b9055 --- /dev/null +++ b/lib/slackify.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'slackify/configuration' +require 'slackify/engine' +require 'slackify/exceptions' +require 'slackify/handlers' + +module Slackify + class << self + attr_writer :configuration + def configuration + @configuration ||= Configuration.new + end + + def reset + @configuration = Configuration.new + end + + def configure + yield(configuration) + end + + def load_handlers + @configuration.handlers = Handlers::Configuration.new + end + end +end diff --git a/lib/toddlerbot/configuration.rb b/lib/slackify/configuration.rb similarity index 63% rename from lib/toddlerbot/configuration.rb rename to lib/slackify/configuration.rb index 1508d7d..646f323 100644 --- a/lib/toddlerbot/configuration.rb +++ b/lib/slackify/configuration.rb @@ -2,9 +2,9 @@ require 'slack' -module Toddlerbot +module Slackify class Configuration - attr_reader :custom_event_subtype_handlers, :slack_bot_token + attr_reader :custom_event_subtype_handlers, :slack_bot_token, :unhandled_handler attr_accessor :handlers, :slack_secret_token, :slack_client def initialize @@ -13,6 +13,18 @@ def initialize @handlers = nil @slack_client = nil @custom_event_subtype_handlers = {} + @unhandled_handler = Handlers::UnhandledHandler + end + + def unhandled_handler=(handler) + raise Exceptions::InvalidHandler, "#{handler.class} is not a subclass of Slackify::Handlers::Base" unless + handler.is_a?(Handlers::Base) + + @unhandled_handler = handler + end + + def remove_unhandled_handler + @unhandled_handler = nil end def slack_bot_token=(token) diff --git a/lib/toddlerbot/engine.rb b/lib/slackify/engine.rb similarity index 58% rename from lib/toddlerbot/engine.rb rename to lib/slackify/engine.rb index 7212297..2a71ef8 100644 --- a/lib/toddlerbot/engine.rb +++ b/lib/slackify/engine.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -module Toddlerbot +module Slackify class Engine < Rails::Engine - isolate_namespace Toddlerbot + isolate_namespace Slackify end end diff --git a/lib/toddlerbot/exceptions.rb b/lib/slackify/exceptions.rb similarity index 92% rename from lib/toddlerbot/exceptions.rb rename to lib/slackify/exceptions.rb index fe82813..c99893d 100644 --- a/lib/toddlerbot/exceptions.rb +++ b/lib/slackify/exceptions.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Toddlerbot +module Slackify module Exceptions class HandlerNotSupported < StandardError; end class InvalidHandler < StandardError; end diff --git a/lib/slackify/handlers.rb b/lib/slackify/handlers.rb new file mode 100644 index 0000000..d981901 --- /dev/null +++ b/lib/slackify/handlers.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative 'handlers/base' +require_relative 'handlers/configuration' +require_relative 'handlers/unhandled_handler' +require_relative 'handlers/validator' + +module Slackify + module Handlers + end +end diff --git a/lib/slackify/handlers/base.rb b/lib/slackify/handlers/base.rb new file mode 100644 index 0000000..9c6e72b --- /dev/null +++ b/lib/slackify/handlers/base.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Slackify + module Handlers + class Base + @@supported_handlers = [] + + class << self + attr_reader :allowed_slash_methods + + def slack_client + Slackify.configuration.slack_client + end + + def allow_slash_method(element) + if @allowed_slash_methods + @allowed_slash_methods.push(*element) + else + @allowed_slash_methods = Array(element) + end + end + + def inherited(subclass) + @@supported_handlers.push(subclass.to_s) + end + + def supported_handlers + @@supported_handlers + end + end + end + end +end diff --git a/lib/slackify/handlers/configuration.rb b/lib/slackify/handlers/configuration.rb new file mode 100644 index 0000000..b137cd3 --- /dev/null +++ b/lib/slackify/handlers/configuration.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'ostruct' + +module Slackify + module Handlers + class Configuration + attr_reader :bot_id + attr_accessor :handlers + delegate :each, to: :@handlers + + def initialize + @handlers = [] + + read_handlers_yaml.each do |handler_yaml| + handler = generate_handler_from_yaml(handler_yaml) + @handlers << handler + end + + environment_configurations + end + + def all_commands + @handlers.collect(&:commands).flatten + end + + def matching_command(message) + all_commands.each { |command| return command if command.regex.match? message } + nil + end + + def call_command(message, params) + return if params.dig(:event, :user) == @bot_id || params.dig(:event, :message, :user) == @bot_id + + command = matching_command(message) + if command.nil? + return unless Slackify.configuration.unhandled_handler + + Slackify.configuration.unhandled_handler.unhandled(params) + else + new_params = params.merge(command_arguments: command.regex.match(message).named_captures) + command.handler.call(new_params) + end + end + + private + + def environment_configurations + @bot_id = + case Rails.env + when 'production', 'staging', 'development' + Slackify.configuration.slack_client.auth_test['user_id'] + when 'test' + '' + end + end + + def read_handlers_yaml + raise 'config/handlers.yml does not exist' unless File.exist?("#{Rails.root}/config/handlers.yml") + + YAML.load_file("#{Rails.root}/config/handlers.yml") || [] + end + + def generate_handler_from_yaml(handler_yaml) + Validator.verify_handler_integrity(handler_yaml) + + handler = OpenStruct.new + handler.name = handler_yaml.keys.first + handler.commands = [] + + handler_yaml[handler.name]['commands']&.each do |command| + built_command = OpenStruct.new + built_command.regex = command['regex'] + built_command.handler = handler.name.camelize.constantize.method(command['action']) + built_command.description = command['description'] + handler.commands << built_command.freeze + end + + handler.commands.freeze + handler.freeze + end + end + end +end diff --git a/lib/slackify/handlers/unhandled_handler.rb b/lib/slackify/handlers/unhandled_handler.rb new file mode 100644 index 0000000..f506610 --- /dev/null +++ b/lib/slackify/handlers/unhandled_handler.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Slackify + module Handlers + class UnhandledHandler < Base + def self.unhandled(params) + slack_client.chat_postMessage( + as_user: true, + channel: params[:event][:user], + text: "This command is not handled at the moment", + ) + end + end + end +end diff --git a/lib/slackify/handlers/validator.rb b/lib/slackify/handlers/validator.rb new file mode 100644 index 0000000..31f1934 --- /dev/null +++ b/lib/slackify/handlers/validator.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Slackify + module Handlers + class Validator + def self.verify_handler_integrity(handler) + handler_name = handler.keys.first + handler_class = handler_name.camelize.constantize + + unless handler[handler_name].key?('commands') && handler.dig(handler_name, 'commands')&.any? + raise Exceptions::InvalidHandler, "#{handler_name} doesn't have any command specified" + end + + handler_errors = [] + + handler.dig(handler_name, 'commands').each do |command| + command_errors = [] + + unless command['regex'].is_a?(Regexp) + command_errors.append('No regex was provided.') + end + + unless !command['action'].to_s.strip.empty? && handler_class.respond_to?(command['action']) + command_errors.append('No valid action was provided.') + end + + handler_errors.append("[#{command['name']}]: #{command_errors.join(' ')}") unless command_errors.empty? + end + + unless handler_errors.empty? + raise Exceptions::InvalidHandler, "#{handler_name} is not valid: #{handler_errors.join(' ')}" + end + rescue NameError + raise Exceptions::InvalidHandler, "#{handler_name} is not defined" + end + end + end +end diff --git a/lib/toddlerbot.rb b/lib/toddlerbot.rb deleted file mode 100644 index 1cc6999..0000000 --- a/lib/toddlerbot.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require 'toddlerbot/configuration' -require 'toddlerbot/engine' -require 'toddlerbot/exceptions' -require 'toddlerbot/handlers/base_handler' -require 'toddlerbot/handlers/handler_validator' -require 'toddlerbot/handlers/unhandled_handler' -require 'toddlerbot/handler_configuration' - -Dir[Toddlerbot::Engine.root + 'app/models/**/*.rb'].collect{ |f| require f } - -module Toddlerbot - class << self - attr_writer :configuration - def configuration - @configuration ||= Configuration.new - end - - def reset - @configuration = Configuration.new - end - - def configure - yield(configuration) - end - - def load_handlers - @configuration.handlers = HandlerConfiguration.new - end - end -end diff --git a/lib/toddlerbot/handler_configuration.rb b/lib/toddlerbot/handler_configuration.rb deleted file mode 100644 index 1664060..0000000 --- a/lib/toddlerbot/handler_configuration.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'ostruct' - -module Toddlerbot - class HandlerConfiguration - attr_reader :bot_id - attr_accessor :handlers - delegate :each, to: :@handlers - - def initialize - @handlers = [] - - read_handlers_yaml.each do |handler_yaml| - handler = generate_handler_from_yaml(handler_yaml) - @handlers << handler - end - - environment_configurations - end - - def all_commands - @handlers.collect(&:commands).flatten - end - - def matching_command(message) - all_commands.each { |command| return command if command.regex.match? message } - nil - end - - def call_command(message, params) - return if params.dig(:event, :user) == @bot_id || params.dig(:event, :message, :user) == @bot_id - - command = matching_command(message) - if command.nil? - UnhandledHandler.unhandled(params) - else - new_params = params.merge(command_arguments: command.regex.match(message).named_captures) - command.handler.call(new_params) - end - end - - private - - def environment_configurations - @bot_id = - case Rails.env - when 'production', 'staging', 'development' - Toddlerbot.configuration.slack_client.auth_test['user_id'] - when 'test' - '' - end - end - - def read_handlers_yaml - raise 'config/handlers.yml does not exist' unless File.exist?("#{Rails.root}/config/handlers.yml") - - YAML.load_file("#{Rails.root}/config/handlers.yml") || [] - end - - def generate_handler_from_yaml(handler_yaml) - Toddlerbot::HandlerValidator.verify_handler_integrity(handler_yaml) - - handler = OpenStruct.new - handler.name = handler_yaml.keys.first - handler.commands = [] - - handler_yaml[handler.name]['commands']&.each do |command| - built_command = OpenStruct.new - built_command.regex = command['regex'] - built_command.handler = handler.name.camelize.constantize.method(command['action']) - built_command.description = command['description'] - handler.commands << built_command.freeze - end - - handler.commands.freeze - handler.freeze - end - end -end diff --git a/lib/toddlerbot/handlers/base_handler.rb b/lib/toddlerbot/handlers/base_handler.rb deleted file mode 100644 index 8a5a55f..0000000 --- a/lib/toddlerbot/handlers/base_handler.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module Toddlerbot - class BaseHandler - @@supported_handlers = [] - - class << self - attr_reader :allowed_slash_methods - - def slack_client - Toddlerbot.configuration.slack_client - end - - def allow_slash_method(element) - if @allowed_slash_methods - @allowed_slash_methods.push(*element) - else - @allowed_slash_methods = Array(element) - end - end - - def inherited(subclass) - @@supported_handlers.push(subclass.to_s) - end - - def supported_handlers - @@supported_handlers - end - end - end -end diff --git a/lib/toddlerbot/handlers/handler_validator.rb b/lib/toddlerbot/handlers/handler_validator.rb deleted file mode 100644 index 2a90c0a..0000000 --- a/lib/toddlerbot/handlers/handler_validator.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Toddlerbot - class HandlerValidator - def self.verify_handler_integrity(handler) - handler_name = handler.keys.first - handler_class = handler_name.camelize.constantize - - unless handler[handler_name].key?('commands') && handler.dig(handler_name, 'commands')&.any? - raise Toddlerbot::Exceptions::InvalidHandler, "#{handler_name} doesn't have any command specified" - end - - handler_errors = [] - - handler.dig(handler_name, 'commands').each do |command| - command_errors = [] - - unless command['regex'].is_a?(Regexp) - command_errors.append('No regex was provided.') - end - - unless !command['action'].to_s.strip.empty? && handler_class.respond_to?(command['action']) - command_errors.append('No valid action was provided.') - end - - handler_errors.append("[#{command['name']}]: #{command_errors.join(' ')}") unless command_errors.empty? - end - - unless handler_errors.empty? - raise Toddlerbot::Exceptions::InvalidHandler, "#{handler_name} is not valid: #{handler_errors.join(' ')}" - end - rescue NameError - raise Toddlerbot::Exceptions::InvalidHandler, "#{handler_name} is not defined" - end - end -end diff --git a/lib/toddlerbot/handlers/unhandled_handler.rb b/lib/toddlerbot/handlers/unhandled_handler.rb deleted file mode 100644 index 263936c..0000000 --- a/lib/toddlerbot/handlers/unhandled_handler.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module Toddlerbot - class UnhandledHandler < Toddlerbot::BaseHandler - class << self - def unhandled(params) - slack_client.chat_postMessage( - as_user: true, - channel: params[:event][:user], - text: "This command is not currently handled at the moment", - ) - end - end - end -end diff --git a/toddlerbot.gemspec b/slackify.gemspec similarity index 61% rename from toddlerbot.gemspec rename to slackify.gemspec index bea76ba..5982419 100644 --- a/toddlerbot.gemspec +++ b/slackify.gemspec @@ -1,20 +1,20 @@ # frozen_string_literal: true Gem::Specification.new do |s| - s.name = 'toddlerbot' - s.version = '0.1.1' + s.name = 'slackify' + s.version = '0.1.2' s.date = '2019-03-17' - s.summary = 'Rails slackbot framework' - s.description = 'Slackbot framework for Rails using the Events API' + s.summary = 'Slackbot framework for Rails using the Events API' + s.description = 'Slackbot framework for Rails using the Events API. Supports events, interactive messages and slash commands.' s.authors = ['Justin Leger', 'Michel Chatmajian'] s.email = 'hey@justinleger.ca' s.files = Dir["{app,config,lib}/**/*", "Rakefile", "README.md", "CHANGELOG.md"] - s.homepage = 'https://github.com/jusleg/toddlerbot' + s.homepage = 'https://github.com/jusleg/slackify' s.license = 'MIT' s.metadata = { - "source_code_uri" => "https://github.com/jusleg/toddlerbot", - "changelog_uri" => "https://github.com/jusleg/toddlerbot/blob/master/CHANGELOG.md" + "source_code_uri" => "https://github.com/jusleg/slackify", + "changelog_uri" => "https://github.com/jusleg/slackify/blob/master/CHANGELOG.md" } s.add_dependency 'rails' diff --git a/test/dummy/app/handlers/dummy_handler.rb b/test/dummy/app/handlers/dummy_handler.rb index fe60f10..f364d63 100644 --- a/test/dummy/app/handlers/dummy_handler.rb +++ b/test/dummy/app/handlers/dummy_handler.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class DummyHandler < Toddlerbot::BaseHandler +class DummyHandler < Slackify::Handlers::Base allow_slash_method :slash_command class << self diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb index bc3e343..1c81d64 100644 --- a/test/dummy/config/application.rb +++ b/test/dummy/config/application.rb @@ -12,7 +12,7 @@ require "rails/test_unit/railtie" Bundler.require(*Rails.groups) -require "toddlerbot" +require "slackify" module Dummy class Application < Rails::Application @@ -24,7 +24,7 @@ class Application < Rails::Application # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. - Toddlerbot.configure do |config| + Slackify.configure do |config| config.slack_bot_token = 'testslacktoken123' config.slack_secret_token = '123abcslacksecrettokenabc123' end diff --git a/test/dummy/config/initializers/01_handlers.rb b/test/dummy/config/initializers/01_handlers.rb index 469ecfb..b281921 100644 --- a/test/dummy/config/initializers/01_handlers.rb +++ b/test/dummy/config/initializers/01_handlers.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -Toddlerbot.load_handlers +Slackify.load_handlers diff --git a/test/integration/slack_controller_test.rb b/test/integration/slack_controller_test.rb index 34a817b..8ee06a6 100644 --- a/test/integration/slack_controller_test.rb +++ b/test/integration/slack_controller_test.rb @@ -2,151 +2,153 @@ require_relative '../test_helper' -class SlackControllerTest < ActionDispatch::IntegrationTest - include Toddlerbot::SlackTestHelper - - setup do - @event_callback_url = "/toddlerbot/event" - @interactive_callback_url = "/toddlerbot/interactive" - @slash_command_callback_url = "/toddlerbot/slash/dummy_handler/slash_command" - end +module Slackify + class SlackControllerTest < ActionDispatch::IntegrationTest + include SlackTestHelper + + setup do + @event_callback_url = "/slackify/event" + @interactive_callback_url = "/slackify/interactive" + @slash_command_callback_url = "/slackify/slash/dummy_handler/slash_command" + end - test "#event_callback returns unauthorized for invalid token" do - params = build_url_verification_params - post @event_callback_url, as: :json, headers: build_webhook_headers(params, token: "invalidblah"), params: params - assert_response :unauthorized + test "#event_callback returns unauthorized for invalid token" do + params = build_url_verification_params + post @event_callback_url, as: :json, headers: build_webhook_headers(params, token: "invalidblah"), params: params + assert_response :unauthorized - params = build_slack_message_params(event: { channel_type: "im" }) - post @event_callback_url, as: :json, headers: build_webhook_headers(params, token: "invalidblah"), params: params - assert_response :unauthorized - end + params = build_slack_message_params(event: { channel_type: "im" }) + post @event_callback_url, as: :json, headers: build_webhook_headers(params, token: "invalidblah"), params: params + assert_response :unauthorized + end - test "#event_callback returns unauthorized when over time range" do - params = build_url_verification_params - post @event_callback_url, as: :json, - headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params - assert_response :unauthorized + test "#event_callback returns unauthorized when over time range" do + params = build_url_verification_params + post @event_callback_url, as: :json, + headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params + assert_response :unauthorized - params = build_slack_message_params(event: { channel_type: "im" }) - post @event_callback_url, as: :json, - headers: build_webhook_headers(params, timestamp: Time.now - 60.seconds), params: params - assert_response :unauthorized - end + params = build_slack_message_params(event: { channel_type: "im" }) + post @event_callback_url, as: :json, + headers: build_webhook_headers(params, timestamp: Time.now - 60.seconds), params: params + assert_response :unauthorized + end - test "#event_callback returns with the challenge when event is url_verification" do - params = build_url_verification_params + test "#event_callback returns with the challenge when event is url_verification" do + params = build_url_verification_params - post @event_callback_url, as: :json, headers: build_webhook_headers(params), params: params + post @event_callback_url, as: :json, headers: build_webhook_headers(params), params: params - assert_response :ok - assert_equal "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", response.body - end + assert_response :ok + assert_equal "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", response.body + end - test "#event_callback call the handlers for an IM event" do - params = build_slack_message_params(event: { channel_type: "im" }) + test "#event_callback call the handlers for an IM event" do + params = build_slack_message_params(event: { channel_type: "im" }) - Toddlerbot.configuration.handlers.expects(:call_command).with( - params[:event][:text], - ActionController::Parameters.new(params) - ) + Slackify.configuration.handlers.expects(:call_command).with( + params[:event][:text], + ActionController::Parameters.new(params) + ) - post @event_callback_url, as: :json, headers: build_webhook_headers(params), params: params + post @event_callback_url, as: :json, headers: build_webhook_headers(params), params: params - assert_response :ok - end + assert_response :ok + end - test "#interactive_callback returns unauthorized with invalid token" do - params = build_slack_interactive_callback + test "#interactive_callback returns unauthorized with invalid token" do + params = build_slack_interactive_callback - post @interactive_callback_url, headers: build_webhook_headers(params, token: "invalidblah"), params: params + post @interactive_callback_url, headers: build_webhook_headers(params, token: "invalidblah"), params: params - assert_response :unauthorized - end + assert_response :unauthorized + end - test "#interactive_callback returns unauthorized when over time range" do - params = build_slack_interactive_callback + test "#interactive_callback returns unauthorized when over time range" do + params = build_slack_interactive_callback - post @interactive_callback_url, - headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params + post @interactive_callback_url, + headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params - assert_response :unauthorized - end + assert_response :unauthorized + end - test "#interactive_callback call the proper handler" do - DummyHandler.expects(:cool_command).once - params = build_slack_interactive_callback - post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params - end + test "#interactive_callback call the proper handler" do + DummyHandler.expects(:cool_command).once + params = build_slack_interactive_callback + post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params + end - test "#interactive_callback returns the proper following attachement" do - # Setting up the Slack Ruby Client Mock - options = { actions: [{ "name" => "btn1", "value" => "btn1", "type" => "button" }], callback_id: "dummy_handler#button_clicked" } - params = build_slack_interactive_callback(options) + test "#interactive_callback returns the proper following attachement" do + # Setting up the Slack Ruby Client Mock + options = { actions: [{ "name" => "btn1", "value" => "btn1", "type" => "button" }], callback_id: "dummy_handler#button_clicked" } + params = build_slack_interactive_callback(options) - post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params - assert_equal "{\"attachments\":[{\"text\":\"Test\"}]}", response.body - assert_response :ok + post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params + assert_equal "{\"attachments\":[{\"text\":\"Test\"}]}", response.body + assert_response :ok - options = { actions: [{ "name" => "btn2", "value" => "btn2", "type" => "button" }], callback_id: "dummy_handler#button_clicked" } - params = build_slack_interactive_callback(options) + options = { actions: [{ "name" => "btn2", "value" => "btn2", "type" => "button" }], callback_id: "dummy_handler#button_clicked" } + params = build_slack_interactive_callback(options) - post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params - assert_equal "{\"attachments\":[{\"text\":\" Button two has been clicked\"}]}", response.body - assert_response :ok - end + post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params + assert_equal "{\"attachments\":[{\"text\":\" Button two has been clicked\"}]}", response.body + assert_response :ok + end - test "#interactive_callback raises error if the handler is not supported" do - options = { actions: [{ "name" => "btn2", "value" => "btn2", "type" => "button" }], callback_id: "random_handler#button_clicked" } - params = build_slack_interactive_callback(options) + test "#interactive_callback raises error if the handler is not supported" do + options = { actions: [{ "name" => "btn2", "value" => "btn2", "type" => "button" }], callback_id: "random_handler#button_clicked" } + params = build_slack_interactive_callback(options) - assert_raise Toddlerbot::Exceptions::HandlerNotSupported do - post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params + assert_raise Slackify::Exceptions::HandlerNotSupported do + post @interactive_callback_url, as: :json, headers: build_webhook_headers(params), params: params + end end - end - test "#slash_command_callback calls the correct handler method" do - # Passing in the dummy_handler class into url encoded payload - params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=toddlerbot&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" + test "#slash_command_callback calls the correct handler method" do + # Passing in the dummy_handler class into url encoded payload + params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=slackify&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" - DummyHandler.expects(:slash_command).once - post @slash_command_callback_url, as: :json, headers: build_webhook_headers(params), params: params - assert_response :ok - end + DummyHandler.expects(:slash_command).once + post @slash_command_callback_url, as: :json, headers: build_webhook_headers(params), params: params + assert_response :ok + end - test "#slash_command_callback returns unauthorized with invalid token" do - params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=toddlerbot&team_id=TEAM1234&text=famingo&token=invalidtoken&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" + test "#slash_command_callback returns unauthorized with invalid token" do + params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=slackify&team_id=TEAM1234&text=famingo&token=invalidtoken&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" - post @slash_command_callback_url, headers: build_webhook_headers(params, token: "invalidtoken"), params: params + post @slash_command_callback_url, headers: build_webhook_headers(params, token: "invalidtoken"), params: params - assert_response :unauthorized - end + assert_response :unauthorized + end - test "#slash_command_callback returns unauthorized when over time limit (3000ms)" do - params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=toddlerbot&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" + test "#slash_command_callback returns unauthorized when over time limit (3000ms)" do + params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=slackify&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" - post @slash_command_callback_url, - headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params + post @slash_command_callback_url, + headers: build_webhook_headers(params, timestamp: Time.now - 31.seconds), params: params - assert_response :unauthorized - end + assert_response :unauthorized + end - test "#slash_command_callback raises an exception if the handler method is not whitelisted" do - # Passing in the dummy_handler class into url encoded payload - params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command_not_permitted&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=toddlerbot&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" + test "#slash_command_callback raises an exception if the handler method is not whitelisted" do + # Passing in the dummy_handler class into url encoded payload + params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=dummy_handler&handler_method=slash_command_not_permitted&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=slackify&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" - DummyHandler.expects(:slash_command_not_permitted).never - assert_raise Toddlerbot::Exceptions::MissingSlashPermission do - post "/toddlerbot/slash/dummy_handler/slash_command_not_permitted", as: :json, headers: build_webhook_headers(params), params: params + DummyHandler.expects(:slash_command_not_permitted).never + assert_raise Exceptions::MissingSlashPermission do + post "/slackify/slash/dummy_handler/slash_command_not_permitted", as: :json, headers: build_webhook_headers(params), params: params + end end - end - test "#slash_command_callback raises an exception if the handler class is not supported" do - # Passing in the dummy_handler class into url encoded payload - params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=random_handler_not_permitted&handler_method=slash_command_not_permitted&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=toddlerbot&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" + test "#slash_command_callback raises an exception if the handler class is not supported" do + # Passing in the dummy_handler class into url encoded payload + params = "action=slash_command_callback&channel_id=DD6S3EGQG&channel_name=directmessage&command=%2Ftest&controller=slack&handler_class=random_handler_not_permitted&handler_method=slash_command_not_permitted&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FTEAM1234%2F479282542757%2Fvrgf0tsH3Hpatycs5Qop3M9S&team_domain=slackify&team_id=TEAM1234&text=famingo&token=Wxb5WWeegLMp4cIAohut26Lo&trigger_id=478980323267.2152147568.e30db4037a528bde78146858cadd4dd6&user_id=USER1234&user_name=jusleg\"" - DummyHandler.expects(:slash_command_not_permitted).never - assert_raise Toddlerbot::Exceptions::HandlerNotSupported do - post "/toddlerbot/slash/random_handler_not_permitted/slash_command_not_permitted", as: :json, headers: build_webhook_headers(params), params: params + DummyHandler.expects(:slash_command_not_permitted).never + assert_raise Exceptions::HandlerNotSupported do + post "/slackify/slash/random_handler_not_permitted/slash_command_not_permitted", as: :json, headers: build_webhook_headers(params), params: params + end end end end diff --git a/test/slack_test_helper.rb b/test/slack_test_helper.rb index 28d3064..732d6ac 100644 --- a/test/slack_test_helper.rb +++ b/test/slack_test_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Toddlerbot +module Slackify module SlackTestHelper def build_url_verification_params(**options) { diff --git a/test/unit/handler_configuration_test.rb b/test/unit/handler_configuration_test.rb deleted file mode 100644 index c9eb0d0..0000000 --- a/test/unit/handler_configuration_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class HandlerConfigurationTest < ActiveSupport::TestCase - test "The proper commands get called when receiving slack messages" do - assert_output(/cool_command called/) do - Toddlerbot.configuration.handlers.call_command('wazza', {}) - end - - assert_output(/another_command called/) do - Toddlerbot.configuration.handlers.call_command('foo', {}) - end - end - - test "Only one command gets called in the event of two regex match. Only the first match is called" do - assert_output(/cool_command called/) do - Toddlerbot.configuration.handlers.call_command('wazza foo', {}) - end - - # checking that we do not output bar - assert_output(/^((?!another_command called).)*$/) do - Toddlerbot.configuration.handlers.call_command('wazza foo', {}) - end - end - - test "#all_commands returns an array with all the commands" do - assert_equal ["method 1", "method 2"], ["method 1", "method 2"] & Toddlerbot.configuration.handlers.all_commands.each.collect(&:description) - end -end diff --git a/test/unit/handlers/handler_configuration_test.rb b/test/unit/handlers/handler_configuration_test.rb new file mode 100644 index 0000000..a68d2fe --- /dev/null +++ b/test/unit/handlers/handler_configuration_test.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'test_helper' + +module Slackify + module Handlers + class ConfigurationTest < ActiveSupport::TestCase + test "The proper commands get called when receiving slack messages" do + assert_output(/cool_command called/) do + Slackify.configuration.handlers.call_command('wazza', {}) + end + + assert_output(/another_command called/) do + Slackify.configuration.handlers.call_command('foo', {}) + end + end + + test "Only one command gets called in the event of two regex match. Only the first match is called" do + assert_output(/cool_command called/) do + Slackify.configuration.handlers.call_command('wazza foo', {}) + end + + # checking that we do not output bar + assert_output(/^((?!another_command called).)*$/) do + Slackify.configuration.handlers.call_command('wazza foo', {}) + end + end + + test "#all_commands returns an array with all the commands" do + assert_equal ["method 1", "method 2"], ["method 1", "method 2"] & Slackify.configuration.handlers.all_commands.each.collect(&:description) + end + end + end +end