diff --git a/deps.edn b/deps.edn index b7b487a..f4652c0 100644 --- a/deps.edn +++ b/deps.edn @@ -19,6 +19,8 @@ org.clojure/math.numeric-tower { :mvn/version "0.0.5" } + org.clojure/tools.cli { :mvn/version "1.0.206" } + camel-snake-kebab/camel-snake-kebab { :mvn/version "0.4.2" } } :aliases { diff --git a/module.nix b/module.nix index 39afe94..839b481 100644 --- a/module.nix +++ b/module.nix @@ -62,16 +62,13 @@ in { environment = { PRICEBOT_EXCHANGE_HOST = cfg.exchange-host; PRICEBOT_BEBOT_URL = cfg.mattermost-url; - PRICEBOT_BEBOT_AUTH_TOKEN_FILE = "%d/auth.token"; PRICEBOT_BEBOT_CHANNEL_ID = opts.mattermost-channel-id; PRICEBOT_TARGET_CURRENCY = opts.currency; PRICEBOT_NOTIFY_USER = opts.notify-user; }; serviceConfig = { ExecStart = pkgs.writeShellScript "launch-pricebot-${currency}.sh" '' - echo "CREDENTIALS_DIRECTORY $CREDENTIALS_DIRECTORY" - echo "CREDENTIALS_PATH $CREDENTIALS_PATH" - ${pricebot}/bin/pricebot + ${pricebot}/bin/pricebot --bebot-auth-token-file=$CREDENTIALS_DIRECTORY/auth.token ''; DynamicUser = true; PrivateTmp = true; diff --git a/src/pricebot/core.clj b/src/pricebot/core.clj index 9113abc..236b849 100644 --- a/src/pricebot/core.clj +++ b/src/pricebot/core.clj @@ -9,7 +9,9 @@ [fudo-clojure.result :as result :refer [map-success send-success dispatch-result success? unwrap]] [fudo-clojure.logging :as log] [clojure.string :as str] - [clojure.math.numeric-tower :refer [floor]]) + [clojure.math.numeric-tower :refer [floor]] + [clojure.tools.cli :refer [parse-opts]] + [camel-snake-kebab.core :refer [->SCREAMING_SNAKE_CASE]]) (:import java.math.RoundingMode java.time.format.DateTimeFormatter java.time.Duration @@ -158,24 +160,50 @@ (let [checks (create-checks logger)] (reify-bot hostname hostname currency checks))) -(defn- getenv-or-fail [var] - (if-let [val (System/getenv var)] - val - (throw (ex-info (str "Failed to read environment variable: " var) {})))) +(def cli-opts + [["-x" "--exchange-host HOST" "Hostname of the Coinbase Pro target host."] + ["-b" "--bebot-url URL" "URL of the target Mattermost server."] + ["-a" "--bebot-auth-token-file FILE" "Path to file containing the Bebot authentication token."] + ["-C" "--bebot-channel-id ID" "ID of channel in which to send price notifications."] + ["-c" "--currency CURRENCY" "Cryptocurrency to watch for price fluctuations."] + ["-u" "--notify-user USER" "User to notify of significant price fluctuations."] + ["-h" "--help"]]) + +(defn get-key [opts k] + (if-let [opt (get opts k)] + [k opt] + [k (System/getenv (System/getenv (format "PRICEBOT_%s" + (-> k name ->SCREAMING_SNAKE_CASE))))])) + +(defn exit! [status msg] + (println msg) + (System/exit status)) + +(defn get-args [keys args] + (let [input-opts (parse-opts args cli-opts) + opts (into {} (map (partial get-key input-opts) keys)) + missing (filter (fn [k] (not (get opts k))) keys)] + (cond (:help opts) (exit! 0 (println (:summary opts))) + (:errors opts) (exit! 1 (println (str/join \newline (:errors opts)))) + (not (empty? missing)) (exit! 2 (println (str "missing arguments: " (str/join " " (map name missing))))) + :else opts))) (defn -main [& args] - (let [exchange-hostname (getenv-or-fail "PRICEBOT_EXCHANGE_HOST") - bebot-host (getenv-or-fail "PRICEBOT_BEBOT_URL") - bebot-auth-token (-> (getenv-or-fail "PRICEBOT_BEBOT_AUTH_TOKEN_FILE") - (slurp)) - bebot-channel-id (getenv-or-fail "PRICEBOT_BEBOT_CHANNEL_ID") - target-currency (-> (getenv-or-fail "PRICEBOT_TARGET_CURRENCY") - (str/lower-case) - (keyword)) - notify-user (getenv-or-fail "PRICEBOT_NOTIFY_USER")] - (let [logger (bebot-log/make-logger bebot-host bebot-auth-token bebot-channel-id notify-user) + (let [required-keys [:exchange-host + :currency + :bebot-url + :bebot-auth-token-file + :bebot-channel-id + :notify-user] + {:keys [exchange-host + currency + bebot-url + bebot-auth-token-file + bebot-channel-id + notify-user]} (get-args required-keys args)] + (let [logger (bebot-log/make-logger bebot-url (slurp bebot-auth-token-file) bebot-channel-id notify-user) checks (create-checks logger) - bot (reify-bot exchange-hostname target-currency) + bot (reify-bot exchange-host (-> currency str/lower-case keyword)) shutdown (chan)] (.addShutdownHook (Runtime/getRuntime) (Thread. (fn []