Files
gravity_control/main.py
Variet Worker 95da3e9307 feat(gateway): API Key 인증 + HTTPS (Caddy) 보안 강화
- gateway.py: auth middleware — /api/* 엔드포인트에 Bearer token 필수
- Caddyfile: Let's Encrypt 자동 HTTPS 리버스 프록시
- docker-compose.yml: Caddy 추가, Gateway 포트 내부 전용
- config.py: GATEWAY_API_KEY 설정 추가
- .env: 키 생성 명령어 가이드 포함
2026-03-11 19:49:24 +09:00

112 lines
3.5 KiB
Python

"""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