package shine.agent.botcoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; import shine.agent.botcoder.codex.CodexClient; import shine.agent.botcoder.config.AppConfig; import shine.agent.botcoder.history.HistoryManager; import shine.agent.botcoder.openai.OpenAiTranscriber; import shine.agent.botcoder.queue.QueueStore; import shine.agent.botcoder.state.RuntimeStateStore; import shine.agent.botcoder.state.SingleInstanceLock; import shine.agent.botcoder.telegram.ProcessedUpdatesStore; import shine.agent.botcoder.telegram.ShineAgentBot; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.concurrent.CountDownLatch; public class BotCoderApplication { private static final Logger log = LoggerFactory.getLogger(BotCoderApplication.class); public static void main(String[] args) throws Exception { Path serviceRoot = Path.of("").toAbsolutePath().normalize(); AppConfig config = AppConfig.load(serviceRoot); Files.createDirectories(config.dataDir()); SingleInstanceLock appLock = SingleInstanceLock.tryAcquire(config.dataDir().resolve("app.lock")); if (appLock == null) { log.error("SHiNE-agent-bot-coder уже запущен: lock занят {}", config.dataDir().resolve("app.lock")); return; } RuntimeStateStore stateStore = new RuntimeStateStore(config.dataDir().resolve("state.json")); QueueStore queueStore = new QueueStore(config.dataDir().resolve("queue.jsonl"), stateStore); HistoryManager historyManager = new HistoryManager( config.dataDir().resolve("history"), config.dataDir().resolve("history").resolve("archive"), stateStore ); List recovered = queueStore.recoverActiveJobs(); if (!recovered.isEmpty()) { historyManager.appendSystemEvent("active_jobs_recovered", java.util.Map.of("jobIds", recovered)); } OpenAiTranscriber transcriber = new OpenAiTranscriber(config.openAiApiKey(), config.openAiTranscribeModel()); CodexClient codexClient = new CodexClient(config.codexBin(), config.codexWorkDir(), config.codexTimeoutSeconds()); ProcessedUpdatesStore processedUpdatesStore = new ProcessedUpdatesStore( config.dataDir().resolve("processed_updates.log"), 5000 ); ShineAgentBot bot = new ShineAgentBot(config, queueStore, historyManager, transcriber, codexClient, processedUpdatesStore); bot.startWorkers(); TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); botsApi.registerBot(bot); log.info("SHiNE-agent-bot-coder запущен. allowed user: @{}", config.allowedTelegramUsername()); CountDownLatch latch = new CountDownLatch(1); Runtime.getRuntime().addShutdownHook(new Thread(() -> { bot.shutdown(); try { appLock.close(); } catch (Exception e) { log.warn("Не удалось закрыть lock-файл", e); } latch.countDown(); }, "shine-agent-bot-shutdown")); latch.await(); } }