Compare commits
No commits in common. "c400dc6f3867f7ef32a6b8e87bba24b3b985e813" and "af146f65e4e4e3237dc47752fde42e5d5d1ef25d" have entirely different histories.
c400dc6f38
...
af146f65e4
222
fedi2html.sh
222
fedi2html.sh
|
@ -6,7 +6,71 @@
|
||||||
# Date:
|
# Date:
|
||||||
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||||
|
|
||||||
|
# Return the ID of a post, based on its URL.
|
||||||
|
# url_id $url
|
||||||
|
url_id() {
|
||||||
|
local url="$1"
|
||||||
|
# Pleroma-style URLs: https://jam.xwx.moe/notice/Ac6PIZAP0ZzkMTYBBg
|
||||||
|
# Mastodon-style URLs: https://esperanto.masto.host/@minjo/111461250815264185
|
||||||
|
if echo "$url" | grep "/notice/" > /dev/null; then
|
||||||
|
echo "$url" \
|
||||||
|
| sed 's%.*/notice/%%'
|
||||||
|
else
|
||||||
|
echo "$url" \
|
||||||
|
| sed 's%.*/@[[:alnum:]]*/%%'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Return the server (including protocol) of a post, based on its URL.
|
||||||
|
# url_server $url
|
||||||
|
url_server() {
|
||||||
|
local url="$1"
|
||||||
|
local protocol="$(echo "$url" | grep --only-matching '[[:alnum:]]*://')"
|
||||||
|
printf "$protocol"
|
||||||
|
echo "$url" \
|
||||||
|
| sed 's%^'"$protocol"'%%' \
|
||||||
|
| sed 's%/.*%%'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Make a request to the /api/v1/statuses/:id/$request API endpoint.
|
||||||
|
# statuses_api_request $post_url $request
|
||||||
|
statuses_api_request() {
|
||||||
|
local post_url="$1"
|
||||||
|
local api_request="$2"
|
||||||
|
if test -n "$api_request"; then
|
||||||
|
api_request="/$api_request"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local id="$(url_id "$url")"
|
||||||
|
local server="$(url_server "$url")"
|
||||||
|
curl --location --header 'Accept: application/json,application/activity+json' \
|
||||||
|
"$server/api/v1/statuses/${id}${api_request}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Require the context-JSON of a post, by URL.
|
||||||
|
# fetch_post_context $url
|
||||||
|
fetch_post_context() {
|
||||||
|
local url="$1"
|
||||||
|
statuses_api_request "$url" "context"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Given a post URL, request its JSON.
|
||||||
|
# fetch_post $url
|
||||||
|
fetch_post() {
|
||||||
|
local url="$1"
|
||||||
|
statuses_api_request "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
POST_TEMPLATE='
|
POST_TEMPLATE='
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<link rel="stylesheet" href="comments.css" type="text/css">
|
||||||
<article class="comment">
|
<article class="comment">
|
||||||
<a class="user" href="$ACCOUNT_URL">
|
<a class="user" href="$ACCOUNT_URL">
|
||||||
<img class="avatar" src="$ACCOUNT_AVATAR">
|
<img class="avatar" src="$ACCOUNT_AVATAR">
|
||||||
|
@ -22,10 +86,9 @@ POST_TEMPLATE='
|
||||||
<div class="attachments">
|
<div class="attachments">
|
||||||
$POST_ATTACHMENTS
|
$POST_ATTACHMENTS
|
||||||
</div>
|
</div>
|
||||||
<div class="responses">
|
|
||||||
$POST_RESPONSES
|
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,48 +120,6 @@ ATTACH_IMAGE_TEMPLATE='
|
||||||
EMOJI_TEMPLATE='<img class="emoji" src="$EMOJI_URL" alt="$EMOJI_SHORTCODE" title="$EMOJI_SHORTCODE">'
|
EMOJI_TEMPLATE='<img class="emoji" src="$EMOJI_URL" alt="$EMOJI_SHORTCODE" title="$EMOJI_SHORTCODE">'
|
||||||
|
|
||||||
|
|
||||||
# Given a note’s JSON, render it as HTML.
|
|
||||||
# The most important part of the script!
|
|
||||||
# render_post $post_data $context_data $tree_level
|
|
||||||
render_post() {
|
|
||||||
local post_data="$1"
|
|
||||||
local responses_data="$2"
|
|
||||||
local POST_TREE_LEVEL="$3"
|
|
||||||
|
|
||||||
local ACCOUNT_URL="$(echo "$post_data" | jq -r .account.url)"
|
|
||||||
local ACCOUNT_ID="$(echo "$post_data" | jq -r .account.fqn)"
|
|
||||||
local ACCOUNT_NAME="$(echo "$post_data" | jq -r .account.display_name | replace_emojis "$(echo "$post_data" | jq -r '.account')")"
|
|
||||||
local ACCOUNT_AVATAR="$(echo "$post_data" | jq -r .account.avatar)"
|
|
||||||
local POST_URL="$(echo "$post_data" | jq -r .url)"
|
|
||||||
local POST_DATE="$(echo "$post_data" | jq -r .created_at)"
|
|
||||||
local POST_CONTENT="$(echo "$post_data" | jq -r .content | replace_emojis "$post_data")"
|
|
||||||
local POST_ATTACHMENTS="$(media_attachments "$post_data")"
|
|
||||||
local POST_RESPONSES="$(render_responses "$post_data" "$responses_data" "$POST_TREE_LEVEL")"
|
|
||||||
env_subst "$POST_TEMPLATE"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Render a post’s responses one-by-one and recursively.
|
|
||||||
# Each branch of the response tree will be rendered completely before proceeding
|
|
||||||
# to the next.
|
|
||||||
# render_responses $post_data $context_data $tree_level
|
|
||||||
render_responses() {
|
|
||||||
local post_data="$1"
|
|
||||||
local responses_data="$2"
|
|
||||||
local level="$3"
|
|
||||||
if test -z "$level"; then level=0; fi
|
|
||||||
|
|
||||||
local id="$(echo "$post_data" | jq -r '.id')"
|
|
||||||
local responses="$(echo "$responses_data" | grep "in_reply_to_id.*$id")"
|
|
||||||
local IFS="
|
|
||||||
"
|
|
||||||
|
|
||||||
for response in $responses; do
|
|
||||||
render_post "$response" "$responses_data" "$(expr "$level" + 1)"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Accepts a string over stdin; it will replace all emoji shortcodes along stdin
|
# Accepts a string over stdin; it will replace all emoji shortcodes along stdin
|
||||||
# input with appropriate <img> HTML, based on $EMOJI_TEMPLATE, and based on
|
# input with appropriate <img> HTML, based on $EMOJI_TEMPLATE, and based on
|
||||||
# a post’s JSON.
|
# a post’s JSON.
|
||||||
|
@ -145,73 +166,6 @@ media_attachments() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Pass a post’s context JSON along stdin; out comes the response_data in JSON.
|
|
||||||
# fetch_post_context $url | context_to_responses
|
|
||||||
context_to_responses() {
|
|
||||||
jq -cr '.descendants[] |= sort_by(.created_at)'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Make a request to the /api/v1/statuses/:id/$request API endpoint.
|
|
||||||
# statuses_api_request $post_url $request
|
|
||||||
statuses_api_request() {
|
|
||||||
local post_url="$1"
|
|
||||||
local api_request="$2"
|
|
||||||
if test -n "$api_request"; then
|
|
||||||
api_request="/$api_request"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local id="$(url_id "$url")"
|
|
||||||
local server="$(url_server "$url")"
|
|
||||||
curl --location --header 'Accept: application/json,application/activity+json' \
|
|
||||||
"$server/api/v1/statuses/${id}${api_request}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Require the context-JSON of a post, by URL.
|
|
||||||
# fetch_post_context $url
|
|
||||||
fetch_post_context() {
|
|
||||||
local url="$1"
|
|
||||||
statuses_api_request "$url" "context"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Given a post URL, request its JSON.
|
|
||||||
# fetch_post $url
|
|
||||||
fetch_post() {
|
|
||||||
local url="$1"
|
|
||||||
statuses_api_request "$url"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Return the ID of a post, based on its URL.
|
|
||||||
# url_id $url
|
|
||||||
url_id() {
|
|
||||||
local url="$1"
|
|
||||||
# Pleroma-style URLs: https://jam.xwx.moe/notice/Ac6PIZAP0ZzkMTYBBg
|
|
||||||
# Mastodon-style URLs: https://esperanto.masto.host/@minjo/111461250815264185
|
|
||||||
if echo "$url" | grep "/notice/" > /dev/null; then
|
|
||||||
echo "$url" \
|
|
||||||
| sed 's%.*/notice/%%'
|
|
||||||
else
|
|
||||||
echo "$url" \
|
|
||||||
| sed 's%.*/@[[:alnum:]]*/%%'
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Return the server (including protocol) of a post, based on its URL.
|
|
||||||
# url_server $url
|
|
||||||
url_server() {
|
|
||||||
local url="$1"
|
|
||||||
local protocol="$(echo "$url" | grep --only-matching '[[:alnum:]]*://')"
|
|
||||||
printf "$protocol"
|
|
||||||
echo "$url" \
|
|
||||||
| sed 's%^'"$protocol"'%%' \
|
|
||||||
| sed 's%/.*%%'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Sanitize a template-string.
|
# Sanitize a template-string.
|
||||||
# AKA, escape quotation-marks.
|
# AKA, escape quotation-marks.
|
||||||
# prep_template $template
|
# prep_template $template
|
||||||
|
@ -235,34 +189,18 @@ env_subst() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
usage() {
|
# Given a note’s JSON, render it as HTML.
|
||||||
echo "usage: $(basename "$0") POST_URL"
|
# The most important part of the script!
|
||||||
echo
|
# render_post $post_data
|
||||||
echo "$(basename "$0") does exactly what it says on the tin: It formats"
|
render_post() {
|
||||||
echo "a fediverse post (and its replies) into simple-and-embeddable HTML."
|
local post_data="$1"
|
||||||
echo
|
local ACCOUNT_URL="$(echo "$post_data" | jq -r .account.url)"
|
||||||
echo "It works with posts from any server that supports Mastodon’s API,"
|
local ACCOUNT_ID="$(echo "$post_data" | jq -r .account.fqn)"
|
||||||
echo "including Pleroma, Akkoma, Glitch, etc."
|
local ACCOUNT_NAME="$(echo "$post_data" | jq -r .account.display_name | replace_emojis "$post_data")"
|
||||||
echo
|
local ACCOUNT_AVATAR="$(echo "$post_data" | jq -r .account.avatar)"
|
||||||
echo "Notably, it supports post-atachments and custom-emoji. Keep in mind"
|
local POST_URL="$(echo "$post_data" | jq -r .url)"
|
||||||
echo "that images are all fetched from remote sources. It is recommended,"
|
local POST_DATE="$(echo "$post_data" | jq -r .created_at)"
|
||||||
echo "if privacy or total archival, is a concern, to use wget(1)’s --mirror"
|
local POST_CONTENT="$(echo "$post_data" | jq -r .content | replace_emojis "$post_data")"
|
||||||
echo "(or something like it) to fetch even these foreign files."
|
local POST_ATTACHMENTS="$(media_attachments "$post_data")"
|
||||||
|
env_subst "$POST_TEMPLATE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
URL="$1"
|
|
||||||
|
|
||||||
if test -z "$URL"; then
|
|
||||||
usage 1>&2
|
|
||||||
exit 2
|
|
||||||
elif test "$URL" = "-h" -o "$URL" = "--help"; then
|
|
||||||
usage
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
POST="$(fetch_post "$URL")"
|
|
||||||
RESPONSES="$(fetch_post_context "$URL" | context_to_responses)"
|
|
||||||
render_post "$POST" "$RESPONSES" 0
|
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue