97 lines
3.5 KiB
Text
97 lines
3.5 KiB
Text
|
(ns mastodon-bot.mastodon-api
|
||
|
(:require
|
||
|
[clojure.spec.alpha :as s]
|
||
|
[clojure.spec.test.alpha :as st]
|
||
|
[orchestra.core :refer-macros [defn-spec]]
|
||
|
[cljs.reader :as edn]
|
||
|
[clojure.set :refer [rename-keys]]
|
||
|
[clojure.string :as string]
|
||
|
[mastodon-bot.infra :as infra]
|
||
|
["mastodon-api" :as mastodon]))
|
||
|
|
||
|
; Todo: think about how namespaced keywords & clj->js can play nicely together
|
||
|
(s/def :access_token string?)
|
||
|
(s/def :account-id string?)
|
||
|
(s/def :api_url string?)
|
||
|
|
||
|
(s/def ::mastodon-config (s/keys :req [:access_token :account-id :access_token]))
|
||
|
|
||
|
(defn-spec mastodon-config ::mastodon-config
|
||
|
[config any?]
|
||
|
(:mastodon config))
|
||
|
|
||
|
(defn-spec mastodon-client any?
|
||
|
[mastodon-config ::mastodon-config]
|
||
|
(or (some-> mastodon-config clj->js mastodon.)
|
||
|
(infra/exit-with-error "missing Mastodon client configuration!")))
|
||
|
|
||
|
(def content-filter-regexes (mapv re-pattern (:content-filters mastodon-config)))
|
||
|
|
||
|
(def keyword-filter-regexes (mapv re-pattern (:keyword-filters mastodon-config)))
|
||
|
|
||
|
(def append-screen-name? (boolean (:append-screen-name? mastodon-config)))
|
||
|
|
||
|
(def max-post-length (:max-post-length mastodon-config))
|
||
|
|
||
|
(defn blocked-content? [text]
|
||
|
(boolean
|
||
|
(or (some #(re-find % text) content-filter-regexes)
|
||
|
(when (not-empty keyword-filter-regexes)
|
||
|
(empty? (some #(re-find % text) keyword-filter-regexes))))))
|
||
|
|
||
|
(defn delete-status [status]
|
||
|
(.delete mastodon-client (str "statuses/" status) #js {}))
|
||
|
|
||
|
(defn set-signature [text]
|
||
|
(if-let [signature (:signature mastodon-config )]
|
||
|
(str text "\n" signature)
|
||
|
text))
|
||
|
|
||
|
(defn post-status
|
||
|
([status-text]
|
||
|
(post-status status-text nil))
|
||
|
([status-text media-ids]
|
||
|
(let [{:keys [sensitive? signature visibility]} mastodon-config]
|
||
|
(.post mastodon-client "statuses"
|
||
|
(clj->js (merge {:status (-> status-text resolve-urls set-signature)}
|
||
|
(when media-ids {:media_ids media-ids})
|
||
|
(when sensitive? {:sensitive sensitive?})
|
||
|
(when visibility {:visibility visibility})))))))
|
||
|
|
||
|
(defn post-image [image-stream description callback]
|
||
|
(-> (.post mastodon-client "media" #js {:file image-stream :description description})
|
||
|
(.then #(-> % .-data .-id callback))))
|
||
|
|
||
|
(defn post-status-with-images
|
||
|
([status-text urls]
|
||
|
(post-status-with-images status-text urls []))
|
||
|
([status-text [url & urls] ids]
|
||
|
(if url
|
||
|
(-> request
|
||
|
(.get url)
|
||
|
(.on "response"
|
||
|
(fn [image-stream]
|
||
|
(post-image image-stream status-text #(post-status-with-images status-text urls (conj ids %))))))
|
||
|
(post-status status-text (not-empty ids)))))
|
||
|
|
||
|
(defn get-mastodon-timeline [callback]
|
||
|
(.then (.get mastodon-client (str "accounts/" (:account-id mastodon-config)"/statuses") #js {})
|
||
|
#(let [response (-> % .-data js->edn)]
|
||
|
(if-let [error (:error response)]
|
||
|
(exit-with-error error)
|
||
|
(callback response)))))
|
||
|
|
||
|
(defn perform-replacements [post]
|
||
|
(assoc post :text (reduce-kv string/replace (:text post) (:replacements mastodon-config)))
|
||
|
)
|
||
|
|
||
|
(defn post-items [last-post-time items]
|
||
|
(doseq [{:keys [text media-links]} (->> items
|
||
|
(remove #(blocked-content? (:text %)))
|
||
|
(filter #(> (:created-at %) last-post-time))
|
||
|
(map perform-replacements))]
|
||
|
(if media-links
|
||
|
(post-status-with-images text media-links)
|
||
|
(when-not (:media-only? mastodon-config)
|
||
|
(post-status text)))))
|