diff --git a/scripts/digital_wellbeing/setup_thesis_work_tracker.sh b/scripts/digital_wellbeing/setup_thesis_work_tracker.sh new file mode 100755 index 0000000..8f2856f --- /dev/null +++ b/scripts/digital_wellbeing/setup_thesis_work_tracker.sh @@ -0,0 +1,335 @@ +#!/bin/bash +# Bachelor Thesis Work Tracker - One-Shot Installer +# +# This script installs a system that: +# 1. Monitors active windows for thesis-related work (Unreal Engine, Unity, Nvidia Omniverse, VS Code with specific repo) +# 2. Tracks accumulated work time with protection against tampering +# 3. Blocks Steam and other distractions via /etc/hosts until work quota is met +# 4. Provides psychological friction against circumvention +# +# The system is designed to be as hard to circumvent as possible: +# - State files are immutable (chattr +i) +# - Runs as a systemd service that auto-restarts +# - Integrated with hosts guard system +# - Protected against easy time manipulation +# +# Usage: +# sudo ./setup_thesis_work_tracker.sh [options] +# +# Options: +# --work-quota MINUTES Set required work time in minutes (default: 120 = 2 hours) +# --decay-rate MINUTES Set decay rate per hour of distraction usage (default: 30) +# --vscode-repo NAME Set required VS Code repository name (default: praca_magisterska) +# --dry-run Show what would be done without making changes +# --uninstall Remove the thesis work tracker system +# -h|--help Show this help +# +# Exit codes: +# 0 = success +# 1 = general failure +# 2 = argument error + +set -euo pipefail + +###################################################################### +# Configuration Defaults +###################################################################### +WORK_QUOTA_MINUTES=120 # 2 hours of work required +DECAY_RATE_MINUTES=30 # Lose 30 minutes per hour of Steam usage +VSCODE_REPO="praca_magisterska" +DRY_RUN=0 +UNINSTALL=0 + +###################################################################### +# Paths +###################################################################### +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +TRACKER_SCRIPT="$SCRIPT_DIR/thesis_work_tracker.sh" +SERVICE_FILE="$SCRIPT_DIR/systemd/thesis-work-tracker@.service" +INSTALL_BIN="/usr/local/bin/thesis_work_tracker.sh" +INSTALL_SERVICE="/etc/systemd/system/thesis-work-tracker@.service" +STATE_DIR="/var/lib/thesis-work-tracker" +LOG_DIR="/var/log/thesis-work-tracker" + +###################################################################### +# Colors and Logging +###################################################################### +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +msg() { printf "${GREEN}[+]${NC} %s\n" "$*"; } +note() { printf "${BLUE}[i]${NC} %s\n" "$*"; } +warn() { printf "${YELLOW}[!]${NC} %s\n" "$*"; } +err() { printf "${RED}[x]${NC} %s\n" "$*" >&2; } + +run() { + if [[ $DRY_RUN -eq 1 ]]; then + printf '%s[DRY-RUN]%s ' "${CYAN}" "${NC}" + printf '%q ' "$@" + printf '\n' + else + "$@" + fi +} + +###################################################################### +# Helpers +###################################################################### +require_root() { + if [[ $EUID -ne 0 ]]; then + exec sudo -E bash "$0" "$@" + fi +} + +usage() { + sed -n '2,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//' +} + +check_dependencies() { + local missing=() + + for cmd in xdotool systemctl; do + if ! command -v "$cmd" &> /dev/null; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + err "Missing required dependencies: ${missing[*]}" + note "Install them with: sudo pacman -S ${missing[*]}" + return 1 + fi +} + +get_current_user() { + # Get the user who invoked sudo, or current user if not using sudo + if [[ -n ${SUDO_USER:-} ]]; then + echo "$SUDO_USER" + else + whoami + fi +} + +###################################################################### +# Parse Arguments +###################################################################### +while [[ $# -gt 0 ]]; do + case "$1" in + --work-quota) + WORK_QUOTA_MINUTES="${2:-}" + [[ -z $WORK_QUOTA_MINUTES ]] && { + err "--work-quota requires a value" + exit 2 + } + shift 2 + ;; + --decay-rate) + DECAY_RATE_MINUTES="${2:-}" + [[ -z $DECAY_RATE_MINUTES ]] && { + err "--decay-rate requires a value" + exit 2 + } + shift 2 + ;; + --vscode-repo) + VSCODE_REPO="${2:-}" + [[ -z $VSCODE_REPO ]] && { + err "--vscode-repo requires a value" + exit 2 + } + shift 2 + ;; + --dry-run) + DRY_RUN=1 + shift + ;; + --uninstall) + UNINSTALL=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + err "Unknown option: $1" + usage + exit 2 + ;; + esac +done + +###################################################################### +# Main Functions +###################################################################### + +uninstall_tracker() { + msg "Uninstalling thesis work tracker..." + + # Get current user for service name + local user + user=$(get_current_user) + + # Stop and disable service + if systemctl is-active --quiet "thesis-work-tracker@$user.service" 2>/dev/null; then + run systemctl stop "thesis-work-tracker@$user.service" + fi + + if systemctl is-enabled --quiet "thesis-work-tracker@$user.service" 2>/dev/null; then + run systemctl disable "thesis-work-tracker@$user.service" + fi + + # Remove service file + if [[ -f $INSTALL_SERVICE ]]; then + run rm -f "$INSTALL_SERVICE" + run systemctl daemon-reload + fi + + # Remove tracker script + if [[ -f $INSTALL_BIN ]]; then + run rm -f "$INSTALL_BIN" + fi + + # Remove state directory (with immutable flags removed) + if [[ -d $STATE_DIR ]]; then + run chattr -i -R "$STATE_DIR" 2>/dev/null || true + note "State directory preserved at: $STATE_DIR" + note "To completely remove state: sudo rm -rf $STATE_DIR" + fi + + msg "Thesis work tracker uninstalled successfully" + note "Log files preserved at: $LOG_DIR" +} + +install_tracker() { + msg "Installing thesis work tracker..." + + # Check dependencies + check_dependencies || exit 1 + + # Verify source files exist + if [[ ! -f $TRACKER_SCRIPT ]]; then + err "Tracker script not found: $TRACKER_SCRIPT" + exit 1 + fi + + if [[ ! -f $SERVICE_FILE ]]; then + err "Service file not found: $SERVICE_FILE" + exit 1 + fi + + # Create directories + msg "Creating directories..." + run mkdir -p "$LOG_DIR" + run chmod 755 "$LOG_DIR" + + # Install tracker script with configuration + msg "Installing tracker script to $INSTALL_BIN..." + + # Copy script and update configuration values + run cp "$TRACKER_SCRIPT" "$INSTALL_BIN" + + # Update configuration in the installed script + local work_quota_seconds=$((WORK_QUOTA_MINUTES * 60)) + local decay_rate_seconds=$((DECAY_RATE_MINUTES * 60)) + + run sed -i "s/^WORK_QUOTA_REQUIRED=.*/WORK_QUOTA_REQUIRED=$work_quota_seconds # $WORK_QUOTA_MINUTES minutes/" "$INSTALL_BIN" + run sed -i "s/^WORK_DECAY_PER_HOUR=.*/WORK_DECAY_PER_HOUR=$decay_rate_seconds # $DECAY_RATE_MINUTES minutes/" "$INSTALL_BIN" + run sed -i "s/^VSCODE_REQUIRED_REPO=.*/VSCODE_REQUIRED_REPO=\"$VSCODE_REPO\"/" "$INSTALL_BIN" + + run chmod 755 "$INSTALL_BIN" + + # Install systemd service + msg "Installing systemd service..." + run cp "$SERVICE_FILE" "$INSTALL_SERVICE" + run chmod 644 "$INSTALL_SERVICE" + run systemctl daemon-reload + + # Get current user for service enablement + local user + user=$(get_current_user) + + # Enable and start service + msg "Enabling and starting service for user: $user..." + run systemctl enable "thesis-work-tracker@$user.service" + run systemctl restart "thesis-work-tracker@$user.service" + + # Wait a moment for service to start + sleep 2 + + # Check service status + if systemctl is-active --quiet "thesis-work-tracker@$user.service"; then + msg "Service started successfully!" + else + warn "Service may not have started properly. Check status with:" + warn " systemctl status thesis-work-tracker@$user.service" + fi + + # Display configuration summary + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Bachelor Thesis Work Tracker - Installation ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "Configuration:" + echo " • Work quota required: ${BOLD}${WORK_QUOTA_MINUTES} minutes${NC}" + echo " • Decay rate (per hour of Steam): ${BOLD}${DECAY_RATE_MINUTES} minutes${NC}" + echo " • VS Code repository: ${BOLD}${VSCODE_REPO}${NC}" + echo "" + echo "Tracked Applications:" + echo " ✓ Unreal Engine (all versions)" + echo " ✓ Unity Editor" + echo " ✓ Nvidia Omniverse" + echo " ✓ Visual Studio Code (only when working on '$VSCODE_REPO')" + echo "" + echo "Blocked Sites (until quota met):" + echo " ⛔ Steam (all domains)" + echo " ⛔ Social media (Reddit, Twitter, Facebook, Instagram)" + echo " ⛔ Video sites (YouTube, Twitch)" + echo " ⛔ Other distractions (9gag, Imgur)" + echo "" + echo "System Protection Features:" + echo " 🔒 State files protected with immutable flags" + echo " 🔒 Auto-restart on failure" + echo " 🔒 Integrated with hosts guard system" + echo " 🔒 Continuous monitoring every 5 seconds" + echo "" + echo "How it works:" + echo " 1. Work on your thesis using the approved applications" + echo " 2. Time accumulates in the background" + echo " 3. After ${WORK_QUOTA_MINUTES} minutes of work, Steam is unblocked" + echo " 4. Steam usage decays your work time at ${DECAY_RATE_MINUTES} min/hour" + echo " 5. When work time drops below quota, Steam is blocked again" + echo "" + echo "Useful Commands:" + echo " • Check status: systemctl status thesis-work-tracker@$user.service" + echo " • View logs: tail -f $LOG_DIR/tracker.log" + echo " • View state: sudo cat $STATE_DIR/work-time.state" + echo " • Restart: sudo systemctl restart thesis-work-tracker@$user.service" + echo " • Uninstall: sudo $0 --uninstall" + echo "" + echo "⚠️ IMPORTANT: This system is designed to be hard to circumvent!" + echo " State files are immutable and the service auto-restarts." + echo " To legitimately modify settings, uninstall and reinstall." + echo "" + echo "Good luck with your bachelor thesis! 🎓" + echo "" +} + +###################################################################### +# Main +###################################################################### +require_root "$@" + +if [[ $UNINSTALL -eq 1 ]]; then + uninstall_tracker +else + install_tracker +fi + +exit 0 diff --git a/scripts/digital_wellbeing/systemd/thesis-work-tracker@.service b/scripts/digital_wellbeing/systemd/thesis-work-tracker@.service new file mode 100644 index 0000000..3237f58 --- /dev/null +++ b/scripts/digital_wellbeing/systemd/thesis-work-tracker@.service @@ -0,0 +1,29 @@ +[Unit] +Description=Bachelor Thesis Work Tracker +Documentation=man:systemd.service(5) +After=graphical.target +Wants=graphical.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/thesis_work_tracker.sh +Restart=always +RestartSec=10 + +# Run as the user who is logged in (for X11/window detection) +User=%i +Environment="DISPLAY=:0" +Environment="XAUTHORITY=/home/%i/.Xauthority" + +# Logging +StandardOutput=append:/var/log/thesis-work-tracker/tracker.log +StandardError=append:/var/log/thesis-work-tracker/tracker.log + +# Security hardening +NoNewPrivileges=false +PrivateTmp=true +ProtectSystem=false +ProtectHome=false + +[Install] +WantedBy=default.target diff --git a/scripts/digital_wellbeing/thesis_work_tracker.sh b/scripts/digital_wellbeing/thesis_work_tracker.sh new file mode 100755 index 0000000..e20cd96 --- /dev/null +++ b/scripts/digital_wellbeing/thesis_work_tracker.sh @@ -0,0 +1,436 @@ +#!/bin/bash +# Bachelor Thesis Work Tracker +# Monitors active windows for thesis-related work (Unreal Engine, Unity, Nvidia Omniverse, VS Code with specific repo) +# Unlocks Steam and other distractions only after sufficient work time is accumulated +# +# This daemon runs continuously and: +# 1. Tracks active window time for approved thesis work applications +# 2. Maintains a protected state file with accumulated work time +# 3. Manages hosts file blocking/unblocking based on work quota +# 4. Provides psychological friction against circumvention + +set -euo pipefail + +# Configuration +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +STATE_DIR="/var/lib/thesis-work-tracker" +STATE_FILE="$STATE_DIR/work-time.state" +LOCK_FILE="$STATE_DIR/tracker.lock" +LOG_DIR="/var/log/thesis-work-tracker" +LOG_FILE="$LOG_DIR/tracker.log" +CHECK_INTERVAL=5 # Check every 5 seconds + +# Work requirements (in seconds) +# 2 hours of work = 7200 seconds required before Steam access +WORK_QUOTA_REQUIRED=7200 # 2 hours +WORK_DECAY_PER_HOUR=1800 # Lose 30 minutes per hour of Steam usage + +# Thesis work applications - process names and window patterns +# These are the applications that count as "thesis work" +declare -A THESIS_APPS=( + ["UnrealEditor"]="Unreal Engine" + ["UE4Editor"]="Unreal Engine 4" + ["UE5Editor"]="Unreal Engine 5" + ["Unity"]="Unity Editor" + ["UnityHub"]="Unity Hub" + ["Code"]="Visual Studio Code" # Special handling for repo check + ["code"]="Visual Studio Code" # lowercase variant + ["omniverse"]="Nvidia Omniverse" + ["kit"]="Nvidia Omniverse Kit" +) + +# VS Code specific repo to track +VSCODE_REQUIRED_REPO="praca_magisterska" + +# Steam and distraction patterns for hosts blocking +STEAM_DOMAINS=( + "steampowered.com" + "steamcommunity.com" + "steamgames.com" + "store.steampowered.com" + "steamcdn-a.akamaihd.net" + "steamstatic.com" + "steamusercontent.com" +) + +# Additional distraction sites that should be blocked +DISTRACTION_DOMAINS=( + "reddit.com" + "twitter.com" + "x.com" + "facebook.com" + "instagram.com" + "youtube.com" + "twitch.tv" + "9gag.com" + "imgur.com" +) + +# Colors for logging +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Logging function +log_message() { + local level="$1" + shift + local message="$*" + local timestamp + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + echo "[${timestamp}] [${level}] ${message}" | tee -a "$LOG_FILE" +} + +log_info() { log_message "INFO" "$@"; } +log_warn() { log_message "WARN" "$@"; } +log_error() { log_message "ERROR" "$@"; } +log_debug() { + if [[ ${DEBUG:-0} -eq 1 ]]; then + log_message "DEBUG" "$@" + fi +} + +# Initialize directories and state file +init_state() { + # Create directories with proper permissions + if [[ ! -d $STATE_DIR ]]; then + sudo mkdir -p "$STATE_DIR" + sudo chmod 700 "$STATE_DIR" + fi + + if [[ ! -d $LOG_DIR ]]; then + sudo mkdir -p "$LOG_DIR" + sudo chmod 755 "$LOG_DIR" + fi + + # Initialize state file if it doesn't exist + if [[ ! -f $STATE_FILE ]]; then + cat < /dev/null +# Thesis Work Tracker State File +# DO NOT EDIT MANUALLY - Managed by thesis_work_tracker daemon +# Last updated: $(date) + +TOTAL_WORK_SECONDS=0 +LAST_UPDATE_TIMESTAMP=$(date +%s) +STEAM_ACCESS_GRANTED=0 +LAST_WORK_SESSION_START=0 +CURRENT_SESSION_SECONDS=0 +EOF + sudo chmod 600 "$STATE_FILE" + sudo chattr +i "$STATE_FILE" 2>/dev/null || true + fi +} + +# Load current state from file +load_state() { + if [[ ! -f $STATE_FILE ]]; then + log_error "State file not found: $STATE_FILE" + return 1 + fi + + # Temporarily remove immutable flag to read + sudo chattr -i "$STATE_FILE" 2>/dev/null || true + + # Source the state file + # shellcheck source=/dev/null + source "$STATE_FILE" + + # Re-apply immutable flag + sudo chattr +i "$STATE_FILE" 2>/dev/null || true +} + +# Save current state to file +save_state() { + local total_work="$1" + local steam_access="$2" + local current_session="$3" + local session_start="$4" + + # Remove immutable flag + sudo chattr -i "$STATE_FILE" 2>/dev/null || true + + # Write new state + cat < /dev/null +# Thesis Work Tracker State File +# DO NOT EDIT MANUALLY - Managed by thesis_work_tracker daemon +# Last updated: $(date) + +TOTAL_WORK_SECONDS=$total_work +LAST_UPDATE_TIMESTAMP=$(date +%s) +STEAM_ACCESS_GRANTED=$steam_access +LAST_WORK_SESSION_START=$session_start +CURRENT_SESSION_SECONDS=$current_session +EOF + + sudo chmod 600 "$STATE_FILE" + # Re-apply immutable flag + sudo chattr +i "$STATE_FILE" 2>/dev/null || true +} + +# Check if a process is running +is_process_running() { + local process_name="$1" + pgrep -x "$process_name" > /dev/null 2>&1 +} + +# Get active window title and process name +get_active_window_info() { + if ! command -v xdotool &> /dev/null; then + log_error "xdotool not installed, cannot detect active window" + return 1 + fi + + local active_window_id + active_window_id=$(xdotool getactivewindow 2>/dev/null || echo "") + + if [[ -z $active_window_id ]]; then + return 1 + fi + + local window_name + window_name=$(xdotool getwindowname "$active_window_id" 2>/dev/null || echo "") + + local window_pid + window_pid=$(xdotool getwindowpid "$active_window_id" 2>/dev/null || echo "") + + local process_name="" + if [[ -n $window_pid ]]; then + process_name=$(ps -p "$window_pid" -o comm= 2>/dev/null || echo "") + fi + + echo "${process_name}|${window_name}" +} + +# Check if VS Code is working on the required repository +is_vscode_on_thesis_repo() { + local window_title="$1" + + # VS Code window titles typically contain the folder/workspace name + # Look for the repo name in the window title + if [[ $window_title == *"$VSCODE_REQUIRED_REPO"* ]]; then + return 0 + fi + + # Also check if VS Code has the repo open by checking recent workspaces + # VS Code stores workspace info in ~/.config/Code/User/workspaceStorage + if [[ -d "$HOME/.config/Code/User/workspaceStorage" ]]; then + if find "$HOME/.config/Code/User/workspaceStorage" -type f -name "workspace.json" -exec grep -l "$VSCODE_REQUIRED_REPO" {} \; 2>/dev/null | grep -q .; then + # Additional check: is this window actually VS Code? + if [[ $window_title == *"Visual Studio Code"* ]]; then + return 0 + fi + fi + fi + + return 1 +} + +# Check if current active window is thesis work +is_thesis_work_active() { + local window_info + window_info=$(get_active_window_info) + + if [[ -z $window_info ]]; then + return 1 + fi + + local process_name + local window_title + IFS='|' read -r process_name window_title <<< "$window_info" + + log_debug "Active window: process='$process_name' title='$window_title'" + + # Check each thesis application + for proc_pattern in "${!THESIS_APPS[@]}"; do + if [[ $process_name == *"$proc_pattern"* ]] || [[ $window_title == *"${THESIS_APPS[$proc_pattern]}"* ]]; then + # Special handling for VS Code - must be on thesis repo + if [[ $proc_pattern == "Code" ]] || [[ $proc_pattern == "code" ]]; then + if is_vscode_on_thesis_repo "$window_title"; then + log_debug "Thesis work detected: VS Code on $VSCODE_REQUIRED_REPO" + return 0 + else + log_debug "VS Code detected but not on thesis repo" + continue + fi + fi + + log_debug "Thesis work detected: ${THESIS_APPS[$proc_pattern]}" + return 0 + fi + done + + return 1 +} + +# Block Steam and distractions in /etc/hosts +block_distractions() { + log_info "Blocking Steam and distractions in /etc/hosts" + + # Remove immutable flag temporarily + sudo chattr -i /etc/hosts 2>/dev/null || true + + # Add blocking entries if not already present + local hosts_modified=0 + + for domain in "${STEAM_DOMAINS[@]}" "${DISTRACTION_DOMAINS[@]}"; do + if ! grep -q "^0.0.0.0[[:space:]]*$domain" /etc/hosts 2>/dev/null; then + echo "0.0.0.0 $domain" | sudo tee -a /etc/hosts > /dev/null + hosts_modified=1 + fi + done + + # Re-apply immutable flag + sudo chattr +i /etc/hosts 2>/dev/null || true + + if [[ $hosts_modified -eq 1 ]]; then + log_info "Added distraction blocks to /etc/hosts" + fi +} + +# Unblock Steam and distractions from /etc/hosts +unblock_distractions() { + log_info "Unblocking Steam and distractions in /etc/hosts" + + # Remove immutable flag temporarily + sudo chattr -i /etc/hosts 2>/dev/null || true + + # Remove blocking entries + local temp_hosts="/tmp/hosts.tmp.$$" + sudo cp /etc/hosts "$temp_hosts" + + for domain in "${STEAM_DOMAINS[@]}" "${DISTRACTION_DOMAINS[@]}"; do + sudo sed -i "/^0.0.0.0[[:space:]]*$domain/d" "$temp_hosts" + done + + sudo mv "$temp_hosts" /etc/hosts + sudo chmod 644 /etc/hosts + + # Re-apply immutable flag + sudo chattr +i /etc/hosts 2>/dev/null || true + + log_info "Removed distraction blocks from /etc/hosts" +} + +# Check if Steam is currently running (to track decay) +is_steam_running() { + pgrep -x "steam" > /dev/null 2>&1 +} + +# Main tracking loop +main_loop() { + log_info "Starting thesis work tracker daemon" + + # Initialize state + init_state + + # Load initial state + load_state + + local total_work_seconds=${TOTAL_WORK_SECONDS:-0} + local steam_access=${STEAM_ACCESS_GRANTED:-0} + local session_start=${LAST_WORK_SESSION_START:-0} + local session_seconds=${CURRENT_SESSION_SECONDS:-0} + + # Apply initial blocking state + if [[ $steam_access -eq 0 ]]; then + block_distractions + fi + + local last_status_log=$(date +%s) + local last_decay_check=$(date +%s) + + while true; do + local current_time=$(date +%s) + + # Check if thesis work is active + if is_thesis_work_active; then + # Track work time + if [[ $session_start -eq 0 ]]; then + session_start=$current_time + log_info "Thesis work session started" + fi + + # Increment session time + session_seconds=$((session_seconds + CHECK_INTERVAL)) + total_work_seconds=$((total_work_seconds + CHECK_INTERVAL)) + + # Check if we've reached the quota + if [[ $total_work_seconds -ge $WORK_QUOTA_REQUIRED ]] && [[ $steam_access -eq 0 ]]; then + log_info "Work quota reached! Granting Steam access." + steam_access=1 + unblock_distractions + fi + + else + # No thesis work active + if [[ $session_start -ne 0 ]]; then + log_info "Thesis work session ended. Session duration: $((session_seconds / 60)) minutes" + session_start=0 + session_seconds=0 + fi + + # Check for Steam usage and apply decay + if [[ $steam_access -eq 1 ]] && is_steam_running; then + local time_since_decay=$((current_time - last_decay_check)) + if [[ $time_since_decay -ge 3600 ]]; then # Every hour + total_work_seconds=$((total_work_seconds - WORK_DECAY_PER_HOUR)) + if [[ $total_work_seconds -lt 0 ]]; then + total_work_seconds=0 + fi + last_decay_check=$current_time + log_info "Steam usage detected. Applied decay. Remaining work time: $((total_work_seconds / 60)) minutes" + + # Revoke access if below quota + if [[ $total_work_seconds -lt $WORK_QUOTA_REQUIRED ]]; then + log_info "Work quota depleted. Revoking Steam access." + steam_access=0 + block_distractions + fi + fi + fi + fi + + # Save state periodically + save_state "$total_work_seconds" "$steam_access" "$session_seconds" "$session_start" + + # Log status every 5 minutes + if [[ $((current_time - last_status_log)) -ge 300 ]]; then + local work_minutes=$((total_work_seconds / 60)) + local quota_minutes=$((WORK_QUOTA_REQUIRED / 60)) + local remaining_minutes=$((quota_minutes - work_minutes)) + if [[ $remaining_minutes -lt 0 ]]; then + remaining_minutes=0 + fi + + log_info "Status: Total work time: ${work_minutes}m / ${quota_minutes}m | Steam access: $steam_access | Need: ${remaining_minutes}m more" + last_status_log=$current_time + fi + + sleep "$CHECK_INTERVAL" + done +} + +# Handle signals for graceful shutdown +cleanup() { + log_info "Received shutdown signal, saving state and exiting" + rm -f "$LOCK_FILE" + exit 0 +} + +trap cleanup SIGTERM SIGINT + +# Check for lock file to prevent multiple instances +if [[ -f $LOCK_FILE ]]; then + log_error "Another instance is already running (lock file exists: $LOCK_FILE)" + exit 1 +fi + +# Create lock file +touch "$LOCK_FILE" + +# Run main loop +main_loop