Init
This commit is contained in:
commit
f5db6ddb21
|
@ -0,0 +1,8 @@
|
||||||
|
# PLEROMA-MIGRATOR
|
||||||
|
|
||||||
|
A horrific group of scripts that can help you copy your fedi posts to a new account. If the server's Pleroma and you have direct access to the database, you can even preserve post dates.
|
||||||
|
|
||||||
|
See invididual script's --help messages, with pleroma-migrator.sh being the one that combines them all for you.
|
||||||
|
|
||||||
|
|
||||||
|
License is CC0, ofc.
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
# Name: fedi-archive.sh
|
||||||
|
# Desc: Downloads (most) posts from a fedi account in parseable format.
|
||||||
|
# Reqs: jq, curl
|
||||||
|
# Date: 2023-05-06
|
||||||
|
# Auth: @jadedctrl@jam.xwx.moe
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
|
||||||
|
|
||||||
|
# Given a JSON file containing /api/v1/accounts/$user/statuses output,
|
||||||
|
# output a post at the given index from the file like so:
|
||||||
|
# FAVOURITE_COUNT DATE_POSTED POST_ID POST_URL
|
||||||
|
# [media: URL DESCRIPTION]
|
||||||
|
# [media: URL DESCRIPTION]
|
||||||
|
# [CONTENT…]
|
||||||
|
# [] meaning "optional". There might be an arbitrary amount of Media: lines.
|
||||||
|
output_post_of_index() {
|
||||||
|
local index="$1"
|
||||||
|
local file="$2"
|
||||||
|
jq -r --arg INDEX "$index" \
|
||||||
|
'.[$INDEX|tonumber] | "\(.favourites_count) \(.created_at) \(.id) \(.url)
|
||||||
|
\(.media_attachments[] | "media: " + .url + " " + .description)
|
||||||
|
\(.content)"' \
|
||||||
|
< "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Fetch a list of a user's statuses, given their server and username.
|
||||||
|
# `max_id` can be passed to return only messages older than said message.
|
||||||
|
fetch_page() {
|
||||||
|
local server="$1"; local user="$2"; local max_id="$3"
|
||||||
|
local url="https://$server/api/v1/accounts/$user/statuses?exclude_replies=true&exclude_reblogs=true&limit=40"
|
||||||
|
if test -n "$max_id"; then
|
||||||
|
url="${url}&max_id=${max_id}"
|
||||||
|
fi
|
||||||
|
curl "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Given a JSON file containing /api/v1/accounts/$user/statuses output,
|
||||||
|
# output each status into an individual file of the format of
|
||||||
|
# output_post_of_index(); see its comment for more information.
|
||||||
|
# Prints the ID of the last post of the file.
|
||||||
|
archive_posts() {
|
||||||
|
local json_file="$1"
|
||||||
|
local prefix="$2"
|
||||||
|
|
||||||
|
local post_file="$prefix-$i"
|
||||||
|
local last_post_file=""
|
||||||
|
local i="0"
|
||||||
|
|
||||||
|
local output_ret=0
|
||||||
|
while test "$output_ret" -eq 0; do
|
||||||
|
post_file="$prefix-$i"
|
||||||
|
echo "$post_file" 1>&2
|
||||||
|
output_post_of_index "$i" "$json_file" \
|
||||||
|
> "$post_file"
|
||||||
|
output_ret="$?"
|
||||||
|
|
||||||
|
if test -e "$post_file" -a -n "$(cat "$post_file")"; then
|
||||||
|
last_post_file="$post_file"
|
||||||
|
elif test -e "$post_file"; then
|
||||||
|
rm "$post_file"
|
||||||
|
fi
|
||||||
|
i="$(echo "$i + 1" | bc)"
|
||||||
|
done
|
||||||
|
|
||||||
|
head -1 "$last_post_file" \
|
||||||
|
| awk '{print $3}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Fetch all posts for the given user at given server.
|
||||||
|
archive_all_posts() {
|
||||||
|
local server="$1"
|
||||||
|
local username="$2"
|
||||||
|
local temp="$(mktemp)"
|
||||||
|
|
||||||
|
fetch_page "$server" "$username" \
|
||||||
|
> "$temp"
|
||||||
|
|
||||||
|
local page="1"
|
||||||
|
local next_id="$(archive_posts "$temp" "$page")"
|
||||||
|
while test -n "$next_id"; do
|
||||||
|
page="$(echo "$page + 1" | bc)"
|
||||||
|
echo "$next_id - $page…"
|
||||||
|
fetch_page "$server" "$username" "$next_id" \
|
||||||
|
> "$temp"
|
||||||
|
next_id="$(archive_posts "$temp" "$page")"
|
||||||
|
done
|
||||||
|
|
||||||
|
rm "$temp"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $(basename $0) username server" 1>&2
|
||||||
|
echo "" 1>&2
|
||||||
|
echo "$(basename $0) is a script that fetches all of a user's Mastodon/Pleroma" 1>&2
|
||||||
|
echo "posts for archival purposes." 1>&2
|
||||||
|
echo "Mainly for use with fedi-post.sh or pleroma-migrate.sh." 1>&2
|
||||||
|
exit 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USERNAME="$1"
|
||||||
|
SERVER="$2"
|
||||||
|
if test -z "$USERNAME" -o -z "$SERVER" -o "$1" = "-h" -o "$1" = "--help"; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
archive_all_posts "$SERVER" "$USERNAME"
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
# Name: fedi-post.sh
|
||||||
|
# Desc: Makes a new post using an post archived with fedi-archive.sh.
|
||||||
|
# Reqs: curl, jq
|
||||||
|
# Date: 2023-05-06
|
||||||
|
# Auth: @jadedctrl@jam.xwx.moe
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
|
||||||
|
|
||||||
|
# Outputs only the status text of a user's post, with a link to the original
|
||||||
|
# appended to the bottom.
|
||||||
|
file_status() {
|
||||||
|
local file="$1"
|
||||||
|
local id="$(head -1 "$file" | awk '{print $3}')"
|
||||||
|
local url="$(head -1 "$file" | awk '{print $4}')"
|
||||||
|
tail +2 "$file" \
|
||||||
|
| grep -v ^media: \
|
||||||
|
| grep -v "$id" \
|
||||||
|
| sed 's%\\%\\\\%g' \
|
||||||
|
| sed 's%"%\\"%g' \
|
||||||
|
| sed -z 's%\n%\\n%g'
|
||||||
|
if test -n "$url"; then
|
||||||
|
printf '<br/>[<a href=\\"%s\\">Originala afiŝo</a>]\\n' "$url"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Turns a space-delimited list of uploaded media-IDs into a JSON array.
|
||||||
|
media_json() {
|
||||||
|
local ids="$1"
|
||||||
|
echo "$ids" \
|
||||||
|
| sed 's%^ %%' \
|
||||||
|
| sed 's% $%%' \
|
||||||
|
| sed 's%^%["%' \
|
||||||
|
| sed 's% %","%g' \
|
||||||
|
| sed 's%$%"]%'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Takes a post message and JSON-array of uploaded media-IDs, outputting
|
||||||
|
# the post's appropriate JSON.
|
||||||
|
post_json() {
|
||||||
|
local message="$1"
|
||||||
|
local media_ids="$2"
|
||||||
|
printf '{ "content_type": "text/html", "visibility": "unlisted"'
|
||||||
|
if test -n "$media_ids"; then
|
||||||
|
printf ', "media_ids": %s, ' "$media_ids"
|
||||||
|
fi
|
||||||
|
printf '"status": "%s" }\n' "$message"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Upload a file to the fedi server with the given description.
|
||||||
|
post_media() {
|
||||||
|
local media_file="$1"
|
||||||
|
local description="$2"
|
||||||
|
|
||||||
|
curl --request POST \
|
||||||
|
--header "Authorization: Bearer $FEDI_AUTH" \
|
||||||
|
--header "Content-Type: multipart/form-data" \
|
||||||
|
--form "file=@$media_file" \
|
||||||
|
--form "description=$description" \
|
||||||
|
"https://jam.xwx.moe/api/v1/media"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Post a status of the given message and JSON-array of uploaded media-IDs.
|
||||||
|
post_status() {
|
||||||
|
local message="$1"
|
||||||
|
local media_ids="$2"
|
||||||
|
|
||||||
|
curl --request POST \
|
||||||
|
--header "Authorization: Bearer $FEDI_AUTH" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--data "$(post_json "$message" "$media_ids")" \
|
||||||
|
"https://jam.xwx.moe/api/v1/statuses"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Take a post file generated by fedi-archive.sh, and post it.
|
||||||
|
# Just *do it*. Why not? What're you scared of? Huh, huh? Huh?!
|
||||||
|
post_archived_post() {
|
||||||
|
local file="$1"
|
||||||
|
IFS="
|
||||||
|
"
|
||||||
|
local ids=""
|
||||||
|
for media in $(grep "^media: " "$file"); do
|
||||||
|
local url="$(echo "$media" | awk '{print $2}')"
|
||||||
|
local desc="$(echo "$media" | awk '{ $1=$2=""; print}' | sed 's%^ %%')"
|
||||||
|
|
||||||
|
curl -o "$(basename "$url")" "$url"
|
||||||
|
ids="$ids $(post_media "$(basename "$url")" "$desc" | jq -r '.id')"
|
||||||
|
rm "$(basename "$url")"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s ' "$(head -1 "$file" | awk '{print $1, $2}')"
|
||||||
|
post_status "$(file_status "$file")" "$(media_json "$ids")" \
|
||||||
|
| jq -r .uri
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $(basename "$0") ARCHIVED-POST" 1>&2
|
||||||
|
echo "" 1>&2
|
||||||
|
echo "Will post a new status with the same text and attachments as one" 1>&2
|
||||||
|
echo "from an archived post (in fedi-archive.sh format)." 1>&2
|
||||||
|
echo "Your authorization key must be borrowed from your web-browser and" 1>&2
|
||||||
|
echo 'placed in the $FEDI_AUTH environment variable.' 1>&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if test -z "$FEDI_AUTH"; then
|
||||||
|
echo 'You need to set the environment variable $FEDI_AUTH!' 1>&2
|
||||||
|
echo 'You can find your auth key by examining the "Authentication: Bearer" header' 1>&2
|
||||||
|
echo "used in requests by your server's web-client." 1>&2
|
||||||
|
echo 'In Firefox, F12→Network.' 1>&2
|
||||||
|
echo "" 1>&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILE="$1"
|
||||||
|
if test -z "$FILE" -o "$1" = "-h" -o "$1" = "--help"; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
post_archived_post "$FILE"
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
# Name: pleroma-migrate.sh
|
||||||
|
# Desc: Downloads posts from a pleroma account on a foreign server, to post them
|
||||||
|
# on your new server. Then directly edits Pleroma's database to match the
|
||||||
|
# new posts' dates with the original posts' dates.
|
||||||
|
# Reqs: fedi-post.sh, fedi-archive.sh, pleroma-redate.sh
|
||||||
|
# Date: 2023-05-06
|
||||||
|
# Auth: @jadedctrl@jam.xwx.moe
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $(basename "$0") USERNAME OLD-SERVER" 1>&2
|
||||||
|
echo "" 1>&2
|
||||||
|
echo "Will archive all posts from your old account (fedi-archive.sh)," 1>&2
|
||||||
|
echo "post them to your new one (fedi-post.sh), and then modify Pleroma's" 1>&2
|
||||||
|
echo "database to match the copy-posts' creation dates with the original posts." 1>&2
|
||||||
|
echo 'The env variable $FEDI_AUTH must contain your authentication key from your browser.' 1>&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USERNAME="$1"
|
||||||
|
SERVER="$2"
|
||||||
|
if test -z "$USERNAME" -o -z "$SERVER" -o "$1" = "-h" -o "$1" = "--help"; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
mkdir archive
|
||||||
|
cd archive/
|
||||||
|
sh ../fedi-archive.sh "$USERNAME" "$SERVER"
|
||||||
|
|
||||||
|
|
||||||
|
for file in ./*; do
|
||||||
|
sh ../fedi-post.sh "$file" \
|
||||||
|
>> imports-data.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
IFS="
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "It's time to re-date your posts!"
|
||||||
|
echo "Are you suuuuuuuuuuuuure you wanna risk your database? Do a backup, first!"
|
||||||
|
echo "^C now, before you risk it! Hit ENTER, if you're sure."
|
||||||
|
read
|
||||||
|
sleep 5
|
||||||
|
echo "Alright, then, you brave fellow! I'm touched you trust me so much, though :o"
|
||||||
|
|
||||||
|
for line in $(cat imports-data.txt); do
|
||||||
|
sh ../pleroma-redate.sh "$(echo "$line" | awk '{print $3}')" \
|
||||||
|
"$(echo "$line" | awk '{print $2}')"
|
||||||
|
done
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
# Name: pleroma-redate.sh
|
||||||
|
# Desc: Changes the creation-date of a post in Pleroma's database.
|
||||||
|
# Reqs: psql, sudo
|
||||||
|
# Date: 2023-05-06
|
||||||
|
# Auth: @jadedctrl@jam.xwx.moe
|
||||||
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "usage: $(basename "$0") URL NEW-DATE" 1>&2
|
||||||
|
echo "" 1>&2
|
||||||
|
echo "Will change the stored date of an archived fedi post" 1>&2
|
||||||
|
echo "in fedi-archive.sh format, by editing directly Pleroma's database." 1>&2
|
||||||
|
echo "URL ought be the direct /object/ URL, and NEW-DATE ought be ISO-8601" 1>&2
|
||||||
|
echo "up to the milisecond." 1>&2
|
||||||
|
echo "Assumes you can sudo as 'postgres' and the database is called 'pleroma'." 1>&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
URL="$1"
|
||||||
|
NEWDATE="$2"
|
||||||
|
if test -z "$URL" -o -z "$NEWDATE" -o "$1" = "-h" -o "$1" = "--help"; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
sudo -u postgres psql --dbname=pleroma \
|
||||||
|
-c "UPDATE objects
|
||||||
|
SET data = jsonb_set(data, '{published}', '\"$NEWDATE\"'::jsonb, false)
|
||||||
|
WHERE CAST(data::json->'id' AS TEXT) = '\"$URL\"' ;"
|
Ŝarĝante…
Reference in New Issue