blue_sky.clj
· 4.6 KiB · Clojure
Orginalformat
(ns micro-blog.blue-sky
(:require
[clj-http.client :as http-client]
[micro-blog.pocket-base :as pb]
[micro-blog.utils :as utils]
[micro-blog.is-tech]
[taoensso.telemere :as tel]
[micro-blog.config :refer [config]]))
(defn create-session []
(let [identifier (@config :blue-sky-username)
api-key (@config :blue-sky-api-key)
body {:identifier identifier :password api-key}
url (str (@config :blue-sky-host) "/com.atproto.server.createSession")
res-schema [:map
[:did string?]
[:accessJwt string?]]]
(-> (http-client/post url
{:form-params body
:content-type :json
:as :json})
:body
(utils/validate-with-throw res-schema)
(#(assoc % :access-jwt (:accessJwt %)))
(select-keys [:did :access-jwt]))))
(def post-res-schema [:map
[:cursor [:maybe :string]]
[:feed [:vector
[:map [:post [:map
[:cid :string]
[:author [:map
[:handle :string]]]
[:embed {:optional true}
[:map [:images {:optional true} [:vector [:map
[:fullsize :string]
[:alt :string]]]]]]
[:record [:map
[:facets {:optional true} [:vector [:map
[:features [:vector [:map
[:$type :string]
[:tag {:optional true} [:maybe :string]]]]]]]]
[:createdAt :string]]]]]]]]])
(defn get-posts-until-id
([session id] (get-posts-until-id session id nil []))
([session id cursor prev-posts]
(tel/log! {:level :info :data {:postId (:remoteId id)}} "Getting posts until id")
(let [limit 5
body
(-> (http-client/get (str (@config :blue-sky-host) "/app.bsky.feed.getAuthorFeed")
{:headers {"Authorization" (str "Bearer " (session :access-jwt))}
:query-params (cond-> {:actor (:did session)
:limit limit}
cursor (assoc :cursor cursor))
:content-type :json
:as :json})
:body
(utils/validate-with-throw post-res-schema))
posts (map :post (:feed body))
new-cursor (:cursor body)
new-posts (take-while #(not= (:cid %) id) posts)
new-and-prev-posts (concat new-posts prev-posts)]
(cond
;; end of posts
(not= (count posts) limit) new-and-prev-posts
;; found post
(some #(= id (:cid %)) posts) new-and-prev-posts
;; recur
:else (recur session id new-cursor new-and-prev-posts)))))
(defn extract-tags [post]
(let [facets (get (post :record) :facets [])
features (flatten (map :features facets))
tag-features (filter #(= (:$type %) "app.bsky.richtext.facet#tag") features)
tags (map :tag tag-features)]
tags))
(defn extract-images [post]
(let [images (get-in post [:embed :images] [])]
(map #(vector (:fullsize %) (:alt %)) images)))
(defn transform-post [post]
(hash-map :source :blue_sky
:fullPost post
:remoteId (:cid post)
:isTech (micro-blog.is-tech/is-tech? (:record post))
:authorId (get-in post [:author :handle])
:tags (extract-tags post)
:images (extract-images post)
:posted (get-in post [:record :createdAt])))
(defn save-post [post]
(tel/log! {:level :info :data {:postId (:remoteId post)}} "Saving post")
(pb/save-post post))
(defn run []
(tel/log! :info "Running blue sky fetcher")
(let [session (create-session)
last-saved-id (pb/get-latest-post-remote-id-by-source :blue_sky)
new-posts (reverse (get-posts-until-id session last-saved-id))]
(doseq [post new-posts]
(-> post transform-post save-post))))
1 | (ns micro-blog.blue-sky |
2 | (:require |
3 | [clj-http.client :as http-client] |
4 | [micro-blog.pocket-base :as pb] |
5 | [micro-blog.utils :as utils] |
6 | [micro-blog.is-tech] |
7 | [taoensso.telemere :as tel] |
8 | [micro-blog.config :refer [config]])) |
9 | |
10 | (defn create-session [] |
11 | (let [identifier (@config :blue-sky-username) |
12 | api-key (@config :blue-sky-api-key) |
13 | body {:identifier identifier :password api-key} |
14 | url (str (@config :blue-sky-host) "/com.atproto.server.createSession") |
15 | res-schema [:map |
16 | [:did string?] |
17 | [:accessJwt string?]]] |
18 | (-> (http-client/post url |
19 | {:form-params body |
20 | :content-type :json |
21 | :as :json}) |
22 | :body |
23 | (utils/validate-with-throw res-schema) |
24 | (#(assoc % :access-jwt (:accessJwt %))) |
25 | (select-keys [:did :access-jwt])))) |
26 | |
27 | (def post-res-schema [:map |
28 | [:cursor [:maybe :string]] |
29 | [:feed [:vector |
30 | [:map [:post [:map |
31 | [:cid :string] |
32 | [:author [:map |
33 | [:handle :string]]] |
34 | |
35 | [:embed {:optional true} |
36 | [:map [:images {:optional true} [:vector [:map |
37 | [:fullsize :string] |
38 | [:alt :string]]]]]] |
39 | [:record [:map |
40 | [:facets {:optional true} [:vector [:map |
41 | [:features [:vector [:map |
42 | [:$type :string] |
43 | [:tag {:optional true} [:maybe :string]]]]]]]] |
44 | [:createdAt :string]]]]]]]]]) |
45 | |
46 | (defn get-posts-until-id |
47 | ([session id] (get-posts-until-id session id nil [])) |
48 | ([session id cursor prev-posts] |
49 | (tel/log! {:level :info :data {:postId (:remoteId id)}} "Getting posts until id") |
50 | (let [limit 5 |
51 | body |
52 | (-> (http-client/get (str (@config :blue-sky-host) "/app.bsky.feed.getAuthorFeed") |
53 | {:headers {"Authorization" (str "Bearer " (session :access-jwt))} |
54 | :query-params (cond-> {:actor (:did session) |
55 | :limit limit} |
56 | cursor (assoc :cursor cursor)) |
57 | :content-type :json |
58 | :as :json}) |
59 | :body |
60 | (utils/validate-with-throw post-res-schema)) |
61 | posts (map :post (:feed body)) |
62 | new-cursor (:cursor body) |
63 | new-posts (take-while #(not= (:cid %) id) posts) |
64 | new-and-prev-posts (concat new-posts prev-posts)] |
65 | (cond |
66 | ;; end of posts |
67 | (not= (count posts) limit) new-and-prev-posts |
68 | ;; found post |
69 | (some #(= id (:cid %)) posts) new-and-prev-posts |
70 | ;; recur |
71 | :else (recur session id new-cursor new-and-prev-posts))))) |
72 | |
73 | (defn extract-tags [post] |
74 | (let [facets (get (post :record) :facets []) |
75 | features (flatten (map :features facets)) |
76 | tag-features (filter #(= (:$type %) "app.bsky.richtext.facet#tag") features) |
77 | tags (map :tag tag-features)] |
78 | tags)) |
79 | |
80 | (defn extract-images [post] |
81 | (let [images (get-in post [:embed :images] [])] |
82 | (map #(vector (:fullsize %) (:alt %)) images))) |
83 | |
84 | (defn transform-post [post] |
85 | (hash-map :source :blue_sky |
86 | :fullPost post |
87 | :remoteId (:cid post) |
88 | :isTech (micro-blog.is-tech/is-tech? (:record post)) |
89 | :authorId (get-in post [:author :handle]) |
90 | :tags (extract-tags post) |
91 | :images (extract-images post) |
92 | :posted (get-in post [:record :createdAt]))) |
93 | |
94 | (defn save-post [post] |
95 | (tel/log! {:level :info :data {:postId (:remoteId post)}} "Saving post") |
96 | (pb/save-post post)) |
97 | |
98 | (defn run [] |
99 | (tel/log! :info "Running blue sky fetcher") |
100 | (let [session (create-session) |
101 | last-saved-id (pb/get-latest-post-remote-id-by-source :blue_sky) |
102 | new-posts (reverse (get-posts-until-id session last-saved-id))] |
103 | (doseq [post new-posts] |
104 | (-> post transform-post save-post)))) |
105 |
is_tech.clj
· 878 B · Clojure
Orginalformat
(ns micro-blog.is-tech
(:require
[micro-blog.config :refer [config]]
[taoensso.telemere :as tel]
[clj-http.client :as client]))
(defn is-tech? [post-text]
(let [url (str (:mistral-host @config) "/v1/conversations")
headers {"Content-Type" "application/json"
"Accept" "application/json"
"Authorization" (str "Bearer " (@config :mistral-api-key))}
body {:inputs post-text
:stream false
:agent_id (@config :mistral-agent-id)}]
(tel/log! {:level :info :data {:url url :agent_id (:agent_id body)}} "making request to mistral agent")
(->
(client/post url {:headers headers
:form-params body
:content-type :json
:as :json})
:body
:outputs
first
:content
(#(if (= "1" %) true false)))))
1 | (ns micro-blog.is-tech |
2 | (:require |
3 | [micro-blog.config :refer [config]] |
4 | [taoensso.telemere :as tel] |
5 | [clj-http.client :as client])) |
6 | |
7 | (defn is-tech? [post-text] |
8 | (let [url (str (:mistral-host @config) "/v1/conversations") |
9 | headers {"Content-Type" "application/json" |
10 | "Accept" "application/json" |
11 | "Authorization" (str "Bearer " (@config :mistral-api-key))} |
12 | body {:inputs post-text |
13 | :stream false |
14 | :agent_id (@config :mistral-agent-id)}] |
15 | (tel/log! {:level :info :data {:url url :agent_id (:agent_id body)}} "making request to mistral agent") |
16 | (-> |
17 | (client/post url {:headers headers |
18 | :form-params body |
19 | :content-type :json |
20 | :as :json}) |
21 | :body |
22 | :outputs |
23 | first |
24 | :content |
25 | (#(if (= "1" %) true false))))) |
26 |