diff --git a/.env.sample b/.env.sample index ad61628..d313e35 100644 --- a/.env.sample +++ b/.env.sample @@ -1,5 +1,7 @@ SLACK_CLIENT_ID= SLACK_CLIENT_SECRET= SLACK_VERIFICATION_TOKEN= +SLACK_SIGNING_SECRET= STRIPE_API_PUBLISHABLE_KEY= STRIPE_API_KEY= +GOOGLE_API_CLIENT_ID= diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6256f2f..fe96bff 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,96 +1,89 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2023-02-26 11:12:18 -0500 using RuboCop version 0.81.0. +# on 2023-03-04 13:50:53 -0500 using RuboCop version 0.81.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 +# Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity. # SupportedStylesAlignWith: keyword, variable, start_of_line Layout/EndAlignment: Exclude: - - 'slack-sup/api/presenters/channel_presenter.rb' + - 'lib/actions.rb' + - 'lib/api/presenters/channel_presenter.rb' # Offense count: 2 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - - 'slack-sup/commands/opt.rb' + - 'lib/commands/opt.rb' # Offense count: 1 Lint/DuplicateMethods: Exclude: - - 'slack-sup/models/channel.rb' + - 'lib/models/channel.rb' -# Offense count: 2 +# Offense count: 1 # Cop supports --auto-correct. Lint/NonDeterministicRequireOrder: Exclude: - - 'slack-sup.rb' - 'spec/spec_helper.rb' # Offense count: 1 Lint/RescueException: Exclude: - - 'slack-sup/models/user.rb' + - 'lib/models/user.rb' # Offense count: 1 Lint/ShadowingOuterLocalVariable: Exclude: - - 'slack-sup/app.rb' + - 'lib/app.rb' # Offense count: 1 # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: Exclude: - - 'slack-sup/app.rb' + - 'lib/app.rb' # Offense count: 1 Lint/UselessAssignment: Exclude: - - 'slack-sup/models/user.rb' + - 'lib/models/user.rb' # Offense count: 1 Naming/AccessorMethodName: Exclude: - - 'slack-sup/models/user.rb' - -# Offense count: 1 -# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. -# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS -Naming/FileName: - Exclude: - - 'slack-sup.rb' + - 'lib/models/user.rb' # Offense count: 2 # Configuration parameters: ForbiddenDelimiters. # ForbiddenDelimiters: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - - 'slack-sup/commands/help.rb' - - 'slack-sup/info.rb' + - 'lib/commands/help.rb' + - 'lib/info.rb' # Offense count: 1 # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Exclude: - - 'slack-sup/models/team.rb' + - 'lib/models/team.rb' # Offense count: 28 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: io, id, to, by, on, in, at, ip, db, os, pp Naming/MethodParameterName: Exclude: - - 'slack-sup/app.rb' - - 'slack-sup/commands/set.rb' - - 'slack-sup/models/channel.rb' - - 'slack-sup/models/round_stats.rb' - - 'slack-sup/models/sup.rb' - - 'slack-sup/models/team.rb' + - 'lib/app.rb' + - 'lib/commands/set.rb' + - 'lib/models/channel.rb' + - 'lib/models/round_stats.rb' + - 'lib/models/sup.rb' + - 'lib/models/team.rb' # Offense count: 1 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros. @@ -101,12 +94,12 @@ Naming/MethodParameterName: Naming/PredicateName: Exclude: - 'spec/**/*' - - 'slack-sup/models/team.rb' + - 'lib/models/team.rb' # Offense count: 6 Style/IdenticalConditionalBranches: Exclude: - - 'slack-sup/commands/set.rb' + - 'lib/commands/set.rb' # Offense count: 1 Style/MixinUsage: @@ -123,24 +116,18 @@ Style/MultilineBlockChain: # Offense count: 1 Style/MultilineTernaryOperator: Exclude: - - 'slack-sup/api/endpoints/teams_endpoint.rb' - -# Offense count: 23 -# Cop supports --auto-correct. -Style/MultilineWhenThen: - Exclude: - - 'slack-sup/commands/set.rb' + - 'lib/api/endpoints/teams_endpoint.rb' # Offense count: 1 Style/MultipleComparison: Exclude: - - 'slack-sup/commands/opt.rb' + - 'lib/commands/opt.rb' # Offense count: 3 Style/NestedTernaryOperator: Exclude: - - 'slack-sup/commands/opt.rb' - - 'slack-sup/models/team.rb' + - 'lib/commands/opt.rb' + - 'lib/models/team.rb' # Offense count: 21 # Cop supports --auto-correct. @@ -149,9 +136,9 @@ Style/NestedTernaryOperator: Style/NumericPredicate: Exclude: - 'spec/**/*' - - 'slack-sup/models/channel_stats.rb' - - 'slack-sup/models/round.rb' - - 'slack-sup/models/round_stats.rb' - - 'slack-sup/models/stats.rb' - - 'slack-sup/models/team.rb' - - 'slack-sup/models/team_stats.rb' + - 'lib/models/channel_stats.rb' + - 'lib/models/round.rb' + - 'lib/models/round_stats.rb' + - 'lib/models/stats.rb' + - 'lib/models/team.rb' + - 'lib/models/team_stats.rb' diff --git a/DEBUGGING.md b/DEBUGGING.md index 438304e..150bf41 100644 --- a/DEBUGGING.md +++ b/DEBUGGING.md @@ -12,14 +12,3 @@ If Mongoid logging is annoying you. Mongoid.logger.level = Logger::INFO Mongo::Logger.logger.level = Logger::INFO ``` - -### Heroku - -``` -heroku run script/console --app=... - -Running `script/console` attached to terminal... up, run.7593 - -2.2.1 > Team.count -=> 3 -``` diff --git a/DEV.md b/DEV.md index e3de0dd..2fbada9 100644 --- a/DEV.md +++ b/DEV.md @@ -1,15 +1,13 @@ ## Development Environment -You may want to watch [Your First Slack Bot Service video](http://code.dblock.org/2016/03/11/your-first-slack-bot-service-video.html) first. - ### Prerequisites Ensure that you can build the project and run tests. You will need these. - [MongoDB](https://docs.mongodb.com/manual/installation/) - [Firefox](https://www.mozilla.org/firefox/new/) -- [Geckodriver](https://github.com/mozilla/geckodriver), download, `tar vfxz` and move to `/usr/local/bin` -- Ruby 2.3.1 +- [Geckodriver](https://github.com/mozilla/geckodriver) +- Ruby 2.7.7 ``` bundle install @@ -22,22 +20,18 @@ Create a Slack team [here](https://slack.com/create). ### Slack App -Create a test app [here](https://api.slack.com/apps). This gives you a client ID and a client secret. - -Under _Features/OAuth & Permissions_, configure the redirect URL to `http://localhost:5000`. +Create a test app [here](https://api.slack.com/apps) from [the manifest](manifest.yml). -Add the following Permission Scope. +Use [ngrok](https://ngrok.com/) to tunnel to `localhost:5000`. -* Add a bot user with the username @bot. +* Choose _Allow users to send Slash commands and messages from the messages tab_ under `App Home`. +* Use `https://....ngrok.io/api/slack/action` for _Interactivity and Shortcuts_. +* Use `https://....ngrok.io` for _Redirect Urls_ in _OAuth & Permissions_. +* Use `https://....ngrok.io/api/slack/event` in _Event Subscriptions_. ### Slack Keys -Create a `.env` file. - -``` -SLACK_CLIENT_ID=slack_client_id -SLACK_CLIENT_SECRET=slack_client_secret -``` +Create a `.env` file from [.env.sample](.env.sample) with at least the Slack keys. ### Stripe Keys @@ -59,18 +53,6 @@ $ foreman start Navigate to [localhost:5000](http://localhost:5000). -### Interactive Buttons - -To test interactive buttons locally you need [ngrok](https://ngrok.com) to tunnel to `localhost:5000`. - -``` -ngrok http 5000 -``` - -This will give you a forwarding HTTPs URL, such as `https://a740cdc9.ngrok.io -> localhost:5000`. - -Enter `https://a740cdc9.ngrok.io/api/slack/action/` in the Slack `Interactive Messages` configuration section of your app under `Request Url`. - ### Google Calendar Integration * Create a new project on https://console.developers.google.com @@ -79,6 +61,3 @@ Enter `https://a740cdc9.ngrok.io/api/slack/action/` in the Slack `Interactive Me * Choose `Add Credentials`, accessing `User Data` via `Google Calendar API` via `Web Browser (Javascript)` * Answer remaining questions and `Create a Client ID` * Set `GOOGLE_API_CLIENT_ID` - - - diff --git a/Gemfile b/Gemfile index 0581bc5..e3548b2 100644 --- a/Gemfile +++ b/Gemfile @@ -12,14 +12,14 @@ gem 'mailchimp_api_v3' gem 'mongoid' gem 'mongoid-scroll' gem 'newrelic_rpm' +gem 'puma' gem 'rack-robotz' gem 'rack-server-pages' gem 'slack-ruby-bot-server' +gem 'slack-ruby-bot-server-events-app-mentions' gem 'slack-ruby-bot-server-mailchimp' -gem 'slack-ruby-bot-server-rtm' gem 'slack-ruby-client' gem 'stripe', '~> 1.58.0' -gem 'unicorn' gem 'wannabe_bool' group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 913d8ca..74f242d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,11 +17,6 @@ GEM console (~> 1.10) nio4r (~> 2.3) timers (~> 4.1) - async-io (1.34.3) - async - async-websocket (0.8.0) - async-io - websocket-driver (~> 0.7.0) bson (4.15.0) builder (3.2.4) byebug (11.1.1) @@ -114,7 +109,6 @@ GEM kaminari-grape (1.0.1) grape kaminari-core (~> 1.0) - kgio (2.11.3) mailchimp_api_v3 (0.2.18) rest-client (~> 2) mime-types (3.3.1) @@ -156,6 +150,8 @@ GEM parser (2.7.1.0) ast (~> 2.4.0) public_suffix (4.0.6) + puma (6.1.0) + nio4r (~> 2.0) racc (1.6.2) rack (2.2.6.2) rack-accept (0.4.5) @@ -170,7 +166,6 @@ GEM rack-test (1.1.0) rack (>= 1.0, < 3) rainbow (3.0.0) - raindrops (0.19.1) rake (12.3.3) regexp_parser (1.7.0) reline (0.3.2) @@ -215,10 +210,6 @@ GEM selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) - slack-ruby-bot (0.16.1) - activesupport - hashie - slack-ruby-client (>= 0.14.0) slack-ruby-bot-server (1.2.1) async foreman @@ -230,13 +221,13 @@ GEM rack-rewrite rack-server-pages slack-ruby-client + slack-ruby-bot-server-events (0.3.2) + slack-ruby-bot-server (>= 0.12.0) + slack-ruby-bot-server-events-app-mentions (0.1.1) + slack-ruby-bot-server-events slack-ruby-bot-server-mailchimp (0.2.0) mailchimp_api_v3 slack-ruby-bot-server (>= 0.10.0) - slack-ruby-bot-server-rtm (0.2.0) - async-websocket (~> 0.8.0) - slack-ruby-bot (>= 0.12.0) - slack-ruby-bot-server (>= 1.0.0) slack-ruby-client (0.14.6) activesupport faraday (>= 0.9) @@ -260,9 +251,6 @@ GEM unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) - unicorn (5.5.4) - kgio (~> 2.6) - raindrops (~> 0.7) vcr (5.1.0) wannabe_bool (0.7.1) webmock (3.8.3) @@ -298,6 +286,7 @@ DEPENDENCIES mongoid-scroll mongoid-shell newrelic_rpm + puma rack-robotz rack-server-pages rack-test @@ -306,13 +295,12 @@ DEPENDENCIES rubocop selenium-webdriver slack-ruby-bot-server + slack-ruby-bot-server-events-app-mentions slack-ruby-bot-server-mailchimp - slack-ruby-bot-server-rtm slack-ruby-client stripe (~> 1.58.0) stripe-ruby-mock (~> 2.4.1) timecop - unicorn vcr wannabe_bool webmock diff --git a/Procfile b/Procfile index 5784a17..4fd3163 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: bundle exec unicorn -p $PORT +web: bundle exec puma -p $PORT diff --git a/Rakefile b/Rakefile index bb5c4b2..4b818e1 100644 --- a/Rakefile +++ b/Rakefile @@ -4,7 +4,7 @@ require 'bundler' Bundler.setup :default, :development $LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'slack-sup' +require 'app' require 'tasks/logger' diff --git a/app.json b/app.json deleted file mode 100644 index 84f2105..0000000 --- a/app.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Slack Sup", - "description": "A Slack bot for team sup.", - "respository": "https://github.com/dblock/slack-sup", - "keywords": ["slack", "bots", "sup", "teams"], - "addons": [ - "mongolab" - ] -} diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..b2f8dc6 --- /dev/null +++ b/app.rb @@ -0,0 +1,20 @@ +ENV['RACK_ENV'] ||= 'development' + +require 'bundler/setup' +Bundler.require :default, ENV['RACK_ENV'] + +Dir[File.expand_path('config/initializers', __dir__) + '/**/*.rb'].sort.each do |file| + require file +end + +Mongoid.load! File.expand_path('config/mongoid.yml', __dir__), ENV['RACK_ENV'] + +require_relative 'lib/service' +require_relative 'lib/version' +require_relative 'lib/info' +require_relative 'lib/models' +require_relative 'lib/events' +require_relative 'lib/actions' +require_relative 'lib/commands' +require_relative 'lib/api' +require_relative 'lib/app' diff --git a/config.ru b/config.ru index 484f1ca..e24ec53 100644 --- a/config.ru +++ b/config.ru @@ -1,22 +1,10 @@ -$LOAD_PATH.unshift(File.dirname(__FILE__)) - -ENV['RACK_ENV'] ||= 'development' - -require 'bundler/setup' -Bundler.require :default, ENV['RACK_ENV'] - -require 'slack-ruby-bot-server' -require 'slack-sup' +require_relative 'app' NewRelic::Agent.manual_start SlackSup::App.instance.prepare! +SlackSup::App.instance.start! -Thread.abort_on_exception = true - -Thread.new do - SlackRubyBotServer::Service.instance.start_from_database! - SlackSup::App.instance.after_start! -end +SlackRubyBotServer::Service.start! run Api::Middleware.instance diff --git a/config/initializers/slack-ruby-bot-server/commands/base.rb b/config/initializers/slack-ruby-bot-server/commands/base.rb deleted file mode 100644 index 00d4d64..0000000 --- a/config/initializers/slack-ruby-bot-server/commands/base.rb +++ /dev/null @@ -1,21 +0,0 @@ -module SlackRubyBot - module Commands - class Base - class << self - # replace https://github.com/slack-ruby/slack-ruby-bot-server-rtm/blob/main/lib/slack-ruby-bot-server-rtm/ext/slack-ruby-bot/commands/base.rb - def invoke(client, data) - _invoke client, data - rescue Mongoid::Errors::Validations => e - logger.info "#{name.demodulize.upcase}: #{client.owner}, error - #{e.document.class}, #{e.document.errors.to_hash}" - client.say(channel: data.channel, text: e.document.errors.map(&:message).join("\n")) - true - rescue StandardError => e - logger.info "#{name.demodulize.upcase}: #{client.owner}, #{e.class}: #{e}" - logger.debug e.backtrace.join("\n") - client.say(channel: data.channel, text: e.message) - true - end - end - end - end -end diff --git a/config/initializers/slack-ruby-bot/hooks/message.rb b/config/initializers/slack-ruby-bot/hooks/message.rb deleted file mode 100644 index b57ab3a..0000000 --- a/config/initializers/slack-ruby-bot/hooks/message.rb +++ /dev/null @@ -1,22 +0,0 @@ -module SlackRubyBot - module Hooks - class Message - # HACK: order command classes predictably - def command_classes - [ - SlackSup::Commands::Help, - SlackSup::Commands::About, - SlackSup::Commands::Subscription, - SlackSup::Commands::Unsubscribe, - SlackSup::Commands::Opt, - SlackSup::Commands::Set, - SlackSup::Commands::Sync, - SlackSup::Commands::Stats, - SlackSup::Commands::Rounds, - SlackSup::Commands::Next, - SlackSup::Commands::GCal - ] - end - end - end -end diff --git a/config/initializers/slack-ruby-client/web/client.rb b/config/initializers/slack-ruby-client/web/client.rb deleted file mode 100644 index d357b34..0000000 --- a/config/initializers/slack-ruby-client/web/client.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Slack - module Web - class PaginatedResult - include Enumerable - - attr_reader :client - attr_reader :method - attr_reader :params - - def initialize(client, method, params = {}) - @client = client - @method = method - @params = params - end - - def each - next_cursor = nil - loop do - query = { limit: 100 }.merge(params).merge(cursor: next_cursor) - response = client.send(method, query) - yield response - break unless response.response_metadata - - next_cursor = response.response_metadata.next_cursor - break if next_cursor.blank? - end - end - end - - class Client - def paginate(method, params = {}) - PaginatedResult.new(self, method, params) - end - end - end -end diff --git a/config/initializers/slack_ruby_bot_server.rb b/config/initializers/slack_ruby_bot_server.rb new file mode 100644 index 0000000..1e39386 --- /dev/null +++ b/config/initializers/slack_ruby_bot_server.rb @@ -0,0 +1,19 @@ +SlackRubyBotServer.configure do |config| + config.oauth_version = :v2 + config.oauth_scope = [ + 'app_mentions:read', + 'channels:history', + 'channels:read', + 'chat:write', + 'groups:history', + 'groups:read', + 'im:history', + 'im:read', + 'im:write', + 'mpim:history', + 'mpim:read', + 'mpim:write', + 'users:read', + 'users.profile:read' + ] +end diff --git a/config/mongoid.yml b/config/mongoid.yml index 150c7b7..567f282 100644 --- a/config/mongoid.yml +++ b/config/mongoid.yml @@ -1,7 +1,7 @@ development: clients: default: - database: slack_sup_development + database: slack_sup2_development hosts: - 127.0.0.1:27017 options: @@ -11,7 +11,7 @@ development: test: clients: default: - database: slack_sup_test + database: slack_sup2_test hosts: - 127.0.0.1:27017 options: diff --git a/lib/actions.rb b/lib/actions.rb new file mode 100644 index 0000000..e4ad30f --- /dev/null +++ b/lib/actions.rb @@ -0,0 +1,36 @@ +SlackRubyBotServer::Events.configure do |config| + config.on :action, 'interactive_message' do |action| + payload = action[:payload] + error! 'Missing actions.', 400 unless payload[:actions] + error! 'Missing action.', 400 unless payload[:actions].first + + case payload[:actions].first[:name] + when 'outcome' then + sup = Sup.find(payload[:callback_id]) || error!('Sup Not Found', 404) + sup.update_attributes!(outcome: payload[:actions].first[:value]) + + Api::Middleware.logger.info "Updated channel #{sup.round.channel}, sup #{sup} outcome to '#{sup.outcome}'." + + message = Sup::ASK_WHO_SUP_MESSAGE.dup + + message[:attachments].first[:callback_id] = sup.id.to_s + message[:attachments].first[:actions].each do |a| + a[:style] = a[:value] == sup.outcome ? 'primary' : 'default' + end + + message[:text] = case sup.outcome + when 'later' + "Thanks, I'll ask again in a couple of days." + else + 'Thanks for letting me know.' + end + + Faraday.post(payload[:response_url], { + response_type: 'in_channel', + thread_ts: payload[:original_message][:ts] + }.merge(message).to_json, 'Content-Type' => 'application/json') + end + + false + end +end diff --git a/lib/api.rb b/lib/api.rb new file mode 100644 index 0000000..8d1cf82 --- /dev/null +++ b/lib/api.rb @@ -0,0 +1,8 @@ +require 'grape' +require 'roar' +require 'grape-roar' + +require_relative 'api/helpers' +require_relative 'api/presenters' +require_relative 'api/endpoints' +require_relative 'api/middleware' diff --git a/lib/api/endpoints.rb b/lib/api/endpoints.rb new file mode 100644 index 0000000..5b6ef1f --- /dev/null +++ b/lib/api/endpoints.rb @@ -0,0 +1,11 @@ +require_relative 'endpoints/teams_endpoint' +require_relative 'endpoints/channels_endpoint' +require_relative 'endpoints/users_endpoint' +require_relative 'endpoints/rounds_endpoint' +require_relative 'endpoints/sups_endpoint' +require_relative 'endpoints/subscriptions_endpoint' +require_relative 'endpoints/status_endpoint' +require_relative 'endpoints/stats_endpoint' +require_relative 'endpoints/credit_cards_endpoint' +require_relative 'endpoints/slack_endpoint' +require_relative 'endpoints/root_endpoint' diff --git a/slack-sup/api/endpoints/channels_endpoint.rb b/lib/api/endpoints/channels_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/channels_endpoint.rb rename to lib/api/endpoints/channels_endpoint.rb diff --git a/slack-sup/api/endpoints/credit_cards_endpoint.rb b/lib/api/endpoints/credit_cards_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/credit_cards_endpoint.rb rename to lib/api/endpoints/credit_cards_endpoint.rb diff --git a/slack-sup/api/endpoints/root_endpoint.rb b/lib/api/endpoints/root_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/root_endpoint.rb rename to lib/api/endpoints/root_endpoint.rb diff --git a/slack-sup/api/endpoints/rounds_endpoint.rb b/lib/api/endpoints/rounds_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/rounds_endpoint.rb rename to lib/api/endpoints/rounds_endpoint.rb diff --git a/lib/api/endpoints/slack_endpoint.rb b/lib/api/endpoints/slack_endpoint.rb new file mode 100644 index 0000000..015cc20 --- /dev/null +++ b/lib/api/endpoints/slack_endpoint.rb @@ -0,0 +1,23 @@ +module Api + module Endpoints + class SlackEndpoint < Grape::API + namespace :slack do + format :json + + before do + ::Slack::Events::Request.new( + request, + signing_secret: SlackRubyBotServer::Events.config.signing_secret, + signature_expires_in: SlackRubyBotServer::Events.config.signature_expires_in + ).verify! + rescue ::Slack::Events::Request::TimestampExpired + error!('Invalid Signature', 403) + end + + mount SlackRubyBotServer::Events::Api::Endpoints::Slack::CommandsEndpoint + mount SlackRubyBotServer::Events::Api::Endpoints::Slack::ActionsEndpoint + mount SlackRubyBotServer::Events::Api::Endpoints::Slack::EventsEndpoint + end + end + end +end diff --git a/slack-sup/api/endpoints/stats_endpoint.rb b/lib/api/endpoints/stats_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/stats_endpoint.rb rename to lib/api/endpoints/stats_endpoint.rb diff --git a/slack-sup/api/endpoints/status_endpoint.rb b/lib/api/endpoints/status_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/status_endpoint.rb rename to lib/api/endpoints/status_endpoint.rb diff --git a/slack-sup/api/endpoints/subscriptions_endpoint.rb b/lib/api/endpoints/subscriptions_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/subscriptions_endpoint.rb rename to lib/api/endpoints/subscriptions_endpoint.rb diff --git a/slack-sup/api/endpoints/sups_endpoint.rb b/lib/api/endpoints/sups_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/sups_endpoint.rb rename to lib/api/endpoints/sups_endpoint.rb diff --git a/slack-sup/api/endpoints/teams_endpoint.rb b/lib/api/endpoints/teams_endpoint.rb similarity index 62% rename from slack-sup/api/endpoints/teams_endpoint.rb rename to lib/api/endpoints/teams_endpoint.rb index 6b0ad71..f1cb407 100644 --- a/slack-sup/api/endpoints/teams_endpoint.rb +++ b/lib/api/endpoints/teams_endpoint.rb @@ -36,28 +36,53 @@ class TeamsEndpoint < Grape::API desc 'Create a team using an OAuth token.' params do requires :code, type: String + optional :state, type: String end post do client = Slack::Web::Client.new raise 'Missing SLACK_CLIENT_ID or SLACK_CLIENT_SECRET.' unless ENV.key?('SLACK_CLIENT_ID') && ENV.key?('SLACK_CLIENT_SECRET') - rc = client.oauth_access( + options = { client_id: ENV['SLACK_CLIENT_ID'], client_secret: ENV['SLACK_CLIENT_SECRET'], code: params[:code] - ) + } + + rc = client.send(SlackRubyBotServer.config.oauth_access_method, options) + + token = nil + access_token = nil + user_id = nil + bot_user_id = nil + team_id = nil + team_name = nil + oauth_scope = nil + oauth_version = SlackRubyBotServer::Config.oauth_version + + case oauth_version + when :v2 + access_token = rc['access_token'] + token = rc['access_token'] + user_id = rc['authed_user']['id'] + bot_user_id = rc['bot_user_id'] + team_id = rc['team']['id'] + team_name = rc['team']['name'] + oauth_scope = rc['scope'] + when :v1 + raise 'invalid OAuth version' + end - token = rc['bot']['bot_access_token'] - bot_user_id = rc['bot']['bot_user_id'] - user_id = rc['user_id'] - access_token = rc['access_token'] team = Team.where(token: token).first - team ||= Team.where(team_id: rc['team_id']).first + team ||= Team.where(team_id: team_id, oauth_version: oauth_version).first + team ||= Team.where(team_id: team_id).first if team + team.ping_if_active! + team.update_attributes!( - token: token, + oauth_version: oauth_version, + oauth_scope: oauth_scope, activated_user_id: user_id, activated_user_access_token: access_token, bot_user_id: bot_user_id @@ -69,8 +94,10 @@ class TeamsEndpoint < Grape::API else team = Team.create!( token: token, - team_id: rc['team_id'], - name: rc['team_name'], + oauth_version: oauth_version, + oauth_scope: oauth_scope, + team_id: team_id, + name: team_name, activated_user_id: user_id, activated_user_access_token: access_token, bot_user_id: bot_user_id @@ -79,8 +106,10 @@ class TeamsEndpoint < Grape::API team.inform! Team::INSTALLED_TEXT - SlackRubyBotServer::Service.instance.create!(team) - present team, with: Api::Presenters::TeamPresenter + options = params.slice(:state) + + SlackRubyBotServer::Service.instance.create!(team, options) + present team, with: Presenters::TeamPresenter end end end diff --git a/slack-sup/api/endpoints/users_endpoint.rb b/lib/api/endpoints/users_endpoint.rb similarity index 100% rename from slack-sup/api/endpoints/users_endpoint.rb rename to lib/api/endpoints/users_endpoint.rb diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb new file mode 100644 index 0000000..9e3e5ed --- /dev/null +++ b/lib/api/helpers.rb @@ -0,0 +1,5 @@ +require_relative 'helpers/cursor_helpers' +require_relative 'helpers/pagination_parameters' +require_relative 'helpers/sort_helpers' +require_relative 'helpers/error_helpers' +require_relative 'helpers/auth_helpers' diff --git a/slack-sup/api/helpers/auth_helpers.rb b/lib/api/helpers/auth_helpers.rb similarity index 100% rename from slack-sup/api/helpers/auth_helpers.rb rename to lib/api/helpers/auth_helpers.rb diff --git a/slack-sup/api/helpers/cursor_helpers.rb b/lib/api/helpers/cursor_helpers.rb similarity index 100% rename from slack-sup/api/helpers/cursor_helpers.rb rename to lib/api/helpers/cursor_helpers.rb diff --git a/slack-sup/api/helpers/error_helpers.rb b/lib/api/helpers/error_helpers.rb similarity index 100% rename from slack-sup/api/helpers/error_helpers.rb rename to lib/api/helpers/error_helpers.rb diff --git a/slack-sup/api/helpers/pagination_parameters.rb b/lib/api/helpers/pagination_parameters.rb similarity index 100% rename from slack-sup/api/helpers/pagination_parameters.rb rename to lib/api/helpers/pagination_parameters.rb diff --git a/slack-sup/api/helpers/sort_helpers.rb b/lib/api/helpers/sort_helpers.rb similarity index 100% rename from slack-sup/api/helpers/sort_helpers.rb rename to lib/api/helpers/sort_helpers.rb diff --git a/slack-sup/api/middleware.rb b/lib/api/middleware.rb similarity index 100% rename from slack-sup/api/middleware.rb rename to lib/api/middleware.rb diff --git a/lib/api/presenters.rb b/lib/api/presenters.rb new file mode 100644 index 0000000..1d41164 --- /dev/null +++ b/lib/api/presenters.rb @@ -0,0 +1,20 @@ +require 'roar/representer' +require 'roar/json' +require 'roar/json/hal' + +require_relative 'presenters/paginated_presenter' +require_relative 'presenters/status_presenter' +require_relative 'presenters/stats_presenter' +require_relative 'presenters/team_stats_presenter' +require_relative 'presenters/channel_stats_presenter' +require_relative 'presenters/team_presenter' +require_relative 'presenters/teams_presenter' +require_relative 'presenters/root_presenter' +require_relative 'presenters/channel_presenter' +require_relative 'presenters/channels_presenter' +require_relative 'presenters/user_presenter' +require_relative 'presenters/users_presenter' +require_relative 'presenters/round_presenter' +require_relative 'presenters/rounds_presenter' +require_relative 'presenters/sup_presenter' +require_relative 'presenters/sups_presenter' diff --git a/slack-sup/api/presenters/channel_presenter.rb b/lib/api/presenters/channel_presenter.rb similarity index 100% rename from slack-sup/api/presenters/channel_presenter.rb rename to lib/api/presenters/channel_presenter.rb diff --git a/slack-sup/api/presenters/channel_stats_presenter.rb b/lib/api/presenters/channel_stats_presenter.rb similarity index 100% rename from slack-sup/api/presenters/channel_stats_presenter.rb rename to lib/api/presenters/channel_stats_presenter.rb diff --git a/slack-sup/api/presenters/channels_presenter.rb b/lib/api/presenters/channels_presenter.rb similarity index 100% rename from slack-sup/api/presenters/channels_presenter.rb rename to lib/api/presenters/channels_presenter.rb diff --git a/slack-sup/api/presenters/paginated_presenter.rb b/lib/api/presenters/paginated_presenter.rb similarity index 100% rename from slack-sup/api/presenters/paginated_presenter.rb rename to lib/api/presenters/paginated_presenter.rb diff --git a/slack-sup/api/presenters/root_presenter.rb b/lib/api/presenters/root_presenter.rb similarity index 100% rename from slack-sup/api/presenters/root_presenter.rb rename to lib/api/presenters/root_presenter.rb diff --git a/slack-sup/api/presenters/round_presenter.rb b/lib/api/presenters/round_presenter.rb similarity index 100% rename from slack-sup/api/presenters/round_presenter.rb rename to lib/api/presenters/round_presenter.rb diff --git a/slack-sup/api/presenters/rounds_presenter.rb b/lib/api/presenters/rounds_presenter.rb similarity index 100% rename from slack-sup/api/presenters/rounds_presenter.rb rename to lib/api/presenters/rounds_presenter.rb diff --git a/slack-sup/api/presenters/stats_presenter.rb b/lib/api/presenters/stats_presenter.rb similarity index 100% rename from slack-sup/api/presenters/stats_presenter.rb rename to lib/api/presenters/stats_presenter.rb diff --git a/slack-sup/api/presenters/status_presenter.rb b/lib/api/presenters/status_presenter.rb similarity index 100% rename from slack-sup/api/presenters/status_presenter.rb rename to lib/api/presenters/status_presenter.rb diff --git a/slack-sup/api/presenters/sup_presenter.rb b/lib/api/presenters/sup_presenter.rb similarity index 100% rename from slack-sup/api/presenters/sup_presenter.rb rename to lib/api/presenters/sup_presenter.rb diff --git a/slack-sup/api/presenters/sups_presenter.rb b/lib/api/presenters/sups_presenter.rb similarity index 100% rename from slack-sup/api/presenters/sups_presenter.rb rename to lib/api/presenters/sups_presenter.rb diff --git a/slack-sup/api/presenters/team_presenter.rb b/lib/api/presenters/team_presenter.rb similarity index 100% rename from slack-sup/api/presenters/team_presenter.rb rename to lib/api/presenters/team_presenter.rb diff --git a/slack-sup/api/presenters/team_stats_presenter.rb b/lib/api/presenters/team_stats_presenter.rb similarity index 100% rename from slack-sup/api/presenters/team_stats_presenter.rb rename to lib/api/presenters/team_stats_presenter.rb diff --git a/slack-sup/api/presenters/teams_presenter.rb b/lib/api/presenters/teams_presenter.rb similarity index 100% rename from slack-sup/api/presenters/teams_presenter.rb rename to lib/api/presenters/teams_presenter.rb diff --git a/slack-sup/api/presenters/user_presenter.rb b/lib/api/presenters/user_presenter.rb similarity index 100% rename from slack-sup/api/presenters/user_presenter.rb rename to lib/api/presenters/user_presenter.rb diff --git a/slack-sup/api/presenters/users_presenter.rb b/lib/api/presenters/users_presenter.rb similarity index 100% rename from slack-sup/api/presenters/users_presenter.rb rename to lib/api/presenters/users_presenter.rb diff --git a/slack-sup/app.rb b/lib/app.rb similarity index 89% rename from slack-sup/app.rb rename to lib/app.rb index e873f7f..776ce4f 100644 --- a/slack-sup/app.rb +++ b/lib/app.rb @@ -5,21 +5,24 @@ def prepare! deactivate_asleep_teams! end - def after_start! - ::Async::Reactor.run do - logger.info 'Starting sup and subscription crons.' - once_and_every 60 * 60 * 24 * 3 do - check_subscribed_teams! - check_expired_subscriptions! - end - once_and_every 60 * 30 do - sync! - sup! - end - once_and_every 60 * 30 do - remind! - ask! - ask_again! + def start! + Thread.new do + Thread.current.abort_on_exception = true + ::Async::Reactor.run do + logger.info 'Starting sup and subscription crons.' + once_and_every 60 * 60 * 24 * 3 do + check_subscribed_teams! + check_expired_subscriptions! + end + once_and_every 60 * 30 do + sync! + sup! + end + once_and_every 60 * 30 do + remind! + ask! + ask_again! + end end end end diff --git a/lib/commands.rb b/lib/commands.rb new file mode 100644 index 0000000..80cb9d7 --- /dev/null +++ b/lib/commands.rb @@ -0,0 +1,28 @@ +require_relative 'commands/mixins' +require_relative 'commands/help' +require_relative 'commands/about' +require_relative 'commands/subscription' +require_relative 'commands/unsubscribe' +require_relative 'commands/opt' +require_relative 'commands/set' +require_relative 'commands/stats' +require_relative 'commands/next' +require_relative 'commands/rounds' +require_relative 'commands/gcal' +require_relative 'commands/sync' + +SlackRubyBotServer::Events::AppMentions.configure do |config| + config.handlers = [ + SlackSup::Commands::Help, + SlackSup::Commands::About, + SlackSup::Commands::Subscription, + SlackSup::Commands::Unsubscribe, + SlackSup::Commands::Set, + SlackSup::Commands::Opt, + SlackSup::Commands::Stats, + SlackSup::Commands::Next, + SlackSup::Commands::Rounds, + SlackSup::Commands::GCal, + SlackSup::Commands::Sync + ] +end diff --git a/lib/commands/about.rb b/lib/commands/about.rb new file mode 100644 index 0000000..ff80be9 --- /dev/null +++ b/lib/commands/about.rb @@ -0,0 +1,14 @@ +module SlackSup + module Commands + class About < SlackRubyBotServer::Events::AppMentions::Mention + mention 'about' + + def self.call(data) + return if data.user == data.team.bot_user_id + + data.team.slack_client.chat_postMessage(channel: data.channel, text: SlackSup::INFO) + logger.info "INFO: #{data.team}, user=#{data.user}" + end + end + end +end diff --git a/lib/commands/gcal.rb b/lib/commands/gcal.rb new file mode 100644 index 0000000..20388d4 --- /dev/null +++ b/lib/commands/gcal.rb @@ -0,0 +1,21 @@ +module SlackSup + module Commands + class GCal < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::Subscribe + + subscribe_command 'gcal' do |data| + raise SlackSup::Error, 'Missing GOOGLE_API_CLIENT_ID.' unless ENV['GOOGLE_API_CLIENT_ID'] + + sup = Sup.where(conversation_id: data.channel).desc(:_id).first + raise SlackSup::Error, "Please `#{data.team.bot_name} cal date/time` inside a S'Up DM channel." unless sup + + Chronic.time_class = sup.channel.sup_tzone + dt = Chronic.parse(data.match['expression']) if data.match['expression'] + raise SlackSup::Error, "Please specify a date/time, eg. `#{data.team.bot_name} cal tomorrow 5pm`." unless dt + + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Click this link to create a gcal for #{dt.strftime('%A, %B %d, %Y')} at #{dt.strftime('%l:%M %P').strip}: #{sup.calendar_href(dt)}") + logger.info "CALENDAR: #{data.team}, user=#{data.user}" + end + end + end +end diff --git a/slack-sup/commands/help.rb b/lib/commands/help.rb similarity index 90% rename from slack-sup/commands/help.rb rename to lib/commands/help.rb index 9b3787b..af615fc 100644 --- a/slack-sup/commands/help.rb +++ b/lib/commands/help.rb @@ -1,6 +1,8 @@ module SlackSup module Commands - class Help < SlackRubyBot::Commands::Base + class Help < SlackRubyBotServer::Events::AppMentions::Mention + mention 'help' + HELP = <<~EOS.freeze ``` Hi there! I'm your team's S'Up bot. @@ -59,12 +61,14 @@ class Help < SlackRubyBot::Commands::Base More information at https://sup.playplay.io ``` EOS - def self.call(client, data, _match) - client.say(channel: data.channel, text: [ + def self.call(data) + return if data.user == data.team.bot_user_id + + data.team.slack_client.chat_postMessage(channel: data.channel, text: [ HELP, - client.owner.reload.subscribed? ? nil : client.owner.trial_message + data.team.reload.subscribed? ? nil : data.team.trial_message ].compact.join("\n")) - logger.info "HELP: #{client.owner}, user=#{data.user}" + logger.info "HELP: #{data.team}, user=#{data.user}" end end end diff --git a/lib/commands/mixins.rb b/lib/commands/mixins.rb new file mode 100644 index 0000000..91339b4 --- /dev/null +++ b/lib/commands/mixins.rb @@ -0,0 +1,4 @@ +require_relative 'mixins/subscribe' +require_relative 'mixins/channel' +require_relative 'mixins/user' +require_relative 'mixins/pluralize' diff --git a/slack-sup/commands/mixins/channel.rb b/lib/commands/mixins/channel.rb similarity index 58% rename from slack-sup/commands/mixins/channel.rb rename to lib/commands/mixins/channel.rb index 53396eb..3374317 100644 --- a/slack-sup/commands/mixins/channel.rb +++ b/lib/commands/mixins/channel.rb @@ -7,9 +7,9 @@ module Channel module ClassMethods def channel_command(*values, &_block) - subscribe_command(*values) do |client, data, match| - channel = client.owner.find_create_or_update_channel_by_channel_id!(data.channel, data.user) - yield client, channel, data, match + subscribe_command(*values) do |data| + channel = data.team.find_create_or_update_channel_by_channel_id!(data.channel, data.user) + yield channel, data end end end diff --git a/slack-sup/commands/mixins/pluralize.rb b/lib/commands/mixins/pluralize.rb similarity index 100% rename from slack-sup/commands/mixins/pluralize.rb rename to lib/commands/mixins/pluralize.rb diff --git a/lib/commands/mixins/subscribe.rb b/lib/commands/mixins/subscribe.rb new file mode 100644 index 0000000..7a05858 --- /dev/null +++ b/lib/commands/mixins/subscribe.rb @@ -0,0 +1,24 @@ +module SlackSup + module Commands + module Mixins + module Subscribe + extend ActiveSupport::Concern + + module ClassMethods + def subscribe_command(*values, &_block) + mention(*values) do |data| + return if data.user == data.team.bot_user_id + + if Stripe.api_key && data.team.reload.subscription_expired? + data.team.slack_client.chat_postMessage channel: data.channel, text: data.team.subscribe_text + logger.info "#{data.team}, user=#{data.user}, text=#{data.text}, subscription expired" + else + yield data + end + end + end + end + end + end + end +end diff --git a/slack-sup/commands/mixins/user.rb b/lib/commands/mixins/user.rb similarity index 53% rename from slack-sup/commands/mixins/user.rb rename to lib/commands/mixins/user.rb index 8cbccc8..30b27ba 100644 --- a/slack-sup/commands/mixins/user.rb +++ b/lib/commands/mixins/user.rb @@ -7,9 +7,9 @@ module User module ClassMethods def user_command(*values, &_block) - subscribe_command(*values) do |client, data, match| - user = client.owner.find_create_or_update_user_in_channel_by_slack_id!(data.channel, data.user) - yield client, user.is_a?(::User) ? user.channel : nil, user, data, match + subscribe_command(*values) do |data| + user = data.team.find_create_or_update_user_in_channel_by_slack_id!(data.channel, data.user) + yield user.is_a?(::User) ? user.channel : nil, user, data end end end diff --git a/lib/commands/next.rb b/lib/commands/next.rb new file mode 100644 index 0000000..0df7adc --- /dev/null +++ b/lib/commands/next.rb @@ -0,0 +1,13 @@ +module SlackSup + module Commands + class Next < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::Channel + + channel_command 'next' do |channel, data| + channels = channel ? [channel] : data.team.channels.enabled.asc(:_id) + data.team.slack_client.chat_postMessage(channel: data.channel, text: channels.map(&:next_sup_at_text).join("\n")) + logger.info "NEXT: #{data.team}, channel=#{data.channel}, user=#{data.user}" + end + end + end +end diff --git a/slack-sup/commands/opt.rb b/lib/commands/opt.rb similarity index 77% rename from slack-sup/commands/opt.rb rename to lib/commands/opt.rb index 2b4dbd1..e3eed40 100644 --- a/slack-sup/commands/opt.rb +++ b/lib/commands/opt.rb @@ -1,15 +1,15 @@ module SlackSup module Commands - class Opt < SlackRubyBot::Commands::Base + class Opt < SlackRubyBotServer::Events::AppMentions::Mention include SlackSup::Commands::Mixins::User include SlackSup::Commands::Mixins::Pluralize - user_command 'opt' do |client, channel, user, data, match| + user_command 'opt' do |channel, user, data| user_ids = [] channel_ids = [] op = nil - parts = match['expression'].split(/[\s]+/) if match['expression'] + parts = data.match['expression'].split(/[\s]+/) if data.match['expression'] parts&.each do |part| if part == 'in' || part == 'out' op = part @@ -23,15 +23,15 @@ class Opt < SlackRubyBot::Commands::Base end if user_ids.any? && channel.nil? - raise SlackSup::Error, "Sorry, only <@#{client.owner.activated_user_id}> or a Slack team admin can opt users in or out." unless client.owner.is_admin?(data.user) + raise SlackSup::Error, "Sorry, only <@#{data.team.activated_user_id}> or a Slack team admin can opt users in or out." unless data.team.is_admin?(data.user) elsif channel && user && user_ids.any? raise SlackSup::Error, "Sorry, only <@#{channel.inviter_id}> or a Slack team admin can opt users in and out." unless user.channel_admin? elsif channel && user && channel_ids.any? - raise SlackSup::Error, "Please DM @#{client.owner.name} to opt users in and out of channels." unless user.channel_admin? + raise SlackSup::Error, "Please DM @#{data.team.name} to opt users in and out of channels." unless user.channel_admin? end user_ids << data.user if user_ids.none? - channel_ids = client.owner.channels.enabled.asc(:_id).map(&:channel_id) if channel_ids.none? + channel_ids = data.team.channels.enabled.asc(:_id).map(&:channel_id) if channel_ids.none? messages = [] @@ -44,7 +44,7 @@ class Opt < SlackRubyBot::Commands::Base myself = (user_id == data.user) channel_ids.each do |channel_id| - channel = client.owner.channels.where(channel_id: channel_id).first + channel = data.team.channels.where(channel_id: channel_id).first raise SlackSup::Error, "Sorry, I can't find an existing S'Up channel <##{channel_id}>." unless channel user = channel.users.where(user_id: user_id).first @@ -86,8 +86,8 @@ class Opt < SlackRubyBot::Commands::Base end end - client.say(channel: data.channel, text: messages.join("\n")) - logger.info "OPT: #{client.owner}, for=#{user}, channel=#{data.channel}, user=#{data.user}" + data.team.slack_client.chat_postMessage(channel: data.channel, text: messages.join("\n")) + logger.info "OPT: #{data.team}, for=#{user}, channel=#{data.channel}, user=#{data.user}" end end end diff --git a/slack-sup/commands/rounds.rb b/lib/commands/rounds.rb similarity index 55% rename from slack-sup/commands/rounds.rb rename to lib/commands/rounds.rb index 23d34cf..290da02 100644 --- a/slack-sup/commands/rounds.rb +++ b/lib/commands/rounds.rb @@ -1,12 +1,12 @@ module SlackSup module Commands - class Rounds < SlackRubyBot::Commands::Base + class Rounds < SlackRubyBotServer::Events::AppMentions::Mention include SlackSup::Commands::Mixins::Channel include SlackSup::Commands::Mixins::Pluralize - def self.parse_arg(match) + def self.parse_arg(data) max = 3 - arguments = match['expression'].split.reject(&:blank?) if match['expression'] + arguments = data.match['expression'].split.reject(&:blank?) if data.match['expression'] arguments ||= [] number = arguments.shift if number @@ -20,8 +20,8 @@ def self.parse_arg(match) max end - channel_command 'rounds' do |client, channel, data, match| - max = parse_arg(match) + channel_command 'rounds' do |channel, data| + max = parse_arg(data) messages = [] if channel messages << "Channel S'Up facilitated #{pluralize(channel.rounds.count, 'round')}." @@ -29,13 +29,13 @@ def self.parse_arg(match) messages << RoundStats.new(round).to_s end else - messages << "Team S'Up facilitated #{pluralize(client.owner.rounds.count, 'round')} in #{pluralize(client.owner.channels.count, 'channel')}." - client.owner.rounds.desc(:_id).limit(max).each do |round| + messages << "Team S'Up facilitated #{pluralize(data.team.rounds.count, 'round')} in #{pluralize(data.team.channels.count, 'channel')}." + data.team.rounds.desc(:_id).limit(max).each do |round| messages << RoundStats.new(round).to_s(true) end end - client.say(channel: data.channel, text: messages.join("\n")) - logger.info "STATS: #{client.owner}, channel=#{data.channel}, user=#{data.user}" + data.team.slack_client.chat_postMessage(channel: data.channel, text: messages.join("\n")) + logger.info "STATS: #{data.team}, channel=#{data.channel}, user=#{data.user}" end end end diff --git a/slack-sup/commands/set.rb b/lib/commands/set.rb similarity index 54% rename from slack-sup/commands/set.rb rename to lib/commands/set.rb index 66569f8..42b0174 100644 --- a/slack-sup/commands/set.rb +++ b/lib/commands/set.rb @@ -1,246 +1,246 @@ module SlackSup module Commands - class Set < SlackRubyBot::Commands::Base + class Set < SlackRubyBotServer::Events::AppMentions::Mention include SlackSup::Commands::Mixins::User class << self - def set_opt_in(client, channel, data, user, v = nil) + def set_opt_in(channel, data, user, v = nil) raise ArgumentError, "Invalid value: #{v}." unless ['in', 'out', nil].include?(v) if user.channel_admin? && v channel.update_attributes!(opt_in: v == 'in') - client.say(channel: data.channel, text: "Users are now opted #{v} by default.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Users are now opted #{v} by default.") elsif v message = [ "Users are opted #{channel.opt_in_s} by default.", "Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else - client.say(channel: data.channel, text: "Users are opted #{channel.opt_in_s} by default.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Users are opted #{channel.opt_in_s} by default.") end logger.info "SET: #{channel}, user=#{data.user}, opt_in=#{channel.opt_in}" end - def set_api(client, channel, data, user, v = nil) + def set_api(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(api: v.to_b) message = [ channel_data_access_message(user.reload, true), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif v message = [ channel_data_access_message(user), "Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ channel_data_access_message(user), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{channel}, user=#{data.user}, api=#{channel.api_s}" end - def set_api_token(client, channel, data, user) + def set_api_token(channel, data, user) if !channel.api? - set_api(client, channel, data, user) + set_api(channel, data, user) elsif user.channel_admin? && !channel.api_token channel.update_attributes!(api_token: SecureRandom.hex) message = [ channel_data_access_message(user.reload, false, true), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif user.channel_admin? && channel.api_token message = [ channel_data_access_message(user), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif !channel.api_token message = [ channel_data_access_message(user), "Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ channel_data_access_message(user), channel.api_url ].join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{channel}, user=#{data.user}, api=#{channel.api_s}, api_token=#{channel.api_token ? '(set)' : '(not set)'}" end - def unset_api_token(client, channel, data, user) + def unset_api_token(channel, data, user) if !channel.api? - set_api(client, channel, data, user) + set_api(channel, data, user) elsif user.channel_admin? && channel.api_token channel.update_attributes!(api_token: nil) message = [ channel_data_access_message(user.reload, true), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif user.channel_admin? && !channel.api_token message = [ channel_data_access_message(user), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ channel_data_access_message(user), "Only <@#{channel.inviter_id}> or a Slack team admin can unset it, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "UNSET: #{channel}, user=#{data.user}, api=#{channel.api_s}, api_token=#{channel.api_token ? '(set)' : '(not set)'}" end - def rotate_api_token(client, channel, data, user) + def rotate_api_token(channel, data, user) if !channel.api? - set_api(client, channel, data, user) + set_api(channel, data, user) elsif user.channel_admin? channel.update_attributes!(api_token: SecureRandom.hex) message = [ channel_data_access_message(user.reload, false, true), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ channel_data_access_message(user), "Only <@#{channel.inviter_id}> or a Slack team admin can rotate it, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{channel}, user=#{data.user}, api=#{channel.api_s}, api_token=(rotated)" end - def team_set_api(client, team, data, user, v = nil) + def team_set_api(team, data, user, v = nil) if team.is_admin?(user) && v team.update_attributes!(api: v.to_b) message = [ team_data_access_message(team, user, true), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif v message = [ team_data_access_message(team, user), "Only <@#{team.activated_user_id}> or a Slack team admin can change that, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ team_data_access_message(team, user), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{team}, user=#{user}, api=#{team.api_s}" end - def team_set_api_token(client, team, data, user) + def team_set_api_token(team, data, user) if !team.api? - team_set_api(client, team, data, user) + team_set_api(team, data, user) elsif team.is_admin?(user) && !team.api_token team.update_attributes!(api_token: SecureRandom.hex) message = [ team_data_access_message(team, user, false, true), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif team.is_admin?(user) && team.api_token message = [ team_data_access_message(team, user), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif !team.api_token message = [ team_data_access_message(team, user), "Only <@#{team.activated_user_id}> or a Slack team admin can change that, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ team_data_access_message(team, user), team.api_url ].join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{team}, user=#{user}, api=#{team.api_s}, api_token=#{team.api_token ? '(set)' : '(not set)'}" end - def team_unset_api_token(client, team, data, user) + def team_unset_api_token(team, data, user) if !team.api? - set_api(client, team, data, user) + set_api(team, data, user) elsif team.is_admin?(user) && team.api_token team.update_attributes!(api_token: nil) message = [ team_data_access_message(team, user, true), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) elsif team.is_admin?(user) && !team.api_token message = [ team_data_access_message(team, user), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ team_data_access_message(team, user), "Only <@#{team.activated_user_id}> or a Slack team admin can unset it, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "UNSET: #{team}, user=#{user}, api=#{team.api_s}, api_token=#{team.api_token ? '(set)' : '(not set)'}" end - def team_rotate_api_token(client, team, data, user) + def team_rotate_api_token(team, data, user) if !team.api? - set_api(client, team, data, user) + set_api(team, data, user) elsif team.is_admin?(user) team.update_attributes!(api_token: SecureRandom.hex) message = [ team_data_access_message(team, user, false, true), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) else message = [ team_data_access_message(team, user), "Only <@#{team.activated_user_id}> or a Slack team admin can rotate it, sorry." ].join(' ') - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) end logger.info "SET: #{team}, user=#{user}, api=#{team.api_s}, api_token=(rotated)" end - def set_day(client, channel, data, user, v = nil) + def set_day(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes(sup_wday: Date.parse(v).wday) - client.say(channel: data.channel, text: "Channel S'Up is now on #{channel.sup_day}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is now on #{channel.sup_day}.") elsif v - client.say(channel: data.channel, text: "Channel S'Up is on #{channel.sup_day}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is on #{channel.sup_day}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up is on #{channel.sup_day}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is on #{channel.sup_day}.") end logger.info "SET: #{channel}, user=#{data.user}, sup_day=#{channel.sup_day}." rescue ArgumentError raise SlackSup::Error, "Day _#{v}_ is invalid, try _Monday_, _Tuesday_, etc. Channel S'Up is on #{channel.sup_day}." end - def set_time(client, channel, data, user, v = nil) + def set_time(channel, data, user, v = nil) if user.channel_admin? && v # attempt to parse a timezone right to left z = [] @@ -252,248 +252,248 @@ def set_time(client, channel, data, user, v = nil) end channel.update_attributes!(sup_tz: tz.name) if tz channel.update_attributes!(sup_time_of_day: DateTime.parse(v).seconds_since_midnight) - client.say(channel: data.channel, text: "Channel S'Up is now after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is now after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}.") elsif v - client.say(channel: data.channel, text: "Channel S'Up is after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up is after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is after #{channel.sup_time_of_day_s} #{channel.sup_tzone_s}.") end logger.info "SET: #{channel}, user=#{data.user}, sup_time_of_day=#{channel.sup_time_of_day_s}." rescue StandardError raise SlackSup::Error, "Time _#{v}_ is invalid. Channel S'Up is after #{channel.reload.sup_time_of_day_s} #{channel.sup_tzone_s}." end - def set_followup_day(client, channel, data, user, v = nil) + def set_followup_day(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes(sup_followup_wday: Date.parse(v).wday) - client.say(channel: data.channel, text: "Channel S'Up followup day is now on #{channel.sup_followup_day}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up followup day is now on #{channel.sup_followup_day}.") elsif v - client.say(channel: data.channel, text: "Channel S'Up followup day is on #{channel.sup_followup_day}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up followup day is on #{channel.sup_followup_day}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up followup day is on #{channel.sup_followup_day}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up followup day is on #{channel.sup_followup_day}.") end logger.info "SET: #{channel}, user=#{data.user}, sup_followup_day=#{channel.sup_followup_day}." rescue ArgumentError raise SlackSup::Error, "Day _#{v}_ is invalid, try _Monday_, _Tuesday_, etc. Channel S'Up followup day is on #{channel.sup_followup_day}." end - def set_weeks(client, channel, data, user, v = nil) + def set_weeks(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(sup_every_n_weeks: v.to_i) - client.say(channel: data.channel, text: "Channel S'Up is now every #{channel.sup_every_n_weeks_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is now every #{channel.sup_every_n_weeks_s}.") elsif v - client.say(channel: data.channel, text: "Channel S'Up is every #{channel.sup_every_n_weeks_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is every #{channel.sup_every_n_weeks_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up is every #{channel.sup_every_n_weeks_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up is every #{channel.sup_every_n_weeks_s}.") end logger.info "SET: #{channel}, user=#{data.user}, sup_every_n_weeks=#{channel.sup_every_n_weeks_s}." rescue StandardError raise SlackSup::Error, "Number _#{v}_ is invalid. Channel S'Up is every #{channel.reload.sup_every_n_weeks_s}." end - def set_size(client, channel, data, user, v = nil) + def set_size(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(sup_size: v.to_i) - client.say(channel: data.channel, text: "Channel S'Up now connects groups of #{channel.sup_size} people.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up now connects groups of #{channel.sup_size} people.") elsif v - client.say(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_size} people. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_size} people. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_size} people.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_size} people.") end logger.info "SET: #{channel}, user=#{data.user}, sup_size=#{channel.sup_size}." rescue StandardError raise SlackSup::Error, "Number _#{v}_ is invalid. Channel S'Up connects groups of #{channel.reload.sup_size} people." end - def set_odd(client, channel, data, user, v = nil) + def set_odd(channel, data, user, v = nil) if user.channel_admin? && !v.nil? channel.update_attributes!(sup_odd: v.to_b) - client.say(channel: data.channel, text: "Channel S'Up now connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up now connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people.") elsif !v.nil? - client.say(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people.") end logger.info "SET: #{channel}, user=#{data.user}, sup_odd=#{channel.sup_odd}." end - def set_timezone(client, channel, data, user, v = nil) + def set_timezone(channel, data, user, v = nil) if user.channel_admin? && v timezone = ActiveSupport::TimeZone.new(v) raise SlackSup::Error, "TimeZone _#{v}_ is invalid, see https://github.com/rails/rails/blob/v#{ActiveSupport.gem_version}/activesupport/lib/active_support/values/time_zone.rb#L30 for a list. Channel S'Up timezone is currently #{channel.sup_tzone}." unless timezone channel.update_attributes!(sup_tz: timezone.name) - client.say(channel: data.channel, text: "Channel S'Up timezone is now #{channel.sup_tzone}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up timezone is now #{channel.sup_tzone}.") elsif v - client.say(channel: data.channel, text: "Channel S'Up timezone is #{channel.sup_tzone}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up timezone is #{channel.sup_tzone}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Channel S'Up timezone is #{channel.sup_tzone}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Channel S'Up timezone is #{channel.sup_tzone}.") end logger.info "SET: #{channel} user=#{data.user}, timezone=#{channel.sup_tzone}." end - def set_custom_profile_team_field(client, channel, data, user, v = nil) + def set_custom_profile_team_field(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(team_field_label: v) - client.say(channel: data.channel, text: "Custom profile team field is now _#{channel.team_field_label}_.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Custom profile team field is now _#{channel.team_field_label}_.") elsif v - client.say(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_.") end logger.info "SET: #{channel}, user=#{data.user}, team_field_label=#{channel.team_field_label || '(not set)'}." end - def unset_custom_profile_team_field(client, channel, data, user) + def unset_custom_profile_team_field(channel, data, user) if user.channel_admin? channel.update_attributes!(team_field_label: nil) - client.say(channel: data.channel, text: 'Custom profile team field is now _not set_.') + data.team.slack_client.chat_postMessage(channel: data.channel, text: 'Custom profile team field is now _not set_.') else - client.say(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Custom profile team field is _#{channel.team_field_label || 'not set'}_. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") end logger.info "UNSET: #{channel}, user=#{data.user}, team_field_label=#{channel.team_field_label || '(not set)'}." end - def set_message(client, channel, data, user, v = nil) + def set_message(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(sup_message: v.to_s) - client.say(channel: data.channel, text: "Now using a custom S'Up message. _#{channel.sup_message}_") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Now using a custom S'Up message. _#{channel.sup_message}_") elsif v && channel.sup_message - client.say(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") elsif v && !channel.sup_message - client.say(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") elsif channel.sup_message - client.say(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_") else - client.say(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_") end logger.info "SET: #{channel}, user=#{data.user}, sup_message=#{channel.sup_message || '(not set)'}." end - def unset_message(client, channel, data, user) + def unset_message(channel, data, user) if user.channel_admin? channel.update_attributes!(sup_message: nil) - client.say(channel: data.channel, text: "Now using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Now using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_") elsif channel.sup_message - client.say(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using a custom S'Up message. _#{channel.sup_message}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") end logger.info "UNSET: #{channel}, user=#{data.user}, sup_message=#{channel.sup_message || '(not set)'}." end - def set_recency(client, channel, data, user, v = nil) + def set_recency(channel, data, user, v = nil) if user.channel_admin? && v channel.update_attributes!(sup_recency: v.to_i) - client.say(channel: data.channel, text: "Now taking special care to not pair the same people more than every #{channel.sup_recency_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Now taking special care to not pair the same people more than every #{channel.sup_recency_s}.") elsif v - client.say(channel: data.channel, text: "Taking special care to not pair the same people more than every #{channel.sup_recency_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Taking special care to not pair the same people more than every #{channel.sup_recency_s}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry.") else - client.say(channel: data.channel, text: "Taking special care to not pair the same people more than every #{channel.sup_recency_s}.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Taking special care to not pair the same people more than every #{channel.sup_recency_s}.") end logger.info "SET: #{channel}, user=#{data.user}, sup_recency=#{channel.sup_recency_s}." rescue StandardError raise SlackSup::Error, "Number _#{v}_ is invalid. Taking special care to not pair the same people more than every #{channel.reload.sup_recency_s}." end - def set_sync(client, channel, data, user, v = nil) + def set_sync(channel, data, user, v = nil) if user.channel_admin? && v case v - when 'now' then + when 'now' channel.update_attributes!(sync: true) else raise SlackSup::Error, "The option _#{v}_ is invalid. Use `now` to schedule a user sync in the next hour." end - client.say(channel: data.channel, text: "#{channel.last_sync_at_text} Come back and run `set sync` or `stats` in a bit.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "#{channel.last_sync_at_text} Come back and run `set sync` or `stats` in a bit.") elsif v - client.say(channel: data.channel, text: "#{channel.last_sync_at_text} Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry.") + data.team.slack_client.chat_postMessage(channel: data.channel, text: "#{channel.last_sync_at_text} Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry.") else - client.say(channel: data.channel, text: channel.last_sync_at_text) + data.team.slack_client.chat_postMessage(channel: data.channel, text: channel.last_sync_at_text) end logger.info "SET: #{channel}, user=#{data.user}, sync_users=#{channel.sync}, last_sync_at=#{channel.last_sync_at}." end - def team_set(client, team, data, user, k, v) + def team_set(team, data, user, k, v) case k - when 'api' then - team_set_api client, team, data, user, v - when 'apitoken' then - team_set_api_token client, team, data, user + when 'api' + team_set_api team, data, user, v + when 'apitoken' + team_set_api_token team, data, user else raise SlackSup::Error, "Invalid global setting _#{k}_, see _help_ for available options." end end - def channel_set(client, channel, data, user, k, v) + def channel_set(channel, data, user, k, v) case k - when 'opt' then - set_opt_in client, channel, data, user, v - when 'api' then - set_api client, channel, data, user, v - when 'apitoken' then - set_api_token client, channel, data, user - when 'day' then - set_day client, channel, data, user, v - when 'followup' then - set_followup_day client, channel, data, user, v - when 'tz', 'timezone' then - set_timezone client, channel, data, user, v - when 'teamfield' then - set_custom_profile_team_field client, channel, data, user, v - when 'weeks' then - set_weeks client, channel, data, user, v - when 'recency' then - set_recency client, channel, data, user, v - when 'time' then - set_time client, channel, data, user, v - when 'size' then - set_size client, channel, data, user, v - when 'odd' then - set_odd client, channel, data, user, v - when 'message' then - set_message client, channel, data, user, v - when 'sync' then - set_sync client, channel, data, user, v + when 'opt' + set_opt_in channel, data, user, v + when 'api' + set_api channel, data, user, v + when 'apitoken' + set_api_token channel, data, user + when 'day' + set_day channel, data, user, v + when 'followup' + set_followup_day channel, data, user, v + when 'tz', 'timezone' + set_timezone channel, data, user, v + when 'teamfield' + set_custom_profile_team_field channel, data, user, v + when 'weeks' + set_weeks channel, data, user, v + when 'recency' + set_recency channel, data, user, v + when 'time' + set_time channel, data, user, v + when 'size' + set_size channel, data, user, v + when 'odd' + set_odd channel, data, user, v + when 'message' + set_message channel, data, user, v + when 'sync' + set_sync channel, data, user, v else raise SlackSup::Error, "Invalid channel setting _#{k}_, see _help_ for available options." end end - def channel_unset(client, channel, data, user, k) + def channel_unset(channel, data, user, k) case k - when 'teamfield' then - unset_custom_profile_team_field client, channel, data, user - when 'apitoken' then - unset_api_token client, channel, data, user - when 'message' then - unset_message client, channel, data, user + when 'teamfield' + unset_custom_profile_team_field channel, data, user + when 'apitoken' + unset_api_token channel, data, user + when 'message' + unset_message channel, data, user else raise SlackSup::Error, "Invalid channel setting _#{k}_, see _help_ for available options." end end - def team_unset(client, team, data, user, k) + def team_unset(team, data, user, k) case k - when 'apitoken' then - team_unset_api_token client, team, data, user + when 'apitoken' + team_unset_api_token team, data, user else raise SlackSup::Error, "Invalid global setting _#{k}_, see _help_ for available options." end end - def channel_rotate(client, channel, data, user, k) + def channel_rotate(channel, data, user, k) case k - when 'apitoken' then - rotate_api_token client, channel, data, user + when 'apitoken' + rotate_api_token channel, data, user else raise SlackSup::Error, "Invalid channel setting _#{k}_, see _help_ for available options." end end - def team_rotate(client, team, data, user, k) + def team_rotate(team, data, user, k) case k - when 'apitoken' then - team_rotate_api_token client, team, data, user + when 'apitoken' + team_rotate_api_token team, data, user else raise SlackSup::Error, "Invalid global setting _#{k}_, see _help_ for available options." end @@ -527,8 +527,22 @@ def team_data_access_message(team, user_id, updated_api = false, updated_token = end end - user_command 'set' do |client, channel, user, data, match| - if !match['expression'] + user_command 'unset' do |channel, user, data| + if !data.match['expression'] + data.team.slack_client.chat_postMessage(channel: data.channel, text: 'Missing setting, see _help_ for available options.') + logger.info "UNSET: #{channel}, user=#{data.user}, failed, missing setting" + else + k, = parse_expression(data.match) + if channel && user + channel_unset channel, data, user, k + else + team_unset data.team, data, user, k + end + end + end + + user_command 'set' do |channel, user, data| + if !data.match['expression'] if channel && user message = [ "Channel S'Up connects groups of #{channel.sup_odd ? 'max ' : ''}#{channel.sup_size} people on #{channel.sup_day} after #{channel.sup_time_of_day_s} every #{channel.sup_every_n_weeks_s} in #{channel.sup_tzone}, taking special care to not pair the same people more frequently than every #{channel.sup_recency_s}.", @@ -537,54 +551,40 @@ def team_data_access_message(team, user_id, updated_api = false, updated_token = channel_data_access_message(user), channel.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) logger.info "SET: #{channel}, user=#{user.user_id}" elsif user - team = client.owner + team = data.team message = [ team.enabled_channels_text, team_data_access_message(team, user), team.api_url ].compact.join("\n") - client.say(channel: data.channel, text: message) + data.team.slack_client.chat_postMessage(channel: data.channel, text: message) logger.info "SET: #{team}, channel=#{data.channel}, user=#{user}" else raise 'expected user' end else - k, v = parse_expression(match) + k, v = parse_expression(data.match) if channel && user - channel_set client, channel, data, user, k, v + channel_set channel, data, user, k, v elsif user - team_set client, client.owner, data, user, k, v - end - end - end - - user_command 'unset' do |client, channel, user, data, match| - if !match['expression'] - client.say(channel: data.channel, text: 'Missing setting, see _help_ for available options.') - logger.info "UNSET: #{channel}, user=#{data.user}, failed, missing setting" - else - k, = parse_expression(match) - if channel && user - channel_unset client, channel, data, user, k - else - team_unset client, client.owner, data, user, k + team_set data.team, data, user, k, v end end end - user_command 'rotate' do |client, channel, user, data, match| - if !match['expression'] - client.say(channel: data.channel, text: 'Missing setting, see _help_ for available options.') + user_command 'rotate' do |channel, user, data| + if !data.match['expression'] + data.team.slack_client.chat_postMessage(channel: data.channel, text: 'Missing setting, see _help_ for available options.') logger.info "UNSET: #{channel}, user=#{data.user}, failed, missing setting" else - k, = parse_expression(match) + k, = parse_expression(data.match) if channel && user - channel_rotate client, channel, data, user, k + channel_rotate channel, data, user, k elsif user - team_rotate client, client.owner, data, user, k + team_rotate data.team, data, user, k end end end diff --git a/lib/commands/stats.rb b/lib/commands/stats.rb new file mode 100644 index 0000000..ce5c17c --- /dev/null +++ b/lib/commands/stats.rb @@ -0,0 +1,13 @@ +module SlackSup + module Commands + class Stats < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::Channel + + channel_command 'stats' do |channel, data| + stats = channel ? ChannelStats.new(channel) : TeamStats.new(data.team) + data.team.slack_client.chat_postMessage(channel: data.channel, text: stats.to_s) + logger.info "STATS: #{data.team}, channel=#{data.channel}, user=#{data.user}" + end + end + end +end diff --git a/lib/commands/subscription.rb b/lib/commands/subscription.rb new file mode 100644 index 0000000..1ecb5ff --- /dev/null +++ b/lib/commands/subscription.rb @@ -0,0 +1,28 @@ +module SlackSup + module Commands + class Subscription < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::User + + subscribe_command 'subscription' do |data| + if data.team.is_admin?(data.user) + subscription_info = [] + if data.team.active_stripe_subscription? + subscription_info << data.team.stripe_customer_text + subscription_info.concat(data.team.stripe_customer_subscriptions_info) + subscription_info.concat(data.team.stripe_customer_invoices_info) + subscription_info.concat(data.team.stripe_customer_sources_info) + subscription_info << data.team.update_cc_text + elsif data.team.subscribed && data.team.subscribed_at + subscription_info << data.team.subscriber_text + else + subscription_info << data.team.trial_message + end + data.team.slack_client.chat_postMessage(channel: data.channel, text: subscription_info.compact.join("\n")) + else + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Only <@#{data.team.activated_user_id}> or a Slack team admin can get subscription details, sorry.") + end + logger.info "SUBSCRIPTION: #{data.team}, user=#{data.user}" + end + end + end +end diff --git a/lib/commands/sync.rb b/lib/commands/sync.rb new file mode 100644 index 0000000..6664e9f --- /dev/null +++ b/lib/commands/sync.rb @@ -0,0 +1,19 @@ +module SlackSup + module Commands + class Sync < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::User + + user_command 'sync' do |channel, user, data| + if channel && user.channel_admin? + channel.update_attributes!(sync: true) + data.team.slack_client.chat_postMessage(channel: data.channel, text: "#{channel.last_sync_at_text} Come back and run `stats` in a bit.") + elsif channel + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Users will sync before the next round. Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry.") + else + data.team.slack_client.chat_postMessage(channel: data.channel, text: 'Please run this command in a channel.') + end + logger.info "SYNC: #{data.team}, #{channel}, user=#{data.user}" + end + end + end +end diff --git a/lib/commands/unsubscribe.rb b/lib/commands/unsubscribe.rb new file mode 100644 index 0000000..c962772 --- /dev/null +++ b/lib/commands/unsubscribe.rb @@ -0,0 +1,33 @@ +module SlackSup + module Commands + class Unsubscribe < SlackRubyBotServer::Events::AppMentions::Mention + include SlackSup::Commands::Mixins::User + + subscribe_command 'unsubscribe' do |data| + if !data.team.stripe_customer_id + data.team.slack_client.chat_postMessage(channel: data.channel, text: "You don't have a paid subscription, all set.") + logger.info "UNSUBSCRIBE: #{data.team}, user=#{data.user} unsubscribe failed, no subscription" + elsif data.team.is_admin?(data.user) && data.team.active_stripe_subscription? + subscription_info = [] + subscription_id = data.match['expression'] + active_subscription = data.team.active_stripe_subscription + if active_subscription && active_subscription.id == subscription_id + active_subscription.delete(at_period_end: true) + amount = ActiveSupport::NumberHelper.number_to_currency(active_subscription.plan.amount.to_f / 100) + subscription_info << "Successfully canceled auto-renew for #{active_subscription.plan.name} (#{amount})." + logger.info "UNSUBSCRIBE: #{data.team}, user=#{data.user}, canceled #{subscription_id}" + elsif subscription_id + subscription_info << "Sorry, I cannot find a subscription with \"#{subscription_id}\"." + else + subscription_info.concat(data.team.stripe_customer_subscriptions_info(true)) + end + data.team.slack_client.chat_postMessage(channel: data.channel, text: subscription_info.compact.join("\n")) + logger.info "UNSUBSCRIBE: #{data.team}, user=#{data.user}" + else + data.team.slack_client.chat_postMessage(channel: data.channel, text: "Only <@#{data.team.activated_user_id}> or a Slack team admin can unsubscribe, sorry.") + logger.info "UNSUBSCRIBE: #{data.team}, user=#{data.user} unsubscribe failed, not admin" + end + end + end + end +end diff --git a/lib/events.rb b/lib/events.rb new file mode 100644 index 0000000..ccb6641 --- /dev/null +++ b/lib/events.rb @@ -0,0 +1,37 @@ +SlackRubyBotServer::Events.configure do |config| + def parse(event) + team = Team.where(team_id: event[:event][:team]).first || raise("Cannot find team with ID #{event[:event][:team]}.") + data = Slack::Messages::Message.new(event[:event]).merge(team: team) + return nil unless data.user == data.team.bot_user_id + + data + end + + config.on :event, 'event_callback', 'member_joined_channel' do |event| + data = parse(event) + next unless data + + Api::Middleware.logger.info "#{data.team.name}: bot joined ##{data.channel}." + data.team.join_channel!(data.channel, data.inviter) + + text = + "Hi there! I'm your team's S'Up bot. " \ + "Thanks for trying me out. Type `#{data.team.bot_name} help` for instructions. " \ + "I plan to setup some S'Ups via Slack DM for all users in this channel next Monday. " \ + 'You may want to `set size`, `set day`, `set timezone`, or `set sync now` users before then.'.freeze + + data.team.slack_client.chat_postMessage(channel: data.channel, text: text) + + { ok: true } + end + + config.on :event, 'event_callback', 'member_left_channel' do |event| + data = parse(event) + next unless data + + Api::Middleware.logger.info "#{data.team.name}: bot left ##{data.channel}." + data.team.leave_channel!(data.channel) + + { ok: true } + end +end diff --git a/slack-sup/info.rb b/lib/info.rb similarity index 100% rename from slack-sup/info.rb rename to lib/info.rb diff --git a/lib/models.rb b/lib/models.rb new file mode 100644 index 0000000..cbfbc62 --- /dev/null +++ b/lib/models.rb @@ -0,0 +1,11 @@ +require_relative 'models/mixins/pluralize' +require_relative 'models/error' +require_relative 'models/team' +require_relative 'models/user' +require_relative 'models/sup' +require_relative 'models/round' +require_relative 'models/stats' +require_relative 'models/team_stats' +require_relative 'models/channel_stats' +require_relative 'models/round_stats' +require_relative 'models/channel' diff --git a/slack-sup/models/channel.rb b/lib/models/channel.rb similarity index 100% rename from slack-sup/models/channel.rb rename to lib/models/channel.rb diff --git a/slack-sup/models/channel_stats.rb b/lib/models/channel_stats.rb similarity index 100% rename from slack-sup/models/channel_stats.rb rename to lib/models/channel_stats.rb diff --git a/slack-sup/models/error.rb b/lib/models/error.rb similarity index 100% rename from slack-sup/models/error.rb rename to lib/models/error.rb diff --git a/slack-sup/models/mixins/pluralize.rb b/lib/models/mixins/pluralize.rb similarity index 100% rename from slack-sup/models/mixins/pluralize.rb rename to lib/models/mixins/pluralize.rb diff --git a/slack-sup/models/round.rb b/lib/models/round.rb similarity index 100% rename from slack-sup/models/round.rb rename to lib/models/round.rb diff --git a/slack-sup/models/round_stats.rb b/lib/models/round_stats.rb similarity index 100% rename from slack-sup/models/round_stats.rb rename to lib/models/round_stats.rb diff --git a/slack-sup/models/stats.rb b/lib/models/stats.rb similarity index 100% rename from slack-sup/models/stats.rb rename to lib/models/stats.rb diff --git a/slack-sup/models/sup.rb b/lib/models/sup.rb similarity index 100% rename from slack-sup/models/sup.rb rename to lib/models/sup.rb diff --git a/slack-sup/models/team.rb b/lib/models/team.rb similarity index 94% rename from slack-sup/models/team.rb rename to lib/models/team.rb index 21e8bce..8aef35e 100644 --- a/slack-sup/models/team.rb +++ b/lib/models/team.rb @@ -53,12 +53,9 @@ def asleep?(dt = 3.weeks) end def inform!(message) - members = slack_client.users_list(presence: false).map(&:members).flatten - members.select(&:is_admin).each do |admin| - channel = slack_client.conversations_open(users: admin.id.to_s) - logger.info "Sending DM '#{message}' to #{admin.name}." - slack_client.chat_postMessage(text: message, channel: channel.channel.id, as_user: true) - end + channel = slack_client.conversations_open(users: activated_user_id) + logger.info "Sending DM '#{message}' to #{activated_user_id}." + slack_client.chat_postMessage(text: message, channel: channel.channel.id, as_user: true) end def subscription_expired? @@ -207,7 +204,7 @@ def api_s INSTALLED_TEXT = "Hi there! I'm your team's S'Up bot. " \ 'Thanks for trying me out. ' \ - 'To start, invite me to a channel. ' \ + 'To start, invite me to a channel that has some users. ' \ 'You can always DM me `help` for instructions.'.freeze SUBSCRIBED_TEXT = diff --git a/slack-sup/models/team_stats.rb b/lib/models/team_stats.rb similarity index 100% rename from slack-sup/models/team_stats.rb rename to lib/models/team_stats.rb diff --git a/slack-sup/models/user.rb b/lib/models/user.rb similarity index 100% rename from slack-sup/models/user.rb rename to lib/models/user.rb diff --git a/slack-sup/service.rb b/lib/service.rb similarity index 100% rename from slack-sup/service.rb rename to lib/service.rb diff --git a/lib/version.rb b/lib/version.rb new file mode 100644 index 0000000..8a9cd5d --- /dev/null +++ b/lib/version.rb @@ -0,0 +1,3 @@ +module SlackSup + VERSION = '2.0.0'.freeze +end diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 0000000..b464611 --- /dev/null +++ b/manifest.yml @@ -0,0 +1,46 @@ +_metadata: + major_version: 1 + minor_version: 1 +display_information: + name: S'Up v2 for Slack Teams + long_description: The most valuable relationships are not made of two people, + they're made of three. A third person will always stabilize and grow the + relationship between the other two. It's called a triad, and the more you + create, the stronger your network. This bot passively generates fresh triads + of channel members to meet every week in an informal standup, or S'Up. + description: Setup a weekly standup with people at your company. + background_color: "#0f2a4a" +settings: + socket_mode_enabled: false + interactivity: + is_enabled: true + request_url: https://sup2.playplay.io/api/slack + event_subscriptions: + bot_events: + - member_left_channel + - member_joined_channel + - app_mention + - message.mpim + - message +features: + bot_user: + display_name: sup +oauth_config: + scopes: + bot: + - app_mentions:read + - channels:history + - channels:read + - chat:write + - groups:history + - groups:read + - im:history + - im:read + - im:write + - mpim:history + - mpim:read + - mpim:write + - users:read + - users.profile:read + redirect_urls: + - https://sup2.playplay.io/ \ No newline at end of file diff --git a/script/console b/script/console index 463c288..e9f109d 100755 --- a/script/console +++ b/script/console @@ -2,4 +2,4 @@ # Usage: script/console # Starts an IRB console with slack-sup loaded. -exec bundle exec irb -I . -r "slack-sup" +exec bundle exec irb -I . -r "app" diff --git a/slack-sup.rb b/slack-sup.rb deleted file mode 100644 index 86a473d..0000000 --- a/slack-sup.rb +++ /dev/null @@ -1,29 +0,0 @@ -ENV['RACK_ENV'] ||= 'development' - -require 'bundler/setup' -Bundler.require :default, ENV['RACK_ENV'] - -Dir[File.expand_path('config/initializers', __dir__) + '/**/*.rb'].each do |file| - require file -end - -Mongoid.load! File.expand_path('config/mongoid.yml', __dir__), ENV['RACK_ENV'] - -SlackRubyBotServer.configure do |config| - config.oauth_version = :v1 - config.oauth_scope = ['bot', 'users.profile:read'] -end - -require 'slack-ruby-bot' -require 'slack-sup/version' -require 'slack-sup/service' -require 'slack-sup/info' -require 'slack-sup/models' -require 'slack-sup/api' -require 'slack-sup/app' -require 'slack-sup/server' -require 'slack-sup/commands' - -SlackRubyBotServer::RealTime.configure do |config| - config.server_class = SlackSup::Server -end diff --git a/slack-sup/api.rb b/slack-sup/api.rb deleted file mode 100644 index 7afe59b..0000000 --- a/slack-sup/api.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'grape' -require 'roar' -require 'grape-roar' - -require 'slack-sup/api/helpers' -require 'slack-sup/api/presenters' -require 'slack-sup/api/endpoints' -require 'slack-sup/api/middleware' diff --git a/slack-sup/api/endpoints.rb b/slack-sup/api/endpoints.rb deleted file mode 100644 index db438e1..0000000 --- a/slack-sup/api/endpoints.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'slack-sup/api/endpoints/teams_endpoint' -require 'slack-sup/api/endpoints/channels_endpoint' -require 'slack-sup/api/endpoints/users_endpoint' -require 'slack-sup/api/endpoints/rounds_endpoint' -require 'slack-sup/api/endpoints/sups_endpoint' -require 'slack-sup/api/endpoints/subscriptions_endpoint' -require 'slack-sup/api/endpoints/status_endpoint' -require 'slack-sup/api/endpoints/stats_endpoint' -require 'slack-sup/api/endpoints/credit_cards_endpoint' -require 'slack-sup/api/endpoints/slack_endpoint' -require 'slack-sup/api/endpoints/root_endpoint' diff --git a/slack-sup/api/endpoints/slack_endpoint.rb b/slack-sup/api/endpoints/slack_endpoint.rb deleted file mode 100644 index 700c711..0000000 --- a/slack-sup/api/endpoints/slack_endpoint.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Api - module Endpoints - class SlackEndpoint < Grape::API - format :json - - namespace :slack do - desc 'Respond to interactive slack buttons and actions.' - - params do - requires :payload, type: String - end - - post '/action' do - payload = Hashie::Mash.new(JSON.parse(params[:payload])) - error! 'Message token is not coming from Slack.', 401 if ENV.key?('SLACK_VERIFICATION_TOKEN') && payload.token != ENV['SLACK_VERIFICATION_TOKEN'] - error! 'Missing actions.', 400 unless payload.actions - error! 'Missing action.', 400 unless payload.actions.first - - case payload.actions.first.name - when 'outcome' then - - sup = Sup.find(payload.callback_id) || error!('Sup Not Found', 404) - sup.update_attributes!(outcome: payload.actions.first.value) - - Api::Middleware.logger.info "Updated channel #{sup.round.channel}, sup #{sup} outcome to '#{sup.outcome}'." - - message = Sup::ASK_WHO_SUP_MESSAGE.dup - - message[:attachments].first[:callback_id] = sup.id.to_s - message[:attachments].first[:actions].each do |action| - action[:style] = action[:value] == sup.outcome ? 'primary' : 'default' - end - - message[:text] = case sup.outcome - when 'later' - "Thanks, I'll ask again in a couple of days." - else - 'Thanks for letting me know.' - end - - { - as_user: true, - channel: payload.channel.id, - ts: payload.original_message.ts, - token: payload.token - }.merge(message) - - else - error!("Unknown Action #{actions.first.name}", 400) - end - end - end - end - end -end diff --git a/slack-sup/api/helpers.rb b/slack-sup/api/helpers.rb deleted file mode 100644 index 90cbb36..0000000 --- a/slack-sup/api/helpers.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'slack-sup/api/helpers/cursor_helpers' -require 'slack-sup/api/helpers/pagination_parameters' -require 'slack-sup/api/helpers/sort_helpers' -require 'slack-sup/api/helpers/error_helpers' -require 'slack-sup/api/helpers/auth_helpers' diff --git a/slack-sup/api/presenters.rb b/slack-sup/api/presenters.rb deleted file mode 100644 index 3729fef..0000000 --- a/slack-sup/api/presenters.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'roar/representer' -require 'roar/json' -require 'roar/json/hal' - -require 'slack-sup/api/presenters/paginated_presenter' -require 'slack-sup/api/presenters/status_presenter' -require 'slack-sup/api/presenters/stats_presenter' -require 'slack-sup/api/presenters/team_stats_presenter' -require 'slack-sup/api/presenters/channel_stats_presenter' -require 'slack-sup/api/presenters/team_presenter' -require 'slack-sup/api/presenters/teams_presenter' -require 'slack-sup/api/presenters/root_presenter' -require 'slack-sup/api/presenters/channel_presenter' -require 'slack-sup/api/presenters/channels_presenter' -require 'slack-sup/api/presenters/user_presenter' -require 'slack-sup/api/presenters/users_presenter' -require 'slack-sup/api/presenters/round_presenter' -require 'slack-sup/api/presenters/rounds_presenter' -require 'slack-sup/api/presenters/sup_presenter' -require 'slack-sup/api/presenters/sups_presenter' diff --git a/slack-sup/commands.rb b/slack-sup/commands.rb deleted file mode 100644 index 6ededbc..0000000 --- a/slack-sup/commands.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'slack-sup/commands/mixins' -require 'slack-sup/commands/help' -require 'slack-sup/commands/about' -require 'slack-sup/commands/subscription' -require 'slack-sup/commands/unsubscribe' -require 'slack-sup/commands/opt' -require 'slack-sup/commands/set' -require 'slack-sup/commands/stats' -require 'slack-sup/commands/next' -require 'slack-sup/commands/rounds' -require 'slack-sup/commands/gcal' -require 'slack-sup/commands/sync' diff --git a/slack-sup/commands/about.rb b/slack-sup/commands/about.rb deleted file mode 100644 index 1a2c876..0000000 --- a/slack-sup/commands/about.rb +++ /dev/null @@ -1,10 +0,0 @@ -module SlackSup - module Commands - class About < SlackRubyBot::Commands::Base - def self.call(client, data, _match) - client.say(channel: data.channel, text: SlackSup::INFO) - logger.info "INFO: #{client.owner}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/gcal.rb b/slack-sup/commands/gcal.rb deleted file mode 100644 index a732870..0000000 --- a/slack-sup/commands/gcal.rb +++ /dev/null @@ -1,21 +0,0 @@ -module SlackSup - module Commands - class GCal < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::Subscribe - - subscribe_command 'gcal' do |client, data, match| - raise SlackSup::Error, 'Missing GOOGLE_API_CLIENT_ID.' unless ENV['GOOGLE_API_CLIENT_ID'] - - sup = Sup.where(conversation_id: data.channel).desc(:_id).first - raise SlackSup::Error, "Please `#{client.owner.bot_name} cal date/time` inside a S'Up DM channel." unless sup - - Chronic.time_class = sup.channel.sup_tzone - dt = Chronic.parse(match['expression']) if match['expression'] - raise SlackSup::Error, "Please specify a date/time, eg. `#{client.owner.bot_name} cal tomorrow 5pm`." unless dt - - client.say(channel: data.channel, text: "Click this link to create a gcal for #{dt.strftime('%A, %B %d, %Y')} at #{dt.strftime('%l:%M %P').strip}: #{sup.calendar_href(dt)}") - logger.info "CALENDAR: #{client.owner}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/mixins.rb b/slack-sup/commands/mixins.rb deleted file mode 100644 index 3487ccb..0000000 --- a/slack-sup/commands/mixins.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'slack-sup/commands/mixins/subscribe' -require 'slack-sup/commands/mixins/channel' -require 'slack-sup/commands/mixins/user' -require 'slack-sup/commands/mixins/pluralize' diff --git a/slack-sup/commands/mixins/subscribe.rb b/slack-sup/commands/mixins/subscribe.rb deleted file mode 100644 index 9fa20f2..0000000 --- a/slack-sup/commands/mixins/subscribe.rb +++ /dev/null @@ -1,22 +0,0 @@ -module SlackSup - module Commands - module Mixins - module Subscribe - extend ActiveSupport::Concern - - module ClassMethods - def subscribe_command(*values, &_block) - command(*values) do |client, data, match| - if Stripe.api_key && client.owner.reload.subscription_expired? - client.say channel: data.channel, text: client.owner.subscribe_text - logger.info "#{client.owner}, user=#{data.user}, text=#{data.text}, subscription expired" - else - yield client, data, match - end - end - end - end - end - end - end -end diff --git a/slack-sup/commands/next.rb b/slack-sup/commands/next.rb deleted file mode 100644 index 2d5e9e2..0000000 --- a/slack-sup/commands/next.rb +++ /dev/null @@ -1,13 +0,0 @@ -module SlackSup - module Commands - class Next < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::Channel - - channel_command 'next' do |client, channel, data, _match| - channels = channel ? [channel] : client.owner.channels.enabled.asc(:_id) - client.say(channel: data.channel, text: channels.map(&:next_sup_at_text).join("\n")) - logger.info "NEXT: #{client.owner}, channel=#{data.channel}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/stats.rb b/slack-sup/commands/stats.rb deleted file mode 100644 index 7adb63f..0000000 --- a/slack-sup/commands/stats.rb +++ /dev/null @@ -1,13 +0,0 @@ -module SlackSup - module Commands - class Stats < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::Channel - - channel_command 'stats' do |client, channel, data, _match| - stats = channel ? ChannelStats.new(channel) : TeamStats.new(client.owner) - client.say(channel: data.channel, text: stats.to_s) - logger.info "STATS: #{client.owner}, channel=#{data.channel}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/subscription.rb b/slack-sup/commands/subscription.rb deleted file mode 100644 index f8cd7de..0000000 --- a/slack-sup/commands/subscription.rb +++ /dev/null @@ -1,28 +0,0 @@ -module SlackSup - module Commands - class Subscription < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::User - - subscribe_command 'subscription' do |client, data, _match| - if client.owner.is_admin?(data.user) - subscription_info = [] - if client.owner.active_stripe_subscription? - subscription_info << client.owner.stripe_customer_text - subscription_info.concat(client.owner.stripe_customer_subscriptions_info) - subscription_info.concat(client.owner.stripe_customer_invoices_info) - subscription_info.concat(client.owner.stripe_customer_sources_info) - subscription_info << client.owner.update_cc_text - elsif client.owner.subscribed && client.owner.subscribed_at - subscription_info << client.owner.subscriber_text - else - subscription_info << client.owner.trial_message - end - client.say(channel: data.channel, text: subscription_info.compact.join("\n")) - else - client.say(channel: data.channel, text: "Only <@#{client.owner.activated_user_id}> or a Slack team admin can get subscription details, sorry.") - end - logger.info "SUBSCRIPTION: #{client.owner}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/sync.rb b/slack-sup/commands/sync.rb deleted file mode 100644 index b3a939c..0000000 --- a/slack-sup/commands/sync.rb +++ /dev/null @@ -1,19 +0,0 @@ -module SlackSup - module Commands - class Sync < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::User - - user_command 'sync' do |client, channel, user, data, _match| - if channel && user.channel_admin? - channel.update_attributes!(sync: true) - client.say(channel: data.channel, text: "#{channel.last_sync_at_text} Come back and run `stats` in a bit.") - elsif channel - client.say(channel: data.channel, text: "Users will sync before the next round. Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry.") - else - client.say(channel: data.channel, text: 'Please run this command in a channel.') - end - logger.info "SYNC: #{client.owner}, #{channel}, user=#{data.user}" - end - end - end -end diff --git a/slack-sup/commands/unsubscribe.rb b/slack-sup/commands/unsubscribe.rb deleted file mode 100644 index 0e0077f..0000000 --- a/slack-sup/commands/unsubscribe.rb +++ /dev/null @@ -1,33 +0,0 @@ -module SlackSup - module Commands - class Unsubscribe < SlackRubyBot::Commands::Base - include SlackSup::Commands::Mixins::User - - subscribe_command 'unsubscribe' do |client, data, match| - if !client.owner.stripe_customer_id - client.say(channel: data.channel, text: "You don't have a paid subscription, all set.") - logger.info "UNSUBSCRIBE: #{client.owner}, user=#{data.user} unsubscribe failed, no subscription" - elsif client.owner.is_admin?(data.user) && client.owner.active_stripe_subscription? - subscription_info = [] - subscription_id = match['expression'] - active_subscription = client.owner.active_stripe_subscription - if active_subscription && active_subscription.id == subscription_id - active_subscription.delete(at_period_end: true) - amount = ActiveSupport::NumberHelper.number_to_currency(active_subscription.plan.amount.to_f / 100) - subscription_info << "Successfully canceled auto-renew for #{active_subscription.plan.name} (#{amount})." - logger.info "UNSUBSCRIBE: #{client.owner}, user=#{data.user}, canceled #{subscription_id}" - elsif subscription_id - subscription_info << "Sorry, I cannot find a subscription with \"#{subscription_id}\"." - else - subscription_info.concat(client.owner.stripe_customer_subscriptions_info(true)) - end - client.say(channel: data.channel, text: subscription_info.compact.join("\n")) - logger.info "UNSUBSCRIBE: #{client.owner}, user=#{data.user}" - else - client.say(channel: data.channel, text: "Only <@#{client.owner.activated_user_id}> or a Slack team admin can unsubscribe, sorry.") - logger.info "UNSUBSCRIBE: #{client.owner}, user=#{data.user} unsubscribe failed, not admin" - end - end - end - end -end diff --git a/slack-sup/models.rb b/slack-sup/models.rb deleted file mode 100644 index 337bac2..0000000 --- a/slack-sup/models.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'slack-sup/models/mixins/pluralize' -require 'slack-sup/models/error' -require 'slack-sup/models/team' -require 'slack-sup/models/user' -require 'slack-sup/models/sup' -require 'slack-sup/models/round' -require 'slack-sup/models/stats' -require 'slack-sup/models/team_stats' -require 'slack-sup/models/channel_stats' -require 'slack-sup/models/round_stats' -require 'slack-sup/models/channel' diff --git a/slack-sup/server.rb b/slack-sup/server.rb deleted file mode 100644 index 698a803..0000000 --- a/slack-sup/server.rb +++ /dev/null @@ -1,25 +0,0 @@ -module SlackSup - class Server < SlackRubyBotServer::RealTime::Server - on :member_joined_channel do |client, data| - next unless data.user == client.owner.bot_user_id - - logger.info "#{client.owner.name}: bot joined ##{data.channel}." - client.owner.join_channel!(data.channel, data.inviter) - - text = - "Hi there! I'm your team's S'Up bot. " \ - "Thanks for trying me out. Type `#{client.owner.bot_name} help` for instructions. " \ - "I plan to setup some S'Ups via Slack DM for all users in this channel next Monday. " \ - 'You may want to `set size`, `set day`, `set timezone`, or `set sync now` users before then.'.freeze - - client.say(channel: data.channel, text: text) - end - - on :member_left_channel do |client, data| - next unless data.user == client.owner.bot_user_id - - logger.info "#{client.owner.name}: bot left ##{data.channel}." - client.owner.leave_channel!(data.channel) - end - end -end diff --git a/slack-sup/version.rb b/slack-sup/version.rb deleted file mode 100644 index b4f530d..0000000 --- a/slack-sup/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module SlackSup - VERSION = '0.1.0'.freeze -end diff --git a/spec/api/documentation_spec.rb b/spec/api/documentation_spec.rb index 74a4055..ed24106 100644 --- a/spec/api/documentation_spec.rb +++ b/spec/api/documentation_spec.rb @@ -10,22 +10,24 @@ end it 'documents root level apis' do expect(subject['paths'].keys.sort).to eq [ - '/api/status', - '/api/teams/{id}', - '/api/teams', - '/api/channels/{id}', '/api/channels', - '/api/users/{id}', - '/api/users', - '/api/rounds/{id}', + '/api/channels/{id}', + '/api/credit_cards', '/api/rounds', + '/api/rounds/{id}', + '/api/slack/action', + '/api/slack/command', + '/api/slack/event', '/api/stats', - '/api/sups/{id}', - '/api/sups', + '/api/status', '/api/subscriptions', - '/api/credit_cards', - '/api/slack/action' - ].sort + '/api/sups', + '/api/sups/{id}', + '/api/teams', + '/api/teams/{id}', + '/api/users', + '/api/users/{id}' + ] end end diff --git a/spec/api/endpoints/slack_endpoint_spec.rb b/spec/api/endpoints/slack_endpoint_spec.rb index e45e6e8..445dbaa 100644 --- a/spec/api/endpoints/slack_endpoint_spec.rb +++ b/spec/api/endpoints/slack_endpoint_spec.rb @@ -3,45 +3,81 @@ describe Api::Endpoints::SlackEndpoint do include Api::Test::EndpointTest + before do + allow_any_instance_of(Slack::Events::Request).to receive(:verify!) + end + context 'outcome' do let(:sup) { Fabricate(:sup) } let(:payload) do { - 'callback_id': sup.id.to_s, - 'channel': { 'id' => '424242424', 'name' => 'directmessage' }, - 'original_message': { - 'ts': '1467321295.000010' - } + type: 'interactive_message', + user: { id: 'user_id' }, + team: { id: 'team_id' }, + callback_id: sup.id.to_s, + channel: { id: '424242424', name: 'directmessage' }, + original_message: { + ts: '1467321295.000010' + }, + response_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX', + token: 'deprecated' } end context 'none' do it 'updates outcome' do + expect(Faraday).to receive(:post).with('https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX', { + response_type: 'in_channel', + thread_ts: '1467321295.000010', + text: 'Thanks for letting me know.', + attachments: [ + text: '', + attachment_type: 'default', + actions: [ + { name: 'outcome', text: 'We All Met', type: 'button', value: 'all', style: 'default' }, + { name: 'outcome', text: 'Some of Us Met', type: 'button', value: 'some', style: 'default' }, + { name: 'outcome', text: "We Haven't Met Yet", type: 'button', value: 'later', style: 'default' }, + { name: 'outcome', text: "We Couldn't Meet", type: 'button', value: 'none', style: 'primary' } + ], + callback_id: sup.id.to_s + ] + }.to_json, 'Content-Type' => 'application/json') post '/api/slack/action', payload: payload.merge( - 'actions': [ - { 'name' => 'outcome', 'type' => 'button', 'value' => 'none' } + actions: [ + { name: 'outcome', type: 'button', value: 'none' } ] ).to_json - expect(last_response.status).to eq 201 - payload = JSON.parse(last_response.body) - expect(payload['text']).to eq 'Thanks for letting me know.' - expect(payload['attachments'].first['actions'].map { |a| a['style'] }).to eq(%w[default default default primary]) + expect(last_response.status).to eq 204 expect(sup.reload.outcome).to eq 'none' end end context 'later' do it 'delays reminding by 48 hours' do + expect(Faraday).to receive(:post).with('https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX', { + response_type: 'in_channel', + thread_ts: '1467321295.000010', + text: "Thanks, I'll ask again in a couple of days.", + attachments: [ + text: '', + attachment_type: 'default', + actions: [ + { name: 'outcome', text: 'We All Met', type: 'button', value: 'all', style: 'default' }, + { name: 'outcome', text: 'Some of Us Met', type: 'button', value: 'some', style: 'default' }, + { name: 'outcome', text: "We Haven't Met Yet", type: 'button', value: 'later', style: 'primary' }, + { name: 'outcome', text: "We Couldn't Meet", type: 'button', value: 'none', style: 'default' } + ], + callback_id: sup.id.to_s + ] + }.to_json, 'Content-Type' => 'application/json') + post '/api/slack/action', payload: payload.merge( 'actions': [ - { 'name' => 'outcome', 'type' => 'button', 'value' => 'later' } + { name: 'outcome', type: 'button', value: 'later' } ] ).to_json - expect(last_response.status).to eq 201 - payload = JSON.parse(last_response.body) - expect(payload['text']).to eq "Thanks, I'll ask again in a couple of days." - expect(payload['attachments'].first['actions'].map { |a| a['style'] }).to eq(%w[default default primary default]) + expect(last_response.status).to eq 204 expect(sup.reload.outcome).to eq 'later' end end @@ -57,30 +93,6 @@ post '/api/slack/action', payload: { }.to_json expect(last_response.status).to eq 400 - expect(JSON.parse(last_response.body)['error']).to eq 'Missing actions.' - end - - it 'requires payload with at least one action' do - post '/api/slack/action', payload: { - 'actions': [] - }.to_json - expect(last_response.status).to eq 400 - expect(JSON.parse(last_response.body)['error']).to eq 'Missing action.' - end - - context 'with a SLACK_VERIFICATION_TOKEN' do - before do - ENV['SLACK_VERIFICATION_TOKEN'] = 'token' - end - after do - ENV.delete 'SLACK_VERIFICATION_TOKEN' - end - it 'returns an error with a non-matching verification token', vcr: { cassette_name: 'msft' } do - post '/api/slack/action', payload: { - 'token': 'invalid' - }.to_json - expect(last_response.status).to eq 401 - expect(JSON.parse(last_response.body)['error']).to eq 'Message token is not coming from Slack.' - end + expect(JSON.parse(last_response.body)['type']).to eq 'param_error' end end diff --git a/spec/api/endpoints/teams_endpoint_spec.rb b/spec/api/endpoints/teams_endpoint_spec.rb index 463a59c..22380d1 100644 --- a/spec/api/endpoints/teams_endpoint_spec.rb +++ b/spec/api/endpoints/teams_endpoint_spec.rb @@ -80,14 +80,18 @@ context 'register' do before do oauth_access = { - 'bot' => { - 'bot_access_token' => 'token', - 'bot_user_id' => 'bot_user_id' + 'access_token' => 'token', + 'token_type' => 'bot', + 'bot_user_id' => 'bot_user_id', + 'team' => { + 'id' => 'team_id', + 'name' => 'team_name' }, - 'access_token' => 'access_token', - 'user_id' => 'activated_user_id', - 'team_id' => 'team_id', - 'team_name' => 'team_name' + 'authed_user' => { + 'id' => 'activated_user_id', + 'access_token' => 'user_token', + 'token_type' => 'user' + } } ENV['SLACK_CLIENT_ID'] = 'client_id' ENV['SLACK_CLIENT_SECRET'] = 'client_secret' @@ -98,7 +102,7 @@ 'id' => 'C1' } ) - allow_any_instance_of(Slack::Web::Client).to receive(:oauth_access).with( + allow_any_instance_of(Slack::Web::Client).to receive(:oauth_v2_access).with( hash_including( code: 'code', client_id: 'client_id', @@ -141,6 +145,7 @@ end it 'returns a useful error when team already exists' do existing_team = Fabricate(:team, token: 'token') + allow_any_instance_of(Team).to receive(:ping_if_active!) expect { client.teams._post(code: 'code') }.to raise_error Faraday::ClientError do |e| json = JSON.parse(e.response[:body]) expect(json['message']).to eq "Team #{existing_team.name} is already registered." diff --git a/spec/integration/teams_spec.rb b/spec/integration/teams_spec.rb index 9fc4f94..aad7b48 100644 --- a/spec/integration/teams_spec.rb +++ b/spec/integration/teams_spec.rb @@ -14,8 +14,21 @@ allow_any_instance_of(Team).to receive(:inform!).with(Team::INSTALLED_TEXT) allow_any_instance_of(Team).to receive(:ping!).and_return(ok: true) expect(SlackRubyBotServer::Service.instance).to receive(:start!) - oauth_access = { 'bot' => { 'bot_access_token' => 'token' }, 'team_id' => 'team_id', 'team_name' => 'team_name' } - allow_any_instance_of(Slack::Web::Client).to receive(:oauth_access).with(hash_including(code: 'code')).and_return(oauth_access) + oauth_access = { + 'access_token' => 'token', + 'token_type' => 'bot', + 'bot_user_id' => 'bot_user_id', + 'team' => { + 'id' => 'team_id', + 'name' => 'team_name' + }, + 'authed_user' => { + 'id' => 'activated_user_id', + 'access_token' => 'user_token', + 'token_type' => 'user' + } + } + allow_any_instance_of(Slack::Web::Client).to receive(:oauth_v2_access).with(hash_including(code: 'code')).and_return(oauth_access) expect do visit '/?code=code' expect(page.find('#messages')).to have_content 'Team successfully registered! Check your DMs.' @@ -30,7 +43,8 @@ expect(title).to eq("S'Up for Slack Teams - Generate Fresh Triads of Team Members to Meet Every Week") end it 'includes a link to add to slack with the client id' do - expect(find("a[href='https://slack.com/oauth/authorize?scope=bot,users.profile:read&client_id=#{ENV['SLACK_CLIENT_ID']}']")) + url = "#{SlackRubyBotServer::Config.oauth_authorize_url}?scope=#{SlackRubyBotServer::Config.oauth_scope_s.gsub('+', ',')}&client_id=#{ENV['SLACK_CLIENT_ID']}" + expect(find("a[href='#{url}']")) end end end diff --git a/spec/slack-sup/commands/about_spec.rb b/spec/slack-sup/commands/about_spec.rb index cea13a3..08a0530 100644 --- a/spec/slack-sup/commands/about_spec.rb +++ b/spec/slack-sup/commands/about_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe SlackSup::Commands::About do + include_context :team + it 'about' do - expect(message: "#{SlackRubyBot.config.user} about").to respond_with_slack_message(SlackSup::INFO) + expect(message: '@sup about').to respond_with_slack_message(SlackSup::INFO) end end diff --git a/spec/slack-sup/commands/gcal_spec.rb b/spec/slack-sup/commands/gcal_spec.rb index 8a2b013..946f012 100644 --- a/spec/slack-sup/commands/gcal_spec.rb +++ b/spec/slack-sup/commands/gcal_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' describe SlackSup::Commands::GCal do - include_context :client - context 'dm' do include_context :subscribed_team @@ -14,7 +12,7 @@ end it 'requires a sup' do - expect(message: "#{SlackRubyBot.config.user} gcal").to respond_with_slack_message( + expect(message: '@sup gcal').to respond_with_slack_message( "Please `@sup cal date/time` inside a S'Up DM channel." ) end @@ -23,7 +21,7 @@ context 'team' do let!(:team) { Fabricate(:team) } it 'requires a subscription' do - expect(message: "#{SlackRubyBot.config.user} gcal").to respond_with_slack_message(team.subscribe_text) + expect(message: '@sup gcal').to respond_with_slack_message(team.subscribe_text) end end @@ -32,7 +30,7 @@ context 'subscribed team' do it 'requires a GOOGLE_API_CLIENT_ID' do - expect(message: "#{SlackRubyBot.config.user} gcal").to respond_with_slack_message( + expect(message: '@sup gcal').to respond_with_slack_message( 'Missing GOOGLE_API_CLIENT_ID.' ) end @@ -45,7 +43,7 @@ end context 'outside of a sup' do it 'requires a sup DM' do - expect(message: "#{SlackRubyBot.config.user} gcal", channel: 'invalid').to respond_with_slack_message( + expect(message: '@sup gcal', channel: 'invalid').to respond_with_slack_message( "Please `@sup cal date/time` inside a S'Up DM channel." ) end @@ -54,14 +52,14 @@ let!(:sup) { Fabricate(:sup, channel: channel, conversation_id: 'sup-channel-id') } let(:monday) { DateTime.parse('2017/1/2 8:00 AM EST').utc } it 'requires a date/time' do - expect(message: "#{SlackRubyBot.config.user} gcal", channel: 'sup-channel-id').to respond_with_slack_message( + expect(message: '@sup gcal', channel: 'sup-channel-id').to respond_with_slack_message( 'Please specify a date/time, eg. `@sup cal tomorrow 5pm`.' ) end it 'creates a link' do Timecop.travel(monday).freeze do Chronic.time_class = channel.sup_tzone - expect(message: "#{SlackRubyBot.config.user} gcal today 5pm", channel: 'sup-channel-id').to respond_with_slack_message( + expect(message: '@sup gcal today 5pm', channel: 'sup-channel-id').to respond_with_slack_message( "Click this link to create a gcal for Monday, January 02, 2017 at 5:00 pm: https://sup.playplay.io/gcal?sup_id=#{sup.id}&dt=1483394400&access_token=#{team.short_lived_token}" ) end diff --git a/spec/slack-sup/commands/help_spec.rb b/spec/slack-sup/commands/help_spec.rb index 7df444a..18c2fb3 100644 --- a/spec/slack-sup/commands/help_spec.rb +++ b/spec/slack-sup/commands/help_spec.rb @@ -1,12 +1,10 @@ require 'spec_helper' describe SlackSup::Commands::Help do - include_context :client - context 'subscribed team' do let!(:team) { Fabricate(:team, subscribed: true) } it 'help' do - expect(message: "#{SlackRubyBot.config.user} help").to respond_with_slack_message( + expect(message: '@sup help').to respond_with_slack_message( SlackSup::Commands::Help::HELP ) end @@ -14,7 +12,7 @@ context 'non-subscribed team after trial' do let!(:team) { Fabricate(:team, created_at: 2.weeks.ago) } it 'help' do - expect(message: "#{SlackRubyBot.config.user} help").to respond_with_slack_message([ + expect(message: '@sup help').to respond_with_slack_message([ SlackSup::Commands::Help::HELP, team.trial_message ].join("\n")) @@ -23,7 +21,7 @@ context 'non-subscribed team during trial' do let!(:team) { Fabricate(:team, created_at: 1.day.ago) } it 'help' do - expect(message: "#{SlackRubyBot.config.user} help").to respond_with_slack_message([ + expect(message: '@sup help').to respond_with_slack_message([ SlackSup::Commands::Help::HELP, team.trial_message ].join("\n")) diff --git a/spec/slack-sup/commands/next_spec.rb b/spec/slack-sup/commands/next_spec.rb index ec535fb..416f551 100644 --- a/spec/slack-sup/commands/next_spec.rb +++ b/spec/slack-sup/commands/next_spec.rb @@ -17,7 +17,7 @@ let!(:channel1) { Fabricate(:channel, team: team, sup_wday: wday, sup_time_of_day: 7 * 60 * 60 + 1, sup_tz: tz) } let!(:channel2) { Fabricate(:channel, team: team, sup_wday: wday, sup_time_of_day: 9 * 60 * 60 + 1, sup_tz: tz) } it 'returns all the next rounds' do - expect(message: "#{SlackRubyBot.config.user} next", channel: 'DM').to respond_with_slack_message([ + expect(message: '@sup next', channel: 'DM').to respond_with_slack_message([ "Next round in #{channel1.slack_mention} is overdue Monday, January 2, 2017 at 7:00 AM EST (7 hours ago).", "Next round in #{channel2.slack_mention} is overdue Monday, January 2, 2017 at 9:00 AM EST (5 hours ago)." ].join("\n")) @@ -28,7 +28,7 @@ channel1.sup! end it 'in a week' do - expect(message: "#{SlackRubyBot.config.user} next", channel: 'DM').to respond_with_slack_message([ + expect(message: '@sup next', channel: 'DM').to respond_with_slack_message([ "Next round in #{channel1.slack_mention} is Monday, January 9, 2017 at 7:00 AM EST (in 6 days).", "Next round in #{channel2.slack_mention} is overdue Monday, January 2, 2017 at 9:00 AM EST (5 hours ago)." ].join("\n")) @@ -39,7 +39,7 @@ context 'channel' do let(:channel) { Fabricate(:channel, channel_id: 'channel', team: team, sup_wday: wday, sup_time_of_day: 7 * 60 * 60 + 1, sup_tz: tz) } it 'no sup' do - expect(message: "#{SlackRubyBot.config.user} next").to respond_with_slack_message( + expect(message: '@sup next').to respond_with_slack_message( "Next round in #{channel.slack_mention} is overdue Monday, January 2, 2017 at 7:00 AM EST (7 hours ago)." ) end @@ -49,7 +49,7 @@ channel.sup! end it 'in a week' do - expect(message: "#{SlackRubyBot.config.user} next").to respond_with_slack_message( + expect(message: '@sup next').to respond_with_slack_message( "Next round in #{channel.slack_mention} is Monday, January 9, 2017 at 7:00 AM EST (in 6 days)." ) end @@ -58,7 +58,7 @@ Timecop.travel(Time.now + 1.day) end it 'in six days' do - expect(message: "#{SlackRubyBot.config.user} next").to respond_with_slack_message( + expect(message: '@sup next').to respond_with_slack_message( "Next round in #{channel.slack_mention} is Monday, January 9, 2017 at 7:00 AM EST (in 5 days)." ) end diff --git a/spec/slack-sup/commands/opt_spec.rb b/spec/slack-sup/commands/opt_spec.rb index 7e1b0ca..edac1c9 100644 --- a/spec/slack-sup/commands/opt_spec.rb +++ b/spec/slack-sup/commands/opt_spec.rb @@ -5,7 +5,7 @@ include_context :team it 'requires a subscription' do - expect(message: "#{SlackRubyBot.config.user} opt").to respond_with_slack_message(team.subscribe_text) + expect(message: '@sup opt').to respond_with_slack_message(team.subscribe_text) end end @@ -18,12 +18,12 @@ allow_any_instance_of(Team).to receive(:is_admin?).and_return(true) end it "shows user's current opt status" do - expect(message: "#{SlackRubyBot.config.user} opt", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt', channel: 'DM').to respond_with_slack_message( 'You were not found in any channels.' ) end it "shows another user's current opt status" do - expect(message: "#{SlackRubyBot.config.user} opt <@some_user>", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt <@some_user>', channel: 'DM').to respond_with_slack_message( 'User <@some_user> was not found in any channels.' ) end @@ -33,7 +33,7 @@ let!(:channel2) { Fabricate(:channel, team: team) } let!(:user2) { Fabricate(:user, channel: channel2, user_id: user1.user_id, opted_in: false) } it 'shows opt in status' do - expect(message: "#{SlackRubyBot.config.user} opt <@#{user1.user_id}>", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt <@#{user1.user_id}>", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is opted in to #{channel1.slack_mention} and opted out of #{channel2.slack_mention}." ) end @@ -44,12 +44,12 @@ allow_any_instance_of(Team).to receive(:is_admin?).and_return(false) end it 'requires an admin' do - expect(message: "#{SlackRubyBot.config.user} opt <@someone>", channel: 'DM').to respond_with_slack_message([ + expect(message: '@sup opt <@someone>', channel: 'DM').to respond_with_slack_message([ "Sorry, only <@#{team.activated_user_id}> or a Slack team admin can opt users in or out." ].join("\n")) end it 'lists channels opted in' do - expect(message: "#{SlackRubyBot.config.user} opt", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt', channel: 'DM').to respond_with_slack_message( 'You were not found in any channels.' ) end @@ -61,52 +61,52 @@ let!(:user1) { Fabricate(:user, channel: channel1, user_id: 'user') } let!(:user2) { Fabricate(:user, channel: channel2, user_id: 'user', opted_in: false) } it 'lists channels opted in' do - expect(message: "#{SlackRubyBot.config.user} opt", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt', channel: 'DM').to respond_with_slack_message( "You are opted in to #{channel1.slack_mention}, opted out of #{channel2.slack_mention} and not a member of #{channel3.slack_mention}." ) end it 'opts out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are now opted out of #{channel1.slack_mention}." ) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'remains opted out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are opted out of #{channel2.slack_mention}." ) end it 'opts in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are now opted in to #{channel2.slack_mention}." ) end it 'remains opted in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are opted in to #{channel1.slack_mention}." ) end it 'opts into multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt in #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are now opted in to #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "You are now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'fails on an unknown channel' do - expect(message: "#{SlackRubyBot.config.user} opt in <#invalid>", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt in <#invalid>', channel: 'DM').to respond_with_slack_message( "Sorry, I can't find an existing S'Up channel <#invalid>." ) end it 'fails on a channel by name' do - expect(message: "#{SlackRubyBot.config.user} opt in #invalid", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup opt in #invalid', channel: 'DM').to respond_with_slack_message( "Sorry, I don't understand who or what #invalid is." ) end @@ -117,116 +117,116 @@ let!(:user1_channel2) { Fabricate(:user, channel: channel2, user_id: user1.user_id, opted_in: false) } let!(:user2_channel2) { Fabricate(:user, channel: channel2, user_id: user2.user_id, opted_in: false) } before do - allow(team).to receive(:is_admin?).and_return(true) + allow_any_instance_of(Team).to receive(:is_admin?).and_return(true) end context 'one user' do it 'lists channels opted in' do - expect(message: "#{SlackRubyBot.config.user} opt #{user1.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt #{user1.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is opted in to #{channel1.slack_mention}, opted out of #{channel2.slack_mention} and not a member of #{channel3.slack_mention}." ) end it 'opts out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{user1.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention}." ) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'remains opted out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{user1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is opted out of #{channel2.slack_mention}." ) end it 'opts in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is now opted in to #{channel2.slack_mention}." ) end it 'remains opted in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is opted in to #{channel1.slack_mention}." ) end it 'opts into multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is now opted in to #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt out #{user1.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message( "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ) end it 'fails on an unknown channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} <#invalid>", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} <#invalid>", channel: 'DM').to respond_with_slack_message( "Sorry, I can't find an existing S'Up channel <#invalid>." ) end it 'fails on a channel by name' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #invalid", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #invalid", channel: 'DM').to respond_with_slack_message( "Sorry, I don't understand who or what #invalid is." ) end end context 'two users' do it 'lists channels opted in' do - expect(message: "#{SlackRubyBot.config.user} opt #{user1.slack_mention} #{user2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt #{user1.slack_mention} #{user2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is opted in to #{channel1.slack_mention}, opted out of #{channel2.slack_mention} and not a member of #{channel3.slack_mention}.", "User #{user2.slack_mention} is opted in to #{channel1.slack_mention}, opted out of #{channel2.slack_mention} and not a member of #{channel3.slack_mention}." ].join("\n")) end it 'opts out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention}.", "User #{user2.slack_mention} is now opted out of #{channel1.slack_mention}." ].join("\n")) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}.", "User #{user2.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ].join("\n")) end it 'remains opted out of a channel' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{user2.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt out #{user1.slack_mention} #{user2.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is opted out of #{channel2.slack_mention}.", "User #{user2.slack_mention} is opted out of #{channel2.slack_mention}." ].join("\n")) end it 'opts in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{user2.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt in #{user1.slack_mention} #{user2.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is now opted in to #{channel2.slack_mention}.", "User #{user2.slack_mention} is now opted in to #{channel2.slack_mention}." ].join("\n")) end it 'remains opted in a channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt in #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is opted in to #{channel1.slack_mention}.", "User #{user2.slack_mention} is opted in to #{channel1.slack_mention}." ].join("\n")) end it 'opts into multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt in #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is now opted in to #{channel1.slack_mention} and #{channel2.slack_mention}.", "User #{user2.slack_mention} is now opted in to #{channel1.slack_mention} and #{channel2.slack_mention}." ].join("\n")) end it 'opts out of multiple channels' do - expect(message: "#{SlackRubyBot.config.user} opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ + expect(message: "@sup opt out #{user1.slack_mention} #{user2.slack_mention} #{channel1.slack_mention} #{channel2.slack_mention}", channel: 'DM').to respond_with_slack_message([ "User #{user1.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}.", "User #{user2.slack_mention} is now opted out of #{channel1.slack_mention} and #{channel2.slack_mention}." ].join("\n")) end it 'fails on an unknown channel' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{user2.slack_mention} <#invalid>", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #{user2.slack_mention} <#invalid>", channel: 'DM').to respond_with_slack_message( "Sorry, I can't find an existing S'Up channel <#invalid>." ) end it 'fails on a channel by name' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user1.slack_mention} #{user2.slack_mention} #invalid", channel: 'DM').to respond_with_slack_message( + expect(message: "@sup opt in #{user1.slack_mention} #{user2.slack_mention} #invalid", channel: 'DM').to respond_with_slack_message( "Sorry, I don't understand who or what #invalid is." ) end @@ -241,38 +241,38 @@ context 'current user' do it 'shows current value of opt' do - expect(message: "#{SlackRubyBot.config.user} opt", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt', user: user.user_id).to respond_with_slack_message( 'You are opted in to <#channel>.' ) end it 'shows current opt-in' do user.update_attributes!(opted_in: true) - expect(message: "#{SlackRubyBot.config.user} opt", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt', user: user.user_id).to respond_with_slack_message( 'You are opted in to <#channel>.' ) end it 'shows current opt-out' do user.update_attributes!(opted_in: false) - expect(message: "#{SlackRubyBot.config.user} opt", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt', user: user.user_id).to respond_with_slack_message( 'You are opted out of <#channel>.' ) end it 'opts in' do user.update_attributes!(opted_in: false) - expect(message: "#{SlackRubyBot.config.user} opt in", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt in', user: user.user_id).to respond_with_slack_message( 'You are now opted in to <#channel>.' ) expect(user.reload.opted_in?).to be true end it 'opts out' do user.update_attributes!(opted_in: true) - expect(message: "#{SlackRubyBot.config.user} opt out", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt out', user: user.user_id).to respond_with_slack_message( 'You are now opted out of <#channel>.' ) expect(user.reload.opted_in?).to be false end it 'invalid opt' do - expect(message: "#{SlackRubyBot.config.user} opt whatever", user: user.user_id).to respond_with_slack_message( + expect(message: '@sup opt whatever', user: user.user_id).to respond_with_slack_message( "Sorry, I don't understand who or what whatever is." ) end @@ -283,7 +283,7 @@ allow_any_instance_of(User).to receive(:channel_admin?).and_return(false) end it 'requires an admin' do - expect(message: "#{SlackRubyBot.config.user} opt in #{user.slack_mention}").to respond_with_slack_message( + expect(message: "@sup opt in #{user.slack_mention}").to respond_with_slack_message( "Sorry, only <@#{channel.inviter_id}> or a Slack team admin can opt users in and out." ) end @@ -294,20 +294,20 @@ end it 'opts a user in' do user.update_attributes!(opted_in: false) - expect(message: "#{SlackRubyBot.config.user} opt in #{user.slack_mention}").to respond_with_slack_message( + expect(message: "@sup opt in #{user.slack_mention}").to respond_with_slack_message( "User #{user.slack_mention} is now opted in to <#channel>." ) expect(user.reload.opted_in).to be true end it 'opts a user out' do user.update_attributes!(opted_in: true) - expect(message: "#{SlackRubyBot.config.user} opt out #{user.slack_mention}").to respond_with_slack_message( + expect(message: "@sup opt out #{user.slack_mention}").to respond_with_slack_message( "User #{user.slack_mention} is now opted out of <#channel>." ) expect(user.reload.opted_in).to be false end it 'errors on an invalid user' do - expect(message: "#{SlackRubyBot.config.user} opt in foobar").to respond_with_slack_message( + expect(message: '@sup opt in foobar').to respond_with_slack_message( "Sorry, I don't understand who or what foobar is." ) end diff --git a/spec/slack-sup/commands/rounds_spec.rb b/spec/slack-sup/commands/rounds_spec.rb index 785d46d..6e70298 100644 --- a/spec/slack-sup/commands/rounds_spec.rb +++ b/spec/slack-sup/commands/rounds_spec.rb @@ -5,7 +5,7 @@ include_context :team it 'requires a subscription' do - expect(message: "#{SlackRubyBot.config.user} rounds").to respond_with_slack_message(team.subscribe_text) + expect(message: '@sup rounds').to respond_with_slack_message(team.subscribe_text) end end @@ -13,7 +13,7 @@ include_context :subscribed_team it 'empty stats' do - expect(message: "#{SlackRubyBot.config.user} rounds", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup rounds', channel: 'DM').to respond_with_slack_message( "Team S'Up facilitated 0 rounds in 0 channels." ) end @@ -42,7 +42,7 @@ end it 'reports counts' do Timecop.travel(Time.now + 731.days) - expect(message: "#{SlackRubyBot.config.user} rounds 4", channel: 'DM').to respond_with_slack_message([ + expect(message: '@sup rounds 4', channel: 'DM').to respond_with_slack_message([ "Team S'Up facilitated 4 rounds in 2 channels.", "* in progress in #{channel2.slack_mention}: 1 S'Up paired 3 users and no outcomes reported.", "* in progress in #{channel1.slack_mention}: 1 S'Up paired 3 users and no outcomes reported.", @@ -57,7 +57,7 @@ include_context :channel it 'empty stats' do - expect(message: "#{SlackRubyBot.config.user} rounds").to respond_with_slack_message( + expect(message: '@sup rounds').to respond_with_slack_message( "Channel S'Up facilitated 0 rounds." ) end @@ -78,7 +78,7 @@ end it 'reports counts' do Timecop.travel(Time.now + 731.days) - expect(message: "#{SlackRubyBot.config.user} rounds 2").to respond_with_slack_message( + expect(message: '@sup rounds 2').to respond_with_slack_message( "Channel S'Up facilitated 2 rounds.\n" \ "* in progress: 1 S'Up paired 3 users and no outcomes reported.\n" \ "* 2 years ago: 1 S'Up paired 3 users, 100% positive outcomes and 100% outcomes reported." @@ -100,7 +100,7 @@ channel.sup! end it 'reports counts' do - expect(message: "#{SlackRubyBot.config.user} rounds 2").to respond_with_slack_message( + expect(message: '@sup rounds 2').to respond_with_slack_message( "Channel S'Up facilitated 1 round.\n" \ "* in progress: 1 S'Up paired 3 users, no outcomes reported, 1 opt out and 2 missed users." ) diff --git a/spec/slack-sup/commands/set_spec.rb b/spec/slack-sup/commands/set_spec.rb index 7a641d5..300caf3 100644 --- a/spec/slack-sup/commands/set_spec.rb +++ b/spec/slack-sup/commands/set_spec.rb @@ -1,9 +1,8 @@ require 'spec_helper' describe SlackSup::Commands::Set do - let!(:team) { Fabricate(:team, subscribed: true) } - let(:app) { SlackSup::Server.new(team: team) } - let(:client) { app.send(:client) } + include_context :subscribed_team + context 'in channel' do let!(:channel) { Fabricate(:channel, team: team, channel_id: 'channel') } let(:admin) { Fabricate(:user, channel: channel, user_name: 'username', is_admin: true) } @@ -14,7 +13,7 @@ expect(team).to receive(:find_create_or_update_user_in_channel_by_slack_id!).and_return(admin) end it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set").to respond_with_slack_message( + expect(message: '@sup set').to respond_with_slack_message( "Channel S'Up connects groups of max 3 people on Monday after 9:00 AM every week in (GMT-05:00) Eastern Time (US & Canada), taking special care to not pair the same people more frequently than every 12 weeks.\n" \ "Channel users are _opted in_ by default.\n" \ "Custom profile team field is _not set_.\n" \ @@ -25,32 +24,32 @@ context 'opt' do it 'shows current value when opted in' do channel.update_attributes!(opt_in: true) - expect(message: "#{SlackRubyBot.config.user} set opt").to respond_with_slack_message( + expect(message: '@sup set opt').to respond_with_slack_message( 'Users are opted in by default.' ) end it 'shows current value when opted out' do channel.update_attributes!(opt_in: false) - expect(message: "#{SlackRubyBot.config.user} set opt").to respond_with_slack_message( + expect(message: '@sup set opt').to respond_with_slack_message( 'Users are opted out by default.' ) end it 'opts in' do channel.update_attributes!(opt_in: false) - expect(message: "#{SlackRubyBot.config.user} set opt in").to respond_with_slack_message( + expect(message: '@sup set opt in').to respond_with_slack_message( 'Users are now opted in by default.' ) expect(channel.reload.opt_in).to be true end it 'outs out' do channel.update_attributes!(opt_in: true) - expect(message: "#{SlackRubyBot.config.user} set opt out").to respond_with_slack_message( + expect(message: '@sup set opt out').to respond_with_slack_message( 'Users are now opted out by default.' ) expect(channel.reload.opt_in).to be false end it 'fails on an invalid opt value' do - expect(message: "#{SlackRubyBot.config.user} set opt invalid").to respond_with_slack_message( + expect(message: '@sup set opt invalid').to respond_with_slack_message( 'Invalid value: invalid.' ) expect(channel.reload.opt_in).to be true @@ -59,26 +58,26 @@ context 'api' do it 'shows current value of API on' do channel.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( "Channel data access via the API is on.\n#{channel.api_url}" ) end it 'shows current value of API off' do channel.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( 'Channel data access via the API is off.' ) end it 'enables API' do channel.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api on").to respond_with_slack_message( + expect(message: '@sup set api on').to respond_with_slack_message( "Channel data access via the API is now on.\n#{SlackRubyBotServer::Service.api_url}/channels/#{channel.id}" ) expect(channel.reload.api).to be true end it 'disables API with set' do channel.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api off").to respond_with_slack_message( + expect(message: '@sup set api off').to respond_with_slack_message( 'Channel data access via the API is now off.' ) expect(channel.reload.api).to be false @@ -92,13 +91,13 @@ end it 'shows current value of API on with API URL' do channel.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( "Channel data access via the API is on.\nhttp://local.api/channels/#{channel.id}" ) end it 'shows current value of API off without API URL' do channel.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( 'Channel data access via the API is off.' ) end @@ -107,27 +106,27 @@ context 'api token' do it 'shows current value of API token' do channel.update_attributes!(api_token: 'token', api: true) - expect(message: "#{SlackRubyBot.config.user} set api token").to respond_with_slack_message( + expect(message: '@sup set api token').to respond_with_slack_message( "Channel data access via the API is on with an access token `#{channel.api_token}`.\n#{channel.api_url}" ) end it "doesn't show current value when API off" do channel.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api token").to respond_with_slack_message( + expect(message: '@sup set api token').to respond_with_slack_message( 'Channel data access via the API is off.' ) end it 'rotate api token' do expect(SecureRandom).to receive(:hex).and_return('new') channel.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} rotate api token").to respond_with_slack_message( + expect(message: '@sup rotate api token').to respond_with_slack_message( "Channel data access via the API is on with a new access token `new`.\n#{channel.api_url}" ) expect(channel.reload.api_token).to eq 'new' end it 'unsets api token' do channel.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} unset api token").to respond_with_slack_message( + expect(message: '@sup unset api token').to respond_with_slack_message( "Channel data access via the API is now on.\n#{channel.api_url}" ) expect(channel.reload.api_token).to be nil @@ -141,13 +140,13 @@ end it 'shows current value of API on with API URL' do channel.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( "Channel data access via the API is on.\nhttp://local.api/channels/#{channel.id}" ) end it 'shows current value of API off without API URL' do channel.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( 'Channel data access via the API is off.' ) end @@ -155,144 +154,144 @@ end context 'day' do it 'defaults to Monday' do - expect(message: "#{SlackRubyBot.config.user} set day").to respond_with_slack_message( + expect(message: '@sup set day').to respond_with_slack_message( "Channel S'Up is on Monday." ) end it 'shows current value of sup day' do channel.update_attributes!(sup_wday: 2) - expect(message: "#{SlackRubyBot.config.user} set day").to respond_with_slack_message( + expect(message: '@sup set day').to respond_with_slack_message( "Channel S'Up is on Tuesday." ) end it 'changes day' do - expect(message: "#{SlackRubyBot.config.user} set day friday").to respond_with_slack_message( + expect(message: '@sup set day friday').to respond_with_slack_message( "Channel S'Up is now on Friday." ) expect(channel.reload.sup_wday).to eq 5 end it 'errors set on an invalid day' do - expect(message: "#{SlackRubyBot.config.user} set day foobar").to respond_with_slack_message( + expect(message: '@sup set day foobar').to respond_with_slack_message( "Day _foobar_ is invalid, try _Monday_, _Tuesday_, etc. Channel S'Up is on Monday." ) end end context 'followup' do it 'defaults to Thursday' do - expect(message: "#{SlackRubyBot.config.user} set followup").to respond_with_slack_message( + expect(message: '@sup set followup').to respond_with_slack_message( "Channel S'Up followup day is on Thursday." ) end it 'shows current value of sup followup' do channel.update_attributes!(sup_followup_wday: 2) - expect(message: "#{SlackRubyBot.config.user} set followup").to respond_with_slack_message( + expect(message: '@sup set followup').to respond_with_slack_message( "Channel S'Up followup day is on Tuesday." ) end it 'changes followup' do - expect(message: "#{SlackRubyBot.config.user} set followup friday").to respond_with_slack_message( + expect(message: '@sup set followup friday').to respond_with_slack_message( "Channel S'Up followup day is now on Friday." ) expect(channel.reload.sup_followup_wday).to eq 5 end it 'errors set on an invalid day' do - expect(message: "#{SlackRubyBot.config.user} set followup foobar").to respond_with_slack_message( + expect(message: '@sup set followup foobar').to respond_with_slack_message( "Day _foobar_ is invalid, try _Monday_, _Tuesday_, etc. Channel S'Up followup day is on Thursday." ) end end context 'time' do it 'defaults to 9AM' do - expect(message: "#{SlackRubyBot.config.user} set time").to respond_with_slack_message( + expect(message: '@sup set time').to respond_with_slack_message( "Channel S'Up is after 9:00 AM #{tzs}." ) end it 'shows current value of sup time' do channel.update_attributes!(sup_time_of_day: 10 * 60 * 60 + 30 * 60) - expect(message: "#{SlackRubyBot.config.user} set time").to respond_with_slack_message( + expect(message: '@sup set time').to respond_with_slack_message( "Channel S'Up is after 10:30 AM #{tzs}." ) end it 'changes sup time' do - expect(message: "#{SlackRubyBot.config.user} set time 11:20PM").to respond_with_slack_message( + expect(message: '@sup set time 11:20PM').to respond_with_slack_message( "Channel S'Up is now after 11:20 PM #{tzs}." ) expect(channel.reload.sup_time_of_day).to eq 23 * 60 * 60 + 20 * 60 end it 'errors set on an invalid time' do - expect(message: "#{SlackRubyBot.config.user} set time foobar").to respond_with_slack_message( + expect(message: '@sup set time foobar').to respond_with_slack_message( "Time _foobar_ is invalid. Channel S'Up is after 9:00 AM #{tzs}." ) end end context 'weeks' do it 'defaults to one' do - expect(message: "#{SlackRubyBot.config.user} set weeks").to respond_with_slack_message( + expect(message: '@sup set weeks').to respond_with_slack_message( "Channel S'Up is every week." ) end it 'shows current value of weeks' do channel.update_attributes!(sup_every_n_weeks: 3) - expect(message: "#{SlackRubyBot.config.user} set weeks").to respond_with_slack_message( + expect(message: '@sup set weeks').to respond_with_slack_message( "Channel S'Up is every 3 weeks." ) end it 'changes weeks' do - expect(message: "#{SlackRubyBot.config.user} set weeks 2").to respond_with_slack_message( + expect(message: '@sup set weeks 2').to respond_with_slack_message( "Channel S'Up is now every 2 weeks." ) expect(channel.reload.sup_every_n_weeks).to eq 2 end it 'errors set on an invalid number of weeks' do - expect(message: "#{SlackRubyBot.config.user} set weeks foobar").to respond_with_slack_message( + expect(message: '@sup set weeks foobar').to respond_with_slack_message( "Number _foobar_ is invalid. Channel S'Up is every week." ) end end context 'recency' do it 'defaults to one' do - expect(message: "#{SlackRubyBot.config.user} set recency").to respond_with_slack_message( + expect(message: '@sup set recency').to respond_with_slack_message( 'Taking special care to not pair the same people more than every 12 weeks.' ) end it 'shows current value of recency' do channel.update_attributes!(sup_recency: 3) - expect(message: "#{SlackRubyBot.config.user} set recency").to respond_with_slack_message( + expect(message: '@sup set recency').to respond_with_slack_message( 'Taking special care to not pair the same people more than every 3 weeks.' ) end it 'changes recency' do - expect(message: "#{SlackRubyBot.config.user} set recency 2").to respond_with_slack_message( + expect(message: '@sup set recency 2').to respond_with_slack_message( 'Now taking special care to not pair the same people more than every 2 weeks.' ) expect(channel.reload.sup_recency).to eq 2 end it 'errors set on an invalid number of weeks' do - expect(message: "#{SlackRubyBot.config.user} set recency foobar").to respond_with_slack_message( + expect(message: '@sup set recency foobar').to respond_with_slack_message( 'Number _foobar_ is invalid. Taking special care to not pair the same people more than every 12 weeks.' ) end end context 'size' do it 'defaults to 3' do - expect(message: "#{SlackRubyBot.config.user} set size").to respond_with_slack_message( + expect(message: '@sup set size').to respond_with_slack_message( "Channel S'Up connects groups of 3 people." ) end it 'shows current value of size' do channel.update_attributes!(sup_size: 3) - expect(message: "#{SlackRubyBot.config.user} set size").to respond_with_slack_message( + expect(message: '@sup set size').to respond_with_slack_message( "Channel S'Up connects groups of 3 people." ) end it 'changes size' do - expect(message: "#{SlackRubyBot.config.user} set size 2").to respond_with_slack_message( + expect(message: '@sup set size 2').to respond_with_slack_message( "Channel S'Up now connects groups of 2 people." ) expect(channel.reload.sup_size).to eq 2 end it 'errors set on an invalid number of size' do - expect(message: "#{SlackRubyBot.config.user} set size foobar").to respond_with_slack_message( + expect(message: '@sup set size foobar').to respond_with_slack_message( "Number _foobar_ is invalid. Channel S'Up connects groups of 3 people." ) end @@ -300,26 +299,26 @@ context 'odd' do it 'shows current value of odd on' do channel.update_attributes!(sup_odd: true) - expect(message: "#{SlackRubyBot.config.user} set odd").to respond_with_slack_message( + expect(message: '@sup set odd').to respond_with_slack_message( "Channel S'Up connects groups of max 3 people." ) end it 'shows current value of odd off' do channel.update_attributes!(sup_odd: false) - expect(message: "#{SlackRubyBot.config.user} set odd").to respond_with_slack_message( + expect(message: '@sup set odd').to respond_with_slack_message( "Channel S'Up connects groups of 3 people." ) end it 'enables odd' do channel.update_attributes!(sup_odd: false) - expect(message: "#{SlackRubyBot.config.user} set odd true").to respond_with_slack_message( + expect(message: '@sup set odd true').to respond_with_slack_message( "Channel S'Up now connects groups of max 3 people." ) expect(channel.reload.sup_odd).to be true end it 'disables odd with set' do channel.update_attributes!(sup_odd: true) - expect(message: "#{SlackRubyBot.config.user} set odd false").to respond_with_slack_message( + expect(message: '@sup set odd false').to respond_with_slack_message( "Channel S'Up now connects groups of 3 people." ) expect(channel.reload.sup_odd).to be false @@ -327,45 +326,45 @@ end context 'timezone' do it 'defaults to Eastern Time (US & Canada)' do - expect(message: "#{SlackRubyBot.config.user} set timezone").to respond_with_slack_message( + expect(message: '@sup set timezone').to respond_with_slack_message( "Channel S'Up timezone is #{ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')}." ) end it 'shows current value of sup timezone' do channel.update_attributes!(sup_tz: 'Hawaii') - expect(message: "#{SlackRubyBot.config.user} set timezone").to respond_with_slack_message( + expect(message: '@sup set timezone').to respond_with_slack_message( "Channel S'Up timezone is #{ActiveSupport::TimeZone.new('Hawaii')}." ) end it 'changes timezone' do - expect(message: "#{SlackRubyBot.config.user} set timezone Hawaii").to respond_with_slack_message( + expect(message: '@sup set timezone Hawaii').to respond_with_slack_message( "Channel S'Up timezone is now #{ActiveSupport::TimeZone.new('Hawaii')}." ) expect(channel.reload.sup_tz).to eq 'Hawaii' end it 'errors set on an invalid timezone' do - expect(message: "#{SlackRubyBot.config.user} set timezone foobar").to respond_with_slack_message( + expect(message: '@sup set timezone foobar').to respond_with_slack_message( "TimeZone _foobar_ is invalid, see https://github.com/rails/rails/blob/v6.1.7.2/activesupport/lib/active_support/values/time_zone.rb#L30 for a list. Channel S'Up timezone is currently #{ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')}." ) end end context 'time and time zone together' do it 'sets time together with a timezone' do - expect(message: "#{SlackRubyBot.config.user} set time 10AM Hawaii").to respond_with_slack_message( + expect(message: '@sup set time 10AM Hawaii').to respond_with_slack_message( "Channel S'Up is now after 10:00 AM #{Time.now.in_time_zone(ActiveSupport::TimeZone.new('Hawaii')).strftime('%Z')}." ) expect(channel.reload.sup_time_of_day).to eq 10 * 60 * 60 expect(channel.reload.sup_tz).to eq 'Hawaii' end it 'sets time together with a timezone' do - expect(message: "#{SlackRubyBot.config.user} set time 10 AM Hawaii").to respond_with_slack_message( + expect(message: '@sup set time 10 AM Hawaii').to respond_with_slack_message( "Channel S'Up is now after 10:00 AM #{Time.now.in_time_zone(ActiveSupport::TimeZone.new('Hawaii')).strftime('%Z')}." ) expect(channel.reload.sup_time_of_day).to eq 10 * 60 * 60 expect(channel.reload.sup_tz).to eq 'Hawaii' end it 'sets time together with a timezone' do - expect(message: "#{SlackRubyBot.config.user} set time 10:00 AM Pacific Time (US & Canada)").to respond_with_slack_message( + expect(message: '@sup set time 10:00 AM Pacific Time (US & Canada)').to respond_with_slack_message( "Channel S'Up is now after 10:00 AM #{Time.now.in_time_zone(ActiveSupport::TimeZone.new('America/Los_Angeles')).strftime('%Z')}." ) expect(channel.reload.sup_time_of_day).to eq 10 * 60 * 60 @@ -374,31 +373,31 @@ end context 'custom profile team field', vcr: { cassette_name: 'team_profile_get' } do it 'is not set by default' do - expect(message: "#{SlackRubyBot.config.user} set team field").to respond_with_slack_message( + expect(message: '@sup set team field').to respond_with_slack_message( 'Custom profile team field is _not set_.' ) end it 'shows current value' do channel.update_attributes!(team_field_label: 'Artsy Team') - expect(message: "#{SlackRubyBot.config.user} set team field").to respond_with_slack_message( + expect(message: '@sup set team field').to respond_with_slack_message( 'Custom profile team field is _Artsy Team_.' ) end it 'changes value' do - expect(message: "#{SlackRubyBot.config.user} set team field Artsy Title").to respond_with_slack_message( + expect(message: '@sup set team field Artsy Title').to respond_with_slack_message( 'Custom profile team field is now _Artsy Title_.' ) expect(channel.reload.team_field_label).to eq 'Artsy Title' expect(channel.reload.team_field_label_id).to eq 'Xf6RKY5F2B' end it 'errors set on an invalid team field' do - expect(message: "#{SlackRubyBot.config.user} set team field Invalid Field").to respond_with_slack_message( + expect(message: '@sup set team field Invalid Field').to respond_with_slack_message( 'Custom profile team field _Invalid Field_ is invalid. Possible values are _Artsy Title_, _Artsy Team_, _Artsy Subteam_, _Personality Type_, _Instagram_, _Twitter_, _Facebook_, _Website_.' ) end it 'unsets' do channel.update_attributes!(team_field_label: 'Artsy Team') - expect(message: "#{SlackRubyBot.config.user} unset team field").to respond_with_slack_message( + expect(message: '@sup unset team field').to respond_with_slack_message( 'Custom profile team field is now _not set_.' ) expect(channel.reload.team_field_label).to be nil @@ -407,25 +406,25 @@ end context 'custom sup message' do it 'is not set by default' do - expect(message: "#{SlackRubyBot.config.user} set message").to respond_with_slack_message( + expect(message: '@sup set message').to respond_with_slack_message( "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_" ) end it 'shows current value' do channel.update_attributes!(sup_message: 'Please meet.') - expect(message: "#{SlackRubyBot.config.user} set message").to respond_with_slack_message( + expect(message: '@sup set message').to respond_with_slack_message( "Using a custom S'Up message. _Please meet._" ) end it 'changes value' do - expect(message: "#{SlackRubyBot.config.user} set message Hello world!").to respond_with_slack_message( + expect(message: '@sup set message Hello world!').to respond_with_slack_message( "Now using a custom S'Up message. _Hello world!_" ) expect(channel.reload.sup_message).to eq 'Hello world!' end it 'unsets' do channel.update_attributes!(sup_message: 'Updated') - expect(message: "#{SlackRubyBot.config.user} unset message").to respond_with_slack_message( + expect(message: '@sup unset message').to respond_with_slack_message( "Now using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_" ) expect(channel.reload.sup_message).to be nil @@ -433,17 +432,17 @@ end context 'invalid' do it 'errors set' do - expect(message: "#{SlackRubyBot.config.user} set invalid on").to respond_with_slack_message( + expect(message: '@sup set invalid on').to respond_with_slack_message( 'Invalid channel setting _invalid_, see _help_ for available options.' ) end it 'errors unset' do - expect(message: "#{SlackRubyBot.config.user} unset invalid").to respond_with_slack_message( + expect(message: '@sup unset invalid').to respond_with_slack_message( 'Invalid channel setting _invalid_, see _help_ for available options.' ) end it 'errors rotate' do - expect(message: "#{SlackRubyBot.config.user} rotate invalid").to respond_with_slack_message( + expect(message: '@sup rotate invalid').to respond_with_slack_message( 'Invalid channel setting _invalid_, see _help_ for available options.' ) end @@ -452,13 +451,13 @@ let!(:requester) { Fabricate(:user, channel: channel, user_id: 'user') } it 'shows next sync' do channel.update_attributes!(sync: true) - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( 'Users will sync in the next hour.' ) end it 'shows last sync' do channel.update_attributes!(sync: false) - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( "Users will sync before the next round. #{channel.next_sup_at_text}" ) end @@ -466,7 +465,7 @@ Timecop.travel(Time.now.utc + 1.minute) channel.update_attributes!(last_sync_at: Time.now.utc) Fabricate(:user, channel: channel) - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( "Last users sync was less than 1 second ago, 1 user updated. Users will sync before the next round. #{channel.next_sup_at_text}" ) end @@ -474,7 +473,7 @@ Fabricate(:user, channel: channel) Timecop.travel(Time.now.utc + 1.minute) channel.update_attributes!(last_sync_at: Time.now.utc) - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( "Last users sync was less than 1 second ago, 0 users updated. Users will sync before the next round. #{channel.next_sup_at_text}" ) end @@ -482,20 +481,20 @@ Timecop.travel(Time.now.utc + 1.minute) channel.update_attributes!(last_sync_at: Time.now.utc) 2.times { Fabricate(:user, channel: channel) } - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( "Last users sync was less than 1 second ago, 2 users updated. Users will sync before the next round. #{channel.next_sup_at_text}" ) end it 'sets sync' do channel.update_attributes!(sup_odd: false) - expect(message: "#{SlackRubyBot.config.user} set sync now").to respond_with_slack_message( + expect(message: '@sup set sync now').to respond_with_slack_message( 'Users will sync in the next hour. Come back and run `set sync` or `stats` in a bit.' ) expect(channel.reload.sync).to be true end it 'errors on invalid sync value' do channel.update_attributes!(sync: false) - expect(message: "#{SlackRubyBot.config.user} set sync foobar").to respond_with_slack_message( + expect(message: '@sup set sync foobar').to respond_with_slack_message( 'The option _foobar_ is invalid. Use `now` to schedule a user sync in the next hour.' ) expect(channel.reload.sync).to be false @@ -505,143 +504,143 @@ context 'not admin' do context 'api' do it 'cannot set opt' do - expect(message: "#{SlackRubyBot.config.user} set opt out").to respond_with_slack_message( + expect(message: '@sup set opt out').to respond_with_slack_message( "Users are opted in by default. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'cannot set api' do - expect(message: "#{SlackRubyBot.config.user} set api true").to respond_with_slack_message( + expect(message: '@sup set api true').to respond_with_slack_message( "Channel data access via the API is on. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see api value' do - expect(message: "#{SlackRubyBot.config.user} set api").to respond_with_slack_message( + expect(message: '@sup set api').to respond_with_slack_message( "Channel data access via the API is on.\n#{channel.api_url}" ) end it 'does not show current value of API token' do channel.update_attributes!(api_token: 'token', api: true) - expect(message: "#{SlackRubyBot.config.user} set api token").to respond_with_slack_message( + expect(message: '@sup set api token').to respond_with_slack_message( "Channel data access via the API is on with an access token visible to admins.\n#{channel.api_url}" ) end it 'rotate api token' do channel.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} rotate api token").to respond_with_slack_message( + expect(message: '@sup rotate api token').to respond_with_slack_message( "Channel data access via the API is on with an access token visible to admins. Only <@#{channel.inviter_id}> or a Slack team admin can rotate it, sorry." ) expect(channel.reload.api_token).to eq 'old' end it 'unsets api token' do channel.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} unset api token").to respond_with_slack_message( + expect(message: '@sup unset api token').to respond_with_slack_message( "Channel data access via the API is on with an access token visible to admins. Only <@#{channel.inviter_id}> or a Slack team admin can unset it, sorry." ) expect(channel.reload.api_token).to eq 'old' end it 'cannot set day' do - expect(message: "#{SlackRubyBot.config.user} set day tuesday").to respond_with_slack_message( + expect(message: '@sup set day tuesday').to respond_with_slack_message( "Channel S'Up is on Monday. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see sup day' do - expect(message: "#{SlackRubyBot.config.user} set day").to respond_with_slack_message( + expect(message: '@sup set day').to respond_with_slack_message( "Channel S'Up is on Monday." ) end it 'cannot set time' do - expect(message: "#{SlackRubyBot.config.user} set time 11:00 AM").to respond_with_slack_message( + expect(message: '@sup set time 11:00 AM').to respond_with_slack_message( "Channel S'Up is after 9:00 AM #{tzs}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see time' do - expect(message: "#{SlackRubyBot.config.user} set time").to respond_with_slack_message( + expect(message: '@sup set time').to respond_with_slack_message( "Channel S'Up is after 9:00 AM #{tzs}." ) end it 'cannot set weeks' do - expect(message: "#{SlackRubyBot.config.user} set weeks 2").to respond_with_slack_message( + expect(message: '@sup set weeks 2').to respond_with_slack_message( "Channel S'Up is every week. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see weeks' do - expect(message: "#{SlackRubyBot.config.user} set weeks").to respond_with_slack_message( + expect(message: '@sup set weeks').to respond_with_slack_message( "Channel S'Up is every week." ) end it 'cannot set followup day' do - expect(message: "#{SlackRubyBot.config.user} set followup 2").to respond_with_slack_message( + expect(message: '@sup set followup 2').to respond_with_slack_message( "Channel S'Up followup day is on Thursday. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see followup day' do - expect(message: "#{SlackRubyBot.config.user} set followup").to respond_with_slack_message( + expect(message: '@sup set followup').to respond_with_slack_message( "Channel S'Up followup day is on Thursday." ) end it 'cannot set recency' do - expect(message: "#{SlackRubyBot.config.user} set recency 2").to respond_with_slack_message( + expect(message: '@sup set recency 2').to respond_with_slack_message( "Taking special care to not pair the same people more than every 12 weeks. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see recency' do - expect(message: "#{SlackRubyBot.config.user} set recency").to respond_with_slack_message( + expect(message: '@sup set recency').to respond_with_slack_message( 'Taking special care to not pair the same people more than every 12 weeks.' ) end it 'cannot set size' do - expect(message: "#{SlackRubyBot.config.user} set size 2").to respond_with_slack_message( + expect(message: '@sup set size 2').to respond_with_slack_message( "Channel S'Up connects groups of 3 people. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see size' do - expect(message: "#{SlackRubyBot.config.user} set size").to respond_with_slack_message( + expect(message: '@sup set size').to respond_with_slack_message( "Channel S'Up connects groups of 3 people." ) end it 'cannot set timezone' do - expect(message: "#{SlackRubyBot.config.user} set tz Hawaii").to respond_with_slack_message( + expect(message: '@sup set tz Hawaii').to respond_with_slack_message( "Channel S'Up timezone is #{ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')}. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see timezone' do - expect(message: "#{SlackRubyBot.config.user} set tz").to respond_with_slack_message( + expect(message: '@sup set tz').to respond_with_slack_message( "Channel S'Up timezone is #{ActiveSupport::TimeZone.new('Eastern Time (US & Canada)')}." ) end it 'cannot set custom profile team field' do - expect(message: "#{SlackRubyBot.config.user} set team field Artsy Team").to respond_with_slack_message( + expect(message: '@sup set team field Artsy Team').to respond_with_slack_message( "Custom profile team field is _not set_. Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see custom profile team field' do - expect(message: "#{SlackRubyBot.config.user} set team field").to respond_with_slack_message( + expect(message: '@sup set team field').to respond_with_slack_message( 'Custom profile team field is _not set_.' ) end it 'cannot set message' do - expect(message: "#{SlackRubyBot.config.user} set message Custom message.").to respond_with_slack_message( + expect(message: '@sup set message Custom message.').to respond_with_slack_message( "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_ Only <@#{channel.inviter_id}> or a Slack team admin can change that, sorry." ) end it 'can see custom sup message' do - expect(message: "#{SlackRubyBot.config.user} set message").to respond_with_slack_message( + expect(message: '@sup set message').to respond_with_slack_message( "Using the default S'Up message. _#{Sup::PLEASE_SUP_MESSAGE}_" ) end it 'can see sync info' do - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( "Users will sync before the next round. #{channel.next_sup_at_text}" ) end it 'can see exact sync date' do channel.update_attributes!(sync: true) - expect(message: "#{SlackRubyBot.config.user} set sync").to respond_with_slack_message( + expect(message: '@sup set sync').to respond_with_slack_message( 'Users will sync in the next hour.' ) end it 'cannot set sync now' do - expect(message: "#{SlackRubyBot.config.user} set sync now").to respond_with_slack_message( + expect(message: '@sup set sync now').to respond_with_slack_message( "Users will sync before the next round. #{channel.next_sup_at_text} Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry." ) end @@ -652,7 +651,7 @@ context 'admin' do context 'set' do it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "S'Up is not enabled in any channels.\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -661,7 +660,7 @@ context 'with a channel' do let!(:channel) { Fabricate(:channel, team: team) } it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "S'Up is enabled in #{channel.slack_mention}.\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -672,7 +671,7 @@ let!(:channel1) { Fabricate(:channel, team: team) } let!(:channel2) { Fabricate(:channel, team: team) } it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "S'Up is enabled in 2 channels (#{channel1.slack_mention} and #{channel2.slack_mention}).\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -683,26 +682,26 @@ context 'api' do it 'shows current value of API on' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is on.\n#{team.api_url}" ) end it 'shows current value of API off' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( 'Team data access via the API is off.' ) end it 'enables API' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api on", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api on', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is now on.\n#{SlackRubyBotServer::Service.api_url}/teams/#{team.id}" ) expect(team.reload.api).to be true end it 'disables API with set' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api off", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api off', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( 'Team data access via the API is now off.' ) expect(team.reload.api).to be false @@ -716,13 +715,13 @@ end it 'shows current value of API on with API URL' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is on.\nhttp://local.api/teams/#{team.id}" ) end it 'shows current value of API off without API URL' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( 'Team data access via the API is off.' ) end @@ -731,27 +730,27 @@ context 'api token' do it 'shows current value of API token' do team.update_attributes!(api_token: 'token', api: true) - expect(message: "#{SlackRubyBot.config.user} set api token", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api token', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is on with an access token `#{team.api_token}`.\n#{team.api_url}" ) end it "doesn't show current value when API off" do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api token", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api token', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( 'Team data access via the API is off.' ) end it 'rotate api token' do expect(SecureRandom).to receive(:hex).and_return('new') team.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} rotate api token", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup rotate api token', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is on with a new access token `new`.\n#{team.api_url}" ) expect(team.reload.api_token).to eq 'new' end it 'unsets api token' do team.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} unset api token", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup unset api token', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is now on.\n#{team.api_url}" ) expect(team.reload.api_token).to be nil @@ -765,13 +764,13 @@ end it 'shows current value of API on with API URL' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( "Team data access via the API is on.\nhttp://local.api/teams/#{team.id}" ) end it 'shows current value of API off without API URL' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM', user: team.activated_user_id).to respond_with_slack_message( 'Team data access via the API is off.' ) end @@ -779,17 +778,17 @@ end context 'invalid' do it 'errors set' do - expect(message: "#{SlackRubyBot.config.user} set invalid on", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set invalid on', channel: 'DM').to respond_with_slack_message( 'Invalid global setting _invalid_, see _help_ for available options.' ) end it 'errors unset' do - expect(message: "#{SlackRubyBot.config.user} unset invalid", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup unset invalid', channel: 'DM').to respond_with_slack_message( 'Invalid global setting _invalid_, see _help_ for available options.' ) end it 'errors rotate' do - expect(message: "#{SlackRubyBot.config.user} rotate invalid", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup rotate invalid', channel: 'DM').to respond_with_slack_message( 'Invalid global setting _invalid_, see _help_ for available options.' ) end @@ -808,7 +807,7 @@ end context 'set' do it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM').to respond_with_slack_message( "S'Up is not enabled in any channels.\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -817,7 +816,7 @@ context 'with a channel' do let!(:channel) { Fabricate(:channel, team: team) } it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM').to respond_with_slack_message( "S'Up is enabled in #{channel.slack_mention}.\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -828,7 +827,7 @@ let!(:channel1) { Fabricate(:channel, team: team) } let!(:channel2) { Fabricate(:channel, team: team) } it 'displays all settings' do - expect(message: "#{SlackRubyBot.config.user} set", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set', channel: 'DM').to respond_with_slack_message( "S'Up is enabled in 2 channels (#{channel1.slack_mention} and #{channel2.slack_mention}).\n" \ "Team data access via the API is on.\n" \ "#{team.api_url}" @@ -839,26 +838,26 @@ context 'api' do it 'shows current value of API on' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on.\n#{team.api_url}" ) end it 'shows current value of API off' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( 'Team data access via the API is off.' ) end it 'cannot enable API' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api on", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api on', channel: 'DM').to respond_with_slack_message( "Team data access via the API is off. Only <@#{team.activated_user_id}> or a Slack team admin can change that, sorry." ) expect(team.reload.api).to be false end it 'disables API with set' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api off", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api off', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on. Only <@#{team.activated_user_id}> or a Slack team admin can change that, sorry." ) expect(team.reload.api).to be true @@ -872,13 +871,13 @@ end it 'shows current value of API on with API URL' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on.\nhttp://local.api/teams/#{team.id}" ) end it 'shows current value of API off without API URL' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( 'Team data access via the API is off.' ) end @@ -887,27 +886,27 @@ context 'api token' do it 'shows current value of API token' do team.update_attributes!(api_token: 'token', api: true) - expect(message: "#{SlackRubyBot.config.user} set api token", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api token', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on with an access token visible to admins.\n#{team.api_url}" ) end it "doesn't show current value when API off" do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api token", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api token', channel: 'DM').to respond_with_slack_message( 'Team data access via the API is off.' ) end it 'cannot rotate api token' do expect(SecureRandom).to_not receive(:hex) team.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} rotate api token", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup rotate api token', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on with an access token visible to admins. Only <@#{team.activated_user_id}> or a Slack team admin can rotate it, sorry." ) expect(team.reload.api_token).to eq 'old' end it 'cannot unset api token' do team.update_attributes!(api: true, api_token: 'old') - expect(message: "#{SlackRubyBot.config.user} unset api token", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup unset api token', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on with an access token visible to admins. Only <@#{team.activated_user_id}> or a Slack team admin can unset it, sorry." ) expect(team.reload.api_token).to eq 'old' @@ -921,13 +920,13 @@ end it 'shows current value of API on with API URL' do team.update_attributes!(api: true) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( "Team data access via the API is on.\nhttp://local.api/teams/#{team.id}" ) end it 'shows current value of API off without API URL' do team.update_attributes!(api: false) - expect(message: "#{SlackRubyBot.config.user} set api", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup set api', channel: 'DM').to respond_with_slack_message( 'Team data access via the API is off.' ) end diff --git a/spec/slack-sup/commands/stats_spec.rb b/spec/slack-sup/commands/stats_spec.rb index 859c60b..5872174 100644 --- a/spec/slack-sup/commands/stats_spec.rb +++ b/spec/slack-sup/commands/stats_spec.rb @@ -5,7 +5,7 @@ include_context :subscribed_team it 'returns global team stats' do - expect(message: "#{SlackRubyBot.config.user} stats", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup stats', channel: 'DM').to respond_with_slack_message( "Team S'Up connects 0 users in 0 channels." ) end @@ -14,7 +14,7 @@ include_context :channel it 'empty stats' do - expect(message: "#{SlackRubyBot.config.user} stats").to respond_with_slack_message( + expect(message: '@sup stats').to respond_with_slack_message( "Channel S'Up connects groups of 3 people on Monday after 9:00 AM every week in <#channel>.\n" \ "Channel S'Up started 3 weeks ago." ) @@ -37,7 +37,7 @@ user2.update_attributes!(opted_in: false) end it 'reports counts' do - expect(message: "#{SlackRubyBot.config.user} stats").to respond_with_slack_message( + expect(message: '@sup stats').to respond_with_slack_message( "Channel S'Up connects groups of 3 people on Monday after 9:00 AM every week in <#channel>.\n" \ "Channel S'Up started 3 weeks ago with 66% (2/3) of users opted in.\n" \ "Facilitated 2 S'Ups in 2 rounds for 3 users with 50% positive outcomes from 50% outcomes reported." diff --git a/spec/slack-sup/commands/subscription_spec.rb b/spec/slack-sup/commands/subscription_spec.rb index 164aded..1e65806 100644 --- a/spec/slack-sup/commands/subscription_spec.rb +++ b/spec/slack-sup/commands/subscription_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' describe SlackSup::Commands::Subscription do - include_context :client - shared_examples_for 'subscription' do include_context :stripe_mock context 'with a plan' do @@ -28,11 +26,11 @@ card = customer.sources.first customer_info += "\nOn file Visa card, #{card.name} ending with #{card.last4}, expires #{card.exp_month}/#{card.exp_year}." customer_info += "\n#{team.update_cc_text}" - expect(message: "#{SlackRubyBot.config.user} subscription", channel: 'DM').to respond_with_slack_message customer_info + expect(message: '@sup subscription', channel: 'DM').to respond_with_slack_message customer_info end it 'requires an admin user' do allow_any_instance_of(Team).to receive(:is_admin?).and_return(false) - expect(message: "#{SlackRubyBot.config.user} subscription", channel: 'DM').to respond_with_slack_message "Only <@#{team.activated_user_id}> or a Slack team admin can get subscription details, sorry." + expect(message: '@sup subscription', channel: 'DM').to respond_with_slack_message "Only <@#{team.activated_user_id}> or a Slack team admin can get subscription details, sorry." end end end @@ -41,7 +39,7 @@ include_context :team it 'is a subscription feature' do - expect(message: "#{SlackRubyBot.config.user} subscription", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup subscription', channel: 'DM').to respond_with_slack_message( "Subscribe your team for $39.99 a year at #{SlackRubyBotServer::Service.url}/subscribe?team_id=#{team.team_id}." ) end @@ -57,7 +55,7 @@ team.update_attributes!(subscribed: true, stripe_customer_id: nil) end it 'reports subscribed' do - expect(message: "#{SlackRubyBot.config.user} subscription", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup subscription', channel: 'DM').to respond_with_slack_message( 'Team is subscribed.' ) end diff --git a/spec/slack-sup/commands/sync_spec.rb b/spec/slack-sup/commands/sync_spec.rb index ba48f01..ed0cf50 100644 --- a/spec/slack-sup/commands/sync_spec.rb +++ b/spec/slack-sup/commands/sync_spec.rb @@ -5,7 +5,7 @@ include_context :subscribed_team it 'does not sync' do - expect(message: "#{SlackRubyBot.config.user} sync", channel: 'DM').to respond_with_slack_message( + expect(message: '@sup sync', channel: 'DM').to respond_with_slack_message( 'Please run this command in a channel.' ) end @@ -18,7 +18,7 @@ expect_any_instance_of(User).to receive(:channel_admin?).and_return(true) end it 'sync' do - expect(message: "#{SlackRubyBot.config.user} sync").to respond_with_slack_message( + expect(message: '@sup sync').to respond_with_slack_message( 'Users will sync in the next hour. Come back and run `stats` in a bit.' ) end @@ -28,7 +28,7 @@ expect_any_instance_of(User).to receive(:channel_admin?).and_return(false) end it 'sets sync' do - expect(message: "#{SlackRubyBot.config.user} sync").to respond_with_slack_message( + expect(message: '@sup sync').to respond_with_slack_message( "Users will sync before the next round. Only <@#{channel.inviter_id}> or a Slack team admin can manually sync, sorry." ) end diff --git a/spec/slack-sup/commands/unsubscribe_spec.rb b/spec/slack-sup/commands/unsubscribe_spec.rb index 872354c..aa1df72 100644 --- a/spec/slack-sup/commands/unsubscribe_spec.rb +++ b/spec/slack-sup/commands/unsubscribe_spec.rb @@ -1,15 +1,13 @@ require 'spec_helper' describe SlackSup::Commands::Unsubscribe do - include_context :client - shared_examples_for 'unsubscribe' do context 'on trial' do before do team.update_attributes!(subscribed: false, subscribed_at: nil, created_at: 1.week.ago) end it 'displays all set message' do - expect(message: "#{SlackRubyBot.config.user} unsubscribe", channel: 'DM').to respond_with_slack_message "You don't have a paid subscription, all set." + expect(message: '@sup unsubscribe', channel: 'DM').to respond_with_slack_message "You don't have a paid subscription, all set." end end context 'with subscribed_at' do @@ -17,7 +15,7 @@ team.update_attributes!(subscribed: true, subscribed_at: 1.year.ago) end it 'displays subscription info' do - expect(message: "#{SlackRubyBot.config.user} unsubscribe", channel: 'DM').to respond_with_slack_message "You don't have a paid subscription, all set." + expect(message: '@sup unsubscribe', channel: 'DM').to respond_with_slack_message "You don't have a paid subscription, all set." end end context 'with a plan' do @@ -50,13 +48,13 @@ "Subscribed to Plan ($29.99), will auto-renew on #{current_period_end}.", "Send `unsubscribe #{active_subscription.id}` to unsubscribe." ].join("\n") - expect(message: "#{SlackRubyBot.config.user} unsubscribe", channel: 'DM').to respond_with_slack_message customer_info + expect(message: '@sup unsubscribe', channel: 'DM').to respond_with_slack_message customer_info end it 'cannot unsubscribe with an invalid subscription id' do - expect(message: "#{SlackRubyBot.config.user} unsubscribe xyz", channel: 'DM').to respond_with_slack_message 'Sorry, I cannot find a subscription with "xyz".' + expect(message: '@sup unsubscribe xyz', channel: 'DM').to respond_with_slack_message 'Sorry, I cannot find a subscription with "xyz".' end it 'unsubscribes' do - expect(message: "#{SlackRubyBot.config.user} unsubscribe #{active_subscription.id}", channel: 'DM').to respond_with_slack_message 'Successfully canceled auto-renew for Plan ($29.99).' + expect(message: "@sup unsubscribe #{active_subscription.id}", channel: 'DM').to respond_with_slack_message 'Successfully canceled auto-renew for Plan ($29.99).' team.reload expect(team.subscribed).to be true expect(team.stripe_customer_id).to_not be nil @@ -67,7 +65,7 @@ expect_any_instance_of(Team).to receive(:is_admin?).and_return(false) end it 'cannot unsubscribe' do - expect(message: "#{SlackRubyBot.config.user} unsubscribe xyz", channel: 'DM').to respond_with_slack_message "Only <@#{team.activated_user_id}> or a Slack team admin can unsubscribe, sorry." + expect(message: '@sup unsubscribe xyz', channel: 'DM').to respond_with_slack_message "Only <@#{team.activated_user_id}> or a Slack team admin can unsubscribe, sorry." end end end diff --git a/spec/slack-sup/events/member_joined_channel_spec.rb b/spec/slack-sup/events/member_joined_channel_spec.rb new file mode 100644 index 0000000..0e1c540 --- /dev/null +++ b/spec/slack-sup/events/member_joined_channel_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'events/member_joined_channel' do + include_context :event + + let(:event) do + { + type: 'member_joined_channel', + team: team.team_id, + user: team.bot_user_id, + channel: 'channel_id', + inviter: 'iviter_id' + } + end + + it 'posts a welcome message' do + expect_any_instance_of(Slack::Web::Client).to receive(:chat_postMessage).with( + channel: 'channel_id', text: /Hi there!/ + ) + + expect do + post '/api/slack/event', event_envelope + expect(last_response.status).to eq 201 + expect(JSON.parse(last_response.body)).to eq('ok' => true) + end.to change(Channel, :count).by(1) + end + + context 'when the bot is already a member of a channel' do + let!(:channel) { Fabricate(:channel, team: team, channel_id: 'channel_id') } + + it 'welcomes the bot again' do + expect_any_instance_of(Slack::Web::Client).to receive(:chat_postMessage).with( + hash_including(channel: 'channel_id') + ) + + expect do + post '/api/slack/event', event_envelope + expect(last_response.status).to eq 201 + expect(JSON.parse(last_response.body)).to eq('ok' => true) + end.to_not change(Channel, :count) + end + end +end diff --git a/spec/slack-sup/events/member_left_channel_spec.rb b/spec/slack-sup/events/member_left_channel_spec.rb new file mode 100644 index 0000000..af8200c --- /dev/null +++ b/spec/slack-sup/events/member_left_channel_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'events/member_left_channel' do + include_context :event + + let(:event) do + { + type: 'member_left_channel', + team: team.team_id, + user: team.bot_user_id, + channel: 'channel_id', + inviter: 'iviter_id' + } + end + + it 'does nothing' do + post '/api/slack/event', event_envelope + expect(last_response.status).to eq 201 + expect(JSON.parse(last_response.body)).to eq('ok' => true) + end + + context 'with a channel' do + let!(:channel) { Fabricate(:channel, team: team, channel_id: event[:channel]) } + + it 'removes bot from channel' do + expect(channel.enabled).to be true + post '/api/slack/event', event_envelope + expect(last_response.status).to eq 201 + expect(JSON.parse(last_response.body)).to eq('ok' => true) + expect(channel.reload.enabled).to be false + end + end +end diff --git a/spec/slack-sup/server_spec.rb b/spec/slack-sup/server_spec.rb deleted file mode 100644 index 053bd39..0000000 --- a/spec/slack-sup/server_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'spec_helper' - -describe SlackSup::Server do - let(:team) { Fabricate(:team) } - let(:client) { subject.send(:client) } - subject do - SlackSup::Server.new(team: team) - end - before do - client.owner.bot_user_id = 'bot_user_id' - end - context '#member_joined_channel' do - it 'sends a welcome message' do - expect(client).to receive(:say).with(channel: 'C12345', text: [ - "Hi there! I'm your team's S'Up bot. Thanks for trying me out. Type `@sup help` for instructions.", - "I plan to setup some S'Ups via Slack DM for all users in this channel next Monday.", - 'You may want to `set size`, `set day`, `set timezone`, or `set sync now` users before then.' - ].join(' ')) - expect do - client.send(:callback, Hashie::Mash.new('channel' => 'C12345', 'user' => 'bot_user_id', 'inviter' => 'inviter'), :member_joined_channel) - end.to change(Channel, :count).by(1) - end - context 'with an existing channel' do - let!(:channel) { Fabricate(:channel, channel_id: 'C12345') } - it 'sends a welcome message' do - expect(client).to receive(:say).with(channel: 'C12345', text: [ - "Hi there! I'm your team's S'Up bot. Thanks for trying me out. Type `@sup help` for instructions.", - "I plan to setup some S'Ups via Slack DM for all users in this channel next Monday.", - 'You may want to `set size`, `set day`, `set timezone`, or `set sync now` users before then.' - ].join(' ')) - expect do - client.send(:callback, Hashie::Mash.new('channel' => 'C12345', 'user' => 'bot_user_id', 'inviter' => 'inviter'), :member_joined_channel) - end.to_not change(Channel, :count) - end - end - end - context '#member_left_channel' do - let!(:channel) { Fabricate(:channel, channel_id: 'C12345') } - it 'deactivates a channel when the bot leaves a channel' do - client.send(:callback, Hashie::Mash.new('channel' => 'C12345', 'user' => 'bot_user_id'), :member_left_channel) - expect(channel.reload.enabled).to be false - end - it 'does not deactivate another channel' do - client.send(:callback, Hashie::Mash.new('channel' => 'other', 'user' => 'bot_user_id'), :member_left_channel) - expect(channel.reload.enabled).to be true - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 358f714..b4aa373 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,8 +7,7 @@ ENV['RACK_ENV'] = 'test' -require 'slack-ruby-bot/rspec' -require 'slack-sup' +require 'app' Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file| require file diff --git a/spec/support/slack/event.rb b/spec/support/slack/event.rb new file mode 100644 index 0000000..04ea6cd --- /dev/null +++ b/spec/support/slack/event.rb @@ -0,0 +1,91 @@ +RSpec.shared_context :event do + include Rack::Test::Methods + + def app + SlackRubyBotServer::Api::Middleware.instance + end + + let!(:team) { Fabricate(:team, bot_user_id: 'bot_user_id') } + let(:event) { {} } + let(:event_envelope) do + { + token: 'deprecated', + api_app_id: 'A19GAJ72T', + event: { + message_ts: '1547842100.001400' + }.merge(event), + type: 'event_callback', + event_id: 'EvFGTNRKLG', + event_time: 1_547_842_101, + authed_users: ['U04KB5WQR'] + } + end + + before do + allow_any_instance_of(Slack::Events::Request).to receive(:verify!) + end +end + +RSpec::Matchers.define :respond_with_slack_message do |expected| + def parse(actual) + actual = { message: actual } unless actual.is_a?(Hash) + attachments = actual[:attachments] + attachments = [attachments] unless attachments.nil? || attachments.is_a?(Array) + [actual[:channel] || 'channel', actual[:user] || 'user', actual[:message], attachments] + end + + match do |actual| + channel, user, message, attachments = parse(actual) + + allow(Team).to receive(:where).with(team_id: team.team_id).and_return([team]) + + allow(Team).to receive(:where).with('_id' => team._id).and_return(double.tap do |where_scope| + allow(where_scope).to receive(:limit).and_return(double.tap do |limit_scope| + allow(limit_scope).to receive(:first).and_return(team) + end) + end) + + allow(team.slack_client).to receive(:chat_postMessage) do |options| + @messages ||= [] + @messages.push options + end + + begin + SlackRubyBotServer::Events.config.run_callbacks( + :event, + %w[event_callback app_mention], + Slack::Messages::Message.new( + 'team_id' => team.team_id, + 'event' => { + 'user' => user || 'user_id', + 'channel' => channel || 'channel_id', + 'text' => message, + 'attachments' => attachments + } + ) + ) + rescue Mongoid::Errors::Validations => e + m = e.document.errors.messages.transform_values(&:uniq).values.join(', ') + Api::Middleware.logger.warn(m) + expect(m).to eq(expected) + return true + rescue StandardError => e + Api::Middleware.logger.warn(e.message) + expect(e.message).to eq(expected) + return true + end + + matcher = have_received(:chat_postMessage).once + matcher = matcher.with(hash_including(channel: channel, text: expected)) if channel && expected + + expect(team.slack_client).to matcher + + true + end + + failure_message do |_actual| + message = "expected to receive message with text: #{expected} once,\n received:" + message += @messages&.any? ? @messages.inspect : 'none' + message + end +end diff --git a/spec/support/slack.rb b/spec/support/slack/shared.rb similarity index 71% rename from spec/support/slack.rb rename to spec/support/slack/shared.rb index 5529a5c..5b12686 100644 --- a/spec/support/slack.rb +++ b/spec/support/slack/shared.rb @@ -1,15 +1,8 @@ -RSpec.shared_context :client do - let(:app) { SlackSup::Server.new(team: team) } - let(:client) { app.send(:client) } -end - RSpec.shared_context :subscribed_team do - include_context :client let!(:team) { Fabricate(:team, subscribed: true) } end RSpec.shared_context :team do - include_context :client let!(:team) { Fabricate(:team) } end