diff --git a/.gitignore b/.gitignore
index 1f771fe..2a9bae4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,9 @@
/.bundle
/.idea
/config/database.yml
-/config/secrets.yml
+/config/service.yml
+/config/secret.yml
+
# Ignore the default SQLite database.
/db/*.sqlite3
@@ -19,6 +21,11 @@
/tmp/*
!/log/.keep
!/tmp/.keep
+/README.html
# Ignore Byebug command history file.
.byebug_history
+
+# Ignore RVM files.
+/.ruby-gemset
+/.ruby-version
diff --git a/.hound.yml b/.hound.yml
new file mode 100644
index 0000000..b7945ab
--- /dev/null
+++ b/.hound.yml
@@ -0,0 +1,3 @@
+ruby:
+ enabled: true
+ config_file: .rubocop.yml
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..e283273
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,96 @@
+AllCops:
+ TargetRubyVersion: 2.3
+ DisplayCopNames: true
+
+ Exclude:
+ - 'db/schema.rb'
+
+Layout/AccessModifierIndentation:
+ EnforcedStyle: indent
+
+Layout/MultilineMethodCallIndentation:
+ EnforcedStyle: indented
+
+Layout/SpaceInsideArrayLiteralBrackets:
+ Exclude:
+ - 'config/**/*'
+
+Lint/AssignmentInCondition:
+ Enabled: false
+
+Lint/ReturnInVoidContext:
+ Enabled: false
+
+Metrics/AbcSize:
+ Enabled: false
+
+Metrics/BlockLength:
+ Exclude:
+ - 'config/routes.rb'
+ - 'spec/**/*'
+
+Metrics/LineLength:
+ Max: 120
+
+Metrics/MethodLength:
+ Enabled: false
+
+Naming/AccessorMethodName:
+ Enabled: true
+
+Rails:
+ Enabled: true
+
+Rails/Delegate:
+ Exclude:
+ - 'lib/**/*'
+
+Rails/FilePath:
+ Enabled: false
+
+Rails/HttpPositionalArguments:
+ Enabled: false
+
+Rails/SkipsModelValidations:
+ Exclude:
+ - 'db/migrate/**/*'
+ - 'spec/**/*'
+
+Rails/UnknownEnv:
+ Environments:
+ - development
+ - test
+ - production
+
+Style/AndOr:
+ EnforcedStyle: conditionals
+
+Style/ClassAndModuleChildren:
+ Enabled: false
+
+Style/Documentation:
+ Enabled: false
+
+Style/DoubleNegation:
+ Enabled: false
+
+Style/FormatString:
+ Enabled: false
+
+Style/FrozenStringLiteralComment:
+ Enabled: false
+
+Style/IfUnlessModifier:
+ Enabled: false
+
+Style/MixinUsage:
+ Enabled: false
+
+Style/RescueModifier:
+ Enabled: true
+
+Style/RescueStandardError:
+ EnforcedStyle: implicit
+
+Bundler/OrderedGems:
+ Enabled: false
diff --git a/Gemfile b/Gemfile
index 1d715cb..35fdc69 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,7 +3,6 @@ source 'https://rubygems.org'
ruby '2.3.1'
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
-gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.0'
@@ -11,6 +10,8 @@ gem 'httparty'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2'
+gem 'aws-sdk', '~> 2'
+gem 'giphy'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
@@ -25,3 +26,7 @@ group :development do
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
+
+group :test do
+ gem 'rubocop', '~> 0.58.0', require: false
+end
diff --git a/Gemfile.lock b/Gemfile.lock
index e0bb13f..648cc28 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,45 +1,56 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (5.0.0.1)
- actionpack (= 5.0.0.1)
- nio4r (~> 1.2)
+ actioncable (5.0.2)
+ actionpack (= 5.0.2)
+ nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1)
- actionmailer (5.0.0.1)
- actionpack (= 5.0.0.1)
- actionview (= 5.0.0.1)
- activejob (= 5.0.0.1)
+ actionmailer (5.0.2)
+ actionpack (= 5.0.2)
+ actionview (= 5.0.2)
+ activejob (= 5.0.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (5.0.0.1)
- actionview (= 5.0.0.1)
- activesupport (= 5.0.0.1)
+ actionpack (5.0.2)
+ actionview (= 5.0.2)
+ activesupport (= 5.0.2)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.0.0.1)
- activesupport (= 5.0.0.1)
+ actionview (5.0.2)
+ activesupport (= 5.0.2)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- activejob (5.0.0.1)
- activesupport (= 5.0.0.1)
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
+ activejob (5.0.2)
+ activesupport (= 5.0.2)
globalid (>= 0.3.6)
- activemodel (5.0.0.1)
- activesupport (= 5.0.0.1)
- activerecord (5.0.0.1)
- activemodel (= 5.0.0.1)
- activesupport (= 5.0.0.1)
+ activemodel (5.0.2)
+ activesupport (= 5.0.2)
+ activerecord (5.0.2)
+ activemodel (= 5.0.2)
+ activesupport (= 5.0.2)
arel (~> 7.0)
- activesupport (5.0.0.1)
+ activesupport (5.0.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
+ addressable (2.5.1)
+ public_suffix (~> 2.0, >= 2.0.2)
arel (7.1.4)
- builder (3.2.2)
+ ast (2.4.0)
+ aws-sdk (2.9.0)
+ aws-sdk-resources (= 2.9.0)
+ aws-sdk-core (2.9.0)
+ aws-sigv4 (~> 1.0)
+ jmespath (~> 1.0)
+ aws-sdk-resources (2.9.0)
+ aws-sdk-core (= 2.9.0)
+ aws-sigv4 (1.0.0)
+ builder (3.2.3)
byebug (9.0.6)
coffee-rails (4.2.1)
coffee-script (>= 2.2.0)
@@ -47,17 +58,34 @@ GEM
coffee-script (2.4.1)
coffee-script-source
execjs
- coffee-script-source (1.10.0)
- concurrent-ruby (1.0.2)
+ coffee-script-source (1.12.2)
+ concurrent-ruby (1.0.5)
debug_inspector (0.0.2)
erubis (2.7.0)
execjs (2.7.0)
- ffi (1.9.14)
+ faraday (0.12.0.1)
+ multipart-post (>= 1.2, < 3)
+ faraday_middleware (0.11.0.1)
+ faraday (>= 0.7.4, < 1.0)
+ faraday_middleware-parse_oj (0.3.1)
+ faraday (~> 0.9)
+ faraday_middleware (>= 0.9.1, < 1.0)
+ oj (~> 2.0)
+ ffi (1.9.18)
+ giphy (3.0.0)
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.9)
+ faraday_middleware-parse_oj (~> 0.3)
+ launchy (~> 2.4)
globalid (0.3.7)
activesupport (>= 4.1.0)
httparty (0.14.0)
multi_xml (>= 0.5.2)
- i18n (0.7.0)
+ i18n (0.8.1)
+ jaro_winkler (1.5.1)
+ jmespath (1.3.1)
+ launchy (2.4.3)
+ addressable (~> 2.3)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@@ -70,75 +98,92 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_portile2 (2.1.0)
- minitest (5.9.1)
- multi_xml (0.5.5)
- nio4r (1.2.1)
- nokogiri (1.6.8.1)
+ minitest (5.10.1)
+ multi_xml (0.6.0)
+ multipart-post (2.0.0)
+ nio4r (2.0.0)
+ nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
- puma (3.6.1)
+ oj (2.18.5)
+ parallel (1.12.1)
+ parser (2.5.1.0)
+ ast (~> 2.4.0)
+ powerpack (0.1.2)
+ public_suffix (2.0.5)
+ puma (3.8.2)
rack (2.0.1)
rack-test (0.6.3)
rack (>= 1.0)
- rails (5.0.0.1)
- actioncable (= 5.0.0.1)
- actionmailer (= 5.0.0.1)
- actionpack (= 5.0.0.1)
- actionview (= 5.0.0.1)
- activejob (= 5.0.0.1)
- activemodel (= 5.0.0.1)
- activerecord (= 5.0.0.1)
- activesupport (= 5.0.0.1)
+ rails (5.0.2)
+ actioncable (= 5.0.2)
+ actionmailer (= 5.0.2)
+ actionpack (= 5.0.2)
+ actionview (= 5.0.2)
+ activejob (= 5.0.2)
+ activemodel (= 5.0.2)
+ activerecord (= 5.0.2)
+ activesupport (= 5.0.2)
bundler (>= 1.3.0, < 2.0)
- railties (= 5.0.0.1)
+ railties (= 5.0.2)
sprockets-rails (>= 2.0.0)
- rails-dom-testing (2.0.1)
+ rails-dom-testing (2.0.2)
activesupport (>= 4.2.0, < 6.0)
- nokogiri (~> 1.6.0)
+ nokogiri (~> 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
- railties (5.0.0.1)
- actionpack (= 5.0.0.1)
- activesupport (= 5.0.0.1)
+ railties (5.0.2)
+ actionpack (= 5.0.2)
+ activesupport (= 5.0.2)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rake (11.3.0)
+ rainbow (3.0.0)
+ rake (12.0.0)
rb-fsevent (0.9.8)
- rb-inotify (0.9.7)
+ rb-inotify (0.9.8)
ffi (>= 0.5.0)
- sass (3.4.22)
+ rubocop (0.58.0)
+ jaro_winkler (~> 1.5.1)
+ parallel (~> 1.10)
+ parser (>= 2.5)
+ powerpack (~> 0.1)
+ rainbow (>= 2.2.2, < 4.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ ruby-progressbar (1.9.0)
+ sass (3.4.23)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
- spring (2.0.0)
+ spring (2.0.1)
activesupport (>= 4.2)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
- sprockets (3.7.0)
+ sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.0)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
- sqlite3 (1.3.12)
- thor (0.19.1)
- thread_safe (0.3.5)
- tilt (2.0.5)
+ thor (0.19.4)
+ thread_safe (0.3.6)
+ tilt (2.0.7)
tzinfo (1.2.2)
thread_safe (~> 0.1)
- uglifier (3.0.3)
+ uglifier (3.1.9)
execjs (>= 0.3.0, < 3)
+ unicode-display_width (1.4.0)
web-console (3.4.0)
actionview (>= 5.0)
activemodel (>= 5.0)
debug_inspector
railties (>= 5.0)
- websocket-driver (0.6.4)
+ websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@@ -146,16 +191,18 @@ PLATFORMS
ruby
DEPENDENCIES
+ aws-sdk (~> 2)
byebug
coffee-rails (~> 4.2)
+ giphy
httparty
listen (~> 3.0.5)
puma (~> 3.0)
rails (~> 5.0.0, >= 5.0.0.1)
+ rubocop (~> 0.58.0)
sass-rails (~> 5.0)
spring
spring-watcher-listen (~> 2.0.0)
- sqlite3
uglifier (>= 1.3.0)
web-console
@@ -163,4 +210,4 @@ RUBY VERSION
ruby 2.3.1p112
BUNDLED WITH
- 1.12.5
+ 1.16.2
diff --git a/README.md b/README.md
index 9b3c2f2..76bd899 100644
--- a/README.md
+++ b/README.md
@@ -2,35 +2,84 @@
Rails application to transfer messages to your basecamp3 HQ.
-## Supported services
+### Supported services
1. [AWS SNS](https://aws.amazon.com/sns/)
+2. [Bugsnag](https://www.bugsnag.com/)
+3. [GitLab](https://gitlab.com)
+4. [Rollbar](https://rollbar.com)
+5. [Giphy](https://giphy.com)
-## Usage
+### Instruction
-1. Register AWS and [create SNS topic](http://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html). Remember Topic ARN! It looks like this:
- `arn:aws:sns:us-east-6:23730808936387:topick_name`
-2. Register Basecamp and [create bot](https://m.signalvnoise.com/new-in-basecamp-3-chatbots-8526618c0c7d#.kabo3hgs1). There
+* [Create a Chatbot](#create-chatbot )
+* [AWS integration](#aws-integration)
+* [Bugsnag integration](#bugsnag-integration)
+* [Gitlab integration](#gitlab-integration)
+* [Rollbar integration](#rollbar-integration)
+* [Giphy integration](#giphy-integration)
+* [References](#references)
+
+#### Create a Chatbot
+
+1. Register Basecamp and [create bot](https://m.signalvnoise.com/new-in-basecamp-3-chatbots-8526618c0c7d#.kabo3hgs1). There
you need a bot long link. Like this:
`https://3.basecamp.com/195539477/integrations/2uH9aHLEVhhaXKPaqrj8yw8P/buckets/2085958501/chats/9007199254741775/lines`
-3. Clone this repo from GitHub.
+2. Clone this repo from GitHub.
1. Run __`bundle install`__ in your project directory.
2. Create a ```secrets.yml``` file in your config folder as described [here](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#config-secrets-yml).
-4. Write your SNS **Topic ARN** and Basecamp **bot url** to /config/service.yml
-5. Start rails server by typing `rails s` command.
-6. [Create subscription](http://docs.aws.amazon.com/sns/latest/dg/SubscribeTopic.html). Use HTTP **Protocol** and `http://your-external-host/api/v1/messages` as **Endpoint**
-7. You will probably want to try running your app locally to test it and make sure it’s working as expected. To do that you will need a service that allows you to expose a web server running on your local machine to the Internet. We recommend to try [Ngrok](http://ngrok.com).
+3. Add your Basecamp __bot url__ to `/config/service.yml`
+4. You will probably want to try running your app locally to test it and make sure it’s working as expected. To do that you will need a service that allows you to expose a web server running on your local machine to the Internet. We recommend to try [Ngrok](http://ngrok.com).
1. If you see this message on rails console:
``Cannot render console from XXX.XXX.XXX.XXX! Allowed networks: 127.0.0.1,...``
You need to whitelist the XXX.XXX.XXX.XXX network space in the Web console config.
- Open ``config/environments/development.rb`` and type your local IP address from the Internet Service Provider:
+ Open ``config/environments/development.rb`` and type IP address of remote server:
``` config.web_console.whitelisted_ips = 'XXX.XXX.XXX.XXX' ```
Read more about IP's whitelisting [here](https://github.com/rails/web-console#configweb_consolewhitelisted_ips).
-8. At first time SNS send confirmation request. After subscription you can
-[publish messages to a topic](http://docs.aws.amazon.com/sns/latest/dg/PublishTopic.html)
+#### AWS integration
+
+1. Register AWS and [create SNS topic](http://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html). Remember Topic ARN! It looks like this:
+ `arn:aws:sns:us-east-6:23730808936387:topick_name`
+2. Add your SNS __Topic ARN__ to `/config/service.yml`
+3. [Aws-sdk gem](https://github.com/aws/aws-sdk-ruby), which used in this App, needs 'aws_access_key' and 'aws_secret_key' to work. To find them navigate to "My Security Credentials" category in your asw.amazon account. Always load your credentials from outside your application! Look at `/config/service.yml` for example. Avoid configuring credentials statically and never commit them to source control!
+ 1) If you're running on AWS EC2 you able to assign AWS keys dynamically with IAM roles.
+ 2. Create an IAM role.
+ 3. Define which AWS services can assume the role (AmazonSNSFullAccess etc.)
+ 4. Specify the role when you launch your instance, or attach the role to a running or stopped instance.
+4. Start rails server by typing `rails s` command.
+5. [Create subscription](http://docs.aws.amazon.com/sns/latest/dg/SubscribeTopic.html). Use HTTP **Protocol** and `http://your-external-host/api/sns/messages` as __Endpoint__
+6. At first time SNS send confirmation request which will be automatically accepted. After subscription you can [publish messages to the topic](http://docs.aws.amazon.com/sns/latest/dg/PublishTopic.html)
+
+#### Bugsnag integration
+
+1. Create [Bugsnag account](https://www.bugsnag.com/) and create new project.
+2. Navigate to 'Integrations' menu.
+3. Select the 'Webhook', enter the full URL where webhooks should be posted (http://your-external-host/api/bugsnag/messages`), choice notification events and enable the integration.
+4. For more instructions, please visit [Bugsnag docs](https://docs.bugsnag.com/api/) and [Ruby on Rails integration](https://docs.bugsnag.com/platforms/ruby/rails/)
+
+#### Gitlab integration
+
+1. Create [Gitlab account](https://gitlab.com) and start the New project.
+2. Navigate to the project's 'Settings/Integrations' menu.
+3. Select the 'Webhook', enter the full URL where webhooks should be posted (http://your-external-host/api/gitlab/messages`). Mark event checkboxes and click the 'Add Webhook' button.
+4. For more instructions, please visit [GitLab Webhooks docs](https://gitlab.com/help/user/project/integrations/webhooks).
+
+#### Rollbar integration
+
+1. Create [Rollbar account](https://rollbar.com) and start the New project.
+2. Navigate to the project, click 'Settings', then click 'Notifications' in the left menu.
+3. Select the 'Webhook', enter the full URL where webhooks should be posted (this App server) and enable the integration.
+4. Once set up, you can add, edit, or remove rules. For more instructions, please visit [Rollbar docs](https://rollbar.com/docs/webhooks) and [Ruby Integration](https://rollbar.com/docs/notifier/rollbar-gem)
+
+#### Giphy integration
+
+1. Add Gipy API 'search query' to `/config/service.yml`. Look at `/config/service.yml` for example.
+2. If you'll send some POST request to `http://your-external-host/api/giphy/messages` Chatbot will post to your Basecamp chat a random funny gif from [Giphy](http://giphy.com/).
+3. Just for fun!
-## References
+#### References
-* Most controller logic based on [Creating SNS subscription endpoints with Ruby on Rails](http://blog.eng.xogrp.com/post/79166302844/creating-sns-subscription-endpoints-with-ruby-on#disqus_thread)
+* AWS-SDK [documentation](http://docs.aws.amazon.com/sdkforruby/api/Aws/SNS/Client.html)
+* Giphy [Api docs](https://github.com/Giphy/GiphyAPI)
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index b12018d..e54c646 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -10,7 +10,4 @@
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
-//= require jquery
-//= require jquery_ujs
-//= require turbolinks
//= require_tree .
diff --git a/app/controllers/api/bugsnag/messages_controller.rb b/app/controllers/api/bugsnag/messages_controller.rb
new file mode 100644
index 0000000..4b6e787
--- /dev/null
+++ b/app/controllers/api/bugsnag/messages_controller.rb
@@ -0,0 +1,34 @@
+class Api::Bugsnag::MessagesController < ApplicationController
+ def create
+ # Get the parsed JSON string
+ bugsnag_parsed = Chatbot.parse_webhook(request.body.read)
+
+ # error: JSON:ParserError
+ # Get the failed JSON request message
+ request_error = bugsnag_parsed[:error]
+
+ # Prepare message for the campfire
+ message = if request_error
+ "Failed or non-JSON request: #{request_error}"
+ else
+ build_message_text bugsnag_parsed
+ end
+
+ # send message to basecamp
+ Chatbot.send_message(command_params[:callback_url], message)
+ end
+
+ private
+
+ def build_message_text(bugsnag_parsed)
+ # Store parsed dada from Bugsnag
+ event = bugsnag_parsed['error']['exceptionClass']
+ error_message = bugsnag_parsed['error']['message']
+ event_url = bugsnag_parsed['error']['url']
+ "Event: #{event}
+ Message: #{error_message}
+ Bugsnag report: #{event_url}"
+ rescue NoMethodError => e # del. exception handlidg to see errors in console
+ "Bugsnag parsing error: #{e}"
+ end
+end
diff --git a/app/controllers/api/giphy/messages_controller.rb b/app/controllers/api/giphy/messages_controller.rb
new file mode 100644
index 0000000..c6fab45
--- /dev/null
+++ b/app/controllers/api/giphy/messages_controller.rb
@@ -0,0 +1,6 @@
+class Api::Giphy::MessagesController < ApplicationController
+ def create
+ image = Giphy.random(command_params[:command]).image_original_url.to_s
+ Chatbot.send_message(command_params[:callback_url], "
")
+ end
+end
diff --git a/app/controllers/api/gitlab/messages_controller.rb b/app/controllers/api/gitlab/messages_controller.rb
new file mode 100644
index 0000000..f8b1052
--- /dev/null
+++ b/app/controllers/api/gitlab/messages_controller.rb
@@ -0,0 +1,34 @@
+class Api::Gitlab::MessagesController < ApplicationController
+ def create
+ # Get the parsed JSON string
+ gitlab_parsed = Chatbot.parse_webhook(request.body.read)
+
+ # error: JSON:ParserError
+ # Get the failed JSON request message
+ request_error = gitlab_parsed[:error]
+
+ # Prepare message for the campfire
+ message = if request_error
+ "Failed or non-JSON request:#{request_error}"
+ else
+ build_message_text gitlab_parsed
+ end
+
+ # send message to basecamp
+ Chatbot.send_message(command_params[:callback_url], message)
+ end
+
+ private
+
+ def build_message_text(gitlab_parsed)
+ # Store parsed dada from GitLab
+ project_name = gitlab_parsed['project']['name']
+ event = gitlab_parsed['object_kind']
+ project_url = gitlab_parsed['project']['web_url']
+ "Gitlab project: #{project_name}
+ Event: #{event}
+ Project url: #{project_url}"
+ rescue NoMethodError => e # del. exception handlidg to see errors in console
+ "GitLab parsing error: #{e}"
+ end
+end
diff --git a/app/controllers/api/rollbar/messages_controller.rb b/app/controllers/api/rollbar/messages_controller.rb
new file mode 100644
index 0000000..71fe9ea
--- /dev/null
+++ b/app/controllers/api/rollbar/messages_controller.rb
@@ -0,0 +1,35 @@
+class Api::Rollbar::MessagesController < ApplicationController
+ def create
+ # Get the parsed JSON string
+ rollbar_parsed = Chatbot.parse_webhook(request.body.read)
+
+ # error: JSON:ParserError
+ # Get the failed JSON request message
+ request_error = rollbar_parsed[:error]
+
+ # Prepare message for the campfire
+ message = if request_error
+ "Failed or non-JSON request: #{request_error}"
+ else
+ build_message_text rollbar_parsed
+ end
+
+ # send message to basecamp
+ Chatbot.send_message(command_params[:callback_url], message)
+ end
+
+ private
+
+ def build_message_text(rollbar_parsed)
+ # Store parsed dada from Rollbar
+ event = rollbar_parsed['event_name']
+ data = rollbar_parsed['data']['item']['title']
+ uuid = rollbar_parsed['data']['occurrence']['uuid']
+ event_url = "https://rollbar.com/instance/uuid?uuid=#{uuid}"
+ "Event: #{event}
+ Body: #{data}
+ Rollbar report: #{event_url}"
+ rescue NoMethodError => e # del. exception handlidg to see errors in console
+ "Rollbar parsing error: #{e}"
+ end
+end
diff --git a/app/controllers/api/sns/messages_controller.rb b/app/controllers/api/sns/messages_controller.rb
new file mode 100644
index 0000000..622fc68
--- /dev/null
+++ b/app/controllers/api/sns/messages_controller.rb
@@ -0,0 +1,32 @@
+class Api::Sns::MessagesController < ApplicationController
+ def create
+ amazon_sns_request = Chatbot.parse_webhook(request.body.read)
+ snstopic_arn = Rails.configuration.service['snstopic_arn']
+ aws_region = Rails.configuration.service['aws_region']
+ client = Aws::SNS::Client.new(region: aws_region)
+
+ if amazon_sns_request['Type'].to_s.casecmp('SubscriptionConfirmation') >= 0
+ client.confirm_subscription(
+ topic_arn: snstopic_arn,
+ token: amazon_sns_request['Token']
+ )
+ elsif amazon_sns_request['Type'].to_s.casecmp('Notification') >= 0
+ message = build_message_text amazon_sns_request
+ end
+
+ # send message to basecamp
+ Chatbot.send_message(command_params[:callback_url], message)
+ end
+
+ private
+
+ def build_message_text(amazon_sns_request)
+ # Store parsed dada from amazon SNS
+ subject = amazon_sns_request['Subject']
+ body = amazon_sns_request['Message']
+ "Subject: #{subject}
+ Body: #{body}
"
+ rescue NoMethodError => e # del. exception handlidg to see errors in console
+ "Amazon SNS parsing error: #{e}"
+ end
+end
diff --git a/app/controllers/api/v1/messages_controller.rb b/app/controllers/api/v1/messages_controller.rb
deleted file mode 100644
index fb570d1..0000000
--- a/app/controllers/api/v1/messages_controller.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'json'
-require 'httparty'
-
-class Api::V1::MessagesController < ApplicationController
- skip_before_action :verify_authenticity_token
-
- def create
- basecampbot_url = Rails.configuration.service['basecampbot_url']
- snstopic_arn = Rails.configuration.service['snstopic_arn']
-
- # get amazon message type and topic
- amz_message_type = request.headers['x-amz-sns-message-type']
- amz_sns_topic = request.headers['x-amz-sns-topic-arn']
-
- # sure you recieve messages from a right topic
- return unless !amz_sns_topic.nil? &&
- amz_sns_topic.to_s.downcase == snstopic_arn
-
- request_body = JSON.parse request.body.read
-
- # if this is the first time confirmation of subscription, then confirm it
- if amz_message_type.to_s.downcase == 'subscriptionconfirmation'
- send_subscription_confirmation request_body
- return
- end
-
- if amz_message_type.to_s.downcase == 'notification'
- # DO WORK HERE
- message = "Subject: #{request_body['Subject']}
#{request_body['Message']}"
- # send message to basecamp
- HTTParty.post basecampbot_url, query: { content: message }
- end
-
- head :ok
- end
-
- private
-
- def send_subscription_confirmation(request_body)
- subscribe_url = request_body['SubscribeURL']
- return nil unless !subscribe_url.to_s.empty? && !subscribe_url.nil?
- subscribe_confirm = HTTParty.get subscribe_url
- end
-
-
-end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1c07694..07c405a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,3 +1,14 @@
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
+ skip_before_action :verify_authenticity_token, if: :json_request?
+
+ private
+
+ def json_request?
+ request.format.json?
+ end
+
+ def command_params
+ params.permit(:command, :callback_url)
+ end
end
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
deleted file mode 100644
index 286b223..0000000
--- a/app/mailers/application_mailer.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-class ApplicationMailer < ActionMailer::Base
- default from: 'from@example.com'
- layout 'mailer'
-end
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
deleted file mode 100644
index 10a4cba..0000000
--- a/app/models/application_record.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class ApplicationRecord < ActiveRecord::Base
- self.abstract_class = true
-end
diff --git a/app/models/chatbot.rb b/app/models/chatbot.rb
new file mode 100644
index 0000000..73c1ac9
--- /dev/null
+++ b/app/models/chatbot.rb
@@ -0,0 +1,13 @@
+class Chatbot
+ def self.parse_webhook(resp)
+ JSON.parse resp
+ # Handle exceptions in case of failed request
+ rescue JSON::ParserError => error
+ { error: error.to_s }
+ end
+
+ def self.send_message(basecampbot_url, message)
+ basecampbot_url ||= Rails.configuration.service['basecampbot_url']
+ HTTParty.post basecampbot_url, query: { content: message }
+ end
+end
diff --git a/bin/bundle b/bin/bundle
index 66e9889..f19acf5 100755
--- a/bin/bundle
+++ b/bin/bundle
@@ -1,3 +1,3 @@
#!/usr/bin/env ruby
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
load Gem.bin_path('bundler', 'bundle')
diff --git a/bin/rails b/bin/rails
index 5badb2f..7a8ff81 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
begin
- load File.expand_path('../spring', __FILE__)
+ load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
diff --git a/bin/rake b/bin/rake
index d87d5f5..0ba8c48 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
begin
- load File.expand_path('../spring', __FILE__)
+ load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
diff --git a/bin/setup b/bin/setup
index 03132c3..fd12165 100755
--- a/bin/setup
+++ b/bin/setup
@@ -4,7 +4,7 @@ require 'fileutils'
include FileUtils
# path to your application root.
-APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+APP_ROOT = Pathname.new File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
diff --git a/bin/spring b/bin/spring
index 9bc076b..1dbfad1 100755
--- a/bin/spring
+++ b/bin/spring
@@ -8,7 +8,7 @@ unless defined?(Spring)
require 'bundler'
lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
- if spring = lockfile.specs.detect { |spec| spec.name == "spring" }
+ if spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
gem 'spring', spring.version
require 'spring/binstub'
diff --git a/bin/update b/bin/update
index a8e4462..32326c7 100755
--- a/bin/update
+++ b/bin/update
@@ -4,7 +4,7 @@ require 'fileutils'
include FileUtils
# path to your application root.
-APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+APP_ROOT = Pathname.new File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
diff --git a/config/environments/production.rb b/config/environments/production.rb
index ae04772..f6d4943 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -65,10 +65,9 @@
# require 'syslog/logger'
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
- if ENV["RAILS_LOG_TO_STDOUT"].present?
+ if ENV['RAILS_LOG_TO_STDOUT'].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
-
end
diff --git a/config/initializers/aws_sdk.rb b/config/initializers/aws_sdk.rb
new file mode 100644
index 0000000..b9a6146
--- /dev/null
+++ b/config/initializers/aws_sdk.rb
@@ -0,0 +1,11 @@
+# aws_secret_file: path to *.yml file with AWS_ACCESS_KEY_ID and AWS_SECRET_KEY_ID if exist
+aws_secret_file = Rails.configuration.service['aws_secret_file']
+
+if aws_secret_file.present?
+ creds = YAML.safe_load(File.read(aws_secret_file))
+ aws_creds = Aws::Credentials.new(
+ creds['aws_access_key'],
+ creds['aws_secret_key']
+ )
+ Aws.config.update(region: creds['aws_region'], credentials: aws_creds)
+end
diff --git a/config/initializers/giphy.rb b/config/initializers/giphy.rb
new file mode 100644
index 0000000..428500c
--- /dev/null
+++ b/config/initializers/giphy.rb
@@ -0,0 +1,3 @@
+Giphy::Configuration.configure do |config|
+ config.api_key = Rails.configuration.service['giphy_api_key']
+end
diff --git a/config/puma.rb b/config/puma.rb
index c7f311f..cc018a1 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -4,16 +4,16 @@
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum, this matches the default thread size of Active Record.
#
-threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
+threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
#
-port ENV.fetch("PORT") { 3000 }
+port ENV.fetch('PORT') { 3000 }
# Specifies the `environment` that Puma will run in.
#
-environment ENV.fetch("RAILS_ENV") { "development" }
+environment ENV.fetch('RAILS_ENV') { 'development' }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
@@ -21,7 +21,7 @@
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
-# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+# workers ENV.fetch('WEB_CONCURRENCY') { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
diff --git a/config/routes.rb b/config/routes.rb
index 49427a4..a5096a6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,8 +1,24 @@
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
- namespace :api, defaults: {format: :json} do
- namespace :v1 do
+ namespace :api, defaults: { format: :json } do
+ namespace :bugsnag do
+ resources :messages, only: :create
+ end
+
+ namespace :gitlab do
+ resources :messages, only: :create
+ end
+
+ namespace :giphy do
+ resources :messages, only: :create
+ end
+
+ namespace :rollbar do
+ resources :messages, only: :create
+ end
+
+ namespace :sns do
resources :messages, only: :create
end
end
diff --git a/config/secrets.yml b/config/secrets.yml
new file mode 100644
index 0000000..e96a23b
--- /dev/null
+++ b/config/secrets.yml
@@ -0,0 +1,10 @@
+development:
+ secret_key_base: c32dd50710233ac4673188552d83136e8a7ee50c718b8a52158bea0e76a0afaa665d0f332b101e6c4024d7d3c2e976c4f856a5b5969b09937c7c408fad77d408
+
+test:
+ secret_key_base: 8d42e7fede4ae5d5678250ed920ffbe92b316d73615d070609670e3ef19e72877e077df603c95d54391b2eb1f87ce8d917c6e5e376c22e94b213fa5cea408240
+
+# Do not keep production secrets in the repository,
+# instead read values from the environment.
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
\ No newline at end of file
diff --git a/config/service.yml b/config/service.yml
index ae11439..822b30b 100644
--- a/config/service.yml
+++ b/config/service.yml
@@ -1,6 +1,13 @@
+
+# Add this file to .gitignore right now !!!
default: &default
- basecampbot_url: https://3.basecamp.com/3601306/integrations/6Mc7Rhug2NkpDY53Qu2x7TGn/buckets/1925463/chats/294329172/lines
- snstopic_arn: arn:aws:sns:eu-central-1:798348585423:basecamp-bot
+ basecampbot_url: https://3.basecamp.com/34334368/integrations/Kvre67sn895bakxFzJLqD99GDRe/buckets/3854557/chats/498331306/lines
+ snstopic_arn: arn:aws:sns:us-west-2:2303078387557:bascamp_chatbot
+ # Path to the .yml file that contains 'aws_access_key' and 'aws_secret_key'. Attention!!! Delete it, if you are using Amazon EC2 hosting
+ aws_secret_file: /home/user/aws.yml
+ aws_region: 'us-west-6'
+ # Giphy API url + public API key
+ giphy_api_key: "dc6zaTOxFJmzC"
development:
<<: *default
@@ -9,4 +16,5 @@ test:
<<: *default
production:
- <<: *default
\ No newline at end of file
+ basecampbot_url: <%= ENV["BASECAMPBOT_URL"] %>
+ giphy_api_key: <%= ENV["GIPHY_API_KEY"] %>
diff --git a/config/spring.rb b/config/spring.rb
index c9119b4..9fa7863 100644
--- a/config/spring.rb
+++ b/config/spring.rb
@@ -1,6 +1,6 @@
-%w(
+%w[
.ruby-version
.rbenv-vars
tmp/restart.txt
tmp/caching-dev.txt
-).each { |path| Spring.watch(path) }
+].each { |path| Spring.watch(path) }
diff --git a/app/models/concerns/.keep b/spec/.keep
similarity index 100%
rename from app/models/concerns/.keep
rename to spec/.keep