From 9b8b2db6370aede8161ecf4655f3c351a5b01a16 Mon Sep 17 00:00:00 2001
From: deadcade <deadcade@noreply.deadca.de>
Date: Fri, 13 Dec 2024 13:59:58 +0100
Subject: [PATCH] Add main script

---
 beatsaber-ntfy.sh | 122 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 beatsaber-ntfy.sh

diff --git a/beatsaber-ntfy.sh b/beatsaber-ntfy.sh
new file mode 100644
index 0000000..9542c00
--- /dev/null
+++ b/beatsaber-ntfy.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+# shellcheck disable=SC2154,SC2034
+# beatsaber-ntfy, a bash script (with jq, bc, and curl) to push notifications to a ntfy.sh server on beatsaber leaderboard statistics updates
+# Global configuration
+INTERVAL=3600
+# Set to empty string to disable
+SCORESABER_DIR="./scoresaber-players/"
+BEATLEADER_DIR="./beatleader-players/"
+
+# Variables taken from SS/BL API
+PLAYER_STATS_VARS="name country pp rank countryRank banned inactive"
+# Settings stored per player
+SETTINGS_VARS="NTFY_URL COUNTRY_THRESHOLD RANK_THRESHOLD PP_THRESHOLD"
+
+stats_routine() {
+	( if [[ -n "$SCORESABER_DIR" ]]; then
+		cd "$SCORESABER_DIR" || exit 1
+		for PLAYER in *; do
+			update_player_stats "$PLAYER" "ScoreSaber" "https://scoresaber.com/api/player/$PLAYER/basic" "https://scoresaber.com/u/$PLAYER"
+		done
+	fi )
+	( if [[ -n "$BEATLEADER_DIR" ]]; then
+		cd "$BEATLEADER_DIR" || exit 1
+		for PLAYER in *; do
+			update_player_stats "$PLAYER" "BeatLeader" "https://api.beatleader.xyz/player/$PLAYER" "https://beatleader.xyz/u/$PLAYER"
+		done
+	fi )
+}
+
+update_player_stats() {
+	# Name arguments
+	PLAYER=$1
+	TITLE=$2
+	API_URL=$3
+	PROFILE_URL=$4
+	# If the API fails, don't continue
+	echo "Fetching stats for $PLAYER on $TITLE"
+	if ! UPDATED_STATS="$(curl -s --fail-with-body "$API_URL")"; then
+		echo "Failed to gather stats for $PLAYER on $TITLE due to:"
+		echo "$UPDATED_STATS"
+		return
+	fi
+	# Get the old playerdata
+	# shellcheck disable=SC1090
+	source "$PLAYER"
+
+	# Set the new playerdata
+	for VAR in $PLAYER_STATS_VARS; do
+		declare "UPD_${VAR}=$(echo "$UPDATED_STATS" | jq -r ".$VAR")"
+	done
+
+	# If there are no known stats, update them without sending a notification
+	if [[ -z "$rank" ]]; then
+		INIT_STATS=true
+	else
+		INIT_STATS=false
+	fi
+
+	# Calculate stat differences
+	RANK_DIFF=$(( rank - UPD_rank ))
+	COUNTRYRANK_DIFF=$(( countryRank - UPD_countryRank ))
+	PP_DIFF=$(bc <<< "$UPD_pp - $pp")
+
+	# Check if criteria for notification are met
+	SEND_NOTIF=false
+	if [[ ${RANK_DIFF#-} -gt ${RANK_THRESHOLD} ]]; then SEND_NOTIF=true; fi
+	if [[ ${COUNTRYRANK_DIFF#-} -gt ${COUNTRYRANK_THRESHOLD} ]]; then SEND_NOTIF=true; fi
+	if [[ 1 -eq "$(bc <<< "${PP_DIFF#-} > ${PP_THRESHOLD}")" ]]; then SEND_NOTIF=true; fi
+	if [[ $inactive != "$UPD_inactive" ]]; then SEND_NOTIF=true; fi
+	if [[ $banned != "$UPD_banned" ]]; then SEND_NOTIF=true; fi
+
+	if ! $SEND_NOTIF; then
+		return
+	fi
+
+	# Add "+" to stats that increased
+	if [[ ${RANK_DIFF} -gt 0 ]]; then RANK_DIFF="+$RANK_DIFF"; fi
+	if [[ ${COUNTRYRANK_DIFF} -gt 0 ]]; then COUNTRYRANK_DIFF="+$COUNTRYRANK_DIFF"; fi
+	if [[ 1 -eq "$(bc <<< "${PP_DIFF} > 0")" ]]; then PP_DIFF="+$PP_DIFF"; fi
+
+	# Create the notification text
+	NOTIF="Global: $rank > $UPD_rank ($RANK_DIFF)
+Country ($UPD_country): $countryRank > $UPD_countryRank ($COUNTRYRANK_DIFF)
+PP: $pp > $UPD_pp ($PP_DIFF)"
+	if [[ $inactive != "$UPD_inactive" ]]; then
+		NOTIF="$NOTIF
+Inactive status has changed: $inactive > $UPD_inactive"
+	fi
+	if [[ $banned != "$UPD_banned" ]]; then
+		NOTIF="$NOTIF
+Banned status has changed: $banned > $UPD_banned"
+	fi
+
+	# Send the notification
+	if ! $INIT_STATS; then
+		echo "${UPD_name}'s $TITLE stats have changed, sending notification"
+		if ! NOTIF_RESPONSE=$(curl -s --fail-with-body -X POST -H "Title: $UPD_name's $TITLE Stats" -H "Click: $PROFILE_URL" -d "$NOTIF" "$NTFY_URL"); then
+			echo "Failed to send notification:"
+			echo "$NOTIF_RESPONSE"
+			# If we can't notify the user, don't update the stats file.
+			# Ensures notifications are accurate after ntfy server is fixed.
+			return
+		fi
+	fi
+
+	# Recreate the player file
+	NEW_PLAYER=""
+	for VAR in $SETTINGS_VARS; do
+		NEW_PLAYER="$NEW_PLAYER
+$VAR=$(eval "echo \$$VAR")"
+	done
+	for VAR in $PLAYER_STATS_VARS; do
+		NEW_PLAYER="$NEW_PLAYER
+$VAR=\"$(eval "echo \"\$UPD_$VAR\"")\""
+	done
+	echo "${NEW_PLAYER:1}" > "$PLAYER"
+}
+
+while true; do
+	stats_routine
+	sleep "$INTERVAL"
+done