最后活跃于 1755323272

blue_sky.clj 原始文件
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 原始文件
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