Roughly working

This commit is contained in:
niten 2023-04-23 09:13:49 -07:00
parent b270cec637
commit 0a763d257d
7 changed files with 105 additions and 235 deletions

122
build.clj
View File

@ -1,122 +0,0 @@
(ns build
(:require [clojure.tools.build.api :as b]
[clojure.pprint :refer [pprint]]
[clojure.edn :as edn]))
(def lib 'org.fudo/tattler)
(def default-version "DEV")
(defn- class-dir [{:keys [target]}] (format "%s/classes" target))
(def basis (b/create-basis {:project "deps.edn"}))
(defn- jar-file [{:keys [target version]
:or {version default-version}}]
(format "%s/%s-%s.jar" target (name lib) version))
(defn- uberjar-file [{:keys [target version]
:or {version default-version}}]
(format "%s/%s-uber-%s.jar" target (name lib) version))
(def default-params
{
:verbose false
:version "DEV"
})
(defn clean [{:keys [target] :as params}]
(b/delete {:path target})
params)
(defn compile-java [{:keys [verbose java-src] :as params}]
(when verbose (println (format "compiling java files in %s..." java-src)))
(b/javac {:src-dirs [java-src]
:class-dir (class-dir params)
:basis basis
:javac-opts ["-source" "16" "-target" "16"]})
params)
(defn compile-clj [{:keys [verbose clj-src] :as params}]
(when verbose (println (format "compiling clj files in %s..." clj-src)))
(b/compile-clj {:basis basis
:src-dirs [clj-src]
:class-dir (class-dir params)}))
(defn- pthru [o] (pprint o) o)
(defn- read-metadata [filename]
(-> filename
(slurp)
(edn/read-string)))
(defn jar [base-params]
(let [params (-> (merge default-params
(read-metadata (or (:metadata base-params)
"metadata.edn"))
base-params)
(update :target str)
(update :version str)
(update :java-src str)
(update :clj-src str)
(update :main-ns str))
{:keys [java-src clj-src main-ns version verbose]} params
classes (class-dir params)]
(when verbose
(print "parameters: ")
(pprint params))
(compile-java params)
(compile-clj params)
(when verbose (println (format "writing POM file to %s..." classes)))
(b/write-pom {
:class-dir classes
:lib lib
:version (str version)
:basis basis
:src-dirs [java-src clj-src]
})
(when verbose (println (format "copying source files from %s to %s..."
[java-src clj-src] classes)))
(b/copy-dir {:src-dirs [java-src clj-src]
:target-dir classes})
(let [jar (jar-file params)]
(when verbose (println (format "writing JAR file to %s..." jar)))
(b/jar {:class-dir classes
:jar-file jar
:main main-ns}))
(when verbose (println "done!"))
params))
(defn uberjar [base-params]
(let [params (-> (merge default-params
(read-metadata (or (:metadata base-params)
"metadata.edn"))
base-params)
(update :target str)
(update :version str)
(update :java-src str)
(update :clj-src str)
(update :main-ns str))
{:keys [java-src clj-src main-ns version verbose]} params
classes (class-dir params)]
(when verbose
(print "parameters: ")
(pprint params))
(compile-java params)
(compile-clj params)
(when verbose (println (format "writing POM file to %s..." classes)))
(b/write-pom {
:class-dir classes
:lib lib
:version (str version)
:basis basis
:src-dirs [java-src clj-src]
})
(when verbose (println (format "copying source files from %s to %s..."
[java-src clj-src] classes)))
(b/copy-dir {:src-dirs [java-src clj-src]
:target-dir classes})
(let [uberjar (uberjar-file params)]
(when verbose (println (format "writing uberjar file to %s..." uberjar)))
(b/uber {:class-dir classes
:uber-file uberjar
:basis basis
:main main-ns}))
(when verbose (println "done!"))
params))

View File

@ -1,23 +1,17 @@
{ {
:paths ["src/clj" "src/java"] :paths ["src"]
:deps { :deps {
org.clojure/clojure { :mvn/version "1.11.1" } org.clojure/clojure { :mvn/version "1.11.1" }
com.github.hypfvieh/dbus-java-core { :mvn/version "4.3.0" } org.clojure/tools.cli {:mvn/version "1.0.214"}
com.github.hypfvieh/dbus-java-transport-native-unixsocket { :mvn/version "4.3.0" } metosin/malli { :mvn/version "0.11.0" }
org.fudo/notifier { :mvn/version "0.1" }
org.fudo/fudo-clojure {
:git/url "https://git.fudo.org/fudo-public/fudo-clojure.git"
:git/sha "2352892ad7d7cf7c6bd294005a28d55ef224862a"
}
org.fudo/milquetoast { org.fudo/milquetoast {
:git/url "https://git.fudo.org/fudo-public/milquetoast.git" :git/url "https://git.fudo.org/fudo-public/milquetoast.git"
:git/sha "ae81b91f0c710632f55b43f0193e16ab0dd81dde" :git/sha "ae81b91f0c710632f55b43f0193e16ab0dd81dde"
} }
metosin/malli { :mvn/version "0.11.0" }
org.fudo/notifier { :mvn/version "0.1" }
}
:aliases {
:build {
:extra-deps {
io.github.clojure/tools.build {:mvn/version "0.9.4"}
}
:ns-default build
:exec-fn uberjar
}
}
} }
}

View File

@ -1,8 +0,0 @@
{
:version "0.1"
:target "target"
:java-src "src/java"
:clj-src "src/clj"
:main-ns "tattler.core"
:verbose true
}

View File

@ -1,43 +0,0 @@
(ns tattler.core
(:import [org.freedesktop Notifications]
[org.freedesktop.dbus.connections.impl
DBusConnectionBuilder]
org.freedesktop.dbus.types.UInt32)
(:gen-class))
(def ^:private ^:const NOTIFICATIONS_PATH "/org/freedesktop/Notifications")
(def ^:private ^:const NOTIFICATIONS_BUS "org.freedesktop.Notifications")
(defn connect-session-bus []
(-> (DBusConnectionBuilder/forSessionBus)
(.build)
(.getRemoteObject NOTIFICATIONS_BUS NOTIFICATIONS_PATH Notifications)))
(defn send-notification
[bus
{:keys [app replace-id icon summary body actions timeout urgency]
:or {replace-id 0
icon ""
actions []
timeout -1}
:as args}]
(doseq [arg [:app :summary :body]]
(when (not (contains? args arg))
(throw (ex-info (format "Missing required argument: %s" arg)
{:arg arg}))))
(.Notify bus
app
(UInt32. replace-id)
icon
summary
body
actions
hints
timeout))
#_(defn send [{app :app conn :conn} notification]
(let []))
(defn -main [& args]
(send-notification (connect-session-bus)
{:app "my_app" :summary "Hey there" :body "How's it going now?"}))

View File

@ -1,47 +0,0 @@
package org.freedesktop;
import java.util.List;
import java.util.Map;
import org.freedesktop.dbus.interfaces.DBusInterface;
// import org.freedesktop.dbus.DBusSignal;
import org.freedesktop.dbus.types.UInt32;
import org.freedesktop.dbus.types.Variant;
import org.freedesktop.dbus.exceptions.DBusException;
public interface Notifications extends DBusInterface {
// public static class ActionInvoked extends DBusSignal {
// public final UInt32 id;
// public final String action_key;
// public ActionInvoked(String path, UInt32 id, String action_key) throws DBusException {
// super(path, id, action_key);
// this.id = id;
// this.action_key = action_key;
// }
// }
// public static class NotificationClosed extends DBusSignal {
// public final UInt32 id;
// public final UInt32 reason;
// public NotificationClosed(String path, UInt32 id, UInt32 reason) throws DBusException {
// super(path, id, reason);
// this.id = id;
// this.reason = reason;
// }
// }
// public void CloseNotification(UInt32 id);
public List<String> GetCapabilities();
// public Quad<String, String, String, String> GetServerInformation();
public UInt32 Notify(
String app_name,
UInt32 replaces_id,
String app_icon,
String summary,
String body,
List<String> actions,
Map<String, Variant<?>> hints,
int expire_timeout);
}

65
src/tattler/cli.clj Normal file
View File

@ -0,0 +1,65 @@
(ns tattler.cli
(:require [clojure.core.async :as async :refer [>!! <!!]]
[clojure.tools.cli :as cli]
[clojure.set :as set]
[clojure.string :as str]
[tattler.core :as tattler]
[milquetoast.client :as mqtt]
[fudo-clojure.logging :as log]))
(def cli-opts
[["-v" "--verbose" "Provide verbose output."]
[nil "--app-name APP" "Name to report for this application."]
[nil "--mqtt-host HOSTNAME" "Hostname of MQTT server."]
[nil "--mqtt-port PORT" "Port on which to connect to the MQTT server."
:parse-fn #(Integer/parseInt %)]
[nil "--mqtt-user USER" "User as which to connect to MQTT server."]
[nil "--mqtt-password-file PASSWD_FILE" "File containing password for MQTT user."]
[nil "--notification-topic TOPIC" "MQTT topic to which events should be published."]])
(defn- msg-quit [status msg]
(println msg)
(System/exit status))
(defn- usage
([summary] (usage summary []))
([summary errors] (->> (concat errors
["usage: tattler-client [opts]"
""
"Options:"
summary])
(str/join \newline))))
(defn- parse-opts [args required cli-opts]
(let [{:keys [options] :as result} (cli/parse-opts args cli-opts)
missing (set/difference required (-> options (keys) (set)))
missing-errors (map #(format "missing required parameter: %s" (name %))
missing)]
(update result :errors concat missing-errors)))
(defn -main [& args]
(let [required-args #{:mqtt-host :mqtt-port :mqtt-user :mqtt-password-file :app-name :notification-topic}
{:keys [options _ errors summary]} (parse-opts args required-args cli-opts)]
(when (seq errors) (msg-quit 1 (usage summary errors)))
(let [{:keys [mqtt-host mqtt-port mqtt-user mqtt-password-file app-name notification-topic]} options
catch-shutdown (async/chan)
mqtt-client (mqtt/connect-json! :host mqtt-host
:port mqtt-port
:username mqtt-user
:password (-> mqtt-password-file
(slurp)
(str/trim)))
logger (log/print-logger)]
(tattler/listen! :app app-name
:mqtt-client mqtt-client
:topic notification-topic
:logger logger)
(.addShutdownHook (Runtime/getRuntime)
(Thread. (fn [] (>!! catch-shutdown true))))
(<!! catch-shutdown)
;; Stopping the MQTT will stop tattler
(mqtt/stop! mqtt-client)
(System/exit 0))))

31
src/tattler/core.clj Normal file
View File

@ -0,0 +1,31 @@
(ns tattler.core
(:require [notifier.core :as notify]
[milquetoast.client :as mqtt]
[clojure.core.async :refer [go-loop <!]]
[fudo-clojure.logging :as log]
[malli.core :as t]))
(defn- sized-string [min max]
(t/schema [:string {:min min :max max}]))
(def Notification
(t/schema [:map
[:summary (sized-string 1 80)]
[:body (sized-string 1 256)]
[:urgency {:optional true} [:enum "low" "medium" "high"]]]))
(defn listen! [& {app :app mqtt-client :mqtt-client topic :topic logger :logger}]
(let [note-chan (mqtt/subscribe! mqtt-client topic)]
(go-loop [note (<! note-chan)]
(if note
(do (if (t/validate Notification note)
(notify/send-notification! mqtt-client
{
:app app
:summary (:summary note)
:body (:body note)
:urgency (-> note :urgency (keyword))
})
(log/error! logger (format "rejecting invalid notification: %s" note)))
(recur (<! note-chan)))
(log/info! logger "stopping")))))