diff options
| author | eltrac <hi@eltr.ac> | 2026-03-23 22:56:01 +0800 |
|---|---|---|
| committer | eltrac <hi@eltr.ac> | 2026-03-23 22:56:01 +0800 |
| commit | 538c362a2cc8d01a418ec9aa7f7efd1d37d2977a (patch) | |
| tree | 58d87a9cabe0ae8ae15b2e83d5dbe9a2065fe1fc /src | |
| parent | 6a806614c57832179cbc24f923c718b684e7eca3 (diff) | |
refactor: separate microformats and wrapper from processor
Diffstat (limited to 'src')
| -rw-r--r-- | src/weepinbell/microformats.clj | 34 | ||||
| -rw-r--r-- | src/weepinbell/processor.clj | 83 | ||||
| -rw-r--r-- | src/weepinbell/wrapper.clj | 47 |
3 files changed, 87 insertions, 77 deletions
diff --git a/src/weepinbell/microformats.clj b/src/weepinbell/microformats.clj new file mode 100644 index 0000000..8fd2545 --- /dev/null +++ b/src/weepinbell/microformats.clj @@ -0,0 +1,34 @@ +(ns weepinbell.microformats + (:require [clojure.string :refer [includes? lower-case]] + [taoensso.timbre :as timbre]) + (:import [com.kylewm.mf2j Mf2Parser] + [java.net URI])) + +(defn parse + "Parse microformats from a :as digestURL." + [url] + (let [max-retries 3] + (loop [attempt 1] + (let [result (try + (let [parser (doto (Mf2Parser.) + (.setIncludeAlternates true) + (.setIncludeRelUrls true))] + {:ok true :data (.parse parser (URI. url))}) + (catch Exception e + {:ok false :exception e}))] + (if (:ok result) + (:data result) + (let [e (:exception result) + msg (.getMessage e)] + (if (and (<= attempt max-retries) + (or (instance? java.net.SocketTimeoutException e) + (includes? (lower-case (str msg)) "timeout"))) + (let [wait-time (* attempt 2000)] + (timbre/warn (format "MF2 Parsing timeout for %s (attempt %d/%d). Retrying in %dms..." + url attempt max-retries wait-time)) + (Thread/sleep wait-time) + (recur (inc attempt))) + (do + (timbre/warn (format "MF2 Parsing failed for %s after %d attempts. Error: %s" + url attempt (or msg "Unknown error"))) + nil)))))))) diff --git a/src/weepinbell/processor.clj b/src/weepinbell/processor.clj index 64df22e..413dd31 100644 --- a/src/weepinbell/processor.clj +++ b/src/weepinbell/processor.clj @@ -1,18 +1,12 @@ (ns weepinbell.processor (:require [clojure.core.async :refer [go-loop <!]] - [clojure.string :refer [includes? lower-case]] + [clojure.string :refer [includes?]] [clj-http.client :as client] [weepinbell.storage :as storage] - [weepinbell.utils :refer [validate-content-types - javamap->map - file-exists? - download-file]] - [weepinbell.config :refer [get-config]] + [weepinbell.utils :refer [validate-content-types]] + [weepinbell.wrapper :as wrapper] [weepinbell.webhook :as webhook] - [taoensso.timbre :as timbre] - [digest :as digest]) - (:import [com.kylewm.mf2j Mf2Parser] - [java.net URI])) + [taoensso.timbre :as timbre])) (defn verify-mention "Get source and follow redirects to check if target link is present." @@ -32,71 +26,6 @@ (timbre/error "Error verifying mention:" (.getMessage e)) false))) -(defn parse-microformats - "Parse microformats from a :as digestURL." - [url] - (let [max-retries 3] - (loop [attempt 1] - (let [result (try - (let [parser (doto (Mf2Parser.) - (.setIncludeAlternates true) - (.setIncludeRelUrls true))] - {:ok true :data (.parse parser (URI. url))}) - (catch Exception e - {:ok false :exception e}))] - (if (:ok result) - (:data result) - (let [e (:exception result) - msg (.getMessage e)] - (if (and (<= attempt max-retries) - (or (instance? java.net.SocketTimeoutException e) - (clojure.string/includes? (lower-case (str msg)) "timeout"))) - (let [wait-time (* attempt 2000)] - (timbre/warn (format "MF2 Parsing timeout for %s (attempt %d/%d). Retrying in %dms..." - url attempt max-retries wait-time)) - (Thread/sleep wait-time) - (recur (inc attempt))) - (do - (timbre/warn (format "MF2 Parsing failed for %s after %d attempts. Error: %s" - url attempt (or msg "Unknown error"))) - nil)))))))) - - -(defn wrap-webmention - "Parse microformats and collect essential info. Wrap source, target and collected data into a Clojure map and return it." - [source target] - (let [mf (-> source - parse-microformats - javamap->map - :items first - javamap->map) - wm {:source source - :target target - :id (digest/md5 (str source target)) - :modified (System/currentTimeMillis)} - raw-photo (or (-> mf - (get-in [:properties "author"]) - first - (get-in ["properties" "photo"]) - first) - (-> mf (get-in [:properties "photo"]) first)) - ;; download if found photo - proxied-photo (if raw-photo - (let [photo-url (str raw-photo) - file-ext (or (re-find #"\.(?:jpg|jpeg|png|gif|webp)" photo-url) ".jpg") - filename (str (digest/md5 photo-url) file-ext) - local-path (str "data/avatars/" filename) - public-path (str "/avatars/" filename)] - ;; download photo if not exists - (when-not (file-exists? local-path) - (timbre/info "Downloading microformats photo to proxy external image.") - (download-file photo-url local-path)) - (str (get-config :base-url) public-path)) - nil)] - {:webmention wm - :microformats mf - :extra {:proxied-photo proxied-photo}})) - (defn process-new-webmention "Store new webmention if verifed. Do nothing otherwise." [source target] @@ -104,7 +33,7 @@ (timbre/info "\nšāāļø Valid webmention received: \n" "from " source "\n" "to " target "\n\n") - (let [data (wrap-webmention source target)] + (let [data (wrapper/wrap source target)] (storage/append-wm data)))) (defn process-existing-webmention @@ -114,7 +43,7 @@ (if (verify-mention source target) (do (storage/update-wm source target - (wrap-webmention source target)) + (wrapper/wrap source target)) (timbre/info "Webmention updated.")) (do (storage/delete-wm source target) diff --git a/src/weepinbell/wrapper.clj b/src/weepinbell/wrapper.clj new file mode 100644 index 0000000..f1493c4 --- /dev/null +++ b/src/weepinbell/wrapper.clj @@ -0,0 +1,47 @@ +(ns weepinbell.wrapper + (:require [weepinbell.utils :refer [javamap->map + file-exists? + download-file]] + [weepinbell.config :refer [get-config]] + [weepinbell.microformats :as microformats] + [digest :as digest] + [taoensso.timbre :as timbre])) + +(defn- find-mf-photo [mf] + (or (-> mf + (get-in [:properties "author"]) + first + (get-in ["properties" "photo"]) + first) + (-> mf (get-in [:properties "photo"]) first))) + +(defn- proxy-mf-photo [raw-photo] + (if raw-photo + (let [photo-url (str raw-photo) + file-ext (or (re-find #"\.(?:jpg|jpeg|png|gif|webp)" photo-url) ".jpg") + filename (str (digest/md5 photo-url) file-ext) + local-path (str "data/avatars/" filename) + public-path (str "/avatars/" filename)] + ;; download photo if not exists + (when-not (file-exists? local-path) + (timbre/info "Downloading microformats photo to proxy external image.") + (download-file photo-url local-path)) + (str (get-config :base-url) public-path)) + nil)) + +(defn wrap + "Parse microformats and collect essential info. Wrap source, target and collected data into a Clojure map and return it." + [source target] + (let [mf (-> source + microformats/parse + :items first + javamap->map) + wm {:source source + :target target + :id (digest/md5 (str source target)) + :modified (System/currentTimeMillis)} + proxied-photo (-> mf find-mf-photo proxy-mf-photo)] + {:webmention wm + :microformats mf + :extra {:proxied-photo proxied-photo}})) + |
