#!/bin/bash # Enhanced Autoclicker script for Sway with improved antiban features # Uses ydotool for Wayland/Sway environments # Features human-like timing patterns and CSV logging for visualization # Press Ctrl+C to stop (standard shell interrupt) # Requires ydotool and ydotoold daemon running # Configuration MIN_DELAY=50 # Minimum delay in milliseconds MAX_DELAY=700 # Maximum delay in milliseconds MIN_CLICKS_BEFORE_BREAK=30 # Minimum clicks before break MAX_CLICKS_BEFORE_BREAK=200 # Maximum clicks before break MIN_BREAK=3 # Minimum break duration in seconds MAX_BREAK=15 # Maximum break duration in seconds # Human-like timing parameters FASTIGUE_FACTOR=0.95 # Gradually slow down over time (0.95 = 5% slower) REACTION_VARIATION=0.3 # 30% variation in reaction times PATTERN_BREAK_CHANCE=0.05 # 5% chance to break pattern # File paths STATE_FILE="/tmp/autoclicker_running" LOCK_FILE="/tmp/autoclicker_lock" LOG_DIR="/tmp/autoclicker_logs" LOG_FILE="$LOG_DIR/click_timings_$(date +%Y%m%d_%H%M%S).csv" # Create log directory if it doesn't exist mkdir -p "$LOG_DIR" # Initialize CSV log file with header echo "click_number,delay_ms,timestamp" > "$LOG_FILE" # Check if ydotool is available if ! command -v ydotool &> /dev/null; then echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool" exit 1 fi # Check if bc is available (needed for calculations) if ! command -v bc &> /dev/null; then echo "Error: bc is not installed. Install it with: sudo pacman -S bc" exit 1 fi # Check if script is already running (prevent multiple instances) if [ -f "$LOCK_FILE" ]; then echo "Autoclicker is already running!" echo "Press Ctrl+C in the terminal where it's running to stop it." # Clean up the incomplete log file that was just created rm -f "$LOG_FILE" exit 0 fi # Create lock file echo $$ > "$LOCK_FILE" # Global exit flag EXIT_REQUESTED=0 # Clean up on exit cleanup() { EXIT_REQUESTED=1 rm -f "$LOCK_FILE" rm -f "$STATE_FILE" echo "" echo "Autoclicker stopped." exit 0 } # Set up traps for cleanup trap cleanup INT TERM # Check if ydotoold daemon is running if ! pgrep -x "ydotoold" > /dev/null; then echo "Starting ydotoold daemon..." sudo ydotoold & sleep 1 fi # Create state file to indicate running echo "running" > "$STATE_FILE" # Function to log click timing to CSV log_click_timing() { local click_number=$1 local delay_ms=$2 local timestamp=$(date +%s%3N) # Milliseconds since epoch echo "$click_number,$delay_ms,$timestamp" >> "$LOG_FILE" } # Function to generate random delay with human-like distribution get_human_delay() { local min=$1 local max=$2 local base_delay=$((min + RANDOM % (max - min + 1))) # Apply reaction time variation (30% up or down) local variation=$(echo "scale=3; $base_delay * $REACTION_VARIATION * (2 * $RANDOM / 32767 - 1)" | bc) local varied_delay=$(echo "$base_delay + $variation" | bc) # Ensure we stay within reasonable bounds if (( $(echo "$varied_delay < $min" | bc -l) )); then varied_delay=$min elif (( $(echo "$varied_delay > $max" | bc -l) )); then varied_delay=$max fi # Convert to seconds for sleep function echo "scale=3; $varied_delay / 1000" | bc } # Function to generate random number of clicks before break get_random_clicks() { local min=$1 local max=$2 echo $((min + RANDOM % (max - min + 1))) } # Function to sleep with interrupt checking interruptible_sleep() { local duration=$1 local start_time=$(date +%s.%N) local end_time=$(echo "$start_time + $duration" | bc) while true; do if [ $EXIT_REQUESTED -eq 1 ]; then return 1 # Exit requested fi local current_time=$(date +%s.%N) if (( $(echo "$current_time >= $end_time" | bc -l) )); then return 0 # Sleep completed fi sleep 0.1 done } # Function to apply fatigue effect (gradually slow down) apply_fatigue() { local current_delay=$1 local click_count=$2 local fatigue_effect=$(echo "scale=3; $current_delay * (1 + $FASTIGUE_FACTOR * $click_count / 1000)" | bc) echo "$fatigue_effect" } # Function to randomly break pattern (5% chance) should_break_pattern() { local threshold=$(echo "$PATTERN_BREAK_CHANCE * 32767" | bc | cut -d'.' -f1) [ $RANDOM -lt $threshold ] } echo "==========================================" echo "Enhanced Autoclicker started!" echo "==========================================" echo "Configuration:" echo " - Click delay: ${MIN_DELAY}-${MAX_DELAY}ms (human-like variation)" echo " - Break every: ${MIN_CLICKS_BEFORE_BREAK}-${MAX_CLICKS_BEFORE_BREAK} clicks (random)" echo " - Break duration: ${MIN_BREAK}-${MAX_BREAK}s" echo " - Fatigue factor: ${FASTIGUE_FACTOR} (gradual slowdown)" echo " - Pattern break chance: ${PATTERN_BREAK_CHANCE} (5%)" echo " - Logging to: $LOG_FILE" echo "" echo "Press Ctrl+C to stop" echo "==========================================" echo "" click_count=0 total_clicks=0 clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK) echo "Next break will be after $clicks_before_break clicks" echo "" while true; do if [ $EXIT_REQUESTED -eq 1 ]; then echo "Exit signal received!" break fi # Generate base random delay base_delay=$(get_human_delay $MIN_DELAY $MAX_DELAY) # Apply fatigue effect (gradually slow down) final_delay=$(apply_fatigue "$base_delay" "$click_count") # Randomly break pattern (5% chance) if should_break_pattern; then # Add a longer, unpredictable delay pattern_break_delay=$(echo "scale=3; ($MAX_DELAY * 2 + $RANDOM % 1000) / 1000" | bc) echo "[Pattern Break] Adding unpredictable delay of $(echo "$pattern_break_delay * 1000" | bc | cut -d'.' -f1)ms" if ! interruptible_sleep "$pattern_break_delay"; then break fi fi # Perform click (suppress ydotool output) ydotool click 0xC0 2>/dev/null click_count=$((click_count + 1)) total_clicks=$((total_clicks + 1)) # Convert delay to milliseconds for display delay_ms=$(echo "$final_delay * 1000" | bc | cut -d'.' -f1) # Log click timing to CSV log_click_timing "$total_clicks" "$delay_ms" # Show each click with its delay echo "Click #$total_clicks (delay: ${delay_ms}ms, next break in $((clicks_before_break - click_count)) clicks)" # Check if it's time for a break if [ $click_count -ge $clicks_before_break ]; then break_duration=$(get_human_delay $((MIN_BREAK * 1000)) $((MAX_BREAK * 1000))) echo "" echo "[Break] Completed $clicks_before_break clicks (total: $total_clicks). Taking a break for ${break_duration}s..." if ! interruptible_sleep "$break_duration"; then break fi click_count=0 # Generate new random click count for next cycle clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK) echo "[Resumed] Next break will be after $clicks_before_break clicks" echo "" else # Wait the calculated delay before next click if ! interruptible_sleep "$final_delay"; then break fi fi done echo "Click timings saved to: $LOG_FILE"