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