""" ws_server.py — سرور WebSocket که caption ها را از افزونه‌ی Chrome می‌گیرد. افزونه (service worker → bridge.js) هر caption را با این پیام می‌فرستد: {type:"TRANSCRIPT_UPDATE", sessionId, speaker, text, startedAt, endedAt} و heartbeat هم: {type:"PING", ts} این فایل را می‌توان مستقل اجرا کرد (فقط WebSocket، برای تست با _smoke_test.py): python ws_server.py ولی در حالت عادی mcp_server.py همین را به‌عنوان background task بالا می‌آورد. """ import asyncio import json from datetime import datetime import websockets from storage import upsert_segment, log HOST = "127.0.0.1" PORT = 8765 async def handle_client(websocket): peer = getattr(websocket, "remote_address", "?") log(f"🔌 افزونه وصل شد: {peer}") try: async for raw in websocket: try: data = json.loads(raw) except (json.JSONDecodeError, TypeError): log(f"⚠️ پیام نامعتبر: {raw!r}") continue msg_type = data.get("type") if msg_type == "PING": await websocket.send(json.dumps({"type": "PONG", "ts": data.get("ts")})) continue if msg_type == "TRANSCRIPT_UPDATE": text = (data.get("text") or "").strip() if not text: continue session_id = data.get("sessionId", "default") speaker = data.get("speaker", "Unknown") upsert_segment(session_id, speaker, text, data.get("startedAt"), data.get("endedAt")) stamp = datetime.now().strftime("%H:%M:%S") log(f"[{stamp}] ({session_id[:8]}) {speaker}: {text[:60]}") await websocket.send(json.dumps({"type": "ACK", "ok": True})) continue log(f"❓ نوع ناشناخته: {msg_type}") except websockets.ConnectionClosed: pass finally: log(f"❌ افزونه قطع شد: {peer}") async def run_forever(): """WebSocket را بالا می‌آورد. اگر پورت اشغال بود (نمونه‌ی دیگری بالاست) graceful رد می‌شود.""" try: async with websockets.serve(handle_client, HOST, PORT): log(f"🚀 WebSocket روی ws://{HOST}:{PORT} گوش می‌دهد …") await asyncio.Future() except OSError as e: log(f"⚠️ نتوانست ws://{HOST}:{PORT} را بگیرد ({e}).") log(" این نمونه فقط MCP را سرو می‌کند و transcript ها را از روی دیسک می‌خواند.") await asyncio.Future() if __name__ == "__main__": try: asyncio.run(run_forever()) except KeyboardInterrupt: log("\n👋 خاموش شد.")