2024-03-28 00:43:34 -05:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
|
|
|
|
# Name: fedi2html.sh
|
|
|
|
|
# Desc:
|
|
|
|
|
# Reqs:
|
|
|
|
|
# Date:
|
|
|
|
|
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Return the ID of a post, based on its URL.
|
|
|
|
|
# url_id $url
|
2024-03-28 00:43:34 -05:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Return the server (including protocol) of a post, based on its URL.
|
|
|
|
|
# url_server $url
|
2024-03-28 00:43:34 -05:00
|
|
|
|
url_server() {
|
|
|
|
|
local url="$1"
|
|
|
|
|
local protocol="$(echo "$url" | grep --only-matching '[[:alnum:]]*://')"
|
|
|
|
|
printf "$protocol"
|
|
|
|
|
echo "$url" \
|
|
|
|
|
| sed 's%^'"$protocol"'%%' \
|
|
|
|
|
| sed 's%/.*%%'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Make a request to the /api/v1/statuses/:id/$request API endpoint.
|
|
|
|
|
# statuses_api_request $post_url $request
|
2024-03-28 00:43:34 -05:00
|
|
|
|
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}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Require the context-JSON of a post, by URL.
|
|
|
|
|
# fetch_post_context $url
|
2024-03-28 00:43:34 -05:00
|
|
|
|
fetch_post_context() {
|
|
|
|
|
local url="$1"
|
|
|
|
|
statuses_api_request "$url" "context"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Given a post URL, request its JSON.
|
|
|
|
|
# fetch_post $url
|
2024-03-28 00:43:34 -05:00
|
|
|
|
fetch_post() {
|
|
|
|
|
local url="$1"
|
|
|
|
|
statuses_api_request "$url"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
POST_TEMPLATE='
|
2024-03-28 01:53:53 -05:00
|
|
|
|
<article class="comment">
|
|
|
|
|
<a class="user" href="$ACCOUNT_URL">
|
|
|
|
|
<img class="avatar" src="$ACCOUNT_AVATAR">
|
|
|
|
|
<strong class="username">$ACCOUNT_NAME</strong>
|
|
|
|
|
<em class="useraddress">$ACCOUNT_ID</em>
|
2024-03-28 00:43:34 -05:00
|
|
|
|
</a>
|
2024-03-28 01:53:53 -05:00
|
|
|
|
<a class="address" href="$POST_URL">
|
|
|
|
|
<time title="$POST_DATE">$(date --date=$POST_DATE)</time>
|
2024-03-28 00:43:34 -05:00
|
|
|
|
</a>
|
2024-03-28 01:53:53 -05:00
|
|
|
|
<section>
|
2024-03-28 00:43:34 -05:00
|
|
|
|
$POST_CONTENT
|
2024-03-28 01:53:53 -05:00
|
|
|
|
</section>
|
|
|
|
|
<div class="attachments">
|
|
|
|
|
$POST_ATTACHMENTS
|
2024-03-28 00:43:34 -05:00
|
|
|
|
</div>
|
2024-03-28 18:23:54 -05:00
|
|
|
|
<b>$tree_level</b>¤
|
|
|
|
|
<div class="responses">
|
|
|
|
|
$POST_RESPONSES
|
|
|
|
|
</div>
|
2024-03-28 01:53:53 -05:00
|
|
|
|
</article>
|
2024-03-28 00:43:34 -05:00
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
|
2024-03-28 01:53:53 -05:00
|
|
|
|
ATTACH_TEMPLATE='
|
|
|
|
|
<a href="$ATTACH_URL">
|
|
|
|
|
<div class="attachment">
|
|
|
|
|
<strong>$ATTACH_NAME</strong>
|
|
|
|
|
$ATTACH_DESC
|
|
|
|
|
</div>
|
|
|
|
|
</a>'
|
|
|
|
|
|
|
|
|
|
ATTACH_IMAGE_TEMPLATE='
|
|
|
|
|
<figure>
|
|
|
|
|
<a href="$ATTACH_URL">
|
|
|
|
|
<img class="attachment" src="$ATTACH_URL" alt="$ATTACH_DESC" title="$ATTACH_DESC">
|
|
|
|
|
</a>
|
|
|
|
|
<figcaption>
|
|
|
|
|
<a href="$ATTACH_URL"><b>$ATTACH_NAME</b></a><br>
|
|
|
|
|
$(if test $(echo "$ATTACH_DESC" | wc -c) -gt 52; then
|
|
|
|
|
echo $ATTACH_DESC | head -c 53 | sed 's%[[:space:]]*$%%'
|
|
|
|
|
echo …
|
|
|
|
|
else
|
|
|
|
|
echo $ATTACH_DESC
|
|
|
|
|
fi)
|
|
|
|
|
</figcaption>
|
|
|
|
|
</figure>
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
EMOJI_TEMPLATE='<img class="emoji" src="$EMOJI_URL" alt="$EMOJI_SHORTCODE" title="$EMOJI_SHORTCODE">'
|
2024-03-28 01:00:23 -05:00
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
# a post’s JSON.
|
|
|
|
|
# echo ":blobcat:" | replace_emojis $post_data
|
2024-03-28 00:43:34 -05:00
|
|
|
|
replace_emojis() {
|
|
|
|
|
local post_data="$1"
|
2024-03-28 00:49:26 -05:00
|
|
|
|
local emojis="$(echo "$post_data" | jq -r '.emojis[]|(.url + "\t" + .shortcode)')"
|
|
|
|
|
local temp="$(mktemp)"
|
|
|
|
|
local IFS="
|
|
|
|
|
"
|
|
|
|
|
cat > "$temp"
|
|
|
|
|
|
|
|
|
|
for line in $emojis; do
|
2024-03-28 01:53:53 -05:00
|
|
|
|
local EMOJI_URL="$(echo "$line" | awk -F'\t' '{print $1}')"
|
|
|
|
|
local EMOJI_SHORTCODE="$(echo "$line" | awk -F'\t' '{print $2}')"
|
2024-03-28 01:00:23 -05:00
|
|
|
|
local value="$(env_subst "$EMOJI_TEMPLATE")"
|
|
|
|
|
sed -i "s%:${EMOJI_SHORTCODE}:%${value}%g" "$temp"
|
2024-03-28 00:49:26 -05:00
|
|
|
|
done
|
|
|
|
|
cat "$temp"
|
|
|
|
|
rm "$temp"
|
2024-03-28 00:43:34 -05:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:53:53 -05:00
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
# Given a post’s JSON data, return the appropriate HTML corresponding to its
|
|
|
|
|
# media attachments, if any. Will return an empty string if none.
|
|
|
|
|
# media_attachments $post_data
|
2024-03-28 01:53:53 -05:00
|
|
|
|
media_attachments() {
|
|
|
|
|
local post_data="$1"
|
|
|
|
|
local attachments="$(echo "$post_data" | jq -r '.media_attachments[]|(.type + "\t" + .url + "\t" + .description + "\t" + .preview_url)')"
|
|
|
|
|
local IFS="
|
|
|
|
|
"
|
|
|
|
|
for line in $attachments; do
|
|
|
|
|
local ATTACH_TYPE="$(echo "$line" | awk -F'\t' '{print $1}')"
|
|
|
|
|
local ATTACH_URL="$(echo "$line" | awk -F'\t' '{print $2}')"
|
|
|
|
|
local ATTACH_DESC="$(echo "$line" | awk -F'\t' '{print $3}')"
|
|
|
|
|
local ATTACH_PREVIEW="$(echo "$line" | awk -F'\t' '{print $4}')"
|
|
|
|
|
local ATTACH_NAME="$(basename "$ATTACH_URL")"
|
|
|
|
|
if test "$ATTACH_TYPE" = "image"; then
|
|
|
|
|
env_subst "$ATTACH_IMAGE_TEMPLATE"
|
|
|
|
|
else
|
|
|
|
|
env_subst "$ATTACH_TEMPLATE"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
|
|
|
|
|
# Sanitize a template-string.
|
|
|
|
|
# AKA, escape quotation-marks.
|
|
|
|
|
# prep_template $template
|
2024-03-28 01:00:23 -05:00
|
|
|
|
prep_template() {
|
|
|
|
|
local template="$1"
|
|
|
|
|
echo "$template" \
|
|
|
|
|
| sed 's%\"%\\\"%g'
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
|
|
|
|
|
# Rough replacement for gettext’s envsubst. Safe!
|
|
|
|
|
# This will evaluate a string’s shell variables (somewhat) safely
|
|
|
|
|
# Probably not good for general use — but for our purposes, it only subsitutes
|
|
|
|
|
# along the “first level.” So environment variables are replaced, but those
|
|
|
|
|
# variables’ contents (AKA, post contents) aren’t evaluated.
|
|
|
|
|
# env_subst $template
|
|
|
|
|
# env_subst "$SHELL" → "/bin/sh"
|
2024-03-28 01:00:23 -05:00
|
|
|
|
env_subst() {
|
|
|
|
|
local template="$1"
|
|
|
|
|
eval "echo \"$(prep_template "$template")\""
|
|
|
|
|
}
|
2024-03-28 00:43:34 -05:00
|
|
|
|
|
2024-03-28 02:13:28 -05:00
|
|
|
|
|
|
|
|
|
# Given a note’s JSON, render it as HTML.
|
|
|
|
|
# The most important part of the script!
|
2024-03-28 18:23:54 -05:00
|
|
|
|
# render_post $post_data $context_data $tree_level
|
2024-03-28 00:43:34 -05:00
|
|
|
|
render_post() {
|
|
|
|
|
local post_data="$1"
|
2024-03-28 18:23:54 -05:00
|
|
|
|
local context_data="$2"
|
|
|
|
|
local POST_TREE_LEVEL="$3"
|
|
|
|
|
|
2024-03-28 00:43:34 -05:00
|
|
|
|
local ACCOUNT_URL="$(echo "$post_data" | jq -r .account.url)"
|
|
|
|
|
local ACCOUNT_ID="$(echo "$post_data" | jq -r .account.fqn)"
|
2024-03-28 18:23:54 -05:00
|
|
|
|
local ACCOUNT_NAME="$(echo "$post_data" | jq -r .account.display_name | replace_emojis "$(echo "$post_data" | jq -r '.account')")"
|
2024-03-28 00:43:34 -05:00
|
|
|
|
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")"
|
2024-03-28 01:53:53 -05:00
|
|
|
|
local POST_ATTACHMENTS="$(media_attachments "$post_data")"
|
2024-03-28 18:23:54 -05:00
|
|
|
|
local POST_RESPONSES="$(render_context "$post_data" "$context_data" "$tree_level")"
|
2024-03-28 01:00:23 -05:00
|
|
|
|
env_subst "$POST_TEMPLATE"
|
2024-03-28 00:43:34 -05:00
|
|
|
|
}
|
2024-03-28 18:23:54 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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_context $post_data $context_data $tree_level
|
|
|
|
|
render_context() {
|
|
|
|
|
|
|
|
|
|
local post_data="$1"
|
|
|
|
|
local context_data="$2"
|
|
|
|
|
local level="$3"
|
|
|
|
|
if test -z "$level"; then level=0; fi
|
|
|
|
|
|
|
|
|
|
local id="$(echo "$post_data" | jq -r '.id')"
|
|
|
|
|
local responses="$(echo "$context_data" | jq -cr '.descendants[]' | grep "in_reply_to_id.*$id")"
|
|
|
|
|
local IFS="
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
for response in $responses; do
|
|
|
|
|
render_post "$response" "$context_data" "$(expr "$level" + 1)"
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|