diff --git a/tools/bin/tp b/tools/bin/tp
new file mode 100755
index 0000000..8b72d9d
--- /dev/null
+++ b/tools/bin/tp
@@ -0,0 +1,116 @@
+#!/usr/bin/env sh
+#
+# tp: "Telodendria Patch"
+#
+# This script is used to manage the patch queue.
+
+if [ -f "$(pwd)/.env" ]; then
+ . "$(pwd)/.env"
+fi
+
+if [ -z "$TELODENDRIA_PUB" ]; then
+ echo "TELODENDRIA_PUB not set."
+ exit 1
+fi
+
+TP_DIR="$TELODENDRIA_PUB/patches"
+
+CURL="curl -s"
+
+if [ ! -d "$TP_DIR" ]; then
+ echo "$TP_DIR does not exist."
+ exit 1
+fi
+
+matrix_send() {
+ msg="$1"
+ if [ ! -z "$msg" ]; then
+ jq --null-input \
+ --arg body "$msg" \
+ --arg formatted_body "$msg" \
+ --arg format "org.matrix.custom.html" \
+ --arg msgtype "m.text" \
+ '{"body":$body,"formatted_body":$formatted_body,"format":$format,"msgtype":$msgtype}' |
+ $CURL -X PUT -d @- "$HOMESERVER/client/v3/rooms/$ROOM_ID/send/m.room.message/$(date +%s)?access_token=$ACCESS_TOKEN"
+ fi
+}
+
+matrix_get_files() {
+ $CURL "$HOMESERVER/client/v3/sync?access_token=$ACCESS_TOKEN" |
+ jq ".rooms.join.\"$ROOM_ID\".timeline.events[] | select(.type==\"m.room.message\") | .content | select(.msgtype==\"m.file\") | [.body,.info.size,.url] | @tsv" |
+ cut -d '"' -f 2 |
+ sed 's/\\t/,/g'
+}
+
+case "$1" in
+ "ingress")
+ matrix_get_files | while IFS="," read -r file size url; do
+ server=$(echo "$url" | cut -d '/' -f 3)
+ id=$(echo "$url" | cut -d '/' -f 4)
+ ext=$(echo "$file" | rev | cut -d '.' -f 1 | rev)
+
+ if [ "$ext" != "patch" ] || [ "$size" -gt "$MAX_SIZE" ] || [ -f "$TP_DIR/ingress/$id.patch" ]; then
+ continue
+ fi
+
+ $CURL -o "$TP_DIR/ingress/$id.patch" "$HOMESERVER/media/v3/download/$server/$id"
+
+ count=$(cat "$TP_DIR/count.txt")
+ count=$((count + 1))
+ cp "$TP_DIR/ingress/$id.patch" "$TP_DIR/p/$count.patch"
+ (
+ cd "$TP_DIR/queued"
+ ln -s "../p/$count.patch" "$count.patch"
+ )
+
+ echo "$count" > "$TP_DIR/count.txt"
+
+ matrix_send "Queued $file
as #$count"
+ done
+ ;;
+ "queue")
+ find "$TP_DIR/queued" -name '*.patch' | while IFS= read -r patch; do
+ n=$(basename "$patch" .patch)
+ echo "Patch #$n:"
+ head -n3 "$patch"
+ echo
+ done
+ ;;
+ "view")
+ if [ -f "$TP_DIR/queued/$2.patch" ]; then
+ less "$TP_DIR/queued/$2.patch"
+ else
+ echo "Patch #$2 doesn't exist in the queue."
+ exit 1
+ fi
+ ;;
+ "apply")
+ if [ -f "$TP_DIR/queued/$2.patch" ]; then
+ patch < "$TP_DIR/queued/$2.patch"
+ else
+ echo "Patch #$2 doesn't exist in the queue."
+ exit 1
+ fi
+ ;;
+ "reverse")
+ if [ -f "$TP_DIR/queued/$2.patch" ]; then
+ patch -R < "$TP_DIR/queued/$2.patch"
+ else
+ echo "Patch #$2 doesn't exist in the queue."
+ exit 1
+ fi
+ ;;
+ "accept"|"reject")
+ if [ -f "$TP_DIR/queued/$2.patch" ]; then
+ mv "$TP_DIR/queued/$2.patch" "$TP_DIR/${1}ed/$2.patch"
+ matrix_send "Patch #$2 was marked as ${1}ed."
+ else
+ echo "Patch #$2 doesn't exist in the queue."
+ exit 1
+ fi
+ ;;
+ *)
+ echo "No action specified."
+ exit 1
+ ;;
+esac