botcat-capoo 1.0.3

A NapCat based QQ bot implemented in Rust
Documentation
use std::{net::SocketAddr, sync::Arc};

use axum::{
    Json, Router,
    extract::State,
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    serve,
};
use tokio::net::TcpListener;
use tracing::{error, info};

use botcat_capoo::{
    context::AppContext, error::Error, handler::handle_bot_event, model::event::BotEvent,
};

type SharedAppContext = Arc<AppContext>;

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_file(true)
        .with_line_number(true)
        .init();
    let context = AppContext::init().await?;

    let app = Router::new()
        .route("/ping", get(ping_handler))
        .route("/", post(napcat_handler))
        .with_state(context.clone());

    let ip = context
        .config
        .botcat_capoo
        .host
        .parse()
        .expect("Invalid host address");
    let port = context.config.botcat_capoo.port;
    let addr = SocketAddr::new(ip, port);
    let listener = TcpListener::bind(addr)
        .await
        .expect("Failed to bind to address, please check if the port is available");

    info!("Listening on http://{addr}");

    serve(listener, app).await.expect("Failed to start server");

    Ok(())
}

async fn ping_handler() -> String {
    "pong".to_string()
}

async fn napcat_handler(
    State(ctx): State<SharedAppContext>,
    Json(event): Json<BotEvent>,
) -> impl IntoResponse {
    match handle_bot_event(&ctx, event).await {
        Ok(_) => StatusCode::NO_CONTENT,
        Err(err) => {
            if let Error::Unimplemented(_) = err {
                info!("{err}");
                StatusCode::NO_CONTENT
            } else {
                error!("Failed to process bot event: {err}");
                StatusCode::INTERNAL_SERVER_ERROR
            }
        }
    }
}