From f40eda8a859add4f4746f1b9a07b9269156e2748 Mon Sep 17 00:00:00 2001 From: agessaman Date: Thu, 27 Nov 2025 21:44:38 -0800 Subject: [PATCH] Improve installation and uninstallation scripts. Add user to dialout group for serial port access in install-service.sh, and implement safety features in uninstall-service.sh, including backup options and confirmation prompts for destructive actions. Update error handling and messaging for clarity across both scripts. --- install-service.sh | 28 ++ modules/commands/hacker_command.py | 311 ++++++++++++++++- translations/en.json | 216 +++++++++++- uninstall-service.sh | 526 +++++++++++++++++++++++++---- 4 files changed, 1006 insertions(+), 75 deletions(-) diff --git a/install-service.sh b/install-service.sh index ccb96d7..6c5afcf 100755 --- a/install-service.sh +++ b/install-service.sh @@ -189,6 +189,29 @@ else else print_warning "User $SERVICE_USER already exists (skipping creation)" fi + + # Add user to dialout group for serial port access (Linux) + print_info "Configuring serial port access permissions" + if getent group dialout > /dev/null 2>&1; then + if groups "$SERVICE_USER" | grep -q "\bdialout\b"; then + print_warning "User $SERVICE_USER is already in dialout group" + else + usermod -a -G dialout "$SERVICE_USER" + print_success "Added $SERVICE_USER to dialout group for serial port access" + fi + else + print_warning "dialout group not found - serial port access may require manual configuration" + print_info "If using serial connection, you may need to: sudo usermod -a -G dialout $SERVICE_USER" + fi + + # Also check for other common serial port groups (tty, uucp, lock) + for group in tty uucp lock; do + if getent group "$group" > /dev/null 2>&1; then + if ! groups "$SERVICE_USER" | grep -q "\b$group\b"; then + usermod -a -G "$group" "$SERVICE_USER" 2>/dev/null && print_info "Added $SERVICE_USER to $group group" || true + fi + fi + done fi print_section "Step 2: Creating Installation Directories" @@ -435,6 +458,11 @@ else echo " • Run as user '$SERVICE_USER' for security" echo " • Log to systemd journal (view with journalctl)" echo "" + print_info "Serial port access:" + echo " • User '$SERVICE_USER' has been added to dialout group for serial port access" + echo " • If using serial connection, ensure the service is restarted after installation" + echo " • Group membership changes take effect after service restart" + echo "" print_info "After editing config.ini, restart the service for changes to take effect:" echo " ${YELLOW}sudo systemctl restart $SERVICE_NAME${NC}" fi diff --git a/modules/commands/hacker_command.py b/modules/commands/hacker_command.py index 6e25f06..4e95d7f 100644 --- a/modules/commands/hacker_command.py +++ b/modules/commands/hacker_command.py @@ -14,7 +14,10 @@ class HackerCommand(BaseCommand): # Plugin metadata name = "hacker" - keywords = ['sudo', 'ps aux', 'grep', 'ls -l', 'ls -la', 'echo $PATH'] + keywords = ['sudo', 'ps aux', 'grep', 'ls -l', 'ls -la', 'echo $PATH', 'rm', 'rm -rf', + 'cat', 'whoami', 'top', 'htop', 'netstat', 'ss', 'kill', 'killall', 'chmod', + 'find', 'history', 'passwd', 'su', 'ssh', 'wget', 'curl', 'df -h', 'free', + 'ifconfig', 'ip addr', 'uname -a'] description = "Simulates hacking a supervillain's mainframe with hilarious error messages" category = "fun" @@ -89,15 +92,15 @@ class HackerCommand(BaseCommand): # grep command errors elif command_lower.startswith('grep'): fallback = [ - "🔍 SEARCH FAILED: The One Ring has corrupted the search index. My precious...", - "📝 PATTERN NOT FOUND: The search database has been deleted by the evil AI.", + "🔍 SEARCH FAILED: The One Ring has corrupted the file search. My precious...", + "📝 PATTERN NOT FOUND: The search pattern has been blocked by the evil AI.", "🎯 MISS: Your search pattern has been shot down by Imperial TIE fighters.", "🧩 PUZZLE ERROR: The search results have been scattered by the Riddler.", - "💻 DATABASE CORRUPTED: The supervillain's search engine has crashed.", + "💻 FILE SYSTEM CORRUPTED: The supervillain's file system has crashed.", "🎮 GAME OVER: The search has been defeated by the final boss.", - "🖥️ SEARCH ENGINE DOWN: Google has been hacked by the Dark Web.", - "🔐 ENCRYPTED RESULTS: The search results have been locked by ransomware.", - "🌐 NETWORK TIMEOUT: The search request got lost in cyberspace.", + "🖥️ SEARCH BLOCKED: File access has been blocked by the Dark Web.", + "🔐 ENCRYPTED FILES: The files are encrypted and cannot be searched.", + "🌐 READ TIMEOUT: The file read request got lost in cyberspace.", "⚡ SEARCH FAILED: The pattern matching algorithm has been fried by a power surge." ] return get_random_error('commands.hacker.grep_errors', fallback) @@ -134,6 +137,292 @@ class HackerCommand(BaseCommand): ] return get_random_error('commands.hacker.echo_path_errors', fallback) + # rm and rm -rf command errors (dangerous deletion!) + elif command_lower.startswith('rm -rf') or command_lower.startswith('rm -r'): + fallback = [ + "💣 DESTRUCTION BLOCKED: The Death Star's safety protocols have prevented mass deletion!", + "🚨 EMERGENCY STOP: Dr. Evil has activated the emergency brake on file destruction.", + "🛡️ PROTECTION MODE: The Matrix has locked all files in read-only mode. No deletion allowed.", + "🔒 FILES LOCKED: Lex Luthor's mainframe has frozen all deletion commands.", + "⚡ POWER FAILURE: The supervillain's delete command has been short-circuited.", + "🎮 GAME SAVE PROTECTED: The final boss has enabled file protection mode.", + "🖥️ DELETION DENIED: The evil AI refuses to delete its own files.", + "🔐 ENCRYPTED FILES: All files are encrypted and cannot be deleted.", + "🌐 CLOUD SYNC: Files are syncing to the Matrix cloud. Deletion pending...", + "💀 SYSTEM REJECTION: The mainframe has rejected your deletion request. Files are too precious." + ] + return get_random_error('commands.hacker.rm_errors', fallback) + elif command_lower.startswith('rm'): + fallback = [ + "🗑️ DELETE FAILED: The supervillain's recycle bin is full and rejecting deletions.", + "🚫 REMOVAL BLOCKED: The Dark Overlord has protected all files from deletion.", + "💻 FILE LOCKED: The file system has been locked by the evil corporation.", + "🔒 PERMISSION DENIED: You don't have permission to delete files on the Death Star.", + "⚡ DELETION ERROR: The file deletion command has been corrupted by malware.", + "🎮 GAME OVER: The file you're trying to delete is the final boss's save file.", + "🖥️ SYSTEM ERROR: The delete command has crashed the file manager.", + "🔐 FILES PROTECTED: All files are protected by the supervillain's antivirus.", + "🌐 NETWORK ERROR: The deletion request got lost in cyberspace.", + "💀 FILE GHOST: The file has become a digital ghost and cannot be deleted." + ] + return get_random_error('commands.hacker.rm_errors', fallback) + + # cat command errors + elif command_lower.startswith('cat'): + fallback = [ + "📄 FILE READ ERROR: The file has been encrypted by the Riddler's cipher.", + "📖 DOCUMENT CORRUPTED: The file contents have been scrambled by malware.", + "📚 ACCESS DENIED: The supervillain has classified this file as top secret.", + "🔍 FILE NOT FOUND: The file has been hidden by the Invisible Man.", + "💻 READ PERMISSION DENIED: The Matrix has locked this file from reading.", + "🎮 GAME FILE: This file belongs to the final boss and cannot be viewed.", + "🖥️ FILE SYSTEM ERROR: The file reader has crashed due to a virus.", + "🔐 ENCRYPTED FILE: The file contents are encrypted with ransomware.", + "🌐 CLOUD FILE: The file is stuck in the Matrix's cloud and cannot be read.", + "💀 FILE GHOST: The file exists but its contents have been deleted by digital ghosts." + ] + return get_random_error('commands.hacker.cat_errors', fallback) + + # whoami command errors + elif command_lower.startswith('whoami'): + fallback = [ + "👤 IDENTITY ERROR: The Matrix has erased your identity. You are nobody.", + "🕵️ SPY DETECTED: The supervillain's system has detected an unknown user.", + "🎭 IDENTITY THEFT: Your identity has been stolen by the Riddler.", + "👻 GHOST USER: You are a digital ghost with no identity.", + "🔒 CLASSIFIED: Your identity is classified by the evil corporation.", + "🎮 GAME OVER: The final boss has deleted your player profile.", + "🖥️ USER DATABASE CORRUPTED: The user identity system has crashed.", + "🔐 IDENTITY ENCRYPTED: Your identity has been encrypted by ransomware.", + "🌐 IDENTITY LOST: Your identity got lost in the Matrix's network.", + "💀 USER DELETED: The Dark Overlord has deleted your user account." + ] + return get_random_error('commands.hacker.whoami_errors', fallback) + + # top and htop command errors + elif command_lower.startswith('htop') or command_lower.startswith('top'): + fallback = [ + "📊 MONITOR ERROR: The process monitor has been hijacked by the Borg Collective.", + "⚙️ SYSTEM OVERLOAD: The Death Star's reactor is overheating. Monitor offline.", + "🤖 PROCESS HIDDEN: All processes have been hidden by the evil AI.", + "💻 MONITOR CRASHED: The system monitor has crashed due to a kernel panic.", + "🎮 GAME PAUSED: The final boss has paused all processes.", + "🖥️ BLUE SCREEN: The monitor has encountered a fatal error.", + "🔐 MONITOR ENCRYPTED: The process monitor has been locked by ransomware.", + "🌐 SYSTEM DISCONNECTED: The monitor cannot access the process table.", + "⚡ POWER SURGE: The monitor has been fried by a power surge.", + "💀 SYSTEM DEAD: The mainframe is dead. No processes to monitor." + ] + return get_random_error('commands.hacker.top_errors', fallback) + + # netstat and ss command errors + elif command_lower.startswith('netstat') or command_lower.startswith('ss '): + fallback = [ + "🌐 NETWORK SCAN BLOCKED: The supervillain's firewall has blocked all network queries.", + "🔍 CONNECTION LIST CORRUPTED: The network connection table has been hacked by malware.", + "📡 SIGNAL JAMMED: Imperial TIE fighters are jamming all network signals.", + "💻 NETWORK DOWN: The Death Star's network stack has been destroyed.", + "🎮 GAME OVER: All network connections have been terminated by the final boss.", + "🖥️ NETWORK ERROR: The network stack has crashed due to a virus.", + "🔐 CONNECTIONS HIDDEN: All network connections have been encrypted and hidden.", + "🌐 MATRIX DISCONNECTED: The network routing table is stuck in the Matrix's void.", + "⚡ NETWORK FRIED: The network interface has been zapped by a power surge.", + "💀 NO CONNECTIONS: The mainframe has no active network connections. It's dead, Jim." + ] + return get_random_error('commands.hacker.netstat_errors', fallback) + + # kill and killall command errors + elif command_lower.startswith('killall') or command_lower.startswith('kill'): + fallback = [ + "💀 KILL DENIED: The supervillain's processes are immortal and cannot be killed.", + "🚫 TERMINATION BLOCKED: The Dark Overlord has protected all processes from termination.", + "🛡️ PROCESS PROTECTED: The Matrix has locked all processes in protected mode.", + "🔒 KILL PERMISSION DENIED: You don't have permission to kill processes on the Death Star.", + "⚡ TERMINATION ERROR: The kill command has been corrupted by malware.", + "🎮 GAME OVER: The process you're trying to kill is the final boss. It's invincible.", + "🖥️ SYSTEM ERROR: The kill signal has been blocked by the kernel.", + "🔐 PROCESSES PROTECTED: All processes are protected and cannot be terminated.", + "🌐 KILL REQUEST LOST: The termination signal got lost in cyberspace.", + "💀 PROCESS GHOST: The process has become a zombie process and cannot be killed." + ] + return get_random_error('commands.hacker.kill_errors', fallback) + + # chmod command errors + elif command_lower.startswith('chmod'): + fallback = [ + "🔐 PERMISSION DENIED: The supervillain has locked all file permissions.", + "🚫 CHMOD BLOCKED: The Dark Overlord refuses to allow permission changes.", + "🛡️ PERMISSIONS PROTECTED: The Matrix has frozen all file permissions.", + "🔒 PERMISSION ERROR: You don't have permission to change permissions. How meta!", + "⚡ CHMOD CORRUPTED: The permission change command has been fried by malware.", + "🎮 GAME OVER: The final boss has locked all file permissions.", + "🖥️ SYSTEM ERROR: The permission system has crashed due to a virus.", + "🔐 PERMISSIONS ENCRYPTED: All permissions are encrypted and cannot be changed.", + "🌐 PERMISSION REQUEST LOST: The permission change got lost in the Matrix.", + "💀 PERMISSIONS DEAD: The permission system is dead. No changes allowed." + ] + return get_random_error('commands.hacker.chmod_errors', fallback) + + # find command errors + elif command_lower.startswith('find'): + fallback = [ + "🔍 SEARCH FAILED: The file search has been blocked by the supervillain's firewall.", + "📁 FILES HIDDEN: All files have been hidden by the Invisible Man's cloak.", + "💻 SEARCH CORRUPTED: The find command has been corrupted by malware.", + "🎯 TARGET NOT FOUND: The files you're searching for have been deleted by the evil AI.", + "🎮 GAME OVER: The final boss has hidden all files in another dimension.", + "🖥️ SEARCH ENGINE DOWN: The file search system has crashed.", + "🔐 FILES ENCRYPTED: All files are encrypted and cannot be found.", + "🌐 SEARCH LOST: The search request got lost in the Matrix's void.", + "⚡ SEARCH FRIED: The file search algorithm has been zapped by a power surge.", + "💀 NO FILES: The mainframe has no files. They've all been deleted." + ] + return get_random_error('commands.hacker.find_errors', fallback) + + # history command errors + elif command_lower.startswith('history'): + fallback = [ + "📜 HISTORY ERASED: The supervillain has deleted all command history.", + "🕰️ TIME TRAVEL ERROR: The command history has been lost in a time paradox.", + "💻 HISTORY CORRUPTED: The history database has been hacked by malware.", + "🔒 ACCESS DENIED: The Dark Overlord has classified your command history as top secret.", + "🎮 GAME OVER: The final boss has reset your command history.", + "🖥️ HISTORY SYSTEM DOWN: The command history system has crashed.", + "🔐 HISTORY ENCRYPTED: Your command history has been encrypted by ransomware.", + "🌐 HISTORY LOST: Your command history got lost in the Matrix's network.", + "⚡ HISTORY FRIED: The history database has been zapped by a power surge.", + "💀 NO HISTORY: You have no command history. You are a blank slate." + ] + return get_random_error('commands.hacker.history_errors', fallback) + + # passwd command errors + elif command_lower.startswith('passwd'): + fallback = [ + "🔐 PASSWORD CHANGE DENIED: The supervillain has locked all password changes.", + "🚫 PASSWORD BLOCKED: The Dark Overlord refuses to allow password modifications.", + "🛡️ PASSWORD PROTECTED: The Matrix has frozen all password changes.", + "🔒 PERMISSION DENIED: You don't have permission to change passwords on the Death Star.", + "⚡ PASSWORD ERROR: The password change command has been corrupted by malware.", + "🎮 GAME OVER: The final boss has locked all passwords.", + "🖥️ SYSTEM ERROR: The password system has crashed due to a virus.", + "🔐 PASSWORDS ENCRYPTED: All passwords are encrypted and cannot be changed.", + "🌐 PASSWORD REQUEST LOST: The password change got lost in the Matrix.", + "💀 PASSWORD SYSTEM DEAD: The password system is dead. No changes allowed." + ] + return get_random_error('commands.hacker.passwd_errors', fallback) + + # su command errors + elif command_lower.startswith('su '): + fallback = [ + "🔄 SWITCH USER DENIED: The supervillain has blocked all user switching attempts.", + "🚫 USER SWITCH BLOCKED: The Dark Overlord refuses to allow user changes.", + "🛡️ USER PROTECTED: The Matrix has locked all user accounts.", + "🔒 PERMISSION DENIED: You don't have permission to switch users on the Death Star.", + "⚡ USER SWITCH ERROR: The su command has been corrupted by malware.", + "🎮 GAME OVER: The final boss has locked all user accounts.", + "🖥️ SYSTEM ERROR: The user system has crashed due to a virus.", + "🔐 USERS ENCRYPTED: All user accounts are encrypted and cannot be accessed.", + "🌐 USER REQUEST LOST: The user switch request got lost in the Matrix.", + "💀 USER SYSTEM DEAD: The user system is dead. No switching allowed." + ] + return get_random_error('commands.hacker.su_errors', fallback) + + # ssh command errors + elif command_lower.startswith('ssh'): + fallback = [ + "🔌 SSH CONNECTION FAILED: The supervillain's server has blocked all SSH attempts.", + "🚫 REMOTE ACCESS DENIED: The Dark Overlord has closed all SSH ports.", + "🛡️ CONNECTION PROTECTED: The Matrix has locked all SSH connections.", + "🔒 SSH BLOCKED: The Death Star's firewall is blocking all SSH connections.", + "⚡ CONNECTION ERROR: The SSH handshake has been corrupted by malware.", + "🎮 GAME OVER: The final boss has disabled all remote access.", + "🖥️ SYSTEM ERROR: The SSH daemon has crashed due to a virus.", + "🔐 SSH DISABLED: All SSH connections have been disabled and blocked.", + "🌐 CONNECTION LOST: The SSH connection got lost in the Matrix's void.", + "💀 SSH DEAD: The SSH daemon is dead. No remote access allowed." + ] + return get_random_error('commands.hacker.ssh_errors', fallback) + + # wget and curl command errors + elif command_lower.startswith('wget') or command_lower.startswith('curl'): + fallback = [ + "📥 DOWNLOAD BLOCKED: The supervillain's firewall has blocked all HTTP requests.", + "🚫 DOWNLOAD DENIED: The Dark Overlord refuses to allow file downloads.", + "🛡️ DOWNLOAD PROTECTED: The Matrix has locked all download capabilities.", + "🔒 DOWNLOAD BLOCKED: The Death Star's network is blocking all outbound connections.", + "⚡ DOWNLOAD ERROR: The HTTP request has been corrupted by malware.", + "🎮 GAME OVER: The final boss has disabled all downloads.", + "🖥️ SYSTEM ERROR: The network stack has crashed due to a virus.", + "🔐 DNS RESOLUTION FAILED: All domain names have been encrypted and blocked.", + "🌐 CONNECTION TIMEOUT: The download request got lost in the Matrix's network.", + "💀 DOWNLOAD DEAD: The network interface is dead. No downloads allowed." + ] + return get_random_error('commands.hacker.download_errors', fallback) + + # df -h command errors + elif command_lower.startswith('df -h') or command_lower.startswith('df'): + fallback = [ + "💾 DISK SPACE ERROR: The supervillain's file system has been corrupted by malware.", + "📊 STORAGE SCAN FAILED: The disk space query has been hijacked by the Borg.", + "💻 DISK CORRUPTED: The file system has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all disk space information.", + "🖥️ SYSTEM ERROR: The file system mount table has crashed.", + "🔐 STORAGE ENCRYPTED: All file system information has been encrypted.", + "🌐 MOUNT FAILED: The disk mount information got lost in the Matrix's cloud.", + "⚡ STORAGE FRIED: The disk controller has been zapped by a power surge.", + "💀 NO STORAGE: The mainframe has no mounted file systems. It's all been deleted.", + "🗄️ FILESYSTEM CORRUPTED: The file system superblock has been corrupted by ransomware." + ] + return get_random_error('commands.hacker.df_errors', fallback) + + # free command errors + elif command_lower.startswith('free'): + fallback = [ + "🧠 MEMORY ERROR: The supervillain's RAM has been corrupted by malware.", + "📊 MEMORY SCAN FAILED: The memory query has been hijacked by the Cybermen.", + "💻 MEMORY CORRUPTED: The RAM has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all memory information.", + "🖥️ SYSTEM ERROR: The memory management system has crashed.", + "🔐 MEMORY ENCRYPTED: All memory information has been encrypted.", + "🌐 MEMORY LOST: The memory statistics got lost in the Matrix's void.", + "⚡ MEMORY FRIED: The memory controller has been zapped by a power surge.", + "💀 NO MEMORY: The mainframe has no accessible memory. It's all been wiped.", + "🧩 MEMORY CORRUPTED: The memory mapping has been corrupted by ransomware." + ] + return get_random_error('commands.hacker.free_errors', fallback) + + # ifconfig and ip addr command errors + elif command_lower.startswith('ifconfig') or command_lower.startswith('ip addr'): + fallback = [ + "🌐 NETWORK INTERFACE ERROR: The supervillain's network interfaces have been corrupted.", + "📡 INTERFACE SCAN FAILED: The network interface query has been hijacked by Imperial forces.", + "💻 INTERFACE CORRUPTED: The network interface configuration has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all network interface information.", + "🖥️ SYSTEM ERROR: The network interface driver has crashed.", + "🔐 INTERFACES ENCRYPTED: All network interface information has been encrypted.", + "🌐 INTERFACES LOST: The network interface data got lost in the Matrix's network.", + "⚡ INTERFACES FRIED: The network interface hardware has been zapped by a power surge.", + "💀 NO INTERFACES: The mainframe has no network interfaces. They've all been disabled.", + "🔌 CONNECTION BROKEN: All network interfaces have been disconnected by the Dark Overlord." + ] + return get_random_error('commands.hacker.ifconfig_errors', fallback) + + # uname -a command errors + elif command_lower.startswith('uname'): + fallback = [ + "🖥️ SYSTEM INFO ERROR: The supervillain has classified all system information as top secret.", + "📊 INFO SCAN FAILED: The system information query has been hidden by the Invisible Man.", + "💻 SYSTEM CORRUPTED: The kernel version information has been destroyed by malware.", + "🎮 GAME OVER: The final boss has deleted all system information.", + "🖥️ SYSTEM ERROR: The kernel information system has crashed. How meta!", + "🔐 SYSTEM ENCRYPTED: All system information has been encrypted by ransomware.", + "🌐 SYSTEM LOST: The kernel version got lost in the Matrix's void.", + "⚡ SYSTEM FRIED: The system call interface has been zapped by a power surge.", + "💀 NO SYSTEM: The mainframe has no kernel information. It's a mystery.", + "🦹‍♂️ CLASSIFIED: Lex Luthor has classified all system information. Access denied." + ] + return get_random_error('commands.hacker.uname_errors', fallback) + # Generic hacker error for other commands else: fallback = [ @@ -161,10 +450,14 @@ class HackerCommand(BaseCommand): content_lower = content.lower() # Commands that should match exactly (no arguments) - exact_match_commands = ['ls -l', 'ls -la', 'echo $PATH'] + exact_match_commands = ['ls -l', 'ls -la', 'echo $PATH', 'df -h', 'whoami', 'history', + 'top', 'htop', 'free', 'uname -a'] # Commands that should match as prefixes (can have arguments) - prefix_match_commands = ['sudo', 'ps aux', 'grep'] + # Note: Longer prefixes must come first (e.g., 'rm -rf' before 'rm') + prefix_match_commands = ['sudo', 'ps aux', 'grep', 'rm -rf', 'rm -r', 'rm', 'cat', + 'netstat', 'ss', 'killall', 'kill', 'chmod', 'find', 'passwd', + 'su', 'ssh', 'wget', 'curl', 'df', 'ifconfig', 'ip addr', 'uname'] # Check for exact matches first for keyword in exact_match_commands: diff --git a/translations/en.json b/translations/en.json index 300155b..01dff9e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -532,15 +532,15 @@ "⚡ POWER SURGE: The supervillain's server farm has fried all running processes." ], "grep_errors": [ - "🔍 SEARCH FAILED: The One Ring has corrupted the search index. My precious...", - "📝 PATTERN NOT FOUND: The search database has been deleted by the evil AI.", + "🔍 SEARCH FAILED: The One Ring has corrupted the file search. My precious...", + "📝 PATTERN NOT FOUND: The search pattern has been blocked by the evil AI.", "🎯 MISS: Your search pattern has been shot down by Imperial TIE fighters.", "🧩 PUZZLE ERROR: The search results have been scattered by the Riddler.", - "💻 DATABASE CORRUPTED: The supervillain's search engine has crashed.", + "💻 FILE SYSTEM CORRUPTED: The supervillain's file system has crashed.", "🎮 GAME OVER: The search has been defeated by the final boss.", - "🖥️ SEARCH ENGINE DOWN: Google has been hacked by the Dark Web.", - "🔐 ENCRYPTED RESULTS: The search results have been locked by ransomware.", - "🌐 NETWORK TIMEOUT: The search request got lost in cyberspace.", + "🖥️ SEARCH BLOCKED: File access has been blocked by the Dark Web.", + "🔐 ENCRYPTED FILES: The files are encrypted and cannot be searched.", + "🌐 READ TIMEOUT: The file read request got lost in cyberspace.", "⚡ SEARCH FAILED: The pattern matching algorithm has been fried by a power surge." ], "ls_errors": [ @@ -567,6 +567,210 @@ "🌐 NETWORK PATH DOWN: The directory paths are stuck in the Matrix's network.", "⚡ PATH FRIED: The system paths have been zapped by a power surge." ], + "rm_errors": [ + "💣 DESTRUCTION BLOCKED: The Death Star's safety protocols have prevented mass deletion!", + "🚨 EMERGENCY STOP: Dr. Evil has activated the emergency brake on file destruction.", + "🛡️ PROTECTION MODE: The Matrix has locked all files in read-only mode. No deletion allowed.", + "🔒 FILES LOCKED: Lex Luthor's mainframe has frozen all deletion commands.", + "⚡ POWER FAILURE: The supervillain's delete command has been short-circuited.", + "🎮 GAME SAVE PROTECTED: The final boss has enabled file protection mode.", + "🖥️ DELETION DENIED: The evil AI refuses to delete its own files.", + "🔐 ENCRYPTED FILES: All files are encrypted and cannot be deleted.", + "🌐 CLOUD SYNC: Files are syncing to the Matrix cloud. Deletion pending...", + "💀 SYSTEM REJECTION: The mainframe has rejected your deletion request. Files are too precious." + ], + "cat_errors": [ + "📄 FILE READ ERROR: The file has been encrypted by the Riddler's cipher.", + "📖 DOCUMENT CORRUPTED: The file contents have been scrambled by malware.", + "📚 ACCESS DENIED: The supervillain has classified this file as top secret.", + "🔍 FILE NOT FOUND: The file has been hidden by the Invisible Man.", + "💻 READ PERMISSION DENIED: The Matrix has locked this file from reading.", + "🎮 GAME FILE: This file belongs to the final boss and cannot be viewed.", + "🖥️ FILE SYSTEM ERROR: The file reader has crashed due to a virus.", + "🔐 ENCRYPTED FILE: The file contents are encrypted with ransomware.", + "🌐 CLOUD FILE: The file is stuck in the Matrix's cloud and cannot be read.", + "💀 FILE GHOST: The file exists but its contents have been deleted by digital ghosts." + ], + "whoami_errors": [ + "👤 IDENTITY ERROR: The Matrix has erased your identity. You are nobody.", + "🕵️ SPY DETECTED: The supervillain's system has detected an unknown user.", + "🎭 IDENTITY THEFT: Your identity has been stolen by the Riddler.", + "👻 GHOST USER: You are a digital ghost with no identity.", + "🔒 CLASSIFIED: Your identity is classified by the evil corporation.", + "🎮 GAME OVER: The final boss has deleted your player profile.", + "🖥️ USER DATABASE CORRUPTED: The user identity system has crashed.", + "🔐 IDENTITY ENCRYPTED: Your identity has been encrypted by ransomware.", + "🌐 IDENTITY LOST: Your identity got lost in the Matrix's network.", + "💀 USER DELETED: The Dark Overlord has deleted your user account." + ], + "top_errors": [ + "📊 MONITOR ERROR: The process monitor has been hijacked by the Borg Collective.", + "⚙️ SYSTEM OVERLOAD: The Death Star's reactor is overheating. Monitor offline.", + "🤖 PROCESS HIDDEN: All processes have been hidden by the evil AI.", + "💻 MONITOR CRASHED: The system monitor has crashed due to a kernel panic.", + "🎮 GAME PAUSED: The final boss has paused all processes.", + "🖥️ BLUE SCREEN: The monitor has encountered a fatal error.", + "🔐 MONITOR ENCRYPTED: The process monitor has been locked by ransomware.", + "🌐 SYSTEM DISCONNECTED: The monitor cannot access the process table.", + "⚡ POWER SURGE: The monitor has been fried by a power surge.", + "💀 SYSTEM DEAD: The mainframe is dead. No processes to monitor." + ], + "netstat_errors": [ + "🌐 NETWORK SCAN BLOCKED: The supervillain's firewall has blocked all network queries.", + "🔍 CONNECTION LIST CORRUPTED: The network connection table has been hacked by malware.", + "📡 SIGNAL JAMMED: Imperial TIE fighters are jamming all network signals.", + "💻 NETWORK DOWN: The Death Star's network stack has been destroyed.", + "🎮 GAME OVER: All network connections have been terminated by the final boss.", + "🖥️ NETWORK ERROR: The network stack has crashed due to a virus.", + "🔐 CONNECTIONS HIDDEN: All network connections have been encrypted and hidden.", + "🌐 MATRIX DISCONNECTED: The network routing table is stuck in the Matrix's void.", + "⚡ NETWORK FRIED: The network interface has been zapped by a power surge.", + "💀 NO CONNECTIONS: The mainframe has no active network connections. It's dead, Jim." + ], + "kill_errors": [ + "💀 KILL DENIED: The supervillain's processes are immortal and cannot be killed.", + "🚫 TERMINATION BLOCKED: The Dark Overlord has protected all processes from termination.", + "🛡️ PROCESS PROTECTED: The Matrix has locked all processes in protected mode.", + "🔒 KILL PERMISSION DENIED: You don't have permission to kill processes on the Death Star.", + "⚡ TERMINATION ERROR: The kill command has been corrupted by malware.", + "🎮 GAME OVER: The process you're trying to kill is the final boss. It's invincible.", + "🖥️ SYSTEM ERROR: The kill signal has been blocked by the kernel.", + "🔐 PROCESSES PROTECTED: All processes are protected and cannot be terminated.", + "🌐 KILL REQUEST LOST: The termination signal got lost in cyberspace.", + "💀 PROCESS GHOST: The process has become a zombie process and cannot be killed." + ], + "chmod_errors": [ + "🔐 PERMISSION DENIED: The supervillain has locked all file permissions.", + "🚫 CHMOD BLOCKED: The Dark Overlord refuses to allow permission changes.", + "🛡️ PERMISSIONS PROTECTED: The Matrix has frozen all file permissions.", + "🔒 PERMISSION ERROR: You don't have permission to change permissions. How meta!", + "⚡ CHMOD CORRUPTED: The permission change command has been fried by malware.", + "🎮 GAME OVER: The final boss has locked all file permissions.", + "🖥️ SYSTEM ERROR: The permission system has crashed due to a virus.", + "🔐 PERMISSIONS ENCRYPTED: All permissions are encrypted and cannot be changed.", + "🌐 PERMISSION REQUEST LOST: The permission change got lost in the Matrix.", + "💀 PERMISSIONS DEAD: The permission system is dead. No changes allowed." + ], + "find_errors": [ + "🔍 SEARCH FAILED: The file search has been blocked by the supervillain's firewall.", + "📁 FILES HIDDEN: All files have been hidden by the Invisible Man's cloak.", + "💻 SEARCH CORRUPTED: The find command has been corrupted by malware.", + "🎯 TARGET NOT FOUND: The files you're searching for have been deleted by the evil AI.", + "🎮 GAME OVER: The final boss has hidden all files in another dimension.", + "🖥️ SEARCH ENGINE DOWN: The file search system has crashed.", + "🔐 FILES ENCRYPTED: All files are encrypted and cannot be found.", + "🌐 SEARCH LOST: The search request got lost in the Matrix's void.", + "⚡ SEARCH FRIED: The file search algorithm has been zapped by a power surge.", + "💀 NO FILES: The mainframe has no files. They've all been deleted." + ], + "history_errors": [ + "📜 HISTORY ERASED: The supervillain has deleted all command history.", + "🕰️ TIME TRAVEL ERROR: The command history has been lost in a time paradox.", + "💻 HISTORY CORRUPTED: The history database has been hacked by malware.", + "🔒 ACCESS DENIED: The Dark Overlord has classified your command history as top secret.", + "🎮 GAME OVER: The final boss has reset your command history.", + "🖥️ HISTORY SYSTEM DOWN: The command history system has crashed.", + "🔐 HISTORY ENCRYPTED: Your command history has been encrypted by ransomware.", + "🌐 HISTORY LOST: Your command history got lost in the Matrix's network.", + "⚡ HISTORY FRIED: The history database has been zapped by a power surge.", + "💀 NO HISTORY: You have no command history. You are a blank slate." + ], + "passwd_errors": [ + "🔐 PASSWORD CHANGE DENIED: The supervillain has locked all password changes.", + "🚫 PASSWORD BLOCKED: The Dark Overlord refuses to allow password modifications.", + "🛡️ PASSWORD PROTECTED: The Matrix has frozen all password changes.", + "🔒 PERMISSION DENIED: You don't have permission to change passwords on the Death Star.", + "⚡ PASSWORD ERROR: The password change command has been corrupted by malware.", + "🎮 GAME OVER: The final boss has locked all passwords.", + "🖥️ SYSTEM ERROR: The password system has crashed due to a virus.", + "🔐 PASSWORDS ENCRYPTED: All passwords are encrypted and cannot be changed.", + "🌐 PASSWORD REQUEST LOST: The password change got lost in the Matrix.", + "💀 PASSWORD SYSTEM DEAD: The password system is dead. No changes allowed." + ], + "su_errors": [ + "🔄 SWITCH USER DENIED: The supervillain has blocked all user switching attempts.", + "🚫 USER SWITCH BLOCKED: The Dark Overlord refuses to allow user changes.", + "🛡️ USER PROTECTED: The Matrix has locked all user accounts.", + "🔒 PERMISSION DENIED: You don't have permission to switch users on the Death Star.", + "⚡ USER SWITCH ERROR: The su command has been corrupted by malware.", + "🎮 GAME OVER: The final boss has locked all user accounts.", + "🖥️ SYSTEM ERROR: The user system has crashed due to a virus.", + "🔐 USERS ENCRYPTED: All user accounts are encrypted and cannot be accessed.", + "🌐 USER REQUEST LOST: The user switch request got lost in the Matrix.", + "💀 USER SYSTEM DEAD: The user system is dead. No switching allowed." + ], + "ssh_errors": [ + "🔌 SSH CONNECTION FAILED: The supervillain's server has blocked all SSH attempts.", + "🚫 REMOTE ACCESS DENIED: The Dark Overlord has closed all SSH ports.", + "🛡️ CONNECTION PROTECTED: The Matrix has locked all SSH connections.", + "🔒 SSH BLOCKED: The Death Star's firewall is blocking all SSH connections.", + "⚡ CONNECTION ERROR: The SSH handshake has been corrupted by malware.", + "🎮 GAME OVER: The final boss has disabled all remote access.", + "🖥️ SYSTEM ERROR: The SSH daemon has crashed due to a virus.", + "🔐 SSH DISABLED: All SSH connections have been disabled and blocked.", + "🌐 CONNECTION LOST: The SSH connection got lost in the Matrix's void.", + "💀 SSH DEAD: The SSH daemon is dead. No remote access allowed." + ], + "download_errors": [ + "📥 DOWNLOAD BLOCKED: The supervillain's firewall has blocked all HTTP requests.", + "🚫 DOWNLOAD DENIED: The Dark Overlord refuses to allow file downloads.", + "🛡️ DOWNLOAD PROTECTED: The Matrix has locked all download capabilities.", + "🔒 DOWNLOAD BLOCKED: The Death Star's network is blocking all outbound connections.", + "⚡ DOWNLOAD ERROR: The HTTP request has been corrupted by malware.", + "🎮 GAME OVER: The final boss has disabled all downloads.", + "🖥️ SYSTEM ERROR: The network stack has crashed due to a virus.", + "🔐 DNS RESOLUTION FAILED: All domain names have been encrypted and blocked.", + "🌐 CONNECTION TIMEOUT: The download request got lost in the Matrix's network.", + "💀 DOWNLOAD DEAD: The network interface is dead. No downloads allowed." + ], + "df_errors": [ + "💾 DISK SPACE ERROR: The supervillain's file system has been corrupted by malware.", + "📊 STORAGE SCAN FAILED: The disk space query has been hijacked by the Borg.", + "💻 DISK CORRUPTED: The file system has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all disk space information.", + "🖥️ SYSTEM ERROR: The file system mount table has crashed.", + "🔐 STORAGE ENCRYPTED: All file system information has been encrypted.", + "🌐 MOUNT FAILED: The disk mount information got lost in the Matrix's cloud.", + "⚡ STORAGE FRIED: The disk controller has been zapped by a power surge.", + "💀 NO STORAGE: The mainframe has no mounted file systems. It's all been deleted.", + "🗄️ FILESYSTEM CORRUPTED: The file system superblock has been corrupted by ransomware." + ], + "free_errors": [ + "🧠 MEMORY ERROR: The supervillain's RAM has been corrupted by malware.", + "📊 MEMORY SCAN FAILED: The memory query has been hijacked by the Cybermen.", + "💻 MEMORY CORRUPTED: The RAM has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all memory information.", + "🖥️ SYSTEM ERROR: The memory management system has crashed.", + "🔐 MEMORY ENCRYPTED: All memory information has been encrypted.", + "🌐 MEMORY LOST: The memory statistics got lost in the Matrix's void.", + "⚡ MEMORY FRIED: The memory controller has been zapped by a power surge.", + "💀 NO MEMORY: The mainframe has no accessible memory. It's all been wiped.", + "🧩 MEMORY CORRUPTED: The memory mapping has been corrupted by ransomware." + ], + "ifconfig_errors": [ + "🌐 NETWORK INTERFACE ERROR: The supervillain's network interfaces have been corrupted.", + "📡 INTERFACE SCAN FAILED: The network interface query has been hijacked by Imperial forces.", + "💻 INTERFACE CORRUPTED: The network interface configuration has been destroyed by a virus.", + "🎮 GAME OVER: The final boss has deleted all network interface information.", + "🖥️ SYSTEM ERROR: The network interface driver has crashed.", + "🔐 INTERFACES ENCRYPTED: All network interface information has been encrypted.", + "🌐 INTERFACES LOST: The network interface data got lost in the Matrix's network.", + "⚡ INTERFACES FRIED: The network interface hardware has been zapped by a power surge.", + "💀 NO INTERFACES: The mainframe has no network interfaces. They've all been disabled.", + "🔌 CONNECTION BROKEN: All network interfaces have been disconnected by the Dark Overlord." + ], + "uname_errors": [ + "🖥️ SYSTEM INFO ERROR: The supervillain has classified all system information as top secret.", + "📊 INFO SCAN FAILED: The system information query has been hidden by the Invisible Man.", + "💻 SYSTEM CORRUPTED: The kernel version information has been destroyed by malware.", + "🎮 GAME OVER: The final boss has deleted all system information.", + "🖥️ SYSTEM ERROR: The kernel information system has crashed. How meta!", + "🔐 SYSTEM ENCRYPTED: All system information has been encrypted by ransomware.", + "🌐 SYSTEM LOST: The kernel version got lost in the Matrix's void.", + "⚡ SYSTEM FRIED: The system call interface has been zapped by a power surge.", + "💀 NO SYSTEM: The mainframe has no kernel information. It's a mystery.", + "🦹‍♂️ CLASSIFIED: Lex Luthor has classified all system information. Access denied." + ], "generic_errors": [ "💻 MAINFRAME ERROR: The supervillain's computer is having a bad day.", "🤖 SYSTEM MALFUNCTION: The evil AI has gone on strike.", diff --git a/uninstall-service.sh b/uninstall-service.sh index 07a50be..13ca221 100755 --- a/uninstall-service.sh +++ b/uninstall-service.sh @@ -1,6 +1,12 @@ #!/bin/bash # MeshCore Bot Service Uninstallation Script -# This script removes the MeshCore Bot systemd service +# This script removes the MeshCore Bot service (systemd or launchd) +# Supports both Linux (systemd) and macOS (launchd) +# +# Safety features: +# - Backs up config.ini before removal +# - Asks for confirmation before destructive actions +# - Optionally preserves installation files set -e @@ -9,89 +15,489 @@ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' NC='\033[0m' # No Color -# Configuration +# Detect operating system +OS="$(uname -s)" +IS_MACOS=false +IS_LINUX=false + +if [[ "$OS" == "Darwin" ]]; then + IS_MACOS=true + SERVICE_TYPE="launchd" +elif [[ "$OS" == "Linux" ]]; then + IS_LINUX=true + SERVICE_TYPE="systemd" +else + echo "Error: Unsupported operating system: $OS" + echo "This script supports Linux (systemd) and macOS (launchd)" + exit 1 +fi + +# Configuration - OS-specific paths SERVICE_NAME="meshcore-bot" -SERVICE_USER="meshcore" -SERVICE_GROUP="meshcore" -INSTALL_DIR="/opt/meshcore-bot" -LOG_DIR="/var/log/meshcore-bot" +PLIST_NAME="com.meshcore.bot" -echo -e "${BLUE}MeshCore Bot Service Uninstaller${NC}" -echo "====================================" +if [[ "$IS_MACOS" == true ]]; then + SERVICE_USER="$(whoami)" + SERVICE_GROUP="staff" + INSTALL_DIR="/usr/local/meshcore-bot" + LOG_DIR="/usr/local/var/log/meshcore-bot" + SERVICE_FILE="com.meshcore.bot.plist" + LAUNCHD_DIR="/Library/LaunchDaemons" +else + SERVICE_USER="meshcore" + SERVICE_GROUP="meshcore" + INSTALL_DIR="/opt/meshcore-bot" + LOG_DIR="/var/log/meshcore-bot" + SERVICE_FILE="meshcore-bot.service" + SYSTEMD_DIR="/etc/systemd/system" +fi -# Check if running as root +# Capture original user before sudo (for backup location) +ORIGINAL_USER="${SUDO_USER:-$USER}" +if [[ -n "$ORIGINAL_USER" ]] && [[ "$ORIGINAL_USER" != "root" ]]; then + # Try to get home directory of original user + HOME_DIR=$(eval echo ~"$ORIGINAL_USER" 2>/dev/null || echo "/home/$ORIGINAL_USER") + # Verify it exists and is writable + if [[ ! -d "$HOME_DIR" ]] || [[ ! -w "$HOME_DIR" ]]; then + HOME_DIR="/tmp" + fi +else + # Fallback to /tmp if we can't determine user home + HOME_DIR="/tmp" +fi + +# Function to print section headers +print_section() { + echo "" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +# Function to print info messages +print_info() { + echo -e "${CYAN}ℹ${NC} $1" +} + +# Function to print success messages +print_success() { + echo -e "${GREEN}✓${NC} $1" +} + +# Function to print warning messages +print_warning() { + echo -e "${YELLOW}⚠${NC} $1" +} + +# Function to print error messages +print_error() { + echo -e "${RED}✗${NC} $1" +} + +# Function to ask yes/no question +ask_yes_no() { + local prompt="$1" + local default="${2:-n}" # Default to 'no' for safety + local response + + if [[ "$default" == "y" ]]; then + prompt="${prompt} [Y/n]: " + else + prompt="${prompt} [y/N]: " + fi + + while true; do + read -p "$(echo -e "${YELLOW}${prompt}${NC}")" response + response="${response:-$default}" + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]) + return 1 + ;; + *) + echo "Please answer yes or no." + ;; + esac + done +} + +print_section "MeshCore Bot Service Uninstaller" +echo "" +if [[ "$IS_MACOS" == true ]]; then + print_info "Detected macOS - will remove launchd service" +else + print_info "Detected Linux - will remove systemd service" +fi +print_warning "This script will remove the MeshCore Bot service" +echo "" + +# Check if script has execute permissions +if [ ! -x "$0" ]; then + print_warning "Script does not have execute permissions. Attempting to set them..." + chmod +x "$0" 2>/dev/null || { + print_error "Could not set execute permissions. Please run: chmod +x uninstall-service.sh" + exit 1 + } + print_success "Execute permissions set" +fi + +# Check if running as root, if not re-execute with sudo if [[ $EUID -ne 0 ]]; then - echo -e "${RED}This script must be run as root (use sudo)${NC}" - exit 1 + print_warning "This script requires root privileges to remove system services" + print_info "Re-executing with sudo..." + echo "" + exec sudo "$0" "$@" fi -echo -e "${YELLOW}Step 1: Stopping and disabling service${NC}" -# Stop service if running -if systemctl is-active --quiet "$SERVICE_NAME"; then - systemctl stop "$SERVICE_NAME" - echo -e "${GREEN}Stopped $SERVICE_NAME service${NC}" +# Check if service exists +SERVICE_EXISTS=false +if [[ "$IS_MACOS" == true ]]; then + if [ -f "$LAUNCHD_DIR/$SERVICE_FILE" ]; then + SERVICE_EXISTS=true + fi else - echo -e "${YELLOW}Service $SERVICE_NAME is not running${NC}" + if [ -f "$SYSTEMD_DIR/$SERVICE_NAME.service" ]; then + SERVICE_EXISTS=true + fi fi -# Disable service -if systemctl is-enabled --quiet "$SERVICE_NAME"; then - systemctl disable "$SERVICE_NAME" - echo -e "${GREEN}Disabled $SERVICE_NAME service${NC}" -else - echo -e "${YELLOW}Service $SERVICE_NAME is not enabled${NC}" +if [[ "$SERVICE_EXISTS" == false ]]; then + print_warning "Service not found - it may have already been removed" + if [[ "$IS_MACOS" == true ]]; then + print_info "Expected service file: $LAUNCHD_DIR/$SERVICE_FILE" + else + print_info "Expected service file: $SYSTEMD_DIR/$SERVICE_NAME.service" + fi + echo "" + if ! ask_yes_no "Continue anyway? (will only remove files if they exist)"; then + print_info "Uninstallation cancelled by user" + exit 0 + fi fi -echo -e "${YELLOW}Step 2: Removing systemd service file${NC}" -# Remove service file -if [ -f "/etc/systemd/system/$SERVICE_NAME.service" ]; then - rm "/etc/systemd/system/$SERVICE_NAME.service" - echo -e "${GREEN}Removed service file${NC}" -else - echo -e "${YELLOW}Service file not found${NC}" -fi - -# Reload systemd -systemctl daemon-reload -echo -e "${GREEN}Reloaded systemd configuration${NC}" - -echo -e "${YELLOW}Step 3: Removing installation directory${NC}" -# Remove installation directory +# Check if installation directory exists +INSTALL_EXISTS=false if [ -d "$INSTALL_DIR" ]; then - rm -rf "$INSTALL_DIR" - echo -e "${GREEN}Removed installation directory: $INSTALL_DIR${NC}" + INSTALL_EXISTS=true + CONFIG_FILE="$INSTALL_DIR/config.ini" else - echo -e "${YELLOW}Installation directory not found${NC}" + CONFIG_FILE="" fi -echo -e "${YELLOW}Step 4: Removing log directory${NC}" -# Remove log directory +# Step 1: Backup config.ini if it exists +if [[ "$INSTALL_EXISTS" == true ]] && [ -f "$CONFIG_FILE" ]; then + print_section "Step 1: Backup Configuration File" + print_info "Found configuration file: $CONFIG_FILE" + + if ask_yes_no "Would you like to backup config.ini?" "y"; then + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="$HOME_DIR/meshcore-bot-config-backup-${TIMESTAMP}.ini" + + # Create backup + cp "$CONFIG_FILE" "$BACKUP_FILE" + + # Try to set ownership to original user if possible + if [[ -n "$ORIGINAL_USER" ]] && [[ "$ORIGINAL_USER" != "root" ]]; then + chown "$ORIGINAL_USER:$(id -gn "$ORIGINAL_USER" 2>/dev/null || echo 'staff')" "$BACKUP_FILE" 2>/dev/null || true + fi + + print_success "Configuration backed up to: $BACKUP_FILE" + if [[ "$HOME_DIR" == "/tmp" ]]; then + print_info "Backup saved to /tmp (home directory not accessible)" + fi + else + print_info "Skipping config.ini backup" + fi + + # Step 1b: Backup database file + print_section "Step 1b: Database Backup" + + # Try to find the main database file (meshcore_bot.db by default, or from config) + MAIN_DB_FILE="" + + # First, try to read db_path from config.ini if it exists + if [ -f "$CONFIG_FILE" ]; then + DB_PATH_FROM_CONFIG=$(grep -E "^db_path\s*=" "$CONFIG_FILE" 2>/dev/null | head -1 | cut -d'=' -f2 | tr -d ' ' || echo "") + if [ -n "$DB_PATH_FROM_CONFIG" ]; then + # If it's a relative path, make it relative to install dir + if [[ "$DB_PATH_FROM_CONFIG" != /* ]]; then + MAIN_DB_FILE="$INSTALL_DIR/$DB_PATH_FROM_CONFIG" + else + MAIN_DB_FILE="$DB_PATH_FROM_CONFIG" + fi + fi + fi + + # If not found in config, try default location + if [ -z "$MAIN_DB_FILE" ] || [ ! -f "$MAIN_DB_FILE" ]; then + MAIN_DB_FILE="$INSTALL_DIR/meshcore_bot.db" + fi + + # Check if main database file exists + if [ -f "$MAIN_DB_FILE" ]; then + db_name=$(basename "$MAIN_DB_FILE") + db_size=$(du -h "$MAIN_DB_FILE" 2>/dev/null | cut -f1) + print_info "Found database file: $db_name (${db_size})" + echo "" + + if ask_yes_no "Would you like to backup the database file?" "y"; then + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="$HOME_DIR/meshcore-bot-db-backup-${TIMESTAMP}.db" + + cp "$MAIN_DB_FILE" "$BACKUP_FILE" + + # Try to set ownership to original user if possible + if [[ -n "$ORIGINAL_USER" ]] && [[ "$ORIGINAL_USER" != "root" ]]; then + chown "$ORIGINAL_USER:$(id -gn "$ORIGINAL_USER" 2>/dev/null || echo 'staff')" "$BACKUP_FILE" 2>/dev/null || true + fi + + print_success "Database backed up to: $BACKUP_FILE" + if [[ "$HOME_DIR" == "/tmp" ]]; then + print_info "Backup saved to /tmp (home directory not accessible)" + fi + else + print_info "Skipping database backup" + fi + else + print_warning "Database file not found: $MAIN_DB_FILE" + print_info "Skipping database backup" + fi +else + print_section "Step 1: Configuration and Database Files" + if [[ "$INSTALL_EXISTS" == false ]]; then + print_warning "Installation directory not found: $INSTALL_DIR" + else + print_warning "Configuration file not found: $CONFIG_FILE" + fi + print_info "Skipping backup step" +fi + +# Step 2: Confirm service removal +print_section "Step 2: Service Removal Confirmation" +print_warning "This will stop and remove the MeshCore Bot service" +if [[ "$IS_MACOS" == true ]]; then + print_info "Service: $PLIST_NAME (launchd)" +else + print_info "Service: $SERVICE_NAME (systemd)" +fi +echo "" + +if ! ask_yes_no "Do you want to remove the service?"; then + print_info "Service removal cancelled by user" + echo "" + if [[ "$INSTALL_EXISTS" == true ]]; then + print_info "Installation files remain at: $INSTALL_DIR" + print_info "You can remove them manually if needed" + fi + exit 0 +fi + +# Step 3: Stop and remove service +print_section "Step 3: Stopping and Removing Service" + +if [[ "$IS_MACOS" == true ]]; then + # macOS: Unload launchd service + if launchctl list "$PLIST_NAME" &>/dev/null 2>&1; then + print_info "Stopping service..." + launchctl stop "$PLIST_NAME" 2>/dev/null || true + launchctl unload "$LAUNCHD_DIR/$SERVICE_FILE" 2>/dev/null || true + print_success "Service stopped and unloaded" + else + print_warning "Service is not currently loaded" + fi + + # Remove plist file + if [ -f "$LAUNCHD_DIR/$SERVICE_FILE" ]; then + rm "$LAUNCHD_DIR/$SERVICE_FILE" + print_success "Removed service plist file" + else + print_warning "Service plist file not found" + fi +else + # Linux: Stop and disable systemd service + if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then + print_info "Stopping service..." + systemctl stop "$SERVICE_NAME" + print_success "Service stopped" + else + print_warning "Service is not currently running" + fi + + if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then + print_info "Disabling service..." + systemctl disable "$SERVICE_NAME" >/dev/null 2>&1 + print_success "Service disabled" + else + print_warning "Service is not enabled" + fi + + # Remove service file + if [ -f "$SYSTEMD_DIR/$SERVICE_NAME.service" ]; then + rm "$SYSTEMD_DIR/$SERVICE_NAME.service" + print_success "Removed service file" + + # Reload systemd + print_info "Reloading systemd configuration" + systemctl daemon-reload + print_success "Systemd configuration reloaded" + else + print_warning "Service file not found" + fi +fi + +# Step 4: Ask about removing installation files +print_section "Step 4: Installation Files" +if [[ "$INSTALL_EXISTS" == true ]]; then + print_warning "Installation directory found: $INSTALL_DIR" + print_info "This contains all bot files, including:" + echo " • Python scripts and modules" + echo " • Configuration files" + echo " • Virtual environment" + echo " • Database files" + echo " • Logs" + echo "" + + if ask_yes_no "Do you want to DELETE the installation directory and all its contents?"; then + print_warning "This action cannot be undone!" + if ask_yes_no "Are you SURE you want to delete $INSTALL_DIR?"; then + print_info "Removing installation directory..." + rm -rf "$INSTALL_DIR" + print_success "Removed installation directory: $INSTALL_DIR" + else + print_info "Installation directory preserved: $INSTALL_DIR" + fi + else + print_info "Installation directory preserved: $INSTALL_DIR" + print_info "You can remove it manually later if needed" + fi +else + print_warning "Installation directory not found: $INSTALL_DIR" + print_info "Nothing to remove" +fi + +# Step 5: Remove log directory (optional) +print_section "Step 5: Log Directory" if [ -d "$LOG_DIR" ]; then - rm -rf "$LOG_DIR" - echo -e "${GREEN}Removed log directory: $LOG_DIR${NC}" + print_info "Log directory found: $LOG_DIR" + if ask_yes_no "Do you want to remove the log directory?"; then + rm -rf "$LOG_DIR" + print_success "Removed log directory: $LOG_DIR" + else + print_info "Log directory preserved: $LOG_DIR" + fi else - echo -e "${YELLOW}Log directory not found${NC}" + print_warning "Log directory not found: $LOG_DIR" fi -echo -e "${YELLOW}Step 5: Removing service user${NC}" -# Remove service user -if id "$SERVICE_USER" &>/dev/null; then - userdel "$SERVICE_USER" - echo -e "${GREEN}Removed user: $SERVICE_USER${NC}" +# Step 6: Remove service user (Linux only) +if [[ "$IS_LINUX" == true ]]; then + print_section "Step 6: Service User" + if id "$SERVICE_USER" &>/dev/null; then + print_info "Service user found: $SERVICE_USER" + if ask_yes_no "Do you want to remove the service user '$SERVICE_USER'?"; then + userdel "$SERVICE_USER" 2>/dev/null || { + print_warning "Could not remove user (may be in use or have dependencies)" + } + print_success "Removed service user: $SERVICE_USER" + else + print_info "Service user preserved: $SERVICE_USER" + fi + else + print_warning "Service user not found: $SERVICE_USER" + fi +fi + +# Final summary +print_section "Uninstallation Complete" +echo "" +print_success "Service uninstallation completed!" +echo "" + +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${BLUE}📋 Summary${NC}" +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" + +if [[ "$IS_MACOS" == true ]]; then + echo -e " ${CYAN}Service:${NC} Removed from launchd" + echo -e " ${CYAN}Service file:${NC} $LAUNCHD_DIR/$SERVICE_FILE (removed)" else - echo -e "${YELLOW}User $SERVICE_USER not found${NC}" + echo -e " ${CYAN}Service:${NC} Removed from systemd" + echo -e " ${CYAN}Service file:${NC} $SYSTEMD_DIR/$SERVICE_NAME.service (removed)" +fi + +if [[ "$INSTALL_EXISTS" == true ]]; then + if [ -d "$INSTALL_DIR" ]; then + echo -e " ${CYAN}Installation:${NC} ${YELLOW}Preserved at $INSTALL_DIR${NC}" + else + echo -e " ${CYAN}Installation:${NC} ${GREEN}Removed${NC}" + fi +else + echo -e " ${CYAN}Installation:${NC} Not found (may have been removed already)" +fi + +if [ -d "$LOG_DIR" ]; then + echo -e " ${CYAN}Logs:${NC} ${YELLOW}Preserved at $LOG_DIR${NC}" +else + echo -e " ${CYAN}Logs:${NC} ${GREEN}Removed${NC}" +fi + +if [[ "$IS_LINUX" == true ]]; then + if id "$SERVICE_USER" &>/dev/null; then + echo -e " ${CYAN}Service user:${NC} ${YELLOW}Preserved: $SERVICE_USER${NC}" + else + echo -e " ${CYAN}Service user:${NC} ${GREEN}Removed${NC}" + fi fi echo "" -echo -e "${GREEN}Uninstallation completed successfully!${NC}" + +# Check for backup files in common locations +CONFIG_BACKUP_FOUND=$(find "$HOME_DIR" /tmp /home -name "meshcore-bot-config-backup-*.ini" -type f 2>/dev/null | head -1) +DB_BACKUP_FOUND=$(find "$HOME_DIR" /tmp /home -name "meshcore-bot-db-backup-*.db" -type f 2>/dev/null | head -1) + +if [ -n "$CONFIG_BACKUP_FOUND" ] || [ -n "$DB_BACKUP_FOUND" ]; then + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE}💾 Backup Files${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + + if [ -n "$CONFIG_BACKUP_FOUND" ]; then + print_success "Configuration backup saved to:" + echo " ${YELLOW}$CONFIG_BACKUP_FOUND${NC}" + echo "" + fi + + if [ -n "$DB_BACKUP_FOUND" ]; then + print_success "Database backup saved to:" + echo " ${YELLOW}$DB_BACKUP_FOUND${NC}" + echo "" + fi + + print_info "You can restore these backups when reinstalling" + echo "" +fi + +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${BLUE}ℹ️ Additional Notes${NC}" +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo "" -echo -e "${BLUE}What was removed:${NC}" -echo " - Systemd service file" -echo " - Installation directory: $INSTALL_DIR" -echo " - Log directory: $LOG_DIR" -echo " - Service user: $SERVICE_USER" +print_info "Python packages installed via pip are not automatically removed" +print_info "If you want to remove them, run:" +echo " ${YELLOW}pip3 uninstall -r requirements.txt${NC}" +echo "" + +if [[ "$INSTALL_EXISTS" == true ]] && [ -d "$INSTALL_DIR" ]; then + print_warning "Installation files are still present at: $INSTALL_DIR" + print_info "You can remove them manually with:" + echo " ${YELLOW}sudo rm -rf $INSTALL_DIR${NC}" + echo "" +fi + +print_success "Uninstallation process completed!" echo "" -echo -e "${YELLOW}Note: Python packages installed via pip are not removed${NC}" -echo -e "${YELLOW}If you want to remove them, run: pip3 uninstall -r requirements.txt${NC}"