summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Fennell <matthew@fennell.dev>2025-12-22 00:13:29 +0000
committerMatthew Fennell <matthew@fennell.dev>2025-12-23 15:41:50 +0000
commit225af43fd23c9b85cbbc8c83fce2e248ea13a7c2 (patch)
tree7d0011696bc12b445a2fdfb31531dd0e019dee04
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--.gitignore2
-rwxr-xr-xMakefile3
-rwxr-xr-xsyndicate.sh108
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}" "![](${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}"