use crate::{bot::Bot, gitea::{GiteaAPI, WebhookType}, open_router::OpenRouterClient, state::AppState}; use dotenvy::dotenv; use tokio::signal::unix::{SignalKind, signal}; use tokio_util::sync::CancellationToken; use tracing::{info, warn}; use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt}; mod api; mod bot; mod bot_actions; mod consts; mod env; mod errors; mod gitea; mod open_router; mod state; fn main() -> anyhow::Result<()> { dotenv().ok(); tracing_subscriber::registry() .with(fmt::layer()) .with( EnvFilter::try_from_default_env() // lit RUST_LOG depuis l'env .unwrap_or_else(|_| EnvFilter::new("info")), ) .init(); let _sentry_guard = if let Ok(sentry_dsn) = env::try_get_env("SENTRY_DSN") { info!("Initialize sentry"); Some(sentry::init(( sentry_dsn, sentry::ClientOptions { release: sentry::release_name!(), send_default_pii: true, ..Default::default() }, ))) } else { warn!("SENTRY_DSN not set, sentry will not be initialized"); None }; tokio::runtime::Runtime::new()?.block_on(run()) } async fn run() -> anyhow::Result<()> { let config = env::load_config()?; let gitea_api = GiteaAPI::new(&config.gitea_url, &config.gitea_token, config.gitea_timeout)?; let gitea_user = gitea_api.get_authorized_user().await?; info!( port = config.http_port, model = %config.open_router_model, gitea_url = %config.gitea_url, bot_name = %gitea_user.login, "Starting Herald" ); let open_router_client = OpenRouterClient::new( &config.open_router_api_key, &config.open_router_model, config.open_router_timeout, )?; let shutdown = CancellationToken::new(); let bot = Bot::new( gitea_user.login, gitea_api, open_router_client, reqwest::Client::new(), config.bot_max_concurrent, config.open_router_model.clone(), ); let (tx, rx) = tokio::sync::mpsc::channel::(config.bot_max_concurrent * 2); let app_state = AppState { bot_tx: tx, bot: bot.clone(), config, }; let signal = async { let mut sigterm = signal(SignalKind::terminate())?; let mut sigint = signal(SignalKind::interrupt())?; tokio::select! { _ = sigterm.recv() => info!("Received SIGTERM"), _ = sigint.recv() => info!("Received SIGINT"), } info!("Shutting down..."); shutdown.cancel(); anyhow::Ok(()) }; tokio::try_join!( bot.start(rx, shutdown.clone()), api::start(app_state, shutdown.clone()), signal )?; info!("Shutdown complete"); Ok(()) }