@ -0,0 +1 @@ | |||||
.env |
@ -0,0 +1,2 @@ | |||||
--format documentation | |||||
--color |
@ -0,0 +1,6 @@ | |||||
AllCops: | |||||
Exclude: | |||||
- vendor/**/* | |||||
- bin/**/* | |||||
inherit_from: .rubocop_todo.yml |
@ -0,0 +1,45 @@ | |||||
# This configuration was generated by `rubocop --auto-gen-config` | |||||
# on 2015-06-19 13:09:29 -0400 using RuboCop version 0.31.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 | |||||
Lint/HandleExceptions: | |||||
Enabled: false | |||||
# Offense count: 2 | |||||
Metrics/AbcSize: | |||||
Max: 21 | |||||
# Offense count: 1 | |||||
Metrics/CyclomaticComplexity: | |||||
Max: 7 | |||||
# Offense count: 19 | |||||
# Configuration parameters: AllowURI, URISchemes. | |||||
Metrics/LineLength: | |||||
Max: 130 | |||||
# Offense count: 1 | |||||
# Configuration parameters: CountComments. | |||||
Metrics/MethodLength: | |||||
Max: 17 | |||||
# Offense count: 16 | |||||
Style/Documentation: | |||||
Enabled: false | |||||
# Offense count: 1 | |||||
# Configuration parameters: Exclude. | |||||
Style/FileName: | |||||
Enabled: false | |||||
# Offense count: 1 | |||||
Style/ModuleFunction: | |||||
Enabled: false | |||||
# Offense count: 1 | |||||
Style/RescueModifier: | |||||
Enabled: false |
@ -0,0 +1,3 @@ | |||||
rvm: | |||||
- 2.1.6 | |||||
@ -0,0 +1,3 @@ | |||||
### 0.1.0 (Next) | |||||
* Initial public release - [@dblock](https://github.com/dblock). |
@ -0,0 +1,125 @@ | |||||
# Contributing to SlackMathbot | |||||
This project is work of [many contributors](https://github.com/dblock/slack-mathbot/graphs/contributors). | |||||
You're encouraged to submit [pull requests](https://github.com/dblock/slack-mathbot/pulls), [propose features and discuss issues](https://github.com/dblock/slack-mathbot/issues). | |||||
In the examples below, substitute your Github username for `contributor` in URLs. | |||||
## Fork the Project | |||||
Fork the [project on Github](https://github.com/dblock/slack-mathbot) and check out your copy. | |||||
``` | |||||
git clone https://github.com/contributor/slack-mathbot.git | |||||
cd slack-mathbot | |||||
git remote add upstream https://github.com/dblock/slack-mathbot.git | |||||
``` | |||||
## Create a Topic Branch | |||||
Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. | |||||
``` | |||||
git checkout master | |||||
git pull upstream master | |||||
git checkout -b my-feature-branch | |||||
``` | |||||
## Bundle Install and Test | |||||
Ensure that you can build the project and run tests. | |||||
``` | |||||
bundle install | |||||
bundle exec rake | |||||
``` | |||||
## Write Tests | |||||
Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. | |||||
Add to [spec](spec). | |||||
We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. | |||||
## Write Code | |||||
Implement your feature or bug fix. | |||||
Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop). | |||||
Run `bundle exec rubocop` and fix any style issues highlighted. | |||||
Make sure that `bundle exec rake` completes without errors. | |||||
## Write Documentation | |||||
Document any external behavior in the [README](README.md). | |||||
## Update Changelog | |||||
Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. | |||||
Make it look like every other line, including your name and link to your Github account. | |||||
## Commit Changes | |||||
Make sure git knows your name and email address: | |||||
``` | |||||
git config --global user.name "Your Name" | |||||
git config --global user.email "contributor@example.com" | |||||
``` | |||||
Writing good commit logs is important. A commit log should describe what changed and why. | |||||
``` | |||||
git add ... | |||||
git commit | |||||
``` | |||||
## Push | |||||
``` | |||||
git push origin my-feature-branch | |||||
``` | |||||
## Make a Pull Request | |||||
Go to https://github.com/contributor/slack-mathbot and select your feature branch. | |||||
Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. | |||||
## Rebase | |||||
If you've been working on a change for a while, rebase with upstream/master. | |||||
``` | |||||
git fetch upstream | |||||
git rebase upstream/master | |||||
git push origin my-feature-branch -f | |||||
``` | |||||
## Update CHANGELOG Again | |||||
Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows. | |||||
``` | |||||
* [#123](https://github.com/dblock/slack-mathbot/pull/123): Reticulated splines - [@contributor](https://github.com/contributor). | |||||
``` | |||||
Amend your previous commit and force push the changes. | |||||
``` | |||||
git commit --amend | |||||
git push origin my-feature-branch -f | |||||
``` | |||||
## Check on Your Pull Request | |||||
Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above. | |||||
## Be Patient | |||||
It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there! | |||||
## Thank You | |||||
Please do know that we really appreciate and value your time and work. We love you, really. |
@ -0,0 +1,25 @@ | |||||
## Installation | |||||
Create a new Bot Integration under [services/new/bot](http://slack.com/services/new/bot). | |||||
![](screenshots/register-bot.png) | |||||
On the next screen, note the API token. | |||||
## Deploy Slack-Gamebot | |||||
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) | |||||
### Environment | |||||
#### SLACK_API_TOKEN | |||||
Set SLACK_API_TOKEN from the Bot integration settings on Slack. | |||||
``` | |||||
heroku config:add SLACK_API_TOKEN=... | |||||
``` | |||||
### Heroku Idling | |||||
Heroku free tier applications will idle. Use [UptimeRobot](http://uptimerobot.com) or similar to prevent your instance from sleeping or pay for a production dyno. |
@ -0,0 +1,23 @@ | |||||
source 'http://rubygems.org' | |||||
ruby '2.1.6' | |||||
gem 'hashie' | |||||
gem 'slack-api', '~> 1.1.6', require: 'slack' | |||||
gem 'puma' | |||||
gem 'sinatra' | |||||
gem 'activesupport' | |||||
gem 'dentaku' | |||||
group :development, :test do | |||||
gem 'rake', '~> 10.4' | |||||
gem 'rubocop', '0.31.0' | |||||
gem 'foreman' | |||||
end | |||||
group :test do | |||||
gem 'rspec', '~> 3.2' | |||||
gem 'rack-test', '~> 0.6.2' | |||||
gem 'vcr' | |||||
gem 'webmock' | |||||
end |
@ -0,0 +1,105 @@ | |||||
GEM | |||||
remote: http://rubygems.org/ | |||||
specs: | |||||
activesupport (4.2.1) | |||||
i18n (~> 0.7) | |||||
json (~> 1.7, >= 1.7.7) | |||||
minitest (~> 5.1) | |||||
thread_safe (~> 0.3, >= 0.3.4) | |||||
tzinfo (~> 1.1) | |||||
addressable (2.3.8) | |||||
ast (2.0.0) | |||||
astrolabe (1.3.0) | |||||
parser (>= 2.2.0.pre.3, < 3.0) | |||||
crack (0.4.2) | |||||
safe_yaml (~> 1.0.0) | |||||
dentaku (1.2.6) | |||||
diff-lcs (1.2.5) | |||||
eventmachine (1.0.7) | |||||
faraday (0.9.1) | |||||
multipart-post (>= 1.2, < 3) | |||||
faraday_middleware (0.9.1) | |||||
faraday (>= 0.7.4, < 0.10) | |||||
faye-websocket (0.9.2) | |||||
eventmachine (>= 0.12.0) | |||||
websocket-driver (>= 0.5.1) | |||||
foreman (0.78.0) | |||||
thor (~> 0.19.1) | |||||
hashie (3.4.1) | |||||
i18n (0.7.0) | |||||
json (1.8.2) | |||||
minitest (5.6.1) | |||||
multi_json (1.11.0) | |||||
multipart-post (2.0.0) | |||||
parser (2.3.0.pre.2) | |||||
ast (>= 1.1, < 3.0) | |||||
powerpack (0.1.1) | |||||
puma (2.11.0) | |||||
rack (>= 1.1, < 2.0) | |||||
rack (1.6.0) | |||||
rack-protection (1.5.3) | |||||
rack | |||||
rack-test (0.6.3) | |||||
rack (>= 1.0) | |||||
rainbow (2.0.0) | |||||
rake (10.4.2) | |||||
rspec (3.2.0) | |||||
rspec-core (~> 3.2.0) | |||||
rspec-expectations (~> 3.2.0) | |||||
rspec-mocks (~> 3.2.0) | |||||
rspec-core (3.2.3) | |||||
rspec-support (~> 3.2.0) | |||||
rspec-expectations (3.2.1) | |||||
diff-lcs (>= 1.2.0, < 2.0) | |||||
rspec-support (~> 3.2.0) | |||||
rspec-mocks (3.2.1) | |||||
diff-lcs (>= 1.2.0, < 2.0) | |||||
rspec-support (~> 3.2.0) | |||||
rspec-support (3.2.2) | |||||
rubocop (0.31.0) | |||||
astrolabe (~> 1.3) | |||||
parser (>= 2.2.2.1, < 3.0) | |||||
powerpack (~> 0.1) | |||||
rainbow (>= 1.99.1, < 3.0) | |||||
ruby-progressbar (~> 1.4) | |||||
ruby-progressbar (1.7.5) | |||||
safe_yaml (1.0.4) | |||||
sinatra (1.4.5) | |||||
rack (~> 1.4) | |||||
rack-protection (~> 1.4) | |||||
tilt (~> 1.3, >= 1.3.4) | |||||
slack-api (1.1.6) | |||||
faraday (>= 0.7, < 0.10) | |||||
faraday_middleware (~> 0.8) | |||||
faye-websocket (~> 0.9.2) | |||||
multi_json (~> 1.0, >= 1.0.3) | |||||
thor (0.19.1) | |||||
thread_safe (0.3.5) | |||||
tilt (1.4.1) | |||||
tzinfo (1.2.2) | |||||
thread_safe (~> 0.1) | |||||
vcr (2.9.3) | |||||
webmock (1.21.0) | |||||
addressable (>= 2.3.6) | |||||
crack (>= 0.3.2) | |||||
websocket-driver (0.5.4) | |||||
websocket-extensions (>= 0.1.0) | |||||
websocket-extensions (0.1.2) | |||||
PLATFORMS | |||||
ruby | |||||
DEPENDENCIES | |||||
activesupport | |||||
dentaku | |||||
foreman | |||||
hashie | |||||
puma | |||||
rack-test (~> 0.6.2) | |||||
rake (~> 10.4) | |||||
rspec (~> 3.2) | |||||
rubocop (= 0.31.0) | |||||
sinatra | |||||
slack-api (~> 1.1.6) | |||||
vcr | |||||
webmock |
@ -0,0 +1,22 @@ | |||||
MIT License | |||||
Copyright (c) 2015 Daniel Doubrovkine, Artsy and Contributors | |||||
Permission is hereby granted, free of charge, to any person obtaining | |||||
a copy of this software and associated documentation files (the | |||||
"Software"), to deal in the Software without restriction, including | |||||
without limitation the rights to use, copy, modify, merge, publish, | |||||
distribute, sublicense, and/or sell copies of the Software, and to | |||||
permit persons to whom the Software is furnished to do so, subject to | |||||
the following conditions: | |||||
The above copyright notice and this permission notice shall be | |||||
included in all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1 @@ | |||||
web: bundle exec puma -p $PORT |
@ -0,0 +1,52 @@ | |||||
Slack-Gamebot | |||||
============= | |||||
[![Build Status](https://travis-ci.org/dblock/slack-mathbot.png)](https://travis-ci.org/dblock/slack-mathbot) | |||||
A math bot for Slack. | |||||
![](screenshots/two-plus-two.gif) | |||||
## Installation | |||||
Create a new Bot Integration under [services/new/bot](http://slack.com/services/new/bot). | |||||
![](screenshots/register-bot.png) | |||||
On the next screen, note the API token. | |||||
Run `SLACK_API_TOKEN=<your API token> foreman start` | |||||
## Production Deployment | |||||
See [DEPLOYMENT](DEPLOYMENT.md). | |||||
## Usage | |||||
### Commands | |||||
#### mathbot calculate [expression] | |||||
Calculates an expression, currently just basic math. See [Dentaku](https://github.com/rubysolo/dentaku) for what's supported. | |||||
#### mathbot | |||||
Shows MathBot version and links. | |||||
#### mathbot hi | |||||
Politely says 'hi' back. | |||||
#### mathbot help | |||||
Get help. | |||||
## Contributing | |||||
See [CONTRIBUTING](CONTRIBUTING.md). | |||||
## Copyright and License | |||||
Copyright (c) 2015, Daniel Doubrovkine, Artsy and [Contributors](CHANGELOG.md). | |||||
This project is licensed under the [MIT License](LICENSE.md). |
@ -0,0 +1,18 @@ | |||||
require 'rubygems' | |||||
require 'bundler' | |||||
Bundler.setup :default, :development | |||||
unless ENV['RACK_ENV'] == 'production' | |||||
require 'rspec/core' | |||||
require 'rspec/core/rake_task' | |||||
RSpec::Core::RakeTask.new(:spec) do |spec| | |||||
spec.pattern = FileList['spec/**/*_spec.rb'] | |||||
end | |||||
require 'rubocop/rake_task' | |||||
RuboCop::RakeTask.new | |||||
task default: [:rubocop, :spec] | |||||
end |
@ -0,0 +1,6 @@ | |||||
{ | |||||
"name": "Math bot for Slack", | |||||
"description": "Slack integration with math.", | |||||
"respository": "https://github.com/dblock/slack-mathbot", | |||||
"keywords": ["slack", "integration", "math"], | |||||
} |
@ -0,0 +1,11 @@ | |||||
require File.expand_path('../config/environment', __FILE__) | |||||
require 'sinatra/base' | |||||
module SlackMathbot | |||||
class Web < Sinatra::Base | |||||
get '/' do | |||||
'Math is good for you.' | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,18 @@ | |||||
require 'slack-mathbot/version' | |||||
require 'slack-mathbot/about' | |||||
require 'slack-mathbot/config' | |||||
require 'slack-mathbot/hooks' | |||||
require 'slack-mathbot/commands' | |||||
require 'slack-mathbot/app' | |||||
module SlackMathbot | |||||
class << self | |||||
def configure | |||||
block_given? ? yield(Config) : Config | |||||
end | |||||
def config | |||||
Config | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,7 @@ | |||||
module SlackMathbot | |||||
ABOUT = <<-ABOUT | |||||
#{SlackMathbot::VERSION} | |||||
https://github.com/dblock/slack-mathbot | |||||
https://twitter.com/dblockdotorg | |||||
ABOUT | |||||
end |
@ -0,0 +1,83 @@ | |||||
module SlackMathbot | |||||
class App | |||||
cattr_accessor :hooks | |||||
include SlackMathbot::Hooks::Hello | |||||
include SlackMathbot::Hooks::Message | |||||
def initialize | |||||
SlackMathbot.configure do |config| | |||||
config.token = ENV['SLACK_API_TOKEN'] || fail("Missing ENV['SLACK_API_TOKEN'].") | |||||
end | |||||
Slack.configure do |config| | |||||
config.token = SlackMathbot.config.token | |||||
end | |||||
end | |||||
def config | |||||
SlackMathbot.config | |||||
end | |||||
def self.instance | |||||
@instance ||= SlackMathbot::App.new | |||||
end | |||||
def run | |||||
auth! | |||||
start! | |||||
end | |||||
def stop! | |||||
client.stop | |||||
end | |||||
private | |||||
def logger | |||||
@logger ||= begin | |||||
$stdout.sync = true | |||||
Logger.new(STDOUT) | |||||
end | |||||
end | |||||
def start! | |||||
loop do | |||||
client.start | |||||
@client = nil | |||||
end | |||||
end | |||||
def client | |||||
@client ||= begin | |||||
client = Slack.realtime | |||||
hooks.each do |hook| | |||||
client.on hook do |data| | |||||
begin | |||||
send hook, data | |||||
rescue StandardError => e | |||||
logger.error e | |||||
begin | |||||
Slack.chat_postMessage(channel: data['channel'], text: e.message) if data.key?('channel') | |||||
rescue | |||||
# ignore | |||||
end | |||||
end | |||||
end | |||||
end | |||||
client | |||||
end | |||||
end | |||||
def auth! | |||||
auth = Slack.auth_test | |||||
SlackMathbot.configure do |config| | |||||
config.url = auth['url'] | |||||
config.team = auth['team'] | |||||
config.user = auth['user'] | |||||
config.team_id = auth['team_id'] | |||||
config.user_id = auth['user_id'] | |||||
end | |||||
logger.info "Welcome '#{SlackMathbot.config.user}' to the '#{SlackMathbot.config.team}' team at #{SlackMathbot.config.url}." | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,6 @@ | |||||
require 'slack-mathbot/commands/base' | |||||
require 'slack-mathbot/commands/calculate' | |||||
require 'slack-mathbot/commands/about' | |||||
require 'slack-mathbot/commands/help' | |||||
require 'slack-mathbot/commands/hi' | |||||
require 'slack-mathbot/commands/unknown' |
@ -0,0 +1,9 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Default < Base | |||||
def self.call(data, _command, _arguments) | |||||
send_message data.channel, SlackMathbot::ABOUT | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,16 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Base | |||||
def self.send_message(channel, text) | |||||
Slack.chat_postMessage(channel: channel, text: text) | |||||
end | |||||
def self.logger | |||||
@logger ||= begin | |||||
$stdout.sync = true | |||||
Logger.new(STDOUT) | |||||
end | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,10 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Calculate < Base | |||||
def self.call(data, _command, arguments) | |||||
result = Dentaku::Calculator.new.evaluate(arguments.join) || 'Got nothing.' | |||||
send_message data.channel, result.to_s | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,9 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Help < Base | |||||
def self.call(data, _command, _arguments) | |||||
send_message data.channel, 'See https://github.com/dblock/slack-mathbot, please.' | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,9 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Hi < Base | |||||
def self.call(data, _command, _arguments) | |||||
send_message data.channel, "Hi <@#{data.user}>!" | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,9 @@ | |||||
module SlackMathbot | |||||
module Commands | |||||
class Unknown < Base | |||||
def self.call(data, _command, _arguments) | |||||
send_message data.channel, "Sorry <@#{data.user}>, I don't understand that command!" | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,13 @@ | |||||
module SlackMathbot | |||||
module Config | |||||
extend self | |||||
attr_accessor :token | |||||
attr_accessor :url | |||||
attr_accessor :user | |||||
attr_accessor :user_id | |||||
attr_accessor :team | |||||
attr_accessor :team_id | |||||
attr_accessor :secret | |||||
end | |||||
end |
@ -0,0 +1,3 @@ | |||||
require 'slack-mathbot/hooks/base' | |||||
require 'slack-mathbot/hooks/hello' | |||||
require 'slack-mathbot/hooks/message' |
@ -0,0 +1,10 @@ | |||||
module SlackMathbot | |||||
module Hooks | |||||
module Base | |||||
def included(caller) | |||||
caller.hooks ||= [] | |||||
caller.hooks << name.demodulize.underscore.to_sym | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,11 @@ | |||||
module SlackMathbot | |||||
module Hooks | |||||
module Hello | |||||
extend Base | |||||
def hello(_data) | |||||
logger.info "Successfully connected to #{SlackMathbot.config.url}." | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,33 @@ | |||||
module SlackMathbot | |||||
module Hooks | |||||
module Message | |||||
extend Base | |||||
def message(data) | |||||
data = Hashie::Mash.new(data) | |||||
bot_name, command, arguments = parse_command(data.text) | |||||
return unless bot_name == SlackMathbot.config.user | |||||
klass = command_to_class(command || 'Default') | |||||
klass.call data, command, arguments | |||||
end | |||||
private | |||||
def parse_command(text) | |||||
return unless text | |||||
text = '= ' + text[1..text.length] if text[0] == '=' | |||||
parts = text.split.reject(&:blank?) | |||||
if parts && parts[0] == '=' | |||||
parts[0] = SlackMathbot.config.user | |||||
parts.insert 1, 'calculate' | |||||
end | |||||
[parts.first.downcase, parts[1].try(:downcase), parts[2..parts.length]] if parts && parts.any? | |||||
end | |||||
def command_to_class(command) | |||||
klass = "SlackMathbot::Commands::#{command.titleize}".constantize rescue nil | |||||
klass || SlackMathbot::Commands::Unknown | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,3 @@ | |||||
module SlackMathbot | |||||
VERSION = '0.1.0' | |||||
end |
@ -0,0 +1 @@ | |||||
require 'slack-mathbot' |
@ -0,0 +1,7 @@ | |||||
require File.expand_path('../app', __FILE__) | |||||
Thread.new do | |||||
SlackMathbot::App.instance.run | |||||
end | |||||
run SlackMathbot::Web |
@ -0,0 +1,14 @@ | |||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'app')) | |||||
$LOAD_PATH.unshift(File.dirname(__FILE__)) | |||||
require 'boot' | |||||
Bundler.require :default, ENV['RACK_ENV'] | |||||
Dir[File.expand_path('../initializers', __FILE__) + '/**/*.rb'].each do |file| | |||||
require file | |||||
end | |||||
require File.expand_path('../application', __FILE__) | |||||
require 'slack_mathbot' |
@ -0,0 +1,5 @@ | |||||
require 'rubygems' | |||||
require 'bundler/setup' | |||||
require 'logger' | |||||
require 'active_support' | |||||
require 'active_support/core_ext' |
@ -0,0 +1,3 @@ | |||||
ENV['RACK_ENV'] ||= 'development' | |||||
require File.expand_path('../application', __FILE__) |
@ -0,0 +1,13 @@ | |||||
module Slack | |||||
module Request | |||||
private | |||||
alias_method :_request, :request | |||||
def request(method, path, options) | |||||
response = _request(method, path, options) | |||||
fail response['error'] unless response['ok'] if response.is_a?(Hash) | |||||
response | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,103 @@ | |||||
--- | |||||
http_interactions: | |||||
- request: | |||||
method: post | |||||
uri: https://slack.com/api/auth.test | |||||
body: | |||||
encoding: UTF-8 | |||||
string: token=token | |||||
headers: | |||||
Accept: | |||||
- application/json; charset=utf-8 | |||||
User-Agent: | |||||
- Slack Ruby Gem 1.1.1 | |||||
Content-Type: | |||||
- application/x-www-form-urlencoded | |||||
Accept-Encoding: | |||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3 | |||||
response: | |||||
status: | |||||
code: 200 | |||||
message: OK | |||||
headers: | |||||
Access-Control-Allow-Origin: | |||||
- '*' | |||||
Cache-Control: | |||||
- private, no-cache, no-store, must-revalidate | |||||
Content-Type: | |||||
- application/json; charset=utf-8 | |||||
Date: | |||||
- Tue, 28 Apr 2015 12:55:23 GMT | |||||
Expires: | |||||
- Mon, 26 Jul 1997 05:00:00 GMT | |||||
Pragma: | |||||
- no-cache | |||||
Server: | |||||
- Apache | |||||
Strict-Transport-Security: | |||||
- max-age=31536000; includeSubDomains; preload | |||||
Vary: | |||||
- Accept-Encoding | |||||
X-Accepted-Oauth-Scopes: | |||||
- identify | |||||
X-Content-Type-Options: | |||||
- nosniff | |||||
X-Oauth-Scopes: | |||||
- identify,read,post,client | |||||
X-Xss-Protection: | |||||
- '0' | |||||
Content-Length: | |||||
- '128' | |||||
Connection: | |||||
- keep-alive | |||||
body: | |||||
encoding: UTF-8 | |||||
string: '{"ok":true,"url":"https:\/\/math.slack.com\/","team":"calcteam","user":"calcuser","team_id":"TDEADBEEF","user_id":"UBAADFOOD"}' | |||||
http_version: | |||||
recorded_at: Tue, 28 Apr 2015 12:55:22 GMT | |||||
- request: | |||||
method: post | |||||
uri: https://slack.com/api/rtm.start | |||||
body: | |||||
encoding: UTF-8 | |||||
string: token=token | |||||
headers: | |||||
Accept: | |||||
- application/json; charset=utf-8 | |||||
User-Agent: | |||||
- Slack Ruby Gem 1.1.1 | |||||
Content-Type: | |||||
- application/x-www-form-urlencoded | |||||
Accept-Encoding: | |||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3 | |||||
response: | |||||
status: | |||||
code: 200 | |||||
message: OK | |||||
headers: | |||||
Access-Control-Allow-Origin: | |||||
- '*' | |||||
Content-Type: | |||||
- application/json; charset=utf-8 | |||||
Date: | |||||
- Tue, 28 Apr 2015 21:41:33 GMT | |||||
Server: | |||||
- Apache | |||||
Strict-Transport-Security: | |||||
- max-age=31536000; includeSubDomains; preload | |||||
Vary: | |||||
- Accept-Encoding | |||||
X-Content-Type-Options: | |||||
- nosniff | |||||
X-Xss-Protection: | |||||
- '0' | |||||
Content-Length: | |||||
- '55' | |||||
Connection: | |||||
- keep-alive | |||||
body: | |||||
encoding: UTF-8 | |||||
string: '{"ok":false,"error":"invalid_auth"}' | |||||
http_version: | |||||
recorded_at: Tue, 28 Apr 2015 21:41:33 GMT | |||||
recorded_with: VCR 2.9.3 |
@ -0,0 +1,59 @@ | |||||
--- | |||||
http_interactions: | |||||
- request: | |||||
method: post | |||||
uri: https://slack.com/api/users.info | |||||
body: | |||||
encoding: UTF-8 | |||||
string: token=token&user=user | |||||
headers: | |||||
Accept: | |||||
- application/json; charset=utf-8 | |||||
User-Agent: | |||||
- Slack Ruby Gem 1.1.1 | |||||
Content-Type: | |||||
- application/x-www-form-urlencoded | |||||
Accept-Encoding: | |||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3 | |||||
response: | |||||
status: | |||||
code: 200 | |||||
message: OK | |||||
headers: | |||||
Access-Control-Allow-Origin: | |||||
- '*' | |||||
Cache-Control: | |||||
- private, no-cache, no-store, must-revalidate | |||||
Content-Type: | |||||
- application/json; charset=utf-8 | |||||
Date: | |||||
- Wed, 29 Apr 2015 16:10:34 GMT | |||||
Expires: | |||||
- Mon, 26 Jul 1997 05:00:00 GMT | |||||
Pragma: | |||||
- no-cache | |||||
Server: | |||||
- Apache | |||||
Strict-Transport-Security: | |||||
- max-age=31536000; includeSubDomains; preload | |||||
Vary: | |||||
- Accept-Encoding | |||||
X-Accepted-Oauth-Scopes: | |||||
- read | |||||
X-Content-Type-Options: | |||||
- nosniff | |||||
X-Oauth-Scopes: | |||||
- identify,read,post,client | |||||
X-Xss-Protection: | |||||
- '0' | |||||
Content-Length: | |||||
- '448' | |||||
Connection: | |||||
- keep-alive | |||||
body: | |||||
encoding: UTF-8 | |||||
string: '{"ok":true,"user":{"id":"U007","name":"username","deleted":false,"status":null,"color":"9f69e7","real_name":"","tz":"America\/Indiana\/Indianapolis","tz_label":"Eastern | |||||
Daylight Time","tz_offset":-14400,"profile":{"real_name":"","real_name_normalized":"","email":"dblock@dblock.org","image_24":"https:\/\/secure.gravatar.com\/avatar\/3d925b45ac07ec0ae5bd04888f6c5b61.jpg?s=24&d=https%3A%2F%2Fslack.global.ssl.fastly.net%2F3654%2Fimg%2Favatars%2Fava_0015-24.png","image_32":"https:\/\/secure.gravatar.com\/avatar\/3d925b45ac07ec0ae5bd04888f6c5b61.jpg?s=32&d=https%3A%2F%2Fslack.global.ssl.fastly.net%2F3654%2Fimg%2Favatars%2Fava_0015-32.png","image_48":"https:\/\/secure.gravatar.com\/avatar\/3d925b45ac07ec0ae5bd04888f6c5b61.jpg?s=48&d=https%3A%2F%2Fslack.global.ssl.fastly.net%2F3654%2Fimg%2Favatars%2Fava_0015-48.png","image_72":"https:\/\/secure.gravatar.com\/avatar\/3d925b45ac07ec0ae5bd04888f6c5b61.jpg?s=72&d=https%3A%2F%2Fslack.global.ssl.fastly.net%2F3654%2Fimg%2Favatars%2Fava_0015-72.png","image_192":"https:\/\/secure.gravatar.com\/avatar\/3d925b45ac07ec0ae5bd04888f6c5b61.jpg?s=192&d=https%3A%2F%2Fslack.global.ssl.fastly.net%2F272a%2Fimg%2Favatars%2Fava_0015.png"},"is_admin":true,"is_owner":true,"is_primary_owner":true,"is_restricted":false,"is_ultra_restricted":false,"is_bot":false,"has_files":false}}' | |||||
http_version: | |||||
recorded_at: Wed, 29 Apr 2015 16:10:34 GMT | |||||
@ -0,0 +1,32 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::App do | |||||
subject do | |||||
SlackMathbot::App.new | |||||
end | |||||
context 'not configured' do | |||||
before do | |||||
@slack_api_token = ENV.delete('SLACK_API_TOKEN') | |||||
end | |||||
after do | |||||
ENV['SLACK_API_TOKEN'] = @slack_api_token | |||||
end | |||||
it 'requires SLACK_API_TOKEN' do | |||||
expect { subject }.to raise_error RuntimeError, "Missing ENV['SLACK_API_TOKEN']." | |||||
end | |||||
end | |||||
context 'configured', vcr: { cassette_name: 'auth_test' } do | |||||
context 'run' do | |||||
before do | |||||
subject.send(:auth!) | |||||
end | |||||
it 'succeeds auth' do | |||||
expect(subject.config.url).to eq 'https://math.slack.com/' | |||||
expect(subject.config.team).to eq 'calcteam' | |||||
expect(subject.config.user).to eq 'calcuser' | |||||
expect(subject.config.team_id).to eq 'TDEADBEEF' | |||||
expect(subject.config.user_id).to eq 'UBAADFOOD' | |||||
end | |||||
end | |||||
end | |||||
end |
@ -0,0 +1,10 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::Commands::Default do | |||||
it 'mathbot' do | |||||
expect(message: 'mathbot').to respond_with_slack_message(SlackMathbot::ABOUT) | |||||
end | |||||
it 'Mathbot' do | |||||
expect(message: 'Mathbot').to respond_with_slack_message(SlackMathbot::ABOUT) | |||||
end | |||||
end |
@ -0,0 +1,16 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::Commands::Calculate, vcr: { cassette_name: 'user_info' } do | |||||
it 'adds two numbers' do | |||||
expect(message: 'mathbot calculate 2+2', channel: 'channel').to respond_with_slack_message('4') | |||||
end | |||||
it 'adds two numbers via =' do | |||||
expect(message: '= 2+2', channel: 'channel').to respond_with_slack_message('4') | |||||
end | |||||
it 'adds two numbers via = without a space' do | |||||
expect(message: '=2+2', channel: 'channel').to respond_with_slack_message('4') | |||||
end | |||||
it 'sends something without an answer' do | |||||
expect(message: 'mathbot calculate pi', channel: 'channel').to respond_with_slack_message('Got nothing.') | |||||
end | |||||
end |
@ -0,0 +1,7 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::Commands::Help do | |||||
it 'help' do | |||||
expect(message: 'mathbot help').to respond_with_slack_message('See https://github.com/dblock/slack-mathbot, please.') | |||||
end | |||||
end |
@ -0,0 +1,7 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::Commands::Hi do | |||||
it 'says hi' do | |||||
expect(message: 'mathbot hi').to respond_with_slack_message('Hi <@user>!') | |||||
end | |||||
end |
@ -0,0 +1,11 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot::Commands::Unknown, vcr: { cassette_name: 'user_info' } do | |||||
it 'invalid command' do | |||||
expect(message: 'mathbot foobar').to respond_with_slack_message("Sorry <@user>, I don't understand that command!") | |||||
end | |||||
it 'does not respond to sad face' do | |||||
expect(SlackMathbot::Commands::Base).to_not receive(:send_message) | |||||
SlackMathbot::App.new.send(:message, text: ':((') | |||||
end | |||||
end |
@ -0,0 +1,7 @@ | |||||
require 'spec_helper' | |||||
describe SlackMathbot do | |||||
it 'has a version' do | |||||
expect(SlackMathbot::VERSION).to_not be nil | |||||
end | |||||
end |
@ -0,0 +1,12 @@ | |||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..')) | |||||
require 'rubygems' | |||||
require 'rspec' | |||||
require 'rack/test' | |||||
require 'config/environment' | |||||
require 'slack-mathbot' | |||||
Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file| | |||||
require file | |||||
end |
@ -0,0 +1,30 @@ | |||||
require 'rspec/expectations' | |||||
RSpec::Matchers.define :respond_with_error do |expected| | |||||
match do |actual| | |||||
channel, user, message = parse(actual) | |||||
app = SlackMathbot::App.new | |||||
SlackMathbot.config.user = 'mathbot' | |||||
begin | |||||
expect do | |||||
app.send(:message, text: message, channel: channel, user: user) | |||||
end.to raise_error ArgumentError, expected | |||||
rescue RSpec::Expectations::ExpectationNotMetError => e | |||||
@error_message = e.message | |||||
raise e | |||||
end | |||||
true | |||||
end | |||||
failure_message do |actual| | |||||
_, _, message = parse(actual) | |||||
@error_message || "expected for '#{message}' to fail with '#{expected}'" | |||||
end | |||||
private | |||||
def parse(actual) | |||||
actual = { message: actual } unless actual.is_a?(Hash) | |||||
[actual[:channel] || 'channel', actual[:user] || 'user', actual[:message]] | |||||
end | |||||
end |
@ -0,0 +1,19 @@ | |||||
require 'rspec/expectations' | |||||
RSpec::Matchers.define :respond_with_slack_message do |expected| | |||||
match do |actual| | |||||
channel, user, message = parse(actual) | |||||
app = SlackMathbot::App.new | |||||
SlackMathbot.config.user = 'mathbot' | |||||
expect(SlackMathbot::Commands::Base).to receive(:send_message).with(channel, expected) | |||||
app.send(:message, text: message, channel: channel, user: user) | |||||
true | |||||
end | |||||
private | |||||
def parse(actual) | |||||
actual = { message: actual } unless actual.is_a?(Hash) | |||||
[actual[:channel] || 'channel', actual[:user] || 'user', actual[:message]] | |||||
end | |||||
end |
@ -0,0 +1,5 @@ | |||||
RSpec.configure do |config| | |||||
config.before :all do | |||||
ENV['SLACK_API_TOKEN'] ||= 'test' | |||||
end | |||||
end |
@ -0,0 +1,4 @@ | |||||
SlackMathbot.configure do |config| | |||||
config.token = 'testtoken' | |||||
config.user = 'mathbot' | |||||
end |
@ -0,0 +1,8 @@ | |||||
require 'vcr' | |||||
VCR.configure do |config| | |||||
config.cassette_library_dir = 'spec/fixtures/slack' | |||||
config.hook_into :webmock | |||||
# config.default_cassette_options = { record: :new_episodes } | |||||
config.configure_rspec_metadata! | |||||
end |