diff --git a/church-tools/service_account.json b/church-tools/service_account.json new file mode 100644 index 0000000..0287af7 --- /dev/null +++ b/church-tools/service_account.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "cpc-roster", + "private_key_id": "87b13e5388800e0e1d552884956ef64be34036b5", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCLvDVVYkEqyh8F\nzGrxG4b0jvtt+rRbE6LcJu9l1l3n2esqXJtd2CtYXLMQGEsRxjkdVlqNM0VbCFg7\n5iae/zn2+t7Gw5sJd+kP7+BT7+r3J3r0qS2GqvZUlpXyq/8oG5NUMUV5n6aAoT9j\nKKqcacMV0cLptymCvD/q4GgZLLqNPYPGsI88OOV20nFOVTxk3nRSHX9TbA01Cd+w\nQwhd7DYXtVKB3s9Ka9dS8o1EAMv/95yyzYUY+KqyeiXKS5aPEodToagAEZimSoYF\nbfp7SSVpTPa2AFY+aBksa3G6anSzN/xwuo2ceJvejsJkFVr9SXe8ZvXtAQF0uVHX\ned3cKfk7AgMBAAECggEAGvs79yEyQR9nDxkSGz07+VP0g59xnNZ1T3orJqhWWJLx\nACyCiYPYlsbLgpOWRzfVxm7qrDIWtcWuN96wXWod2K1HP+z8Lj9bnsqsL7j3T0Lt\n0HksQ8EWMG4lRQbmDWoJsCuVZS8pGNRJ540uKGaZl0h6EOPfnZFY7DLpltbWrLfH\nf/aluEW4ozqKWe0vAs650TGcOj/rtiFjVXBeOujooVtrXBEwYxYglK9i0kPwKima\nEJNZ/jKQwjX70zBSepd/RCLkLrw2SCJZDM5dsXfqa7zdt+mCXuIRPNb1Pt6T4x+4\nH1GWIT0rCpARUQ5oUxUgLDbGh2Jyn6THgvSrj9ZK8QKBgQDBJbG9c/dNRemxWGnx\nNnsJQmCNxP19Duqyb9Imdro+JRnrSuwxUaxaAjdem/1GhuM0PTe9XYJ6jEHlQztj\nF0EPLudBiU/TuqWzKKS8m/oyunNaMAuXsxG2/4qmcQbpbCPWp9haIYIV0cmeL7nO\n18BdRLEpwIk8Sah1xGFeWUnq8QKBgQC5NPl8M3dRJPriZx15sKeFFC4UyihoCCjB\nocDJj5ORMcD8bltJrZAl5ThA+0j+EmMWfnpTpts1D0D7BipGKYn1k52OVacYfllP\nx4GBmwUcYktI0PGiH5V1Q59rVFEMdksTuovkGfwSMsesMBMOSUCnnYLIZcegtkUp\nPKy4/pgu6wKBgAUT859SfC745pL2Cxr/Xq64owc3JZ7zFHKKhBDk0DKwEQVhm+IU\nDp7zLfd7zGXdR/3omh2NZs/H/jeD0/zf8KLwjb3oWUPsxUx3hhU7WJGa20uEtdlQ\nKOgRwdhsYk0ivbJ4CTUdamkecdmJ0a3BeFo7YxkA6dq+6QHnntO3Lz1RAoGATW0u\nF5RUf2HcWKYPFn6ZqfLjyOEyvzsZ/skmlZ0mbf7E1UM1ernKl20adWAYBc+eCRta\n/Hm3DpmP6uGK7sYS3M2XgpegQYcgw09+frZWDqWxH4HqYoKdsuZQUUhwkasqe9bz\nAKs0TE9aVkvKy6AoHWql8C1MpY1NoI2zD6uv530CgYBBgtLxiOZQtxf08GWFKxan\n1mPXAYBkni0DWOau2eL9lNgcYhwMDbYHFmNIenl+Tg5VTDyWCtxLP7/6vohai7e6\nXE8vvuuWNifHM2hoW29gJByewmslztXY68JjWWBd/m9M6bdsSWjIj2Lr8s1VQyce\nPgYpw0rTVL4NjY8+0bm/eg==\n-----END PRIVATE KEY-----\n", + "client_email": "cpcmusic@cpc-roster.iam.gserviceaccount.com", + "client_id": "100783042323775881315", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/cpcmusic%40cpc-roster.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +} diff --git a/prod_tools/messenger-wrapper/README.md b/prod_tools/messenger-wrapper/README.md new file mode 100644 index 0000000..013e138 --- /dev/null +++ b/prod_tools/messenger-wrapper/README.md @@ -0,0 +1,35 @@ +# Messenger Desktop Wrapper + +A lightweight Python desktop wrapper for Facebook Messenger using PyWebView. + +## Features +- Native desktop window for Messenger +- System tray icon for background running +- Persistent login sessions +- Minimize to tray on close + +## Installation + +1. **Install Python dependencies:** +```bash +pip install -r requirements.txt +``` + +2. **Run the application:** +```bash +python messenger_app.py +``` + +## Usage + +- **Close button** minimizes to system tray (app continues running) +- **Right-click tray icon** for options (Show/Quit) +- **Double-click tray icon** to restore window + +## Building an Executable (Optional) + +To create a standalone `.exe` file: +```bash +pip install pyinstaller +pyinstaller --onefile --windowed --icon=messenger.ico messenger_app.py +``` diff --git a/prod_tools/messenger-wrapper/messenger_app.py b/prod_tools/messenger-wrapper/messenger_app.py new file mode 100644 index 0000000..c50c16c --- /dev/null +++ b/prod_tools/messenger-wrapper/messenger_app.py @@ -0,0 +1,201 @@ +""" +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() diff --git a/prod_tools/messenger-wrapper/messenger_icon.ico b/prod_tools/messenger-wrapper/messenger_icon.ico new file mode 100644 index 0000000..dbf6150 Binary files /dev/null and b/prod_tools/messenger-wrapper/messenger_icon.ico differ diff --git a/prod_tools/messenger-wrapper/messenger_icon.png b/prod_tools/messenger-wrapper/messenger_icon.png new file mode 100644 index 0000000..01d2602 Binary files /dev/null and b/prod_tools/messenger-wrapper/messenger_icon.png differ diff --git a/prod_tools/messenger-wrapper/messenger_icon.svg b/prod_tools/messenger-wrapper/messenger_icon.svg new file mode 100644 index 0000000..59bb5bd --- /dev/null +++ b/prod_tools/messenger-wrapper/messenger_icon.svg @@ -0,0 +1 @@ +Messenger diff --git a/prod_tools/messenger-wrapper/requirements.txt b/prod_tools/messenger-wrapper/requirements.txt new file mode 100644 index 0000000..3c2c36b --- /dev/null +++ b/prod_tools/messenger-wrapper/requirements.txt @@ -0,0 +1,3 @@ +pywebview>=4.0 +pystray +Pillow