import asyncio import logging import signal import os from dotenv import load_dotenv from db.database import init_db, close_db_pool from db.repository import Repository from core.metrics import start_metrics_server from core.llm import LLMClient from core.queue import RedisQueue from services.queue_consumer import QueueConsumerService from services.ai_processor import AIProcessor from services.collector import CollectorService from services.admin_bot import AdminBotService from services.publisher import PublisherService # Load environment variables load_dotenv() # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s" ) logger = logging.getLogger("copykar.main") async def main(): logger.info("Starting Copykar System...") # 1. Start Prometheus metrics server metrics_port = int(os.getenv("METRICS_PORT", "8000")) start_metrics_server(metrics_port) # 2. Initialize Database schema & Redis Queue await init_db() logger.info("Database schema initialized.") redis_queue = RedisQueue() await redis_queue.connect() repo = Repository() llm = LLMClient() # 3. Create Services admin_bot = AdminBotService(repo=repo) ai_processor = AIProcessor(repo=repo, llm=llm) # Wrap AI processor to automatically push reviewed posts to admin review channel original_process_post = ai_processor.process_post async def process_and_notify(post_id: int): post = await original_process_post(post_id) if post: await admin_bot.send_review_post(post.id) return post ai_processor.process_post = process_and_notify collector = CollectorService(repo=repo, ai_processor=ai_processor, queue=redis_queue) admin_bot.set_collector(collector) publisher = PublisherService(repo=repo) queue_consumer = QueueConsumerService(queue=redis_queue, ai_processor=ai_processor) # 4. Start all services await admin_bot.start() await collector.start(notify_fn=admin_bot.notify_admins) await queue_consumer.start() await publisher.start() logger.info("All Copykar services are active and running.") # Graceful shutdown event stop_event = asyncio.Event() loop = asyncio.get_running_loop() for sig in (signal.SIGINT, signal.SIGTERM): try: loop.add_signal_handler(sig, stop_event.set) except NotImplementedError: pass # Windows or specific platforms try: await stop_event.wait() except (asyncio.CancelledError, KeyboardInterrupt): pass finally: logger.info("Shutting down Copykar services...") await collector.stop() await queue_consumer.stop() await publisher.stop() await admin_bot.stop() await redis_queue.close() await close_db_pool() logger.info("Copykar cleanly shut down.") if __name__ == "__main__": asyncio.run(main())