From 7007e8bd03e309b2fe5d8d34964da7282dc8debd Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Fri, 3 May 2024 09:23:59 +0200 Subject: [PATCH] [new] Add Slack handler --- project.clj | 5 +- src/taoensso/telemere/slack.clj | 84 +++++++++++++++++++++++++++++++++ wiki/4-Handlers.md | 2 +- 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/taoensso/telemere/slack.clj diff --git a/project.clj b/project.clj index b69f4c6..2549f6a 100644 --- a/project.clj +++ b/project.clj @@ -53,8 +53,9 @@ [io.opentelemetry/opentelemetry-api "1.37.0"] #_[io.opentelemetry/opentelemetry-sdk-extension-autoconfigure "1.37.0"] #_[io.opentelemetry/opentelemetry-exporter-otlp "1.37.0"] - [metosin/jsonista "0.3.8"] - [com.draines/postal "2.0.5"]] + [metosin/jsonista "0.3.8"] + [com.draines/postal "2.0.5"] + [org.julienxx/clj-slack "0.8.3"]] :plugins [[lein-pprint "1.3.2"] diff --git a/src/taoensso/telemere/slack.clj b/src/taoensso/telemere/slack.clj new file mode 100644 index 0000000..2ca78bb --- /dev/null +++ b/src/taoensso/telemere/slack.clj @@ -0,0 +1,84 @@ +(ns taoensso.telemere.slack + "Slack handler using `clj-slack`, + Ref. " + (:require + [taoensso.encore :as enc :refer [have have?]] + [taoensso.telemere.utils :as utils] + [clj-slack.core :as slack] + [clj-slack.chat :as slack.chat])) + +(comment + (require '[taoensso.telemere :as tel]) + (remove-ns 'taoensso.telemere.slack) + (:api (enc/interns-overview))) + +(def default-dispatch-opts + {:min-level :info + :rate-limit + [[5 (enc/msecs :mins 1)] + [10 (enc/msecs :mins 15)] + [15 (enc/msecs :hours 1)] + [30 (enc/msecs :hours 6)] + ]}) + +(defn handler:slack + "Experimental, subject to change. + + Needs `clj-slack`, Ref. . + + Returns a (fn handler [signal]) that: + - Takes a Telemere signal (map). + - Writes the signal as a string to specified Slack channel. + + Can output signals as human or machine-readable (edn, JSON) strings. + + Default handler dispatch options (override when calling `add-handler!`): + `:min-level` - `:info` + `:rate-limit` - + [[5 (enc/msecs :mins 1)] ; Max 5 posts in 1 min + [10 (enc/msecs :mins 15)] ; Max 10 posts in 15 mins + [15 (enc/msecs :hours 1)] ; Max 15 posts in 1 hour + [30 (enc/msecs :hours 6)] ; Max 30 posts in 6 hours + ] + + Options: + `:conn-opts` - Map of connection opts given to `clj-slack.chat/post-message` + Examples: + {:token \"MY-TOKEN\"} + {:token \"MY-TOKEN\", :api-url \"https://slack.com/api\"} + + `:post-opts` - Map of post opts given to `clj-slack.chat/post-message` + Examples: + {:channel-id \"C12345678\", :username \"MY_BOT\"} + + `:output-fn` - (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn` + + Tips: + - See `clj-slack` docs for more info on its options." + + ;; ([] (handler:slack nil)) + ([{:keys [conn-opts post-opts output-fn] + :or + {conn-opts {:api-url "https://slack.com/api", :token nil} + post-opts {:channel-id nil, :username nil} + output-fn (utils/format-signal-fn)}}] + + (let [{:keys [api-url token] + :or {api-url "https://slack.com/api"}} conn-opts + + {:keys [channel-id]} post-opts + post-opts (dissoc post-opts :channel-id) + + _ (when-not (string? token) (throw (ex-info "Expected `:conn-opts/token` string" (enc/typed-val token)))) + _ (when-not (string? channel-id) (throw (ex-info "Expected `:post-opts/channel-id` string" (enc/typed-val channel-id)))) + + handler-fn + (fn a-handler:slack + ([]) ; Shut down (no-op) + ([signal] + (when-let [output (output-fn signal)] + (slack.chat/post-message conn-opts channel-id + output post-opts))))] + + (with-meta handler-fn + {:dispatch-opts default-dispatch-opts})))) diff --git a/wiki/4-Handlers.md b/wiki/4-Handlers.md index 9434445..a262360 100644 --- a/wiki/4-Handlers.md +++ b/wiki/4-Handlers.md @@ -14,7 +14,7 @@ A number of signal handlers are included out-the box. Alphabetically: | [`handler:logstash`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.logstash#handler:logstash) [0] | Clj | [Logstash](https://www.elastic.co/logstash) | TODO | | [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry-logger) | Clj | [OpenTelemetry](https://opentelemetry.io/) [Java client](https://github.com/open-telemetry/opentelemetry-java) | [LogRecord](https://opentelemetry.io/docs/specs/otel/logs/data-model/) | | [`handler:postal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) | Clj | Email (via [postal](https://github.com/drewr/postal)) | String [2] | -| [`handler:slack`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.slack#handler:slack) [0] | Clj | [Slack](https://slack.com/) (via [clj-slack](https://github.com/julienXX/clj-slack)) | String [2] | +| [`handler:slack`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.slack#handler:slack) | Clj | [Slack](https://slack.com/) (via [clj-slack](https://github.com/julienXX/clj-slack)) | String [2] | | [`handler:tcp-socket`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:tcp-socket) | Clj | TCP socket | String [2] | | [`handler:udp-socket`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:udp-socket) | Clj | UDP socket | String [2] |