feat: Mise en place Framework Scheduler/Api with Actix
This commit is contained in:
parent
a03cb86f7a
commit
1bd8b07f47
212
Cargo.lock
generated
212
Cargo.lock
generated
@ -19,6 +19,21 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-cors"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.11.0"
|
||||
@ -31,12 +46,15 @@ dependencies = [
|
||||
"actix-utils",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.9.1",
|
||||
"brotli",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"derive_more",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"foldhash",
|
||||
"futures-core",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
@ -52,6 +70,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -73,6 +92,7 @@ dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"http 0.2.12",
|
||||
"regex",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"tracing",
|
||||
@ -143,6 +163,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"cookie",
|
||||
"derive_more",
|
||||
"encoding_rs",
|
||||
"foldhash",
|
||||
@ -155,6 +176,7 @@ dependencies = [
|
||||
"mime",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"regex",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -227,6 +249,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc-no-stdlib"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||
|
||||
[[package]]
|
||||
name = "alloc-stdlib"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -248,6 +285,24 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-web",
|
||||
"clickhouse_pool",
|
||||
"config",
|
||||
"database",
|
||||
"poise",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-actix-web",
|
||||
"utoipa",
|
||||
"utoipa-actix-web",
|
||||
"utoipa-scalar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
@ -412,6 +467,27 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.0"
|
||||
@ -491,6 +567,8 @@ version = "1.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@ -525,11 +603,12 @@ checksum = "93a719913643003b84bd13022b4b7e703c09342cd03b679c4641c7d2e50dc34d"
|
||||
name = "cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"api",
|
||||
"bot",
|
||||
"config",
|
||||
"cron_scheduler",
|
||||
"database",
|
||||
"tokio",
|
||||
"tokio-cron-scheduler",
|
||||
"tool_tracing",
|
||||
"tracing",
|
||||
]
|
||||
@ -605,6 +684,17 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@ -639,6 +729,20 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cron_scheduler"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"database",
|
||||
"poise",
|
||||
"tokio",
|
||||
"tokio-cron-scheduler",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "croner"
|
||||
version = "2.1.0"
|
||||
@ -1478,6 +1582,7 @@ checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1501,6 +1606,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
@ -1657,6 +1772,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutually_exclusive_features"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94e1e6445d314f972ff7395df2de295fe51b71821694f0b0e1e79c4f12c8577"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
@ -3212,6 +3333,21 @@ dependencies = [
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-actix-web"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2340b7722695166c7fc9b3e3cd1166e7c74fedb9075b8f0c74d3822d2e41caf5"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"mutually_exclusive_features",
|
||||
"opentelemetry",
|
||||
"pin-project",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
@ -3444,6 +3580,52 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "5.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-actix-web"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7eda9c23c05af0fb812f6a177514047331dac4851a2c8e9c4b895d6d826967f"
|
||||
dependencies = [
|
||||
"actix-service",
|
||||
"actix-web",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "5.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-scalar"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59559e1509172f6b26c1cdbc7247c4ddd1ac6560fe94b584f81ee489b141f719"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.16.0"
|
||||
@ -4129,3 +4311,31 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
@ -8,6 +8,8 @@ members = [
|
||||
'libs/database',
|
||||
'libs/bot',
|
||||
'libs/config',
|
||||
'libs/cron_scheduler',
|
||||
'libs/api',
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
|
@ -9,11 +9,9 @@ tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
database = { path = "../../libs/database" }
|
||||
tool_tracing = { path = "../../libs/tool_tracing" }
|
||||
tokio-cron-scheduler = { version = "0.14", features = [
|
||||
"tracing-subscriber",
|
||||
"signal",
|
||||
] }
|
||||
config = { path = "../../libs/config" }
|
||||
bot = { path = "../../libs/bot" }
|
||||
cron_scheduler = { path = "../../libs/cron_scheduler" }
|
||||
api = { path = "../../libs/api" }
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -1,11 +1,16 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use api::init_api;
|
||||
use bot::start_bot;
|
||||
use config::parse_local_config;
|
||||
use cron_scheduler::ScheduleJob;
|
||||
use database::{create_manager_and_init, create_pool_manager};
|
||||
use tokio::{sync::oneshot, time::sleep};
|
||||
use tool_tracing::init::init_tracing;
|
||||
use tracing::{error, info};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> Result<(), ()> {
|
||||
println!(include_str!("banner.art"));
|
||||
let config = parse_local_config();
|
||||
init_tracing(config.tracing.clone(), config.bot_name.clone());
|
||||
@ -19,10 +24,30 @@ async fn main() {
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to create database manager: {}", e);
|
||||
return;
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
info!("Database manager initialized successfully");
|
||||
let mut cron_scheduler = match ScheduleJob::start_cron_scheduler().await {
|
||||
Ok(scheduler) => {
|
||||
info!("Cron scheduler started successfully");
|
||||
scheduler
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Failed to start cron scheduler");
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
let (tx_bot, rx_bot) = oneshot::channel();
|
||||
let http = start_bot(config.clone(), manager.clone(), rx_bot).await;
|
||||
|
||||
start_bot(config, manager).await;
|
||||
let _ = init_api(config, manager, http).await;
|
||||
|
||||
tx_bot.send(()).unwrap_or_else(|_| {
|
||||
error!("Failed to send shutdown signal to bot");
|
||||
});
|
||||
cron_scheduler.stop_cron_scheduler().await;
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
//process::exit(1);
|
||||
Ok(())
|
||||
}
|
||||
|
22
libs/api/Cargo.toml
Normal file
22
libs/api/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
poise = { workspace = true }
|
||||
|
||||
utoipa-actix-web = "0.1"
|
||||
actix-web = "4"
|
||||
actix-cors = "0.7"
|
||||
utoipa-scalar = { version = "0.3", features = ["actix-web"] }
|
||||
utoipa = "5"
|
||||
tracing-actix-web = { version = "0.7", features = ["opentelemetry_0_29"] }
|
||||
|
||||
config = { path = "../../libs/config" }
|
||||
database = { path = "../../libs/database" }
|
||||
clickhouse_pool = { path = "../../libs/clickhouse_pool" }
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
43
libs/api/project.json
Normal file
43
libs/api/project.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "api",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"sourceRoot": "libs/api/src",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@monodon/rust:check",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/api"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"cache": true,
|
||||
"executor": "@monodon/rust:test",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/api"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"release": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"cache": true,
|
||||
"executor": "@monodon/rust:lint",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/api"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
25
libs/api/src/apidocs.rs
Normal file
25
libs/api/src/apidocs.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
info(
|
||||
title = "Bot API",
|
||||
description = "API documentation for the Bot application",
|
||||
version = "1.0.0"
|
||||
),
|
||||
tags(
|
||||
( name = "Bot",
|
||||
description = "Bot related endpoints"
|
||||
),
|
||||
( name = "User",
|
||||
description = "User related endpoints"
|
||||
),
|
||||
( name = "Admin",
|
||||
description = "Admin related endpoints"
|
||||
),
|
||||
( name = "Cron",
|
||||
description = "Cron job related endpoints"
|
||||
),
|
||||
)
|
||||
)]
|
||||
pub struct ApiDocs;
|
63
libs/api/src/lib.rs
Normal file
63
libs/api/src/lib.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{dev::Service, http::header, App, HttpServer};
|
||||
use apidocs::ApiDocs;
|
||||
use clickhouse_pool::pool_manager::PoolManager;
|
||||
use config::Config;
|
||||
use poise::serenity_prelude::Http;
|
||||
use std::{net::Ipv4Addr, sync::Arc};
|
||||
use tracing::{error, info};
|
||||
use tracing_actix_web::{RequestId, TracingLogger};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_actix_web::AppExt;
|
||||
use utoipa_scalar::{Scalar, Servable as ScalarServable};
|
||||
|
||||
pub mod apidocs;
|
||||
|
||||
pub async fn init_api(config: Config, pool: Arc<PoolManager>, http: Arc<Http>) -> Result<(), ()> {
|
||||
let port = config.port.clone();
|
||||
HttpServer::new(move || {
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.allow_any_method()
|
||||
.allow_any_header()
|
||||
.max_age(3600);
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.wrap_fn(|mut req, srv| {
|
||||
let request_id_asc = req.extract::<RequestId>();
|
||||
let fut = srv.call(req);
|
||||
async move {
|
||||
let mut res = fut.await?;
|
||||
let request_id: RequestId = request_id_asc.await.unwrap();
|
||||
let request_id_str = format!("{}", request_id);
|
||||
let headers = res.headers_mut();
|
||||
headers.insert(
|
||||
header::HeaderName::from_static("x-request-id"),
|
||||
header::HeaderValue::from_str(request_id_str.as_str()).unwrap(),
|
||||
);
|
||||
Ok(res)
|
||||
}
|
||||
})
|
||||
.wrap(TracingLogger::default())
|
||||
.into_utoipa_app()
|
||||
.openapi(ApiDocs::openapi())
|
||||
.app_data(actix_web::web::Data::new(pool.clone()))
|
||||
.app_data(actix_web::web::Data::new(http.clone()))
|
||||
.app_data(actix_web::web::Data::new(config.clone()))
|
||||
.openapi_service(|api| Scalar::with_url("/api/docs", api))
|
||||
.into_app()
|
||||
})
|
||||
.bind((Ipv4Addr::UNSPECIFIED, port))
|
||||
.map_err(|e| {
|
||||
error!("Failed to bind API server: {}", e);
|
||||
()
|
||||
})?
|
||||
.run()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed to start API server: {}", e);
|
||||
()
|
||||
})?;
|
||||
info!("Api server stopped");
|
||||
Ok(())
|
||||
}
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||
use clickhouse_pool::pool_manager::PoolManager;
|
||||
use event::event_handler;
|
||||
use poise::serenity_prelude::{GatewayIntents, Http};
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::info;
|
||||
use trivia::trivia;
|
||||
use utility::{age::age, help::help, server::servers};
|
||||
@ -23,7 +24,11 @@ pub struct Data {
|
||||
pub type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
pub type Context<'a> = poise::Context<'a, Data, Error>;
|
||||
|
||||
pub async fn start_bot(config: Config, datalake_config: Arc<PoolManager>) -> Arc<Http> {
|
||||
pub async fn start_bot(
|
||||
config: Config,
|
||||
datalake_config: Arc<PoolManager>,
|
||||
rx: oneshot::Receiver<()>,
|
||||
) -> Arc<Http> {
|
||||
let intents = GatewayIntents::GUILD_MESSAGES
|
||||
| GatewayIntents::DIRECT_MESSAGES
|
||||
| GatewayIntents::MESSAGE_CONTENT
|
||||
@ -69,6 +74,27 @@ pub async fn start_bot(config: Config, datalake_config: Arc<PoolManager>) -> Arc
|
||||
}
|
||||
};
|
||||
let http = client.http.clone();
|
||||
client.start().await.unwrap();
|
||||
let shard_manager = client.shard_manager.clone();
|
||||
tokio::spawn(async move {
|
||||
match rx.await {
|
||||
Ok(_) => {
|
||||
tracing::info!("Received shutdown signal");
|
||||
shard_manager.shutdown_all().await;
|
||||
tracing::info!("Shutting down bot");
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::info!("Channel dropped signal");
|
||||
shard_manager.shutdown_all().await;
|
||||
tracing::info!("Shutting down bot");
|
||||
}
|
||||
}
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
info!("Bot is running...");
|
||||
if let Err(why) = client.start_autosharded().await {
|
||||
tracing::error!("Client error: {why:?}");
|
||||
}
|
||||
info!("Bot is stopped...");
|
||||
});
|
||||
http
|
||||
}
|
||||
|
19
libs/cron_scheduler/Cargo.toml
Normal file
19
libs/cron_scheduler/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "cron_scheduler"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true }
|
||||
poise = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
database = { path = "../../libs/database" }
|
||||
config = { path = "../../libs/config" }
|
||||
tokio-cron-scheduler = { version = "0.14", features = [
|
||||
"tracing-subscriber",
|
||||
"signal",
|
||||
] }
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
43
libs/cron_scheduler/project.json
Normal file
43
libs/cron_scheduler/project.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "cron_scheduler",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"sourceRoot": "libs/cron_scheduler/src",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@monodon/rust:check",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/cron_scheduler"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"cache": true,
|
||||
"executor": "@monodon/rust:test",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/cron_scheduler"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"release": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"cache": true,
|
||||
"executor": "@monodon/rust:lint",
|
||||
"outputs": [
|
||||
"{options.target-dir}"
|
||||
],
|
||||
"options": {
|
||||
"target-dir": "dist/target/cron_scheduler"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
147
libs/cron_scheduler/src/lib.rs
Normal file
147
libs/cron_scheduler/src/lib.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use poise::serenity_prelude::Http;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Display},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_cron_scheduler::JobScheduler;
|
||||
use tracing::{error, info, instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StopScheduleJob {
|
||||
JobNotFound,
|
||||
RemoveFailed,
|
||||
}
|
||||
|
||||
impl Display for StopScheduleJob {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScheduleJob {
|
||||
pub job_id: Arc<RwLock<HashMap<(u64, u64), Uuid>>>,
|
||||
pub scheduler: JobScheduler,
|
||||
}
|
||||
|
||||
impl Clone for ScheduleJob {
|
||||
fn clone(&self) -> Self {
|
||||
ScheduleJob {
|
||||
job_id: self.job_id.clone(),
|
||||
scheduler: self.scheduler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScheduleJob {
|
||||
#[instrument(level = "info")]
|
||||
pub async fn start_cron_scheduler() -> Result<Self, ()> {
|
||||
let scheduler = JobScheduler::new().await;
|
||||
let mut future_self = match scheduler {
|
||||
Ok(scheduler) => ScheduleJob {
|
||||
job_id: Default::default(),
|
||||
scheduler,
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Error starting cron scheduler: {:?}", e);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
future_self.scheduler.set_shutdown_handler(Box::new(|| {
|
||||
Box::pin(async {
|
||||
info!("Cron scheduler stopped");
|
||||
})
|
||||
}));
|
||||
future_self.scheduler.shutdown_on_ctrl_c();
|
||||
match future_self.scheduler.start().await {
|
||||
Ok(_) => {
|
||||
info!("Cron scheduler started");
|
||||
Ok(future_self)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error starting cron scheduler: {:?}", e);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, _http), level = "info")]
|
||||
pub async fn load_all_trivial_cron_job(&mut self, _http: &Http) -> Result<(), bool> {
|
||||
// Load all trivial jobs
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "info")]
|
||||
pub async fn stop_cron_scheduler(&mut self) -> &mut Self {
|
||||
let job_id = self.job_id.write().await;
|
||||
|
||||
for (server_id, channel_id) in job_id.keys() {
|
||||
match self
|
||||
.scheduler
|
||||
.remove(job_id.get(&(*server_id, *channel_id)).unwrap())
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
info!("Cron job removed");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error removing cron job: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(job_id);
|
||||
match self.scheduler.shutdown().await {
|
||||
Ok(_) => {
|
||||
info!("Cron scheduler stopped");
|
||||
self
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error stopping cron scheduler: {:?}", e);
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, http), level = "info")]
|
||||
pub async fn add_trivial_cron_job(
|
||||
&mut self,
|
||||
server_id: u64,
|
||||
channel_id: u64,
|
||||
cron_expression: String,
|
||||
http: &Http,
|
||||
) -> Result<Uuid, ()> {
|
||||
let _http = Arc::new(Http::new(http.token()));
|
||||
// Create a new job with the provided cron expression
|
||||
Err(())
|
||||
}
|
||||
#[instrument(skip(self), level = "info")]
|
||||
pub async fn stop_scheduled_job(
|
||||
&mut self,
|
||||
server_id: u64,
|
||||
channel_id: u64,
|
||||
) -> Result<(), StopScheduleJob> {
|
||||
let remove_job = {
|
||||
let mut job_id = self.job_id.write().await;
|
||||
job_id.remove(&(server_id, channel_id))
|
||||
};
|
||||
match remove_job {
|
||||
Some(job_uid) => match self.scheduler.remove(&job_uid).await {
|
||||
Ok(_) => {
|
||||
info!("Cron job removed");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error removing cron job: {:?}", e);
|
||||
Err(StopScheduleJob::RemoveFailed)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
error!("Cron job not found");
|
||||
Err(StopScheduleJob::JobNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user