diff options
| author | Matthew Fennell <matthew@fennell.dev> | 2025-12-22 00:13:29 +0000 |
|---|---|---|
| committer | Matthew Fennell <matthew@fennell.dev> | 2025-12-23 15:41:50 +0000 |
| commit | 225af43fd23c9b85cbbc8c83fce2e248ea13a7c2 (patch) | |
| tree | 7d0011696bc12b445a2fdfb31531dd0e019dee04 | |
Initial commit
Add LordGnome, a bot which syndicates new issues of Private Eye as well as new
releases of Page 94 to a specified Lemmy community.
The bot is designed to be run in a cronjob. It takes a config file, in which
you can provide login/community/instance details.
It then checks the Private Eye website, as well as the Page 94 RSS feed, for
the latest releases. It compares these to whatever was last posted. If the
latest release is newer than the latest post, it will create a new post linking
to that release.
In order to prevent the bot from posting the latest (and potentially outdated)
magazine issue and podcast straight away on the first run, you can also provide
a last posted issue. This gets used as a fallback if the bot cannot find any
previous posts.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rwxr-xr-x | Makefile | 3 | ||||
| -rwxr-xr-x | syndicate.sh | 108 |
3 files changed, 113 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c54d628 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.conf +install diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..211930f --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +install: syndicate.sh prod.conf + cp syndicate.sh prod.conf /opt/lord-gnome + touch install diff --git a/syndicate.sh b/syndicate.sh new file mode 100755 index 0000000..8ecc68b --- /dev/null +++ b/syndicate.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +set -u +set -o pipefail + +config_file=$1 + +community=$(grep "^community=" "${config_file}" | cut --delimiter = --fields 2) +instance=$(grep "^instance=" "${config_file}" | cut --delimiter = --fields 2) +token=$(grep "^token=" "${config_file}" | cut --delimiter = --fields 2) +username=$(grep "^username=" "${config_file}" | cut --delimiter = --fields 2) +first_posted_magazine=$(grep "^first_posted_magazine=" "${config_file}" | cut --delimiter = --fields 2) +first_posted_podcast=$(grep "^first_posted_podcast=" "${config_file}" | cut --delimiter = --fields 2) + +remote_post_history="${instance}/feeds/u/${username}.xml?sort=New" +local_post_history="/tmp/${username}.rss" + +function post { + local name="$1" + local url="$2" + local image="$3" + local body="$4" + + echo "Creating post \"${name}\" with url \"${url}\", thumbnail \"${image}\" and body \"${body}\" on instance ${instance}, community ${community}" + + community_id=$(curl --silent --fail "${instance}/api/v3/community?name=${community}" | jq --raw-output .community_view.community.id | grep --only-match "[[:digit:]]*") + curl --silent --fail-with-body --oauth2-bearer "${token}" --json "{\"name\":\"${name}\",\"community_id\":${community_id},\"url\":\"${url}\",\"body\":\"${body}\",\"language_id\":37,\"custom_thumbnail\":\"${image}\"}" "${instance}/api/v3/post" +} + +function syndicate_magazine { + echo "Syndicating magazine" + + template="Private Eye Issue" + last_posted_issue=$(xq --extract "/rss/channel/item/title[starts-with(., '${template} ')]" "${local_post_history}" | grep --only-match "[[:digit:]]*") + + if [ -z "${last_posted_issue}" ]; + then + echo "No previous post found: defaulting to issue ${first_posted_magazine}" + last_posted_issue="${first_posted_magazine}" + fi + + issue_to_try_posting=$((last_posted_issue + 1)) + + echo "Last posted issue was ${last_posted_issue}. Checking issue ${issue_to_try_posting}" + + url="https://www.private-eye.co.uk/covers/cover-${issue_to_try_posting}" + image="https://www.private-eye.co.uk/pictures/covers/full/${issue_to_try_posting}_big.jpg" + name="${template} ${issue_to_try_posting}" + + private_eye_response=$(curl --silent --output /dev/null --write-out "%{http_code}" "${image}") + + if [ "${private_eye_response}" != "200" ]; + then + echo "Received ${private_eye_response} for ${image}. Stop" + return + fi + + echo "Received ${private_eye_response} for ${image}. Intend to post it" + post "${name}" "${url}" "${image}" "" +} + +function syndicate_podcast { + echo "Syndicating podcast" + + remote_rss="https://audioboom.com/channels/5112392.rss" + local_rss="/tmp/page94.rss" + template="https://www.private-eye.co.uk/podcast/" + + last_posted_issue=$(xq --extract "/rss/channel/item/enclosure/@url[starts-with(., '${template}')]" "${local_post_history}" | cut -d / -f 5 | grep --only-match "[[:digit:]]*") + + if [ -z "${last_posted_issue}" ]; + then + echo "No previous post found: defaulting to issue ${first_posted_podcast}" + last_posted_issue="${first_posted_podcast}" + fi + + curl --silent "${remote_rss}" --output "${local_rss}" + + most_recently_published_issue=$(xq --extract "/rss/channel/item/itunes:episode" "${local_rss}" | grep --only-match "[[:digit:]]*") + + name=$(xq --extract "/rss/channel/item/itunes:title" "${local_rss}") + url="https://www.private-eye.co.uk/podcast/${most_recently_published_issue}" + image=$(xq --extract "/rss/channel/item/itunes:image/@href" "${local_rss}") + audio=$(xq --extract "/rss/channel/item/enclosure/@url" "${local_rss}" | cut -d '?' -f 1) + + rm -f "${local_rss}" + + echo "Last posted issue was ${last_posted_issue}. Last released issue was ${most_recently_published_issue}" + + if [ "${most_recently_published_issue}" -le "${last_posted_issue}" ]; + then + echo "No issue more recently published than ${most_recently_published_issue}. Stop" + return + fi + + echo "${most_recently_published_issue} is newer than ${last_posted_issue}. Intend to post it" + + post "${name}" "${url}" "${image}" "[Direct link to audio](${audio})" +} + +echo "Acting as ${username} on ${instance}" + +curl --silent "${remote_post_history}" --output "${local_post_history}" + +syndicate_magazine +syndicate_podcast + +rm -f "${local_post_history}" |
