feat: basic activity status

This commit is contained in:
2025-09-13 01:04:46 -05:00
parent 430e2a255b
commit 624247ee14
5 changed files with 64 additions and 3 deletions

11
Cargo.lock generated
View File

@@ -184,6 +184,7 @@ dependencies = [
"futures",
"governor",
"http 1.3.1",
"num-format",
"once_cell",
"poise",
"rand 0.9.2",
@@ -1699,6 +1700,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "num-integer"
version = "0.1.46"

View File

@@ -44,5 +44,6 @@ url = "2.5"
governor = "0.10.1"
once_cell = "1.21.3"
serde_path_to_error = "0.1.17"
num-format = "0.4.4"
[dev-dependencies]

View File

@@ -1,5 +1,6 @@
use figment::value::UncasedStr;
use serenity::all::{ClientBuilder, GatewayIntents};
use num_format::{Locale, ToFormattedString};
use serenity::all::{ActivityData, ClientBuilder, GatewayIntents};
use tokio::signal;
use tracing::{error, info, warn};
use tracing_subscriber::{EnvFilter, FmtSubscriber};
@@ -27,6 +28,21 @@ mod services;
mod state;
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]
async fn main() {
dotenvy::dotenv().ok();
@@ -102,7 +118,7 @@ async fn main() {
.expect("Failed to create BannerApi");
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");
// Create BannerState for web service
@@ -174,6 +190,27 @@ async fn main() {
)
.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 })
})
})

View File

@@ -86,7 +86,7 @@ impl SubjectJob {
.execute(db_pool)
.await
.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}"))
}

View File

@@ -5,24 +5,28 @@ use crate::banner::Course;
use anyhow::Result;
use redis::AsyncCommands;
use redis::Client;
use sqlx::PgPool;
use std::sync::Arc;
#[derive(Clone)]
pub struct AppState {
pub banner_api: Arc<BannerApi>,
pub redis: Arc<Client>,
pub db_pool: PgPool,
}
impl AppState {
pub fn new(
banner_api: Arc<BannerApi>,
redis_url: &str,
db_pool: PgPool,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let redis_client = Client::open(redis_url)?;
Ok(Self {
banner_api,
redis: Arc::new(redis_client),
db_pool,
})
}
@@ -45,4 +49,12 @@ impl AppState {
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)
}
}