diff --git a/src/core/bot.rs b/src/core/bot.rs index 2261265..52d85f6 100644 --- a/src/core/bot.rs +++ b/src/core/bot.rs @@ -51,45 +51,12 @@ impl EventHandler for Handler { warn!(error=%e, "ensure_forum_channel failed"); } } - // Orphan reconciliation: purge DB rows whose channel/thread no longer exists - let tickets = - crate::feature::ticket::service::count::list_tickets(&pool_clone).await; - let mut purged = 0; - for (cid_str, gid_str, opener) in tickets { - if gid_str != gid.to_string() { - continue; - } - if let Ok(cid) = cid_str.parse::() { - let cid = serenity::all::ChannelId::new(cid); - if http2.get_channel(cid).await.is_err() { - let _ = sqlx::query("DELETE FROM tickets WHERE channel_id=?") - .bind(&cid_str) - .execute(&pool_clone) - .await; - let _ = sqlx::query("DELETE FROM ticket_claims WHERE channel_id=?") - .bind(&cid_str) - .execute(&pool_clone) - .await; - purged += 1; - warn!(channel=%cid_str, opener=%opener, "orphan ticket purged"); - // Log to ticket-logs without transcript (manually deleted) - let _ = serenity::all::ChannelId::new(1509503717127356446) - .send_message(&http2, serenity::all::CreateMessage::new().embed(serenity::all::CreateEmbed::new().title("⚠️ Ticket Manually Deleted").description(format!("Ticket <#{cid_str}> (opener <@{opener}>) manually deleted — transcript unavailable.")).color(0x00ED_4245))) - .await; - } - } - } - if purged > 0 { - info!(purged, "orphan tickets reconciled"); - } - // Presence: watching N tickets + crate::feature::ticket::service::count::reconcile_orphans(&http2, &pool_clone, gid) + .await; let count = crate::feature::ticket::service::count::count_tickets(&pool_clone, gid).await; ctx_clone.set_presence( - Some(serenity::all::ActivityData::custom(format!( - "watching {} tickets", - count - ))), + Some(serenity::all::ActivityData::watching(format!("{} tickets", count))), serenity::all::OnlineStatus::Online, ); } diff --git a/src/feature/ticket/service/count.rs b/src/feature/ticket/service/count.rs index da20956..8ad15d8 100644 --- a/src/feature/ticket/service/count.rs +++ b/src/feature/ticket/service/count.rs @@ -1,4 +1,6 @@ +use serenity::http::Http; use sqlx::SqlitePool; +use tracing::{info, warn}; #[rustfmt::skip] pub async fn list_tickets(p: &SqlitePool) -> Vec<(String, String, String)> { let _ = sqlx::query("CREATE TABLE IF NOT EXISTS tickets (channel_id TEXT PRIMARY KEY, guild_id TEXT NOT NULL, opener_id TEXT NOT NULL)").execute(p).await; @@ -9,3 +11,32 @@ pub async fn count_tickets(p: &SqlitePool, g: u64) -> i64 { let _ = sqlx::query("CREATE TABLE IF NOT EXISTS tickets (channel_id TEXT PRIMARY KEY, guild_id TEXT NOT NULL, opener_id TEXT NOT NULL)").execute(p).await; sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM tickets WHERE guild_id=?").bind(g.to_string()).fetch_one(p).await.unwrap_or(0) } +pub async fn reconcile_orphans(http: &Http, pool: &SqlitePool, guild_id: u64) -> usize { + let tickets = list_tickets(pool).await; + let mut purged = 0; + for (cid_str, gid_str, opener) in tickets { + if gid_str != guild_id.to_string() { + continue; + } + if let Ok(cid) = cid_str.parse::() { + let cid = serenity::all::ChannelId::new(cid); + if http.get_channel(cid).await.is_err() { + let _ = sqlx::query("DELETE FROM tickets WHERE channel_id=?") + .bind(&cid_str) + .execute(pool) + .await; + let _ = sqlx::query("DELETE FROM ticket_claims WHERE channel_id=?") + .bind(&cid_str) + .execute(pool) + .await; + purged += 1; + warn!(channel=%cid_str, opener=%opener, "orphan ticket purged"); + let _ = serenity::all::ChannelId::new(1509503717127356446).send_message(http, serenity::all::CreateMessage::new().embed(serenity::all::CreateEmbed::new().title("⚠️ Ticket Manually Deleted").description(format!("Ticket <#{cid_str}> (opener <@{opener}>) manually deleted — transcript unavailable.")).color(0x00ED_4245))).await; + } + } + } + if purged > 0 { + info!(purged, "orphan tickets reconciled"); + } + purged +}