""" Messenger Desktop Wrapper A lightweight desktop application for Facebook Messenger using PyWebView. """ import webview import threading import sys import os # Try to import system tray libraries (optional feature) try: import pystray from PIL import Image TRAY_AVAILABLE = True except ImportError: TRAY_AVAILABLE = False print("Note: pystray/Pillow not installed. System tray disabled.") def resource_path(relative_path): """ Get absolute path to resource, works for dev and for PyInstaller """ try: # PyInstaller creates a temp folder and stores path in _MEIPASS base_path = sys._MEIPASS except Exception: base_path = os.path.dirname(os.path.abspath(__file__)) return os.path.join(base_path, relative_path) # Configuration MESSENGER_URL = "https://www.messenger.com" WINDOW_TITLE = "Messenger" WINDOW_WIDTH = 1200 WINDOW_HEIGHT = 800 # Get the directory where this script is located ICON_PATH = resource_path("messenger_icon.png") ICO_PATH = resource_path("messenger_icon.ico") class MessengerApp: def __init__(self): self.window = None self.tray_icon = None self.is_quitting = False # Set AppUserModelID early - MUST be before window creation # This tells Windows to use our app identity instead of python.exe if sys.platform == 'win32': try: import ctypes ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("MessengerDesktopWrapper") except Exception as e: print(f"Warning: Could not set AppUserModelID: {e}") def create_tray_image(self): """Load the Messenger icon from PNG file.""" size = 64 # Try to load the PNG icon if os.path.exists(ICON_PATH): try: image = Image.open(ICON_PATH) image = image.resize((size, size), Image.Resampling.LANCZOS) return image.convert('RGBA') except Exception as e: print(f"Warning: Could not load icon: {e}") # Fallback: create a simple icon programmatically from PIL import ImageDraw image = Image.new('RGBA', (size, size), (0, 0, 0, 0)) draw = ImageDraw.Draw(image) draw.ellipse([4, 4, size-4, size-4], fill=(0, 132, 255)) draw.ellipse([16, 20, size-16, size-20], fill=(255, 255, 255)) return image def show_window(self, icon=None, item=None): """Show the main window.""" if self.window: self.window.show() self.window.restore() def quit_app(self, icon=None, item=None): """Quit the application completely.""" self.is_quitting = True if self.tray_icon: self.tray_icon.stop() if self.window: self.window.destroy() def on_closing(self): """Handle window close - minimize to tray instead of quitting.""" if TRAY_AVAILABLE and not self.is_quitting: self.window.hide() return False # Prevent window destruction return True # Allow window destruction def setup_tray(self): """Set up the system tray icon.""" if not TRAY_AVAILABLE: return menu = pystray.Menu( pystray.MenuItem("Show Messenger", self.show_window, default=True), pystray.MenuItem("Quit", self.quit_app) ) self.tray_icon = pystray.Icon( "Messenger", self.create_tray_image(), "Messenger", menu ) # Run tray icon in a separate thread tray_thread = threading.Thread(target=self.tray_icon.run, daemon=True) tray_thread.start() def set_window_icon(self): """Set the window icon using Windows API.""" if sys.platform != 'win32' or not os.path.exists(ICO_PATH): return try: import ctypes from ctypes import wintypes import time # Windows API constants WM_SETICON = 0x0080 ICON_SMALL = 0 ICON_BIG = 1 IMAGE_ICON = 1 LR_LOADFROMFILE = 0x0010 LR_DEFAULTSIZE = 0x0040 user32 = ctypes.windll.user32 # Load icon from file hicon = user32.LoadImageW( None, ICO_PATH, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE ) if not hicon: print(f"Warning: Failed to load icon from {ICO_PATH}") return # Small delay to ensure window is fully created time.sleep(0.5) # Try FindWindowW first hwnd = user32.FindWindowW(None, WINDOW_TITLE) # Fallback to GetForegroundWindow if FindWindowW fails if not hwnd: hwnd = user32.GetForegroundWindow() if hwnd: # Set both small (title bar) and big (taskbar/Alt-Tab) icons user32.SendMessageW(hwnd, WM_SETICON, ICON_SMALL, hicon) user32.SendMessageW(hwnd, WM_SETICON, ICON_BIG, hicon) print("Window icon set successfully") else: print("Warning: Could not find window handle") except Exception as e: print(f"Warning: Could not set window icon: {e}") def run(self): """Start the Messenger application.""" # Set up system tray first self.setup_tray() # Create the main webview window # Force edgechromium to avoid IE fallback # Set a modern User Agent to prevent sites from serving legacy constrained versions self.window = webview.create_window( title=WINDOW_TITLE, url=MESSENGER_URL, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, resizable=True, min_size=(400, 300) ) # Set up close handler for minimize-to-tray behavior if TRAY_AVAILABLE: self.window.events.closing += self.on_closing # Define storage path for persistence (Login & Notifications) data_dir = os.path.join(os.getenv('APPDATA'), 'MessengerWrapper') if not os.path.exists(data_dir): os.makedirs(data_dir, exist_ok=True) # Start the webview with icon callback webview.start( self.set_window_icon, private_mode=False, # Enable persistence (cookies, local storage) storage_path=data_dir, # Store data in AppData to remember login gui='edgechromium', # Force Edge rendering debug=False, # Disable debug console (remove developer window) user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36' ) # Clean up tray icon when done if self.tray_icon: self.tray_icon.stop() def main(): """Entry point for the application.""" app = MessengerApp() app.run() if __name__ == "__main__": main()