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

Telegram notification support #714

Merged
merged 8 commits into from Dec 12, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/riemann/telegram.clj
@@ -0,0 +1,48 @@
(ns ^{:doc "Send events to Telegram"}
riemann.telegram
(:require [clj-http.client :as client]
[clojure.string :refer [join]]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add clojure.string/escape here, that's why the test is failing.


(def ^:private api-url "https://api.telegram.org/bot%s/%s")

(defn- format-message [ev]
"Formats a message, accepts a single
event or a sequence of events."
(join "\n\n"
(map
(fn [e]
(str
"<strong>Host:</strong> " (or (:host e) "-") "\n"
"<strong>Service:</strong> " (or (:service e) "-") "\n"
"<strong>State:</strong> " (or (:state e) "-") "\n"
"<strong>Metric:</strong> " (or (:metric e) "-") "\n"
"<strong>Description:</strong> "
(escape (or (:description e) "-") {\< "&lt;", \> "&gt;", \& "&amp;"})))
(flatten [ev]))))

(defn- post
"POST to the Telegram API."
[token chat_id event]
(client/post (format api-url token "sendMessage")
{:form-params {:chat_id chat_id
:parse_mode "HTML"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something good to have: Markdown support and exposing parse_mode as configurable parameter of the plugin.

:text (format-message event)}
:throw-entire-message? true}))

(defn telegram
"Send events to Telegram chat. Uses your bot token and returns a function,
which send message through API to specified chat.

Format event (or events) to string with markdown syntax.

Telegram bots API documentation: https://core.telegram.org/bots/api

Usage:

(def token \"define_your_token\")
(def chat_id \"0123456\")

(streams
(rollup 5 3600 (telegram {:token token :chat_id chat_id})))"
[{:keys [token chat_id]}]
(fn [e] (post token chat_id e)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like telegram have messages limit of 1 message per second. It will be nice if we can batch up events and call telegram api once every second (as default). Something for your reference as done in datadog plugin (https://github.com/riemann/riemann/blob/master/src/riemann/datadog.clj#L56-L59)

Copy link
Contributor Author

@islander islander Aug 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, in datadog plugin batching events made outside of plugin (using native riemann's batch stream).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are right. you need to see if telegram api supports posting a list/vector of messages in one HTTP call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in Telegram API docs I don't see such method, so I just map all events in one big message.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we can go ahead with this. Thanks for searching about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, we need some batch method. Now I get this error from Telegram API when I try to send a lot of events in one message: {"ok":false,"error_code":400,"description":"Message is too long"}

36 changes: 36 additions & 0 deletions test/riemann/telegram_test.clj
@@ -0,0 +1,36 @@
(ns riemann.telegram-test
(:use riemann.telegram
clojure.test)
(:require [riemann.logging :as logging]))

(def api-token (System/getenv "TELEGRAM_API_TOKEN"))
(def chat-id (System/getenv "TELEGRAM_CHAT_ID"))

(when-not api-token
(println "export TELEGRAM_API_TOKEN=\"...\" to run these tests."))

(when-not chat-id
(println "export TELEGRAM_CHAT_ID=\"...\" to run these tests."))

(logging/init)

(deftest ^:telegram ^:integration single_event
(let [tg (telegram {:token api-token :chat_id chat-id})]
(tg {:host "localhost"
:service "telegram single"
:description "Testing single event"
:metric 42
:state "ok"})))

(deftest ^:telegram ^:integration multiple_events
(let [tg (telegram {:token api-token :chat_id chat-id})]
(tg [{:host "localhost"
:service "telegram multiple"
:description "Testing multiple events"
:metric 43
:state "ok"}
{:host "localhost"
:service "telegram multiple"
:description "Still testing multiple events"
:metric 44
:state "ok"}])))