Miscallaneous scripts for working with Mastodon.
Everything we do via the API needs the URL for the instance, the auth token, etc. We store this in a "Mastodon map", which you build once, then re-use.
(require '[com.aphyr.mastodon-utils :as m] :reload)
; Register an app
(def m (m/register-app! {:url "https://your.instance"}))
; Get the URL to get a code to authorize the app. Visit this in your browser
; and copy the code...
(m/auth-code-url m)
; Get auth token
(def m (m/access-token! m "ABC..."))
; And save to ~/.mastodon-utils.edn, so you can use it later
(m/save! m)
; Load it on future runs with
(def m (m/load)); Fetch a local account (accounts is a lazy seq with automatic pagination)
(first (m/accounts m {:origin "local"}))
; Find all local accounts
(def locals (vec (m/accounts m {:origin "local", :limit 200})))
; And for each of those, find their followers on 4bear
(def bear->woof
(->> locals
(mapcat (fn [{:keys [username] :as local}]
(prn :local username)
(->> (m/followers m local {:limit 80})
(keep (fn [{:keys [acct] :as follower}]
(when (re-find #"@4bear.com$" acct)
[acct (str username "@woof.group")]))))))
vec))
; Vice-versa, locals who follow people on 4bear
(def woof->bear
(->> locals
(mapcat (fn [{:keys [username] :as local}]
(prn :local username)
(->> (m/following m local {:limit 80})
(keep (fn [{:keys [acct] :as following}]
(when (re-find #"@4bear.com$" acct)
[(str username "@woof.group") acct]))))))
vec))
; Spit those to JSON files
(require '[cheshire.core :as json])
(spit "bear->woof.json" (json/generate-string bear->woof))
(spit "woof->bear.json" (json/generate-string woof->bear))Iterating over [follower, following] pairs and re-following each. Run this at
the rails console (RAILS_ENV=production bin/rails console):
fs = FollowService.new
ufs = UnfollowService.new
rels = JSON.parse(File.read("/home/mastodon/woof->bear.json"))
rels.each do |rel|
follower, following = rel
follower_username, _ = follower.split('@')
following_username, following_domain = following.split('@')
follower_account = Account.find_by(username: follower_username, domain: nil)
following_account = Account.find_by(username: following_username, domain: following_domain)
if follower_account and following_account
sleep 5 # Not sure how much we should sleep here
puts "#{follower} -> #{following}"
ufs.call(follower_account, following_account)
fs.call(follower_account, following_account)
else
if follower_account
puts "Couldn't find following #{following_username}@#{following_domain}"
else
puts "Couldn't find follower #{follower_username}"
end
end
endCopyright (c) 2023 Kyle Kingsbury