"""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 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 components bot = GravityBot(event_queue) try: # Start Gateway HTTP API + WebSocket Hub from gateway import GatewayAPI from hub import WSHub from auth import TokenManager # Initialize Hub token_mgr = TokenManager( secret=Config.GRAVITY_HUB_SECRET, registration_code=Config.GRAVITY_REGISTRATION_CODE, ) hub = WSHub(token_mgr) gateway_port = int(os.environ.get('GATEWAY_PORT', '8585')) gateway = GatewayAPI( bot, port=gateway_port, api_key=Config.GATEWAY_API_KEY, hub=hub, ) bot.gateway = gateway # Enable _write_command → gateway.push_command bot.hub = hub # Enable Hub-based message routing await gateway.start() logger.info(f"Gateway API + WS Hub 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 not bot.is_closed(): await bot.close() logger.info("Gravity Control shutdown complete") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: pass