"""Gravity Control — Antigravity Discord Bridge. Entry point that runs the brain watcher and Discord bot together. """ import asyncio import io import logging import os import sys from config import Config from watcher import BrainWatcher from bot import GravityBot # Logging setup (UTF-8 forced for Windows cp949 compatibility) _utf8_stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", handlers=[ logging.StreamHandler(_utf8_stdout), logging.FileHandler("gravity_control.log", encoding="utf-8"), ], ) logger = logging.getLogger("gravity_control") async def main(): """Run the bridge: watcher + Discord bot.""" # Validate config errors = Config.validate() if errors: for e in errors: logger.error(f"Config error: {e}") logger.error("Fix configuration issues and restart.") sys.exit(1) logger.info("=" * 50) logger.info("Gravity Control — Antigravity Discord Bridge") logger.info("=" * 50) logger.info(f"Brain path: {Config.BRAIN_PATH}") logger.info(f"Debounce: {Config.DEBOUNCE_SECONDS}s") logger.info(f"Bot mode: {Config.BOT_MODE}") # Shared event queue event_queue = asyncio.Queue() # Get the running loop loop = asyncio.get_running_loop() # Create transport based on BOT_MODE transport = None # None → LocalTransport (default) if Config.BOT_MODE == "remote": from bridge import RemoteTransport if not Config.REMOTE_BRIDGE_URL: logger.error("REMOTE_BRIDGE_URL is required for remote mode") sys.exit(1) transport = RemoteTransport(Config.REMOTE_BRIDGE_URL) logger.info(f"Remote transport: {Config.REMOTE_BRIDGE_URL}") # Create components watcher = None if Config.BOT_MODE != 'gateway': watcher = BrainWatcher(event_queue, loop) bot = GravityBot(event_queue) # Inject transport if specified (otherwise bot uses default LocalTransport) if transport is not None: from bridge import BridgeProtocol bot.bridge = BridgeProtocol(transport) try: # Start watcher (local mode only — gateway receives data via HTTP) if watcher: watcher.start() logger.info(f"Watcher started, {len(watcher.known_sessions)} existing sessions") else: logger.info("Gateway mode — watcher disabled (data via HTTP API)") # Start Gateway HTTP API (gateway mode) if Config.BOT_MODE == 'gateway': from gateway import GatewayAPI gateway_port = int(os.environ.get('GATEWAY_PORT', '8585')) gateway = GatewayAPI(bot, port=gateway_port, api_key=Config.GATEWAY_API_KEY) await gateway.start() logger.info(f"Gateway API running on port {gateway_port}") # Run Discord bot (blocks until bot disconnects) await bot.start(Config.DISCORD_TOKEN) except KeyboardInterrupt: logger.info("Received keyboard interrupt") except Exception as e: logger.error(f"Fatal error: {e}", exc_info=True) finally: # Cleanup if watcher: watcher.stop() if not bot.is_closed(): await bot.close() logger.info("Gravity Control shutdown complete") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: pass