#!/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