#!/bin/bash # Checks if cinnyapp/cinny has published a new stable release; notifies Matrix if so. # Runs daily at noon via /etc/cron.d/cinny-upstream-check # Requires /etc/cinny-monitor.env: # MATRIX_TOKEN, MATRIX_SERVER, MATRIX_ROOM, MATRIX_PING_USER set -euo pipefail ENV_FILE="/etc/cinny-monitor.env" STATE_FILE="/var/lib/cinny-monitor/last-upstream-tag" LOG="/var/log/cinny-monitor.log" exec >> "$LOG" 2>&1 echo "=== $(date) === Cinny upstream release check ===" [ -f "$ENV_FILE" ] || { echo "ERROR: $ENV_FILE missing"; exit 1; } set -a # shellcheck source=/dev/null source "$ENV_FILE" set +a mkdir -p "$(dirname "$STATE_FILE")" API_RESPONSE=$(curl -sf \ -H "Accept: application/vnd.github.v3+json" \ -H "User-Agent: lotus-cinny-monitor/1.0" \ "https://api.github.com/repos/cinnyapp/cinny/releases/latest") || { echo "ERROR: GitHub API request failed" exit 1 } LATEST_TAG=$(echo "$API_RESPONSE" | jq -r '.tag_name') LATEST_NAME=$(echo "$API_RESPONSE" | jq -r '.name') RELEASE_URL=$(echo "$API_RESPONSE" | jq -r '.html_url') RELEASE_BODY=$(echo "$API_RESPONSE" | jq -r '.body | split("\n")[0:3] | join(" | ")') LAST_TAG="" [ -f "$STATE_FILE" ] && LAST_TAG=$(cat "$STATE_FILE") if [ "$LATEST_TAG" = "$LAST_TAG" ]; then echo "No new release (still at $LATEST_TAG), nothing to do." exit 0 fi echo "New release: $LATEST_TAG (was: ${LAST_TAG:-none})" export _CM_LATEST_TAG="$LATEST_TAG" export _CM_LATEST_NAME="$LATEST_NAME" export _CM_RELEASE_URL="$RELEASE_URL" export _CM_RELEASE_BODY="$RELEASE_BODY" export _CM_LAST_TAG="$LAST_TAG" python3 << 'PYEOF' import urllib.request, urllib.parse, json, time, sys, os token = os.environ.get('MATRIX_TOKEN', '') server = os.environ.get('MATRIX_SERVER', '').rstrip('/') room = os.environ.get('MATRIX_ROOM', '') ping = os.environ.get('MATRIX_PING_USER', '') if not all([token, server, room]): print("ERROR: MATRIX_TOKEN/MATRIX_SERVER/MATRIX_ROOM not set in env file") sys.exit(1) tag = os.environ['_CM_LATEST_TAG'] name = os.environ['_CM_LATEST_NAME'] url = os.environ['_CM_RELEASE_URL'] body = os.environ['_CM_RELEASE_BODY'] last_tag = os.environ['_CM_LAST_TAG'] from_str = f" (was {last_tag})" if last_tag else "" ping_safe = urllib.parse.quote(ping, safe=":@") plain = ( f"{ping}: Cinny {tag} released{from_str}.\n" f"{body}\n" f"Review release notes, then send !cinny-update to merge + rebuild.\n{url}" ) html = ( f"{ping}: " f"Cinny {tag} released{from_str}.
" f"{body}
" f"Release notes ยท Send !cinny-update to merge + rebuild." ) txn = str(int(time.time() * 1000)) api = f"{server}/_matrix/client/v3/rooms/{urllib.parse.quote(room, safe='')}/send/m.room.message/{txn}" body_json = json.dumps({"msgtype": "m.text", "body": plain, "format": "org.matrix.custom.html", "formatted_body": html}).encode() req = urllib.request.Request(api, data=body_json, method="PUT", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }) urllib.request.urlopen(req, timeout=10) print(f"Notification sent for {tag}") PYEOF echo "$LATEST_TAG" > "$STATE_FILE" echo "State updated to $LATEST_TAG"