From 2fcf010c8a80c8e9207c79a9ee5efea420d9162c Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 3 Jan 2026 16:03:22 +0100 Subject: [PATCH] fixes for compulsiuve opening and midnight shutdown --- .../block_compulsive_opening.sh | 91 ++++++- .../setup_midnight_shutdown.sh | 223 ++++++++++++++---- 2 files changed, 258 insertions(+), 56 deletions(-) diff --git a/scripts/digital_wellbeing/block_compulsive_opening.sh b/scripts/digital_wellbeing/block_compulsive_opening.sh index 302d3dc..1475301 100755 --- a/scripts/digital_wellbeing/block_compulsive_opening.sh +++ b/scripts/digital_wellbeing/block_compulsive_opening.sh @@ -9,10 +9,18 @@ set -euo pipefail -# Source common library for shared functions -SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" -# shellcheck source=../lib/common.sh -source "$SCRIPT_DIR/../lib/common.sh" +# Send desktop notification (inlined from common.sh to avoid dependency issues +# when script is installed to /usr/local/bin) +notify() { + local title="$1" + local message="$2" + local urgency="${3:-normal}" + local timeout="${4:-5000}" + + if command -v notify-send &>/dev/null; then + notify-send -u "$urgency" -t "$timeout" "$title" "$message" 2>/dev/null || true + fi +} # Configuration STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/compulsive-block" @@ -253,6 +261,71 @@ install_all() { echo "Each app can now only be opened once per hour." echo "State files stored in: $STATE_DIR" echo "Logs stored in: $LOG_FILE" + + # Install pacman hook to re-wrap after package updates + install_pacman_hook +} + +# Install pacman hook to re-install wrappers after package updates +install_pacman_hook() { + local hook_dir="/etc/pacman.d/hooks" + local hook_file="$hook_dir/95-compulsive-block-rewrap.hook" + + echo "" + echo "Installing pacman hook..." + + mkdir -p "$hook_dir" + + cat >"$hook_file" <<'HOOK_EOF' +[Trigger] +Operation = Upgrade +Operation = Install +Type = Package +Target = beeper +Target = signal-desktop +Target = discord + +[Action] +Description = Re-installing compulsive opening blockers after package update +When = PostTransaction +Exec = /usr/local/bin/block-compulsive-opening.sh rewrap-quiet +HOOK_EOF + + chmod 644 "$hook_file" + echo "✓ Pacman hook installed: $hook_file" + echo " Wrappers will be automatically re-installed after beeper/signal/discord updates" +} + +# Uninstall pacman hook +uninstall_pacman_hook() { + local hook_file="/etc/pacman.d/hooks/95-compulsive-block-rewrap.hook" + if [[ -f "$hook_file" ]]; then + rm -f "$hook_file" + echo "✓ Pacman hook removed" + fi +} + +# Quietly re-wrap apps (for pacman hook - no interactive output) +rewrap_quiet() { + log_message "REWRAP: Pacman hook triggered, re-installing wrappers" + + for app in "${!APPS[@]}"; do + local wrapper_path="${APPS[$app]}" + + # Check if wrapper was overwritten (no longer our wrapper script) + if [[ -f "$wrapper_path" ]] && ! grep -q "block-compulsive-opening" "$wrapper_path" 2>/dev/null; then + # Wrapper was overwritten by package update + log_message "REWRAP: $app wrapper was overwritten, re-installing" + + # Remove old .orig if exists (it's now stale) + rm -f "${wrapper_path}.orig" + + # Re-install wrapper + install_wrapper "$app" >>"$LOG_FILE" 2>&1 || true + fi + done + + log_message "REWRAP: Complete" } # Uninstall all wrappers @@ -266,6 +339,9 @@ uninstall_all() { rm -f "/usr/local/bin/block-compulsive-opening.sh" + # Remove pacman hook + uninstall_pacman_hook + echo "" echo "Uninstallation complete." } @@ -402,6 +478,13 @@ main() { reset-all) reset_all ;; + rewrap-quiet) + # Called by pacman hook - quietly re-wrap apps after package updates + if [[ $EUID -ne 0 ]]; then + exit 1 + fi + rewrap_quiet + ;; wrapper) if [[ -z ${2:-} ]]; then echo "Error: wrapper requires app name" diff --git a/scripts/digital_wellbeing/setup_midnight_shutdown.sh b/scripts/digital_wellbeing/setup_midnight_shutdown.sh index 0ba80aa..b11b053 100755 --- a/scripts/digital_wellbeing/setup_midnight_shutdown.sh +++ b/scripts/digital_wellbeing/setup_midnight_shutdown.sh @@ -388,12 +388,11 @@ EOF # Create unlock script with psychological delay cat >"$unlock_script" <<'EOF' #!/bin/bash -# Unlock shutdown schedule config for editing with psychological friction +# Unlock shutdown schedule config for editing with smart friction # This script: -# 1. Makes you wait (psychological friction to discourage casual changes) -# 2. Temporarily removes protection -# 3. Opens the config in an editor -# 4. Re-applies protection after editing +# - NO delay if making schedule STRICTER (earlier shutdown hours) +# - DELAY if making schedule more LENIENT (later shutdown hours) +# - BLOCKS lowering MORNING_END_HOUR (that would shorten the shutdown window) set -euo pipefail @@ -402,6 +401,7 @@ CONFIG_FILE="/etc/shutdown-schedule.conf" CANONICAL_FILE="/usr/local/share/locked-shutdown-schedule.conf" LOG_FILE="/var/log/shutdown-schedule-guard.log" EDITOR="${EDITOR:-nano}" +TEMP_FILE="/tmp/shutdown-schedule-edit.$$" log() { printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2 @@ -416,39 +416,31 @@ fi # Log the unlock attempt log "=== UNLOCK ATTEMPT by $(logname 2>/dev/null || echo 'unknown') from TTY $(tty 2>/dev/null || echo 'unknown') ===" +# Load current values +OLD_MON_WED="" +OLD_THU_SUN="" +OLD_MORNING_END="" +if [[ -f "$CANONICAL_FILE" ]]; then + source "$CANONICAL_FILE" 2>/dev/null || true + OLD_MON_WED="${MON_WED_HOUR:-}" + OLD_THU_SUN="${THU_SUN_HOUR:-}" + OLD_MORNING_END="${MORNING_END_HOUR:-}" +fi + echo "" echo "╔══════════════════════════════════════════════════════════════════╗" -echo "║ SHUTDOWN SCHEDULE CONFIG UNLOCK - PSYCHOLOGICAL FRICTION ║" +echo "║ SHUTDOWN SCHEDULE CONFIG UNLOCK ║" echo "╚══════════════════════════════════════════════════════════════════╝" echo "" -echo "You are about to modify the shutdown schedule configuration." -echo "This file controls when your PC automatically shuts down for" -echo "digital wellbeing purposes." -echo "" echo "Current schedule:" -if [[ -f "$CONFIG_FILE" ]]; then - chattr -i "$CONFIG_FILE" 2>/dev/null || true - source "$CONFIG_FILE" 2>/dev/null || true - chattr +i "$CONFIG_FILE" 2>/dev/null || true - echo " Monday-Wednesday: ${MON_WED_HOUR:-??}:00 - 0${MORNING_END_HOUR:-?}:00" - echo " Thursday-Sunday: ${THU_SUN_HOUR:-??}:00 - 0${MORNING_END_HOUR:-?}:00" -fi +echo " Monday-Wednesday: ${OLD_MON_WED:-??}:00 - 0${OLD_MORNING_END:-?}:00" +echo " Thursday-Sunday: ${OLD_THU_SUN:-??}:00 - 0${OLD_MORNING_END:-?}:00" echo "" -echo "Are you making this change for a good reason, or are you just" -echo "trying to stay up later? Remember why you set these limits." +echo "Rules:" +echo " ✓ Making shutdown EARLIER (stricter) = No delay" +echo " ⏳ Making shutdown LATER (lenient) = ${DELAY_SECONDS}s delay required" +echo " ❌ Lowering MORNING_END_HOUR = BLOCKED (would shorten shutdown window)" echo "" -echo "To proceed, you must wait $DELAY_SECONDS seconds..." -echo "" - -# Countdown with opportunity to cancel -for ((i=DELAY_SECONDS; i>0; i--)); do - printf "\r ⏳ Waiting: %2d seconds remaining... (Ctrl+C to cancel)" "$i" - sleep 1 -done -echo "" -echo "" - -log "User waited through delay, proceeding with unlock" # Stop the path watcher temporarily systemctl stop shutdown-schedule-guard.path 2>/dev/null || true @@ -457,18 +449,131 @@ systemctl stop shutdown-schedule-guard.path 2>/dev/null || true chattr -i -a "$CONFIG_FILE" 2>/dev/null || true chattr -i -a "$CANONICAL_FILE" 2>/dev/null || true -echo "Config file unlocked. Opening editor..." -echo "After saving, protection will be re-applied automatically." +# Copy config to temp file for editing +cp "$CONFIG_FILE" "$TEMP_FILE" + +echo "Opening editor..." echo "" -# Open editor -$EDITOR "$CONFIG_FILE" +# Open editor on temp file +$EDITOR "$TEMP_FILE" + +# Load new values from edited file +NEW_MON_WED="" +NEW_THU_SUN="" +NEW_MORNING_END="" +source "$TEMP_FILE" 2>/dev/null || true +NEW_MON_WED="${MON_WED_HOUR:-}" +NEW_THU_SUN="${THU_SUN_HOUR:-}" +NEW_MORNING_END="${MORNING_END_HOUR:-}" echo "" -echo "Re-applying protection..." +echo "Checking changes..." + +# Check for blocked changes (lowering MORNING_END_HOUR) +if [[ -n "$OLD_MORNING_END" ]] && [[ -n "$NEW_MORNING_END" ]]; then + if [[ "$NEW_MORNING_END" -lt "$OLD_MORNING_END" ]]; then + echo "" + echo "╔══════════════════════════════════════════════════════════════════╗" + echo "║ ❌ CHANGE BLOCKED - CANNOT LOWER MORNING_END_HOUR ❌ ║" + echo "╚══════════════════════════════════════════════════════════════════╝" + echo "" + echo "You tried to lower MORNING_END_HOUR from $OLD_MORNING_END to $NEW_MORNING_END" + echo "This would SHORTEN the shutdown window, making it more lenient." + echo "" + echo "This change is NOT allowed. The shutdown window must end at" + echo "0${OLD_MORNING_END}:00 or later." + echo "" + rm -f "$TEMP_FILE" + # Re-apply protection + chattr +i "$CONFIG_FILE" 2>/dev/null || true + chattr +i "$CANONICAL_FILE" 2>/dev/null || true + systemctl start shutdown-schedule-guard.path 2>/dev/null || true + log "BLOCKED: User tried to lower MORNING_END_HOUR from $OLD_MORNING_END to $NEW_MORNING_END" + exit 1 + fi +fi + +# Check if changes require delay (making schedule more lenient) +NEEDS_DELAY=false +LENIENT_CHANGES=() + +if [[ -n "$OLD_MON_WED" ]] && [[ -n "$NEW_MON_WED" ]]; then + if [[ "$NEW_MON_WED" -gt "$OLD_MON_WED" ]]; then + NEEDS_DELAY=true + LENIENT_CHANGES+=("Mon-Wed: ${OLD_MON_WED}:00 → ${NEW_MON_WED}:00 (later)") + fi +fi + +if [[ -n "$OLD_THU_SUN" ]] && [[ -n "$NEW_THU_SUN" ]]; then + if [[ "$NEW_THU_SUN" -gt "$OLD_THU_SUN" ]]; then + NEEDS_DELAY=true + LENIENT_CHANGES+=("Thu-Sun: ${OLD_THU_SUN}:00 → ${NEW_THU_SUN}:00 (later)") + fi +fi + +# Check for stricter changes (allowed without delay) +STRICTER_CHANGES=() + +if [[ -n "$OLD_MON_WED" ]] && [[ -n "$NEW_MON_WED" ]]; then + if [[ "$NEW_MON_WED" -lt "$OLD_MON_WED" ]]; then + STRICTER_CHANGES+=("Mon-Wed: ${OLD_MON_WED}:00 → ${NEW_MON_WED}:00 (earlier)") + fi +fi + +if [[ -n "$OLD_THU_SUN" ]] && [[ -n "$NEW_THU_SUN" ]]; then + if [[ "$NEW_THU_SUN" -lt "$OLD_THU_SUN" ]]; then + STRICTER_CHANGES+=("Thu-Sun: ${OLD_THU_SUN}:00 → ${NEW_THU_SUN}:00 (earlier)") + fi +fi + +if [[ -n "$OLD_MORNING_END" ]] && [[ -n "$NEW_MORNING_END" ]]; then + if [[ "$NEW_MORNING_END" -gt "$OLD_MORNING_END" ]]; then + STRICTER_CHANGES+=("Morning end: 0${OLD_MORNING_END}:00 → 0${NEW_MORNING_END}:00 (later = longer window)") + fi +fi + +# Report stricter changes +if [[ ${#STRICTER_CHANGES[@]} -gt 0 ]]; then + echo "" + echo "✓ Stricter changes detected (no delay needed):" + for s in "${STRICTER_CHANGES[@]}"; do + echo " • $s" + done +fi + +# Handle lenient changes +if [[ "$NEEDS_DELAY" == true ]]; then + echo "" + echo "⚠️ More lenient changes detected:" + for l in "${LENIENT_CHANGES[@]}"; do + echo " • $l" + done + echo "" + echo "Are you making this change for a good reason, or are you just" + echo "trying to stay up later? Remember why you set these limits." + echo "" + echo "To proceed, you must wait $DELAY_SECONDS seconds..." + echo "" + + # Countdown with opportunity to cancel + for ((i=DELAY_SECONDS; i>0; i--)); do + printf "\r ⏳ Waiting: %2d seconds remaining... (Ctrl+C to cancel)" "$i" + sleep 1 + done + echo "" + echo "" + log "User waited through delay for lenient changes: ${LENIENT_CHANGES[*]}" +else + echo "" + echo "✓ No delay required (schedule is same or stricter)" +fi + +# Apply the changes +cp "$TEMP_FILE" "$CONFIG_FILE" +cp "$TEMP_FILE" "$CANONICAL_FILE" +rm -f "$TEMP_FILE" -# Copy to canonical -cp "$CONFIG_FILE" "$CANONICAL_FILE" chmod 644 "$CONFIG_FILE" chmod 644 "$CANONICAL_FILE" @@ -572,25 +677,37 @@ create_shutdown_timer() { local timer_file="/etc/systemd/system/day-specific-shutdown.timer" - cat >"$timer_file" <<'EOF' + # Calculate earliest shutdown hour (minimum of MON_WED and THU_SUN) + local earliest_hour=$SCHEDULE_MON_WED_HOUR + if [[ $SCHEDULE_THU_SUN_HOUR -lt $earliest_hour ]]; then + earliest_hour=$SCHEDULE_THU_SUN_HOUR + fi + + # Generate timer entries dynamically from earliest_hour to MORNING_END_HOUR + # This ensures timer fires at all possible shutdown times + { + cat <"$timer_file" echo "✓ Created systemd timer: $timer_file" + echo " Timer covers: ${earliest_hour}:00 to 0${SCHEDULE_MORNING_END_HOUR}:00" } # Function to create management script