Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support send notification to Slack, RocketChat etc. using Webhooks #1

Closed
jmcshane opened this issue Jul 17, 2019 · 16 comments
Closed
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@jmcshane
Copy link

It appears that the notification integration is set here:

https://github.com/brotandgames/ciao/blob/master/app/models/check.rb#L52

It would be great to be able to send a notification to a slack channel instead of an email based on specific configuration. I would be interested in helping here using https://github.com/slack-ruby/slack-ruby-client.

Is this something the project is willing to support?

@mterron
Copy link

mterron commented Jul 17, 2019

Or a webhook? That is a more flexible solution for integration.

@brotandgames
Copy link
Owner

@jmcshane @mterron Thanks for the comments.

Support for webhooks is on the roadmap.

@jmcshane Mind if we integrate Slack using webhooks? If you are eager to help us with this you're welcome.

@brotandgames brotandgames added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers and removed good first issue Good for newcomers labels Jul 18, 2019
@jmcshane
Copy link
Author

@brotandgames that's great.

Definitely willing to jump in a bit here. I like the simplicity of the way that your email integration is set up and that can be more challenging given the structure of a webhook. What would you think about a default JSON format with something that can be overridden based on a configuration file in the image? Then, an environment var could trigger webook (and disable email) like ENABLE_WEBHOOK_NOTIFICATION, reading a set of environment variables to trigger the HTTP request.

@brotandgames
Copy link
Owner

brotandgames commented Jul 18, 2019

Should it be allowed to use more than one Notification target like Mail, Webhook::Slack or Webhook::RocketChat at the same time? Our opinion: yes.

As of now if you don't specify a SMTP_ADDRESS the Mailer configuration is not read thus no e-mails are sent. This approach would be preferred in the context of Webhooks.

Webhooks should somehow be configurable through ENV variables to stay in the 12-factor app context.

Furthermore there should be no updates on the app if we want to support another notification target since it's a webhook: calling an endpoint with a payload.

What about having the following approach:

2 ENV variables per Webhook like:

CIAO_WEBHOOK_ENDPOINT_1="https://webhook.slack.com/***/***"
CIAO_WEBHOOK_PAYLOAD_1="{foo=bar}"

CIAO_WEBHOOK_ENDPOINT_2="https://webhook.rocketchat.com/***/***"
CIAO_WEBHOOK_PAYLOAD_2="{foo=bar}"

etc.

The app reads the ENV vars on startup and configures the Notifier which is then triggered by a status change.

@brotandgames brotandgames changed the title Support send notification to Slack Support send notification to Slack, RocketChat etc. using Webhooks Jul 18, 2019
@brotandgames
Copy link
Owner

brotandgames commented Jul 19, 2019

Tested this a little bit:

CIAO_WEBHOOK_PAYLOAD_* ENV variable has to be a valid JSON one-liner wrapped in single quotes like '{"text":"Example message"}'.

require 'net/http'
require 'json'
require 'uri'

# Following ENV vars have to be set prior to executing this script
# export CIAO_WEBHOOK_ENDPOINT_1=https://chat.yourhost.com/hooks/*****
# export CIAO_WEBHOOK_PAYLOAD_1='{"username":"Brot & Games","icon_url":"https://avatars0.githubusercontent.com/u/43862266?s=400&v=4","text":"Example message","attachments":[{"title":"Rocket.Chat","title_link":"https://rocket.chat","text":"Rocket.Chat, the best open source chat","image_url":"/images/integration-attachment-example.png","color":"#764FA5"}]}'

endpoint = ENV["CIAO_WEBHOOK_ENDPOINT_1"]
payload = ENV["CIAO_WEBHOOK_PAYLOAD_1"]

uri = URI.parse(endpoint)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == "https"

request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
request.body = payload

response = http.request(request)
p response

This has been tested with RocketChat (with the above payload $CIAO_WEBHOOK_PAYLOAD_1)

$ ruby webhoook_test.rb
#<Net::HTTPOK 200 OK readbody=true>

grafik

@jmcshane Do you currently have a Slack channel where you could test this?

@tareksamni
Copy link
Contributor

tareksamni commented Jul 29, 2019

@brotandgames I can implement this in a simple dynamic way where the user will be able to CRUD multiple webhooks and the payload structure (basic templating) from the web UI. I can deliver it today evening!

Should I go forward or any opinions?

@brotandgames
Copy link
Owner

@tareksamni The basic idea of ciao is to keep everything configurable per ENV variables.
The basic approach of having ENV variables like:

WEBHOOK_ENDPOINT_ROCKETCHAT
WEBHOOK_PAYLOAD_ROCKETCHAT

or

WEBHOOK_ENDPOINT_SLACK
WEBHOOK_PAYLOAD_SLACK

would be desirable.

Keeping ciao's configuration stateless is keeping it simple.

@tareksamni
Copy link
Contributor

tareksamni commented Jul 29, 2019

Good, then we go with ENV variables WEBHOOK_ENDPOINT_1 & WEBHOOK_PAYLOAD_TEMPLATE_1.

Something like:

WEBHOOK_ENDPOINT_1: https://myendpoint.com/webhook
WEBHOOK_PAYLOAD_TEMPLATE_1: '{ "status": "#{{status}}" }' # I would use http://mustache.github.io/#demo for simple payload templating!

You can define multiple webhooks with this regex: ^WEBHOOK_(ENDPOINT||PAYLOAD_TEMPLATE)_[0-9]+

What do you think?

@brotandgames
Copy link
Owner

brotandgames commented Jul 29, 2019

Sounds good.

We need an initializer config/initializers/notifications.rb that

  • reads all WEBHOOK_ENDPOINT_ variables,
  • finds the corresponding WEBHOOK_PAYLOAD_ and
  • instantiates an WebhookNotification < Notification object

Something like this:

lib/ciao/notification.rb

module Ciao

  class Notification

    def initialize
    end

    def send
    end

  end

  class WebhookNotification < Notification
    # HTTP endpoint
    attr_reader :endpoint

    # Payload (JSON string)
    attr_reader :payload

    def initialize(endpoint, payload)
      @endpoint = endpoint
      @payload = payload
    end

    def send
      uri = URI.parse(@endpoint)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true if uri.scheme == "https"

      request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
      request.body = @payload

      response = http.request(request)
      p response
    end

    def to_s
      puts "Endpoint: #{@endpoint}"
      puts "Payload: #{@payload}"
    end

  end

end

config/initializers/notifications.rb

# Notifications
notifications = []

# Webhooks
# Read all ENV variables starting with WEBHOOK_ENDPOINT_*
# and initialize appropriate class
# Test
# export WEBHOOK_ENDPOINT_1=https://chat.yourhost.net/*****
# export WEBHOOK_PAYLOAD_1='{"username":"Brot & Games","icon_url":"https://avatars0.githubusercontent.com/u/43862266?s=400&v=4","text":"Example message","attachments":[{"title":"Rocket.Chat","title_link":"https://rocket.chat","text":"Rocket.Chat, the best open source chat","image_url":"/images/integration-attachment-example.png","color":"#764FA5"}]}'


# endpoint = ENV["WEBHOOK_ENDPOINT_1"]
# payload = ENV["WEBHOOK_PAYLOAD_1"]
# webhook = Ciao::WebhookNotification.new(endpoint, payload)
# notifications << webhook
#
#
# NOTIFICATIONS = notifications

@tareksamni
Copy link
Contributor

Yes, exactly what I had in mind :) but with slight templating abilities to the PAYLOAD. To generically support all webhooks based notifications so we do not have to implement different JSON structures for different systems.

@brotandgames
Copy link
Owner

Perfect.

@brotandgames
Copy link
Owner

@tareksamni @gnomus @mterron @jmcshane Is this a good timing to open a chat like gitter.im for this project?

@tareksamni
Copy link
Contributor

YES YES YES! would be way easier to discuss features, implementation strategies, etc.

@brotandgames
Copy link
Owner

Here we go: https://gitter.im/brotandgames/ciao

@brotandgames
Copy link
Owner

brotandgames commented Jul 30, 2019

@tareksamni
Why use mustache and not substitute a payload like this:
WEBHOOK_PAYLOAD_* contains __name__, __status_after__, __status_before__ and so on and is just substituted (gsub) before send with current values.

All WebhookNotification are instantiated in the initializer and saved in an Array (notifications).
On status change go through array and each.payload=payload.gsub() + each.send|notify()?

@brotandgames
Copy link
Owner

@tareksamni Thank you for your great work!

Closed by #25.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants