diff --git a/src/zyron_linux/agents/system.py b/src/zyron_linux/agents/system.py index 5e013e7..17315b6 100644 --- a/src/zyron_linux/agents/system.py +++ b/src/zyron_linux/agents/system.py @@ -10,42 +10,78 @@ import numpy as np import requests import json +import platform import zyron_linux.features.activity as activity_monitor import zyron_linux.features.clipboard as clipboard_monitor # import zyron_linux.features.files.finder as file_finder # Uses the new smart finder we just created -PROCESS_NAMES = { - # Browsers - "chrome": "chrome.exe", "googlechrome": "chrome.exe", "google": "chrome.exe", - "brave": "brave.exe", "bravebrowser": "brave.exe", - "edge": "msedge.exe", "msedge": "msedge.exe", "microsoftedge": "msedge.exe", - "firefox": "firefox.exe", "mozilla": "firefox.exe", - "opera": "opera.exe", - - # System & Tools - "notepad": "notepad.exe", - "calculator": "calc.exe", "calc": "calc.exe", - "cmd": "cmd.exe", "terminal": "WindowsTerminal.exe", - "explorer": "explorer.exe", "fileexplorer": "explorer.exe", - "taskmanager": "Taskmgr.exe", - - # Media - "spotify": "spotify.exe", - "vlc": "vlc.exe", - - # Coding - "vscode": "Code.exe", "code": "Code.exe", "visualstudiocode": "Code.exe", - "pycharm": "pycharm64.exe", - "androidstudio": "studio64.exe", - "intellij": "idea64.exe", - "python": "python.exe", - - # Social - "telegram": "Telegram.exe", - "discord": "Discord.exe", - "whatsapp": "WhatsApp.exe", - "zoom": "Zoom.exe" -} +# Platform-specific process names +if platform.system() == "Windows": + PROCESS_NAMES = { + # Browsers + "chrome": "chrome.exe", "googlechrome": "chrome.exe", "google": "chrome.exe", + "brave": "brave.exe", "bravebrowser": "brave.exe", + "edge": "msedge.exe", "msedge": "msedge.exe", "microsoftedge": "msedge.exe", + "firefox": "firefox.exe", "mozilla": "firefox.exe", + "opera": "opera.exe", + + # System & Tools + "notepad": "notepad.exe", + "calculator": "calc.exe", "calc": "calc.exe", + "cmd": "cmd.exe", "terminal": "WindowsTerminal.exe", + "explorer": "explorer.exe", "fileexplorer": "explorer.exe", + "taskmanager": "Taskmgr.exe", + + # Media + "spotify": "spotify.exe", + "vlc": "vlc.exe", + + # Coding + "vscode": "Code.exe", "code": "Code.exe", "visualstudiocode": "Code.exe", + "pycharm": "pycharm64.exe", + "androidstudio": "studio64.exe", + "intellij": "idea64.exe", + "python": "python.exe", + + # Social + "telegram": "Telegram.exe", + "discord": "Discord.exe", + "whatsapp": "WhatsApp.exe", + "zoom": "Zoom.exe" + } +else: # Linux + PROCESS_NAMES = { + # Browsers + "chrome": "chrome", "googlechrome": "chrome", "google": "chrome", + "brave": "brave", "bravebrowser": "brave", + "edge": "msedge", "msedge": "msedge", "microsoftedge": "msedge", + "firefox": "firefox", "mozilla": "firefox", + "opera": "opera", + + # System & Tools + "notepad": "gedit", # Linux text editor + "calculator": "gnome-calculator", "calc": "gnome-calculator", + "cmd": "gnome-terminal", "terminal": "gnome-terminal", + "explorer": "nautilus", "fileexplorer": "nautilus", + "taskmanager": "gnome-system-monitor", + + # Media + "spotify": "spotify", + "vlc": "vlc", + + # Coding + "vscode": "code", "code": "code", "visualstudiocode": "code", + "pycharm": "pycharm", + "androidstudio": "studio", + "intellij": "idea", + "python": "python", + + # Social + "telegram": "telegram-desktop", + "discord": "discord", + "whatsapp": "whatsapp", + "zoom": "zoom" + } def get_laptop_location(): """ @@ -162,47 +198,80 @@ def get_browser_path(browser_name): """Finds browser executable dynamically without hardcoded paths.""" browser_name = browser_name.lower().strip() - exes = { - "chrome": "chrome.exe", - "google": "chrome.exe", - "brave": "brave.exe", - "firefox": "firefox.exe", - "mozilla": "firefox.exe", - "edge": "msedge.exe", - "msedge": "msedge.exe", - "opera": "launcher.exe" - } - - executable = exes.get(browser_name, f"{browser_name}.exe") - if not executable.endswith(".exe"): executable += ".exe" - - - path = shutil.which(executable) - if path: return path - - - possible_roots = [ - os.environ.get("PROGRAMFILES"), - os.environ.get("PROGRAMFILES(X86)"), - os.environ.get("LOCALAPPDATA") - ] - - common_subdirs = [ - "Google\\Chrome\\Application", - "BraveSoftware\\Brave-Browser\\Application", - "Microsoft\\Edge\\Application", - "Mozilla Firefox", - "Opera" - ] + if platform.system() == "Windows": + exes = { + "chrome": "chrome.exe", + "google": "chrome.exe", + "brave": "brave.exe", + "firefox": "firefox.exe", + "mozilla": "firefox.exe", + "edge": "msedge.exe", + "msedge": "msedge.exe", + "opera": "launcher.exe" + } + + executable = exes.get(browser_name, f"{browser_name}.exe") + if not executable.endswith(".exe"): executable += ".exe" + + # Try system PATH first + path = shutil.which(executable) + if path: return path + + # Search common Windows locations + possible_roots = [ + os.environ.get("PROGRAMFILES"), + os.environ.get("PROGRAMFILES(X86)"), + os.environ.get("LOCALAPPDATA") + ] + + common_subdirs = [ + "Google\\Chrome\\Application", + "BraveSoftware\\Brave-Browser\\Application", + "Microsoft\\Edge\\Application", + "Mozilla Firefox", + "Opera" + ] + + for root in possible_roots: + if not root: continue + for subdir in common_subdirs: + full_path = os.path.join(root, subdir, executable) + if os.path.exists(full_path): + return full_path + + return None - for root in possible_roots: - if not root: continue - for subdir in common_subdirs: - full_path = os.path.join(root, subdir, executable) - if os.path.exists(full_path): - return full_path - - return None + else: # Linux + exes = { + "chrome": "google-chrome", + "google": "google-chrome", + "brave": "brave-browser", + "firefox": "firefox", + "mozilla": "firefox", + "edge": "microsoft-edge", + "msedge": "microsoft-edge", + "opera": "opera" + } + + executable = exes.get(browser_name, browser_name) + + # Try system PATH + path = shutil.which(executable) + if path: return path + + # Search common Linux locations + common_paths = [ + f"/usr/bin/{executable}", + f"/usr/local/bin/{executable}", + f"/snap/bin/{executable}", + os.path.expanduser(f"~/.local/bin/{executable}") + ] + + for path in common_paths: + if os.path.exists(path): + return path + + return None def capture_webcam(): @@ -317,27 +386,44 @@ def get_system_health(): def clear_recycle_bin(): """ - Clears the Windows Recycle Bin permanently. - Uses PowerShell command to empty all recycle bins on all drives. + Clears the Recycle Bin/Trash permanently (cross-platform). + Windows: Uses PowerShell to empty recycle bin + Linux: Uses gio trash command or removes trash files """ - print("šŸ—‘ļø Clearing Recycle Bin...") + print("šŸ—‘ļø Clearing Recycle Bin/Trash...") try: - # PowerShell command to clear recycle bin for all drives - ps_command = 'Clear-RecycleBin -Force -ErrorAction SilentlyContinue' - - # Execute PowerShell command - result = os.system(f'powershell -Command "{ps_command}"') + if platform.system() == "Windows": + # PowerShell command to clear recycle bin for all drives + ps_command = 'Clear-RecycleBin -Force -ErrorAction SilentlyContinue' + result = os.system(f'powershell -Command "{ps_command}"') + + if result == 0: + print("āœ… Recycle Bin cleared successfully!") + return "Recycle Bin has been emptied successfully. All deleted files have been permanently removed." + else: + print("āš ļø Recycle Bin might be already empty or operation completed with warnings.") + return "Recycle Bin operation completed. It may have been already empty." - if result == 0: - print("āœ… Recycle Bin cleared successfully!") - return "Recycle Bin has been emptied successfully. All deleted files have been permanently removed." - else: - print("āš ļø Recycle Bin might be already empty or operation completed with warnings.") - return "Recycle Bin operation completed. It may have been already empty." + else: # Linux + # Try gio trash command first (modern Linux) + result = os.system('gio trash --empty 2>/dev/null') + + if result == 0: + print("āœ… Trash cleared successfully!") + return "Trash has been emptied successfully. All deleted files have been permanently removed." + else: + # Fallback: manually remove trash files + trash_path = os.path.expanduser("~/.local/share/Trash/files") + if os.path.exists(trash_path): + os.system(f'rm -rf {trash_path}/* 2>/dev/null') + print("āœ… Trash cleared successfully!") + return "Trash has been emptied successfully." + else: + return "Trash is already empty or not found." except Exception as e: - error_msg = f"Error clearing recycle bin: {e}" + error_msg = f"Error clearing trash: {e}" print(f"āŒ {error_msg}") return error_msg @@ -475,12 +561,18 @@ def close_application(app_name): if app_key in PROCESS_NAMES: exe_name = PROCESS_NAMES[app_key] else: - exe_name = f"{app_key}.exe" + if platform.system() == "Windows": + exe_name = f"{app_key}.exe" + else: + exe_name = app_key print(f"šŸ’€ Killing process target: {exe_name}") try: - os.system(f"taskkill /f /im {exe_name} /t") + if platform.system() == "Windows": + os.system(f"taskkill /f /im {exe_name} /t") + else: # Linux + os.system(f"pkill -9 {exe_name}") except Exception as e: print(f"Error closing app: {e}") @@ -491,10 +583,16 @@ def open_file_path(path): elif "desktop" in path.lower(): path = os.path.join(os.path.expanduser("~"), "Desktop") elif "c drive" in path.lower() or "c:" in path.lower(): - path = "C:/" + if platform.system() == "Windows": + path = "C:/" + else: + path = "/" # Root directory on Linux if os.path.exists(path): - os.startfile(path) + if platform.system() == "Windows": + os.startfile(path) + else: # Linux + os.system(f'xdg-open "{path}" &') else: print(f"āŒ Path not found: {path}") except Exception as e: diff --git a/src/zyron_linux/agents/telegram.py b/src/zyron_linux/agents/telegram.py index 2168d2a..e3174aa 100644 --- a/src/zyron_linux/agents/telegram.py +++ b/src/zyron_linux/agents/telegram.py @@ -690,7 +690,7 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): query = command_json.get("query", "").lower() # Helper to find targets - import zyron.features.browser_control as browser_control + import zyron_linux.features.browser_control as browser_control # --- SMART MATCHING LOGIC --- # 1. Get all open tabs @@ -741,7 +741,7 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): tab_title = best_match.get('title') # Save Context for "Play it again" - from zyron.core import memory + from zyron_linux.core import memory memory.update_context("browser_interaction", tab_title) if tab_id: @@ -766,7 +766,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): loader = await update.message.reply_text("šŸ“ø Capturing tab...", reply_markup=get_main_keyboard()) # Wait for file - shot_path = os.path.join(os.environ.get('TEMP', ''), 'zyron_tab_screenshot.png') + import tempfile + shot_path = os.path.join(tempfile.gettempdir(), 'zyron_tab_screenshot.png') # Remove old file if exists to avoid sending stale one if os.path.exists(shot_path): diff --git a/src/zyron_linux/core/browser_host.py b/src/zyron_linux/core/browser_host.py index 690e4ec..d65c387 100644 --- a/src/zyron_linux/core/browser_host.py +++ b/src/zyron_linux/core/browser_host.py @@ -2,6 +2,7 @@ import json import struct import os +import tempfile from pathlib import Path # The native messaging host must read and write from/to stdin/stdout. @@ -27,7 +28,7 @@ def send_message(message): import time # --- Command Queue Logic --- -COMMAND_FILE_PATH = Path(os.environ.get('TEMP', '')) / 'zyron_firefox_commands.json' +COMMAND_FILE_PATH = Path(tempfile.gettempdir()) / 'zyron_firefox_commands.json' def poll_command_queue(): """Background thread to check for commands from Zyron.""" @@ -79,7 +80,7 @@ def main(): elif message.get("action") == "update_tabs": # For now, we'll just save this to a local file that activity.py can read # In the future, we might use a faster IPC or shared memory - temp_path = Path(os.environ.get('TEMP', '')) / 'zyron_firefox_tabs.json' + temp_path = Path(tempfile.gettempdir()) / 'zyron_firefox_tabs.json' try: with open(temp_path, 'w') as f: json.dump(message.get("tabs", []), f) @@ -96,7 +97,7 @@ def main(): header, encoded = data_url.split(",", 1) img_data = base64.b64decode(encoded) - shot_path = Path(os.environ.get('TEMP', '')) / 'zyron_tab_screenshot.png' + shot_path = Path(tempfile.gettempdir()) / 'zyron_tab_screenshot.png' with open(shot_path, 'wb') as f: f.write(img_data) @@ -110,7 +111,7 @@ def main(): except Exception as e: # We can't easily log to a console, so we might want to log to a file - log_path = Path(os.environ.get('TEMP', '')) / 'zyron_native_host_error.log' + log_path = Path(tempfile.gettempdir()) / 'zyron_native_host_error.log' with open(log_path, 'a') as f: f.write(f"Error: {str(e)}\n") diff --git a/src/zyron_linux/features/activity.py b/src/zyron_linux/features/activity.py index e9cfcef..14c16c0 100644 --- a/src/zyron_linux/features/activity.py +++ b/src/zyron_linux/features/activity.py @@ -4,6 +4,8 @@ import subprocess import sqlite3 import shutil +import platform +import tempfile from collections import defaultdict from pathlib import Path import time @@ -68,18 +70,21 @@ def get_chrome_tabs(): tabs = [] try: - # Chrome user data path - chrome_path = os.path.join( - os.environ.get('LOCALAPPDATA', ''), - 'Google', 'Chrome', 'User Data', 'Default' - ) + # Chrome user data path (cross-platform) + if platform.system() == "Windows": + chrome_path = os.path.join( + os.environ.get('LOCALAPPDATA', ''), + 'Google', 'Chrome', 'User Data', 'Default' + ) + else: # Linux + chrome_path = os.path.expanduser('~/.config/google-chrome/Default') # Try to read History file history_db = os.path.join(chrome_path, 'History') if os.path.exists(history_db): # Copy to temp to avoid locking - temp_db = os.path.join(os.environ.get('TEMP', ''), 'chrome_history_temp.db') + temp_db = os.path.join(tempfile.gettempdir(), 'chrome_history_temp.db') try: shutil.copy2(history_db, temp_db) @@ -126,16 +131,19 @@ def get_brave_tabs(): tabs = [] try: - # Brave user data path - brave_path = os.path.join( - os.environ.get('LOCALAPPDATA', ''), - 'BraveSoftware', 'Brave-Browser', 'User Data', 'Default' - ) + # Brave user data path (cross-platform) + if platform.system() == "Windows": + brave_path = os.path.join( + os.environ.get('LOCALAPPDATA', ''), + 'BraveSoftware', 'Brave-Browser', 'User Data', 'Default' + ) + else: # Linux + brave_path = os.path.expanduser('~/.config/BraveSoftware/Brave-Browser/Default') history_db = os.path.join(brave_path, 'History') if os.path.exists(history_db): - temp_db = os.path.join(os.environ.get('TEMP', ''), 'brave_history_temp.db') + temp_db = os.path.join(tempfile.gettempdir(), 'brave_history_temp.db') try: shutil.copy2(history_db, temp_db) @@ -179,16 +187,19 @@ def get_edge_tabs(): tabs = [] try: - # Edge user data path - edge_path = os.path.join( - os.environ.get('LOCALAPPDATA', ''), - 'Microsoft', 'Edge', 'User Data', 'Default' - ) + # Edge user data path (cross-platform) + if platform.system() == "Windows": + edge_path = os.path.join( + os.environ.get('LOCALAPPDATA', ''), + 'Microsoft', 'Edge', 'User Data', 'Default' + ) + else: # Linux + edge_path = os.path.expanduser('~/.config/microsoft-edge/Default') history_db = os.path.join(edge_path, 'History') if os.path.exists(history_db): - temp_db = os.path.join(os.environ.get('TEMP', ''), 'edge_history_temp.db') + temp_db = os.path.join(tempfile.gettempdir(), 'edge_history_temp.db') try: shutil.copy2(history_db, temp_db) @@ -232,7 +243,7 @@ def get_firefox_tabs(): tabs = [] # Try Native Bridge first (Real-time data) - temp_path = Path(os.environ.get('TEMP', '')) / 'zyron_firefox_tabs.json' + temp_path = Path(tempfile.gettempdir()) / 'zyron_firefox_tabs.json' if temp_path.exists(): try: mtime = temp_path.stat().st_mtime @@ -254,11 +265,14 @@ def get_firefox_tabs(): print(f"Error reading Firefox Native Bridge data: {e}") try: - # Firefox profiles path - firefox_path = os.path.join( - os.environ.get('APPDATA', ''), - 'Mozilla', 'Firefox', 'Profiles' - ) + # Firefox profiles path (cross-platform) + if platform.system() == "Windows": + firefox_path = os.path.join( + os.environ.get('APPDATA', ''), + 'Mozilla', 'Firefox', 'Profiles' + ) + else: # Linux + firefox_path = os.path.expanduser('~/.mozilla/firefox') if not os.path.exists(firefox_path): return tabs @@ -272,7 +286,7 @@ def get_firefox_tabs(): places_db = os.path.join(profile_path, 'places.sqlite') if os.path.exists(places_db): - temp_db = os.path.join(os.environ.get('TEMP', ''), 'firefox_places_temp.db') + temp_db = os.path.join(tempfile.gettempdir(), 'firefox_places_temp.db') try: shutil.copy2(places_db, temp_db) diff --git a/src/zyron_linux/features/browser_control.py b/src/zyron_linux/features/browser_control.py index 13d87c6..f354666 100644 --- a/src/zyron_linux/features/browser_control.py +++ b/src/zyron_linux/features/browser_control.py @@ -1,8 +1,9 @@ import json import os +import tempfile from pathlib import Path -COMMAND_FILE_PATH = Path(os.environ.get('TEMP', '')) / 'zyron_firefox_commands.json' +COMMAND_FILE_PATH = Path(tempfile.gettempdir()) / 'zyron_firefox_commands.json' def send_browser_command(action, **kwargs): """ diff --git a/src/zyron_linux/features/clipboard.py b/src/zyron_linux/features/clipboard.py index 1c5c680..0ca9159 100644 --- a/src/zyron_linux/features/clipboard.py +++ b/src/zyron_linux/features/clipboard.py @@ -8,7 +8,8 @@ import threading import json import os -import ctypes # Added for Windows API access +import platform +import ctypes # For Windows clipboard optimization from datetime import datetime # File to store clipboard history @@ -85,14 +86,19 @@ def monitor_clipboard(): print("šŸ‘ļø Clipboard monitoring started...") - # [FIX] Setup Windows API to check sequence number without locking - try: - user32 = ctypes.windll.user32 - user32.GetClipboardSequenceNumber.restype = ctypes.c_ulong - last_sequence_number = user32.GetClipboardSequenceNumber() - has_ctypes = True - except Exception as e: - print(f"āš ļø Windows API not available, falling back to basic polling: {e}") + # [FIX] Setup Windows API to check sequence number without locking (Windows only) + has_ctypes = False + if platform.system() == "Windows": + try: + user32 = ctypes.windll.user32 + user32.GetClipboardSequenceNumber.restype = ctypes.c_ulong + last_sequence_number = user32.GetClipboardSequenceNumber() + has_ctypes = True + except Exception as e: + print(f"āš ļø Windows API not available, falling back to basic polling: {e}") + has_ctypes = False + else: + # Linux - use simple polling without Windows API has_ctypes = False while monitoring_active: diff --git a/src/zyron_linux/features/files/tracker.py b/src/zyron_linux/features/files/tracker.py index e59b8be..acd8277 100644 --- a/src/zyron_linux/features/files/tracker.py +++ b/src/zyron_linux/features/files/tracker.py @@ -10,10 +10,21 @@ import sqlite3 import shutil import urllib.parse +import platform +import tempfile from datetime import datetime, timedelta from collections import defaultdict -import win32gui -import win32process + +# Windows-specific imports (Conditional) +HAS_WIN32 = False +if platform.system() == "Windows": + try: + import win32gui + import win32process + HAS_WIN32 = True + except ImportError: + pass + import psutil # Configuration @@ -28,26 +39,50 @@ currently_open_files = {} # Track files currently being accessed # List of apps that are browsers (need special handling for local files) -BROWSER_APPS = { - 'chrome.exe': 'Google Chrome', - 'msedge.exe': 'Microsoft Edge', - 'brave.exe': 'Brave Browser', - 'firefox.exe': 'Mozilla Firefox', - 'opera.exe': 'Opera' -} +if platform.system() == "Windows": + BROWSER_APPS = { + 'chrome.exe': 'Google Chrome', + 'msedge.exe': 'Microsoft Edge', + 'brave.exe': 'Brave Browser', + 'firefox.exe': 'Mozilla Firefox', + 'opera.exe': 'Opera' + } +else: + BROWSER_APPS = { + 'chrome': 'Google Chrome', + 'google-chrome': 'Google Chrome', + 'msedge': 'Microsoft Edge', + 'brave': 'Brave Browser', + 'firefox': 'Mozilla Firefox', + 'opera': 'Opera' + } # System/temp paths to ignore -IGNORE_PATHS = [ - "\\AppData\\Local\\Temp", - "\\Windows\\", - "\\System32\\", - "\\Program Files\\", - "\\ProgramData\\", - "\\$Recycle.Bin", - "\\.git", - "\\node_modules", - "\\venv\\", - "\\__pycache__", +if platform.system() == "Windows": + IGNORE_PATHS = [ + "\\AppData\\Local\\Temp", + "\\Windows\\", + "\\System32\\", + "\\Program Files\\", + "\\ProgramData\\", + "\\$Recycle.Bin", + ] +else: + IGNORE_PATHS = [ + "/tmp/", + "/var/tmp/", + "/proc/", + "/sys/", + "/dev/", + "/usr/lib/", + ] + +# Common ignore patterns +IGNORE_PATHS += [ + ".git", + "node_modules", + "venv", + "__pycache__", ] # File extensions we care about @@ -125,21 +160,34 @@ def get_browser_local_file(browser_process_name, window_title): """ try: history_db = None - user_data_dir = os.environ.get('LOCALAPPDATA', '') - - # Define paths to History DB based on browser - if browser_process_name == 'chrome.exe': - history_db = os.path.join(user_data_dir, 'Google', 'Chrome', 'User Data', 'Default', 'History') - elif browser_process_name == 'msedge.exe': - history_db = os.path.join(user_data_dir, 'Microsoft', 'Edge', 'User Data', 'Default', 'History') - elif browser_process_name == 'brave.exe': - history_db = os.path.join(user_data_dir, 'BraveSoftware', 'Brave-Browser', 'User Data', 'Default', 'History') + if platform.system() == "Windows": + user_data_dir = os.environ.get('LOCALAPPDATA', '') + + # Define paths to History DB based on browser + if browser_process_name == 'chrome.exe': + history_db = os.path.join(user_data_dir, 'Google', 'Chrome', 'User Data', 'Default', 'History') + elif browser_process_name == 'msedge.exe': + history_db = os.path.join(user_data_dir, 'Microsoft', 'Edge', 'User Data', 'Default', 'History') + elif browser_process_name == 'brave.exe': + history_db = os.path.join(user_data_dir, 'BraveSoftware', 'Brave-Browser', 'User Data', 'Default', 'History') + else: + # Linux paths + if 'chrome' in browser_process_name: + history_db = os.path.expanduser('~/.config/google-chrome/Default/History') + elif 'msedge' in browser_process_name: + history_db = os.path.expanduser('~/.config/microsoft-edge/Default/History') + elif 'brave' in browser_process_name: + history_db = os.path.expanduser('~/.config/BraveSoftware/Brave-Browser/Default/History') + elif 'firefox' in browser_process_name: + # Firefox is trickier because of profile names, but activity.py handles it. + # For brevity here, we skip or use a simple heuristic if needed. + pass if not history_db or not os.path.exists(history_db): return None # Copy DB to temp to avoid locking issues - temp_db = os.path.join(os.environ.get('TEMP', ''), 'tracker_history_temp.db') + temp_db = os.path.join(tempfile.gettempdir(), 'tracker_history_temp.db') try: shutil.copy2(history_db, temp_db) @@ -190,18 +238,33 @@ def get_browser_local_file(browser_process_name, window_title): def get_active_window_file(): """Get file path from currently active window using multiple detection methods""" try: - # Get active window handle - hwnd = win32gui.GetForegroundWindow() - if not hwnd: - return None, None - - # Get window title - window_title = win32gui.GetWindowText(hwnd) - if not window_title: - return None, None + if platform.system() == "Windows" and HAS_WIN32: + # Get active window handle + hwnd = win32gui.GetForegroundWindow() + if not hwnd: + return None, None + + # Get window title + window_title = win32gui.GetWindowText(hwnd) + if not window_title: + return None, None + + # Get process ID + _, pid = win32process.GetWindowThreadProcessId(hwnd) + else: + # Linux fallback (Basic process identification without window focus tracking) + # For a proper Linux implementation, we'd use something like xdotool if available. + # But for now, we'll try to find any process from BROWSER_APPS that's currently active. + # This is a simplification. + window_title = "" + pid = None + for p in psutil.process_iter(['pid', 'name']): + if p.info['name'].lower() in BROWSER_APPS: + pid = p.info['pid'] + break - # Get process ID - _, pid = win32process.GetWindowThreadProcessId(hwnd) + if not pid: + return None, None # Get process info try: diff --git a/src/zyron_linux/features/focus_mode.py b/src/zyron_linux/features/focus_mode.py index f3eef7b..4b9116c 100644 --- a/src/zyron_linux/features/focus_mode.py +++ b/src/zyron_linux/features/focus_mode.py @@ -173,8 +173,8 @@ def kill_process(proc_name): def check_and_block(): """Main Enforcer Loop - Optimized for performance.""" - import zyron.features.activity as activity_monitor - import zyron.features.browser_control as browser_control + import zyron_linux.features.activity as activity_monitor + import zyron_linux.features.browser_control as browser_control print("šŸ›”ļø Focus Mode Enforcer Started.") diff --git a/src/zyron_linux/main.py b/src/zyron_linux/main.py index dc82290..9a6865a 100644 --- a/src/zyron_linux/main.py +++ b/src/zyron_linux/main.py @@ -4,7 +4,7 @@ from .agents.system import execute_command # Import file tracker - it will auto-start when imported -# import zyron.features.files.tracker as file_tracker +import zyron_linux.features.files.tracker as file_tracker def main(): print("⚔ ZYRON ONLINE: Say 'Hey Pikachu' to start...") @@ -41,5 +41,5 @@ def main(): except KeyboardInterrupt: print("\n⚔ System shutting down.") # Stop file tracking on shutdown - # file_tracker.stop_tracking() + file_tracker.stop_tracking() \ No newline at end of file diff --git a/src/zyron_linux/scripts/register_native_host.py b/src/zyron_linux/scripts/register_native_host.py index f2ab9d3..daed873 100644 --- a/src/zyron_linux/scripts/register_native_host.py +++ b/src/zyron_linux/scripts/register_native_host.py @@ -1,52 +1,97 @@ import os import sys import json -import winreg +import platform from pathlib import Path +# Platform-specific imports +if platform.system() == "Windows": + import winreg + def register(): + current_os = platform.system() + # 1. Setup paths project_root = Path(__file__).parent.parent.parent.parent.absolute() - host_script = project_root / 'src' / 'zyron' / 'core' / 'browser_host.py' - manifest_template = project_root / 'src' / 'zyron' / 'core' / 'native_manifest.json' - manifest_output = project_root / 'src' / 'zyron' / 'core' / 'zyron_native_host.json' - - # Batch file to launch python invisibly or at least with correct env - # Using 'pythonw' if available to avoid a console window popping up - python_exe = sys.executable - pythonw_exe = python_exe.replace("python.exe", "pythonw.exe") + host_script = project_root / 'src' / 'zyron_linux' / 'core' / 'browser_host.py' + manifest_template = project_root / 'src' / 'zyron_linux' / 'core' / 'native_manifest.json' - if os.path.exists(pythonw_exe): - exe_to_use = pythonw_exe - else: - exe_to_use = python_exe + if current_os == "Windows": + manifest_output = project_root / 'src' / 'zyron_linux' / 'core' / 'zyron_native_host.json' + + # Batch file to launch python invisibly or at least with correct env + # Using 'pythonw' if available to avoid a console window popping up + python_exe = sys.executable + pythonw_exe = python_exe.replace("python.exe", "pythonw.exe") + + if os.path.exists(pythonw_exe): + exe_to_use = pythonw_exe + else: + exe_to_use = python_exe - bat_content = f'@echo off\n"{exe_to_use}" -u "{host_script}" %*' - bat_path = project_root / 'zyron_host.bat' - - with open(bat_path, 'w') as f: - f.write(bat_content) + bat_content = f'@echo off\n"{exe_to_use}" -u "{host_script}" %*' + bat_path = project_root / 'zyron_host.bat' + + with open(bat_path, 'w') as f: + f.write(bat_content) + + # 2. Update manifest + with open(manifest_template, 'r') as f: + manifest = json.load(f) + + manifest['path'] = str(bat_path) + + with open(manifest_output, 'w') as f: + json.dump(manifest, f, indent=2) + + # 3. Registry update + reg_key = r"Software\Mozilla\NativeMessagingHosts\zyron.native.host" + try: + winreg.CreateKey(winreg.HKEY_CURRENT_USER, reg_key) + with winreg.OpenKey(winreg.HKEY_CURRENT_USER, reg_key, 0, winreg.KEY_WRITE) as key: + winreg.SetValueEx(key, "", 0, winreg.REG_SZ, str(manifest_output)) + print(f"āœ… Successfully registered Zyron Native Host in Windows Registry.") + print(f"šŸ“ Manifest: {manifest_output}") + print(f"šŸš€ Host: {bat_path}") + except Exception as e: + print(f"āŒ Registry error: {e}") - # 2. Update manifest - with open(manifest_template, 'r') as f: - manifest = json.load(f) - - manifest['path'] = str(bat_path) - - with open(manifest_output, 'w') as f: - json.dump(manifest, f, indent=2) - - # 3. Registry update - reg_key = r"Software\Mozilla\NativeMessagingHosts\zyron.native.host" - try: - winreg.CreateKey(winreg.HKEY_CURRENT_USER, reg_key) - with winreg.OpenKey(winreg.HKEY_CURRENT_USER, reg_key, 0, winreg.KEY_WRITE) as key: - winreg.SetValueEx(key, "", 0, winreg.REG_SZ, str(manifest_output)) - print(f"āœ… Successfully registered Zyron Native Host in Registry.") + elif current_os == "Linux": + # Linux uses ~/.mozilla/native-messaging-hosts/ + mozilla_native_dir = Path.home() / '.mozilla' / 'native-messaging-hosts' + mozilla_native_dir.mkdir(parents=True, exist_ok=True) + + manifest_output = mozilla_native_dir / 'zyron_native_host.json' + + # Create shell script to launch Python + shell_script_content = f'''#!/bin/bash +"{sys.executable}" -u "{host_script}" "$@" +''' + shell_script_path = project_root / 'zyron_host.sh' + + with open(shell_script_path, 'w') as f: + f.write(shell_script_content) + + # Make shell script executable + os.chmod(shell_script_path, 0o755) + + # 2. Update manifest + with open(manifest_template, 'r') as f: + manifest = json.load(f) + + manifest['path'] = str(shell_script_path) + + # 3. Write manifest to Mozilla directory + with open(manifest_output, 'w') as f: + json.dump(manifest, f, indent=2) + + print(f"āœ… Successfully registered Zyron Native Host for Linux Firefox.") print(f"šŸ“ Manifest: {manifest_output}") - print(f"šŸš€ Host: {bat_path}") - except Exception as e: - print(f"āŒ Registry error: {e}") + print(f"šŸš€ Host: {shell_script_path}") + + else: + print(f"āŒ Unsupported operating system: {current_os}") + print(" Zyron currently supports Windows and Linux only.") if __name__ == "__main__": register()