fixes for compulsiuve opening and midnight shutdown

This commit is contained in:
Krzysztof kuhy Rudnicki 2026-01-03 16:03:22 +01:00
parent 4ebd6c52be
commit 2fcf010c8a
2 changed files with 258 additions and 56 deletions

View File

@ -9,10 +9,18 @@
set -euo pipefail set -euo pipefail
# Source common library for shared functions # Send desktop notification (inlined from common.sh to avoid dependency issues
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" # when script is installed to /usr/local/bin)
# shellcheck source=../lib/common.sh notify() {
source "$SCRIPT_DIR/../lib/common.sh" 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 # Configuration
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/compulsive-block" 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 "Each app can now only be opened once per hour."
echo "State files stored in: $STATE_DIR" echo "State files stored in: $STATE_DIR"
echo "Logs stored in: $LOG_FILE" 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 # Uninstall all wrappers
@ -266,6 +339,9 @@ uninstall_all() {
rm -f "/usr/local/bin/block-compulsive-opening.sh" rm -f "/usr/local/bin/block-compulsive-opening.sh"
# Remove pacman hook
uninstall_pacman_hook
echo "" echo ""
echo "Uninstallation complete." echo "Uninstallation complete."
} }
@ -402,6 +478,13 @@ main() {
reset-all) reset-all)
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) wrapper)
if [[ -z ${2:-} ]]; then if [[ -z ${2:-} ]]; then
echo "Error: wrapper requires app name" echo "Error: wrapper requires app name"

View File

@ -388,12 +388,11 @@ EOF
# Create unlock script with psychological delay # Create unlock script with psychological delay
cat >"$unlock_script" <<'EOF' cat >"$unlock_script" <<'EOF'
#!/bin/bash #!/bin/bash
# Unlock shutdown schedule config for editing with psychological friction # Unlock shutdown schedule config for editing with smart friction
# This script: # This script:
# 1. Makes you wait (psychological friction to discourage casual changes) # - NO delay if making schedule STRICTER (earlier shutdown hours)
# 2. Temporarily removes protection # - DELAY if making schedule more LENIENT (later shutdown hours)
# 3. Opens the config in an editor # - BLOCKS lowering MORNING_END_HOUR (that would shorten the shutdown window)
# 4. Re-applies protection after editing
set -euo pipefail set -euo pipefail
@ -402,6 +401,7 @@ CONFIG_FILE="/etc/shutdown-schedule.conf"
CANONICAL_FILE="/usr/local/share/locked-shutdown-schedule.conf" CANONICAL_FILE="/usr/local/share/locked-shutdown-schedule.conf"
LOG_FILE="/var/log/shutdown-schedule-guard.log" LOG_FILE="/var/log/shutdown-schedule-guard.log"
EDITOR="${EDITOR:-nano}" EDITOR="${EDITOR:-nano}"
TEMP_FILE="/tmp/shutdown-schedule-edit.$$"
log() { log() {
printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2 printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2
@ -416,23 +416,139 @@ fi
# Log the unlock attempt # Log the unlock attempt
log "=== UNLOCK ATTEMPT by $(logname 2>/dev/null || echo 'unknown') from TTY $(tty 2>/dev/null || echo 'unknown') ===" 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 "╔══════════════════════════════════════════════════════════════════╗" echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ SHUTDOWN SCHEDULE CONFIG UNLOCK - PSYCHOLOGICAL FRICTION ║" echo "║ SHUTDOWN SCHEDULE CONFIG UNLOCK ║"
echo "╚══════════════════════════════════════════════════════════════════╝" echo "╚══════════════════════════════════════════════════════════════════╝"
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:" echo "Current schedule:"
if [[ -f "$CONFIG_FILE" ]]; then echo " Monday-Wednesday: ${OLD_MON_WED:-??}:00 - 0${OLD_MORNING_END:-?}:00"
chattr -i "$CONFIG_FILE" 2>/dev/null || true echo " Thursday-Sunday: ${OLD_THU_SUN:-??}:00 - 0${OLD_MORNING_END:-?}:00"
source "$CONFIG_FILE" 2>/dev/null || true echo ""
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 ""
# Stop the path watcher temporarily
systemctl stop shutdown-schedule-guard.path 2>/dev/null || true
# Remove immutable attributes
chattr -i -a "$CONFIG_FILE" 2>/dev/null || true
chattr -i -a "$CANONICAL_FILE" 2>/dev/null || true
# Copy config to temp file for editing
cp "$CONFIG_FILE" "$TEMP_FILE"
echo "Opening editor..."
echo ""
# 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 "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 "$CONFIG_FILE" 2>/dev/null || true
echo " Monday-Wednesday: ${MON_WED_HOUR:-??}:00 - 0${MORNING_END_HOUR:-?}:00" chattr +i "$CANONICAL_FILE" 2>/dev/null || true
echo " Thursday-Sunday: ${THU_SUN_HOUR:-??}:00 - 0${MORNING_END_HOUR:-?}:00" 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
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 ""
echo "Are you making this change for a good reason, or are you just" 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 "trying to stay up later? Remember why you set these limits."
@ -447,28 +563,17 @@ for ((i=DELAY_SECONDS; i>0; i--)); do
done done
echo "" echo ""
echo "" echo ""
log "User waited through delay for lenient changes: ${LENIENT_CHANGES[*]}"
log "User waited through delay, proceeding with unlock" else
# Stop the path watcher temporarily
systemctl stop shutdown-schedule-guard.path 2>/dev/null || true
# Remove immutable attributes
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."
echo "" echo ""
echo "✓ No delay required (schedule is same or stricter)"
fi
# Open editor # Apply the changes
$EDITOR "$CONFIG_FILE" cp "$TEMP_FILE" "$CONFIG_FILE"
cp "$TEMP_FILE" "$CANONICAL_FILE"
rm -f "$TEMP_FILE"
echo ""
echo "Re-applying protection..."
# Copy to canonical
cp "$CONFIG_FILE" "$CANONICAL_FILE"
chmod 644 "$CONFIG_FILE" chmod 644 "$CONFIG_FILE"
chmod 644 "$CANONICAL_FILE" chmod 644 "$CANONICAL_FILE"
@ -572,25 +677,37 @@ create_shutdown_timer() {
local timer_file="/etc/systemd/system/day-specific-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 <<EOF
[Unit] [Unit]
Description=Timer for automatic PC shutdown with day-specific windows Description=Timer for automatic PC shutdown with day-specific windows
Requires=day-specific-shutdown.service Requires=day-specific-shutdown.service
[Timer] [Timer]
OnCalendar=*-*-* 23:00:00 EOF
OnCalendar=*-*-* 23:30:00 # Evening hours: from earliest shutdown hour to 23:30
OnCalendar=*-*-* 00:00:00 for hour in $(seq "$earliest_hour" 23); do
OnCalendar=*-*-* 00:30:00 printf 'OnCalendar=*-*-* %02d:00:00\n' "$hour"
OnCalendar=*-*-* 01:00:00 printf 'OnCalendar=*-*-* %02d:30:00\n' "$hour"
OnCalendar=*-*-* 01:30:00 done
OnCalendar=*-*-* 02:00:00
OnCalendar=*-*-* 02:30:00 # Morning hours: from 00:00 to MORNING_END_HOUR
OnCalendar=*-*-* 03:00:00 for hour in $(seq 0 "$SCHEDULE_MORNING_END_HOUR"); do
OnCalendar=*-*-* 03:30:00 printf 'OnCalendar=*-*-* %02d:00:00\n' "$hour"
OnCalendar=*-*-* 04:00:00 if [[ $hour -lt $SCHEDULE_MORNING_END_HOUR ]]; then
OnCalendar=*-*-* 04:30:00 printf 'OnCalendar=*-*-* %02d:30:00\n' "$hour"
OnCalendar=*-*-* 05:00:00 fi
done
cat <<EOF
Persistent=false Persistent=false
AccuracySec=1s AccuracySec=1s
WakeSystem=false WakeSystem=false
@ -599,8 +716,10 @@ RandomizedDelaySec=0
[Install] [Install]
WantedBy=timers.target WantedBy=timers.target
EOF EOF
} >"$timer_file"
echo "✓ Created systemd timer: $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 # Function to create management script