mirror of
https://github.com/Xevion/banner.git
synced 2025-12-06 01:14:22 -06:00
feat: basic activity status
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -184,6 +184,7 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"governor",
|
"governor",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
|
"num-format",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"poise",
|
"poise",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
@@ -1699,6 +1700,16 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-format"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.46"
|
version = "0.1.46"
|
||||||
|
|||||||
@@ -44,5 +44,6 @@ url = "2.5"
|
|||||||
governor = "0.10.1"
|
governor = "0.10.1"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
serde_path_to_error = "0.1.17"
|
serde_path_to_error = "0.1.17"
|
||||||
|
num-format = "0.4.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
41
src/main.rs
41
src/main.rs
@@ -1,5 +1,6 @@
|
|||||||
use figment::value::UncasedStr;
|
use figment::value::UncasedStr;
|
||||||
use serenity::all::{ClientBuilder, GatewayIntents};
|
use num_format::{Locale, ToFormattedString};
|
||||||
|
use serenity::all::{ActivityData, ClientBuilder, GatewayIntents};
|
||||||
use tokio::signal;
|
use tokio::signal;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
||||||
@@ -27,6 +28,21 @@ mod services;
|
|||||||
mod state;
|
mod state;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
async fn update_bot_status(
|
||||||
|
ctx: &serenity::all::Context,
|
||||||
|
app_state: &AppState,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let course_count = app_state.get_course_count().await?;
|
||||||
|
|
||||||
|
ctx.set_activity(Some(ActivityData::playing(format!(
|
||||||
|
"Querying {:} classes",
|
||||||
|
course_count.to_formatted_string(&Locale::en)
|
||||||
|
))));
|
||||||
|
|
||||||
|
tracing::info!(course_count = course_count, "Updated bot status");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
@@ -102,7 +118,7 @@ async fn main() {
|
|||||||
.expect("Failed to create BannerApi");
|
.expect("Failed to create BannerApi");
|
||||||
|
|
||||||
let banner_api_arc = Arc::new(banner_api);
|
let banner_api_arc = Arc::new(banner_api);
|
||||||
let app_state = AppState::new(banner_api_arc.clone(), &config.redis_url)
|
let app_state = AppState::new(banner_api_arc.clone(), &config.redis_url, db_pool.clone())
|
||||||
.expect("Failed to create AppState");
|
.expect("Failed to create AppState");
|
||||||
|
|
||||||
// Create BannerState for web service
|
// Create BannerState for web service
|
||||||
@@ -174,6 +190,27 @@ async fn main() {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
|
|
||||||
|
// Start status update task
|
||||||
|
let status_app_state = app_state.clone();
|
||||||
|
let status_ctx = ctx.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut interval = tokio::time::interval(std::time::Duration::from_secs(30));
|
||||||
|
|
||||||
|
// Update status immediately on startup
|
||||||
|
if let Err(e) = update_bot_status(&status_ctx, &status_app_state).await {
|
||||||
|
tracing::error!(error = %e, "Failed to update status on startup");
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
|
||||||
|
if let Err(e) = update_bot_status(&status_ctx, &status_app_state).await {
|
||||||
|
tracing::error!(error = %e, "Failed to update bot status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(Data { app_state })
|
Ok(Data { app_state })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ impl SubjectJob {
|
|||||||
.execute(db_pool)
|
.execute(db_pool)
|
||||||
.await
|
.await
|
||||||
.map(|result| {
|
.map(|result| {
|
||||||
trace!(result = ?result, "Course upserted");
|
trace!(subject = course.subject, crn = course.course_reference_number, result = ?result, "Course upserted");
|
||||||
})
|
})
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to upsert course: {e}"))
|
.map_err(|e| anyhow::anyhow!("Failed to upsert course: {e}"))
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/state.rs
12
src/state.rs
@@ -5,24 +5,28 @@ use crate::banner::Course;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use redis::AsyncCommands;
|
use redis::AsyncCommands;
|
||||||
use redis::Client;
|
use redis::Client;
|
||||||
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub banner_api: Arc<BannerApi>,
|
pub banner_api: Arc<BannerApi>,
|
||||||
pub redis: Arc<Client>,
|
pub redis: Arc<Client>,
|
||||||
|
pub db_pool: PgPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
banner_api: Arc<BannerApi>,
|
banner_api: Arc<BannerApi>,
|
||||||
redis_url: &str,
|
redis_url: &str,
|
||||||
|
db_pool: PgPool,
|
||||||
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let redis_client = Client::open(redis_url)?;
|
let redis_client = Client::open(redis_url)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
banner_api,
|
banner_api,
|
||||||
redis: Arc::new(redis_client),
|
redis: Arc::new(redis_client),
|
||||||
|
db_pool,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,4 +49,12 @@ impl AppState {
|
|||||||
|
|
||||||
Err(anyhow::anyhow!("Course not found for CRN {crn}"))
|
Err(anyhow::anyhow!("Course not found for CRN {crn}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the total number of courses in the database
|
||||||
|
pub async fn get_course_count(&self) -> Result<i64> {
|
||||||
|
let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM courses")
|
||||||
|
.fetch_one(&self.db_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(count.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user