mirror of
https://github.com/kuhyx/scripts.git
synced 2026-07-04 15:23:11 +02:00
Add bachelor thesis work tracker system - core implementation
Co-authored-by: kuhyx <147418882+kuhyx@users.noreply.github.com>
This commit is contained in:
parent
e07be0a873
commit
02efc737a3
335
scripts/digital_wellbeing/setup_thesis_work_tracker.sh
Executable file
335
scripts/digital_wellbeing/setup_thesis_work_tracker.sh
Executable file
@ -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
|
||||
@ -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
|
||||
436
scripts/digital_wellbeing/thesis_work_tracker.sh
Executable file
436
scripts/digital_wellbeing/thesis_work_tracker.sh
Executable file
@ -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 <<EOF | sudo tee "$STATE_FILE" > /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 <<EOF | sudo tee "$STATE_FILE" > /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
|
||||
Loading…
Reference in New Issue
Block a user