mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-09 00:56:16 +00:00
157 lines
5.0 KiB
Bash
157 lines
5.0 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
export PATH="/usr/local/bin:/usr/bin:/bin"
|
|
export PLAYWRIGHT_BROWSERS_PATH=0
|
|
export TZ="${TZ:-UTC}"
|
|
|
|
cd /usr/src/microsoft-rewards-bot
|
|
|
|
LOCKFILE=/tmp/run_daily.lock
|
|
|
|
# -------------------------------
|
|
# Function: Check and fix lockfile integrity
|
|
# -------------------------------
|
|
self_heal_lockfile() {
|
|
# If lockfile exists but is empty → remove it
|
|
if [ -f "$LOCKFILE" ]; then
|
|
local lock_content
|
|
lock_content=$(<"$LOCKFILE" || echo "")
|
|
|
|
if [[ -z "$lock_content" ]]; then
|
|
echo "[$(date)] [run_daily.sh] Found empty lockfile → removing."
|
|
rm -f "$LOCKFILE"
|
|
return
|
|
fi
|
|
|
|
# If lockfile contains non-numeric PID → remove it
|
|
if ! [[ "$lock_content" =~ ^[0-9]+$ ]]; then
|
|
echo "[$(date)] [run_daily.sh] Found corrupted lockfile content ('$lock_content') → removing."
|
|
rm -f "$LOCKFILE"
|
|
return
|
|
fi
|
|
|
|
# If lockfile contains PID but process is dead → remove it
|
|
if ! kill -0 "$lock_content" 2>/dev/null; then
|
|
echo "[$(date)] [run_daily.sh] Lockfile PID $lock_content is dead → removing stale lock."
|
|
rm -f "$LOCKFILE"
|
|
return
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# -------------------------------
|
|
# Function: Acquire lock
|
|
# -------------------------------
|
|
acquire_lock() {
|
|
local max_attempts=5
|
|
local attempt=0
|
|
local timeout_hours=${STUCK_PROCESS_TIMEOUT_HOURS:-8}
|
|
local timeout_seconds=$((timeout_hours * 3600))
|
|
|
|
while [ $attempt -lt $max_attempts ]; do
|
|
# Try to create lock with current PID
|
|
if (set -C; echo "$$" > "$LOCKFILE") 2>/dev/null; then
|
|
echo "[$(date)] [run_daily.sh] Lock acquired successfully (PID: $$)"
|
|
return 0
|
|
fi
|
|
|
|
# Lock exists, validate it
|
|
if [ -f "$LOCKFILE" ]; then
|
|
local existing_pid
|
|
existing_pid=$(<"$LOCKFILE" || echo "")
|
|
|
|
echo "[$(date)] [run_daily.sh] Lock file exists with PID: '$existing_pid'"
|
|
|
|
# If lockfile content is invalid → delete and retry
|
|
if [[ -z "$existing_pid" || ! "$existing_pid" =~ ^[0-9]+$ ]]; then
|
|
echo "[$(date)] [run_daily.sh] Removing invalid lockfile → retrying..."
|
|
rm -f "$LOCKFILE"
|
|
continue
|
|
fi
|
|
|
|
# If process is dead → delete and retry
|
|
if ! kill -0 "$existing_pid" 2>/dev/null; then
|
|
echo "[$(date)] [run_daily.sh] Removing stale lock (dead PID: $existing_pid)"
|
|
rm -f "$LOCKFILE"
|
|
continue
|
|
fi
|
|
|
|
# Check process runtime → kill if exceeded timeout
|
|
local process_age
|
|
if process_age=$(ps -o etimes= -p "$existing_pid" 2>/dev/null | tr -d ' '); then
|
|
if [ "$process_age" -gt "$timeout_seconds" ]; then
|
|
echo "[$(date)] [run_daily.sh] Killing stuck process $existing_pid (${process_age}s > ${timeout_hours}h)"
|
|
kill -TERM "$existing_pid" 2>/dev/null || true
|
|
sleep 5
|
|
kill -KILL "$existing_pid" 2>/dev/null || true
|
|
rm -f "$LOCKFILE"
|
|
continue
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
echo "[$(date)] [run_daily.sh] Lock held by PID $existing_pid, attempt $((attempt + 1))/$max_attempts"
|
|
sleep 2
|
|
((attempt++))
|
|
done
|
|
|
|
echo "[$(date)] [run_daily.sh] Could not acquire lock after $max_attempts attempts; exiting."
|
|
return 1
|
|
}
|
|
|
|
# -------------------------------
|
|
# Function: Release lock
|
|
# -------------------------------
|
|
release_lock() {
|
|
if [ -f "$LOCKFILE" ]; then
|
|
local lock_pid
|
|
lock_pid=$(<"$LOCKFILE")
|
|
if [ "$lock_pid" = "$$" ]; then
|
|
rm -f "$LOCKFILE"
|
|
echo "[$(date)] [run_daily.sh] Lock released (PID: $$)"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Always release lock on exit — but only if we acquired it
|
|
trap 'release_lock' EXIT INT TERM
|
|
|
|
# -------------------------------
|
|
# MAIN EXECUTION FLOW
|
|
# -------------------------------
|
|
echo "[$(date)] [run_daily.sh] Current process PID: $$"
|
|
|
|
# Self-heal any broken or empty locks before proceeding
|
|
self_heal_lockfile
|
|
|
|
# Attempt to acquire the lock safely
|
|
if ! acquire_lock; then
|
|
exit 0
|
|
fi
|
|
|
|
# Random sleep between MIN and MAX to spread execution
|
|
MINWAIT=${MIN_SLEEP_MINUTES:-5}
|
|
MAXWAIT=${MAX_SLEEP_MINUTES:-50}
|
|
MINWAIT_SEC=$((MINWAIT*60))
|
|
MAXWAIT_SEC=$((MAXWAIT*60))
|
|
|
|
if [ "${SKIP_RANDOM_SLEEP:-false}" != "true" ]; then
|
|
SLEEPTIME=$(( MINWAIT_SEC + RANDOM % (MAXWAIT_SEC - MINWAIT_SEC) ))
|
|
echo "[$(date)] [run_daily.sh] Sleeping for $((SLEEPTIME/60)) minutes ($SLEEPTIME seconds)"
|
|
sleep "$SLEEPTIME"
|
|
else
|
|
echo "[$(date)] [run_daily.sh] Skipping random sleep"
|
|
fi
|
|
|
|
# Start the actual script
|
|
echo "[$(date)] [run_daily.sh] Starting script..."
|
|
if npm start; then
|
|
echo "[$(date)] [run_daily.sh] Script completed successfully."
|
|
else
|
|
echo "[$(date)] [run_daily.sh] ERROR: Script failed!" >&2
|
|
fi
|
|
|
|
echo "[$(date)] [run_daily.sh] Script finished"
|
|
# Lock is released automatically via trap
|