diff --git a/autoclicker-enhanced.sh b/autoclicker-enhanced.sh new file mode 100755 index 0000000..2a4e53f --- /dev/null +++ b/autoclicker-enhanced.sh @@ -0,0 +1,235 @@ +#!/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" \ No newline at end of file diff --git a/autoclicker.sh b/autoclicker.sh index 00c55e1..6fd25e5 100755 --- a/autoclicker.sh +++ b/autoclicker.sh @@ -133,4 +133,3 @@ while true; do sleep "$delay" fi done - diff --git a/click-visualizer.html b/click-visualizer.html new file mode 100644 index 0000000..03d6b57 --- /dev/null +++ b/click-visualizer.html @@ -0,0 +1,619 @@ + + + + + + Autoclicker Timing Visualizer + + + +
+

🖱️ Autoclicker Timing Visualizer

+ +
+ 📊 Real-time Data Server +

This visualizer connects to a local Python server that serves real log files from /tmp/autoclicker_logs/

+

Make sure to run: python3 click_server.py in a separate terminal

+
+ +
+ Testing API connection... +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
Click Timing Distribution
+
+ +
+
+ +
+
Delay Over Time
+
+ +
+
+ +
+

Available Log Files

+
+
Loading log files from server...
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/click_server.py b/click_server.py new file mode 100755 index 0000000..dbb7059 --- /dev/null +++ b/click_server.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +""" +Simple web server for autoclicker visualization +Serves the HTML visualizer and provides API endpoints for CSV log files +Converts CSV to JSON for the frontend +""" + +import os +import json +import csv +import http.server +import socketserver +from urllib.parse import urlparse, parse_qs +from datetime import datetime + +PORT = 8661 +LOG_DIR = "/tmp/autoclicker_logs" + +class ClickServerHandler(http.server.SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, directory=".", **kwargs) + + def do_GET(self): + # Handle API requests + if self.path.startswith('/api/'): + self.handle_api_request() + return + + # Serve the visualizer HTML for root path + if self.path == '/' or self.path == '/index.html': + self.path = '/click-visualizer.html' + + # Serve static files normally + return super().do_GET() + + def handle_api_request(self): + """Handle API endpoints for log file operations""" + try: + if self.path.startswith('/api/logs'): + self.handle_logs_request() + elif self.path.startswith('/api/log/'): + self.handle_log_file_request() + else: + self.send_error(404, "API endpoint not found") + except Exception as e: + self.send_error(500, f"Server error: {str(e)}") + + def handle_logs_request(self): + """Return list of available log files""" + if not os.path.exists(LOG_DIR): + self.send_json_response({"error": "Log directory not found", "logs": []}) + return + + try: + files = [] + for filename in sorted(os.listdir(LOG_DIR), reverse=True): + if filename.endswith('.csv'): + filepath = os.path.join(LOG_DIR, filename) + stat = os.stat(filepath) + + # Check if CSV is valid + if self.is_valid_csv(filepath): + files.append({ + "name": filename, + "size": self.format_file_size(stat.st_size), + "date": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'), + "path": filename, + "valid": True + }) + else: + files.append({ + "name": filename, + "size": self.format_file_size(stat.st_size), + "date": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'), + "path": filename, + "valid": False, + "error": "Invalid CSV format" + }) + + self.send_json_response({"logs": files}) + except Exception as e: + self.send_json_response({"error": str(e), "logs": []}) + + def handle_log_file_request(self): + """Return contents of a specific log file as JSON""" + # Extract filename from path + parts = self.path.split('/') + if len(parts) < 4: + self.send_error(400, "Invalid log file request") + return + + filename = parts[3] + filepath = os.path.join(LOG_DIR, filename) + + if not os.path.exists(filepath): + self.send_error(404, "Log file not found") + return + + try: + if filename.endswith('.csv'): + data = self.csv_to_json(filepath) + if data is None: + self.send_json_response({ + "error": "Failed to parse CSV file", + "filename": filename, + "valid": False + }) + return + + self.send_json_response({ + "data": data, + "filename": filename, + "valid": True + }) + else: + self.send_error(400, "Unsupported file format") + except Exception as e: + self.send_error(500, f"Error reading log file: {str(e)}") + + def csv_to_json(self, filepath): + """Convert CSV file to JSON array""" + try: + data = [] + with open(filepath, 'r') as f: + reader = csv.DictReader(f) + for row in reader: + data.append({ + "click": int(row["click_number"]), + "delay_ms": int(row["delay_ms"]), + "timestamp": int(row["timestamp"]) + }) + return data + except Exception as e: + print(f"Error parsing CSV {filepath}: {str(e)}") + return None + + def is_valid_csv(self, filepath): + """Check if a file contains valid CSV""" + try: + with open(filepath, 'r') as f: + reader = csv.DictReader(f) + # Try to read first row + next(reader) + return True + except Exception: + return False + + def send_json_response(self, data): + """Send JSON response with proper headers""" + response = json.dumps(data, indent=2) + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Content-Length', str(len(response))) + self.end_headers() + self.wfile.write(response.encode()) + + def format_file_size(self, size_bytes): + """Format file size in human-readable format""" + for unit in ['B', 'KB', 'MB', 'GB']: + if size_bytes < 1024.0: + return f"{size_bytes:.1f} {unit}" + size_bytes /= 1024.0 + return f"{size_bytes:.1f} TB" + +def run_server(): + """Start the web server""" + print(f"Starting autoclicker visualization server on port {PORT}") + print(f"Log directory: {LOG_DIR}") + print("Open your browser to: http://localhost:8661") + print("Press Ctrl+C to stop the server") + + try: + with socketserver.TCPServer(("", PORT), ClickServerHandler) as httpd: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nServer stopped") + except Exception as e: + print(f"Server error: {str(e)}") + +if __name__ == "__main__": + run_server() \ No newline at end of file diff --git a/home_dotfiles/.config/sway/config b/home_dotfiles/.config/sway/config index 7e66c41..4117cf3 100644 --- a/home_dotfiles/.config/sway/config +++ b/home_dotfiles/.config/sway/config @@ -74,24 +74,24 @@ output * bg /home/raga/.config/sway/wallpaper.jpg fill # Multi-monitor setup # You can get your output names by running: swaymsg -t get_outputs # DP-2 is the primary monitor on the left, and DP-1 is on the right. -output HDMI-A-1 { +output DP-1 { position 0 0 -# ECO -# mode 2560x1440@60Hz + # ECO + # mode 2560x1440@60Hz -# FULL - mode 2560x1440@120Hz + # FULL + mode 3840x2160@165Hz } -output DP-1 { - position 2560 0 +output DP-2 { + position 3840 0 -# ECO -# mode 2560x1440@60Hz + # ECO + mode 2560x1440@60Hz -# FULL - modeline 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync + # FULL +# modeline 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync } ############################################################################### @@ -457,10 +457,10 @@ mode "Resize Mode" { bindsym Up resize shrink height 6 px or 6 ppt bindsym Right resize grow width 6 px or 6 ppt - bindsym Shift+Left resize shrink width 24 px or 24 ppt - bindsym Shift+Down resize grow height 24 px or 24 ppt - bindsym Shift+Up resize shrink height 24 px or 24 ppt - bindsym Shift+Right resize grow width 24 px or 24 ppt + bindsym Shift+Left resize shrink width 48 px or 48 ppt + bindsym Shift+Down resize grow height 48 px or 48 ppt + bindsym Shift+Up resize shrink height 48 px or 48 ppt + bindsym Shift+Right resize grow width 48 px or 48 ppt ## Resize // Resize Window // k j h l ## bindsym $left resize shrink width 6 px or 6 ppt @@ -468,10 +468,10 @@ mode "Resize Mode" { bindsym $up resize shrink height 6 px or 6 ppt bindsym $right resize grow width 6 px or 6 ppt - bindsym Shift+$left resize shrink width 24 px or 24 ppt - bindsym Shift+$down resize grow height 24 px or 24 ppt - bindsym Shift+$up resize shrink height 24 px or 24 ppt - bindsym Shift+$right resize grow width 24 px or 24 ppt + bindsym Shift+$left resize shrink width 48 px or 48 ppt + bindsym Shift+$down resize grow height 48 px or 48 ppt + bindsym Shift+$up resize shrink height 48 px or 48 ppt + bindsym Shift+$right resize grow width 48 px or 48 ppt ## Resize // Exit Resize Mode // Escape or Enter ## bindsym Return mode "default" @@ -546,6 +546,7 @@ exec lxqt-policykit-agent # Authentication agent (comment out if not needed) exec --no-startup-id blueman-applet exec --no-startup-id nm-applet +#exec --no-startup-id home_dotfiles/.config/sway/set_random_bg.sh # Include additional config files if they exist include /etc/sway/config.d/* diff --git a/home_dotfiles/.config/sway/i3status-rust.toml b/home_dotfiles/.config/sway/i3status-rust.toml index f48d615..423379e 100644 --- a/home_dotfiles/.config/sway/i3status-rust.toml +++ b/home_dotfiles/.config/sway/i3status-rust.toml @@ -10,6 +10,15 @@ click = [ {button = "left", cmd = "~/.config/sway/osrs-mode-toggle.sh", update = true} ] +[[block]] +block = "custom" +command = "echo ''" +format = " $text " +interval = 1 +click = [ + {button = "left", cmd = "~/.config/sway/set_random_bg.sh", update = false}, +] + #[[block]] #block = "custom" #command = "~/.config/sway/get-focused-window.sh" diff --git a/home_dotfiles/.config/sway/set_random_bg.sh b/home_dotfiles/.config/sway/set_random_bg.sh new file mode 100755 index 0000000..7e1a383 --- /dev/null +++ b/home_dotfiles/.config/sway/set_random_bg.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Find a random image file +#IMAGE=$(find /run/media/raga/970/qBittorrent/nsfw/xd -type f \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' \) | shuf -n 1) +IMAGE=$(find /run/media/raga/970/images/cars/wallpaper -type f \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' \) | shuf -n 1) + +if [ -n "$IMAGE" ]; then + echo "Image: $IMAGE" + + # Try to get image dimensions using identify (ImageMagick) + DIMENSIONS=$(identify -format "%wx%h" "$IMAGE" 2>/dev/null) + + if [ -n "$DIMENSIONS" ]; then + echo "Dimensions: $DIMENSIONS" + WIDTH=$(echo "$DIMENSIONS" | cut -d'x' -f1) + HEIGHT=$(echo "$DIMENSIONS" | cut -d'x' -f2) + + # Determine if portrait (height > width) or landscape + if [ "$HEIGHT" -gt "$WIDTH" ]; then + # Portrait image - use "fit" to maintain aspect ratio + echo "Portrait image - using fit" + swaymsg "output * bg \"$IMAGE\" fit" + else + # Landscape image - use "fill" to fill the screen + echo "Landscape image - using fill" + swaymsg "output * bg \"$IMAGE\" fill" + fi + else + # Fallback to fill if we can't determine dimensions + echo "Could not determine dimensions - using fill" + swaymsg "output * bg \"$IMAGE\" fill" + fi +fi diff --git a/home_dotfiles/.config/sway/weather-sway.sh b/home_dotfiles/.config/sway/weather-sway.sh index bedbc99..bd604a7 100755 --- a/home_dotfiles/.config/sway/weather-sway.sh +++ b/home_dotfiles/.config/sway/weather-sway.sh @@ -2,12 +2,12 @@ set -Eeu -o pipefail -# Simplified weather script for i3status-rust -# Based on the original weather script but adapted for Sway/i3status-rust +# Improved weather script for i3status-rust using JSON API +# Based on the original weather script but optimized for Sway/i3status-rust -# Information on the various formats: https://github.com/chubin/wttr.in -VALUE_WEATHER_FORMAT=${weather_format:-"%c%f"} -VALUE_WEATHER_FORMAT="?format=${VALUE_WEATHER_FORMAT}" +# Location (defaults to Pärnu as in your original script) +VALUE_WEATHER_LOCATION=${weather_location:-"Pärnu"} +VALUE_WEATHER_ERROR_MESSAGE=${error_message:-"⛔"} # Determine units to use for temperature # We don't supply a default here because wttr.in is "smart" enough to choose for us @@ -16,40 +16,100 @@ if [ -n "${WEATHER_UNIT}" ]; then WEATHER_UNIT="&${WEATHER_UNIT}" fi -# Location (defaults to Pärnu as in your original script) -VALUE_WEATHER_LOCATION=${weather_location:-"Pärnu"} -VALUE_WEATHER_ERROR_MESSAGE=${error_message:-"⛔"} -VALUE_FETCH_WEATHER_URL="https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_WEATHER_FORMAT}${WEATHER_UNIT}" +# Use JSON API to get all data in a single request +VALUE_FETCH_WEATHER_URL="https://wttr.in/${VALUE_WEATHER_LOCATION}?format=j1" -# Get weather data -WEATHER=$(curl -sS "$VALUE_FETCH_WEATHER_URL" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}") - -# Get sunrise/sunset data -VALUE_SUNSET_SUNRISE_FORMAT="?format=%S-%s%20%m+" -SUNRISE_SUNSET=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_SUNSET_SUNRISE_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "") - -if [ -n "$SUNRISE_SUNSET" ]; then - SUNRISE_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 1-5) - SUNSET_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 10-14) - SUNRISE_SUNSET_TEXT=" (${SUNRISE_VALUE}-${SUNSET_VALUE})" -else - SUNRISE_SUNSET_TEXT="" -fi +# Get weather data using JSON API +WEATHER_JSON=$(curl -sS "$VALUE_FETCH_WEATHER_URL" 2>/dev/null || echo "{}") # Check for errors -if echo "${WEATHER}" | grep -q -P "Unknown\slocation"; then - WEATHER=${VALUE_WEATHER_ERROR_MESSAGE} +if echo "$WEATHER_JSON" | grep -q "Unknown location"; then + echo "${VALUE_WEATHER_ERROR_MESSAGE}" + exit 0 fi -# Output for i3status-rust (plain text) -echo "${WEATHER}${SUNRISE_SUNSET_TEXT}" +# Extract data using jq (should be available on most systems) +if command -v jq &>/dev/null; then + # Extract current weather condition and temperature + WEATHER_CONDITION=$(echo "$WEATHER_JSON" | jq -r '.current_condition[0].weatherDesc[0].value' 2>/dev/null || echo "") + TEMP_C=$(echo "$WEATHER_JSON" | jq -r '.current_condition[0].temp_C' 2>/dev/null || echo "") + + # Extract sunrise and sunset times + SUNRISE=$(echo "$WEATHER_JSON" | jq -r '.weather[0].astronomy[0].sunrise' 2>/dev/null || echo "") + SUNSET=$(echo "$WEATHER_JSON" | jq -r '.weather[0].astronomy[0].sunset' 2>/dev/null || echo "") + + # Convert to simple format if we have data + if [ -n "$WEATHER_CONDITION" ] && [ -n "$TEMP_C" ]; then + # Map weather conditions to icons (simplified) + case "$WEATHER_CONDITION" in + *"Sunny"*|*"Clear"*) ICON="☀️";; + *"Partly cloudy"*|*"Cloudy"*) ICON="⛅";; + *"Overcast"*) ICON="☁️";; + *"Rain"*|*"Drizzle"*) ICON="🌧️";; + *"Snow"*) ICON="❄️";; + *"Thunder"*) ICON="⛈️";; + *"Fog"*|*"Mist"*) ICON="🌫️";; + *) ICON="🌤️";; + esac + + WEATHER="${ICON} ${TEMP_C}°C" + + # Format sunrise/sunset times (convert from "09:10 AM" to "09:10") + if [ -n "$SUNRISE" ] && [ -n "$SUNSET" ]; then + # Remove AM/PM and convert to 24-hour format + SUNRISE_24=$(echo "$SUNRISE" | sed 's/ AM//;s/ PM//;s/^0//' | awk -F: '{if($1==12) print "00:"$2; else if($1<12) print $1":"$2; else print $1":"$2}') + SUNSET_24=$(echo "$SUNSET" | sed 's/ AM//;s/ PM//;s/^0//' | awk -F: '{if($1==12) print "00:"$2; else if($1<12) print $1":"$2; else print $1":"$2}') + + # Remove leading zeros from hours for consistency + SUNRISE_24=$(echo "$SUNRISE_24" | sed 's/^0//') + SUNSET_24=$(echo "$SUNSET_24" | sed 's/^0//') + + SUNRISE_SUNSET_TEXT=" (${SUNRISE_24}-${SUNSET_24})" + else + SUNRISE_SUNSET_TEXT="" + fi + + # Output for i3status-rust (plain text) + echo "${WEATHER}${SUNRISE_SUNSET_TEXT}" + else + echo "${VALUE_WEATHER_ERROR_MESSAGE}" + fi +else + # Fallback to original method if jq is not available + echo "⚠️ jq not found, using fallback method" + + # Get basic weather data + VALUE_WEATHER_FORMAT=${weather_format:-"%c%f"} + VALUE_WEATHER_FORMAT="?format=${VALUE_WEATHER_FORMAT}" + WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_WEATHER_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}") + + # Get sunrise/sunset data + VALUE_SUNSET_SUNRISE_FORMAT="?format=%S-%s" + SUNRISE_SUNSET=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_SUNSET_SUNRISE_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "") + + if [ -n "$SUNRISE_SUNSET" ]; then + SUNRISE_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 1-5) + SUNSET_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 7-11) + SUNRISE_SUNSET_TEXT=" (${SUNRISE_VALUE}-${SUNSET_VALUE})" + else + SUNRISE_SUNSET_TEXT="" + fi + + # Check for errors + if echo "${WEATHER}" | grep -q -P "Unknown\slocation"; then + WEATHER=${VALUE_WEATHER_ERROR_MESSAGE} + fi + + # Output for i3status-rust (plain text) + echo "${WEATHER}${SUNRISE_SUNSET_TEXT}" +fi # Handle click events (for i3status-rust custom block) if [ "${BLOCK_BUTTON:-}" = "1" ]; then - # Left click - show detailed weather - FULL_WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S+%s" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}") - notify-send "Weather Details" "$FULL_WEATHER" -t 10000 + # Left click - show detailed weather using JSON + DETAILED_WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S-%s" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}") + notify-send "Weather Details" "$DETAILED_WEATHER" -t 10000 elif [ "${BLOCK_BUTTON:-}" = "3" ]; then # Right click - open weather website xdg-open "https://wttr.in/${VALUE_WEATHER_LOCATION}" >/dev/null 2>&1 & -fi \ No newline at end of file +fi \ No newline at end of file diff --git a/home_dotfiles/wallpapers/1742471076220983.jpg b/home_dotfiles/wallpapers/1742471076220983.jpg new file mode 100755 index 0000000..0f4142c Binary files /dev/null and b/home_dotfiles/wallpapers/1742471076220983.jpg differ diff --git a/install-arch-sway.sh b/install-arch-sway.sh index 18cc1f0..9452f97 100755 --- a/install-arch-sway.sh +++ b/install-arch-sway.sh @@ -60,6 +60,13 @@ for script in osrs-mode-status.sh osrs-mode-toggle.sh get-focused-window.sh; do fi done +# === CHANGE WALLPAPER SCRIPT === +echo "" +echo "Installing change wallpaper script..." +cp $SCRIPT_DIR/home_dotfiles/.config/sway/set_random_bg.sh ~/.config/sway/set_random_bg.sh +chmod +x ~/.config/sway/set_random_bg.sh +echo "✓ Change wallpaper script installed to ~/.config/sway/set_random_bg.sh" + # === WEATHER SCRIPT === # Comment out this section if you don't want the weather script echo "" @@ -103,8 +110,9 @@ echo "✓ swayimg set as default image viewer" # === SET WALLPAPER === echo "" echo "Setting wallpaper..." -#WALLPAPER_NAME="chris-czermak-PamFFHL6fVY-unsplash.jpg" -WALLPAPER_NAME="lucas-gallone-2dClJIAR404-unsplash.jpg" +WALLPAPER_NAME="chris-czermak-PamFFHL6fVY-unsplash.jpg" +#WALLPAPER_NAME="lucas-gallone-2dClJIAR404-unsplash.jpg" +#WALLPAPER_NAME="1742471076220983.jpg" WALLPAPER_PATH="$SCRIPT_DIR/home_dotfiles/wallpapers/$WALLPAPER_NAME" cp $WALLPAPER_PATH ~/.config/sway/wallpaper.jpg echo "✓ Wallpaper installed to ~/.config/sway/wallpaper.jpg" diff --git a/visualize-clicks.sh b/visualize-clicks.sh new file mode 100755 index 0000000..1ec927d --- /dev/null +++ b/visualize-clicks.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# Script to visualize autoclicker log files +# Starts Python server and opens the HTML visualization tool + +VISUALIZER_FILE="click-visualizer.html" +LOG_DIR="/tmp/autoclicker_logs" +SERVER_SCRIPT="click_server.py" +SERVER_PORT=8661 + +# Check if visualization file exists +if [ ! -f "$VISUALIZER_FILE" ]; then + echo "Error: Visualization tool not found at $VISUALIZER_FILE" + exit 1 +fi + +# Check if server script exists +if [ ! -f "$SERVER_SCRIPT" ]; then + echo "Error: Server script not found at $SERVER_SCRIPT" + exit 1 +fi + +# Check if Python is available +if ! command -v python3 &> /dev/null; then + echo "Error: Python 3 is not installed. Install it with: sudo pacman -S python" + exit 1 +fi + +# Check if we have any log files +if [ ! -d "$LOG_DIR" ] || [ -z "$(ls -A "$LOG_DIR")" ]; then + echo "No autoclicker log files found in $LOG_DIR" + echo "Run the enhanced autoclicker first to generate data." + echo "" + echo "Starting server anyway (you can generate logs while it's running)..." +fi + +echo "Starting autoclicker visualization server..." +echo "Server will be available at: http://localhost:$SERVER_PORT" +echo "Log directory: $LOG_DIR" +echo "" + +# Start the Python server in the background +python3 "$SERVER_SCRIPT" & +SERVER_PID=$! + +# Give the server a moment to start +sleep 2 + +# Check if server started successfully +if ! ps -p $SERVER_PID > /dev/null; then + echo "Error: Failed to start server" + exit 1 +fi + +# Open browser +if command -v xdg-open &> /dev/null; then + xdg-open "http://localhost:$SERVER_PORT" +elif command -v open &> /dev/null; then + open "http://localhost:$SERVER_PORT" +else + echo "Server started successfully!" + echo "Open your browser and navigate to: http://localhost:$SERVER_PORT" +fi + +echo "" +echo "Server is running in the background (PID: $SERVER_PID)" +echo "Press Ctrl+C to stop the server when you're done" +echo "" + +# Wait for server to finish (it will run until manually stopped) +wait $SERVER_PID \ No newline at end of file