python-toolbox/prod_tools/messenger-wrapper/messenger_app.py
2026-01-16 10:56:58 +10:00

201 lines
6.3 KiB
Python

"""
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.")
# Configuration
MESSENGER_URL = "https://www.messenger.com"
WINDOW_TITLE = "Messenger"
WINDOW_WIDTH = 1200
WINDOW_HEIGHT = 800
# Get the directory where this script is located
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ICON_PATH = os.path.join(SCRIPT_DIR, "messenger_icon.png")
ICO_PATH = os.path.join(SCRIPT_DIR, "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
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
# Start the webview with icon callback
webview.start(
self.set_window_icon, # Called when window is ready
private_mode=False, # Enable cookies/session persistence
storage_path=None # Use default storage location
)
# 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()