diff --git a/src/db.rs b/src/db.rs index daa5f3b..5b5fd9c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -2439,6 +2439,69 @@ pub async fn upsert_message_observed( Ok(()) } +pub async fn get_observed_message( + pool: &PgPool, + bot_id: UserId, + message_id: MessageId, +) -> Result, String)>, sqlx::Error> { + let row = sqlx::query_as::<_, (Option, String)>( + r#" + SELECT author_id, content + FROM message_log + WHERE bot_id = $1 + AND message_id = $2 + LIMIT 1; + "#, + ) + .bind(bot_id.get() as i64) + .bind(message_id.get() as i64) + .fetch_optional(pool) + .await?; + + Ok(row.map(|(author_id, content)| { + ( + author_id + .and_then(|id| u64::try_from(id).ok()) + .map(UserId::new), + content, + ) + })) +} + +pub async fn upsert_message_observed_partial( + pool: &PgPool, + bot_id: UserId, + message_id: MessageId, + guild_id: Option, + channel_id: ChannelId, + author_id: Option, + content: &str, +) -> Result<(), sqlx::Error> { + sqlx::query( + r#" + INSERT INTO message_log (bot_id, message_id, guild_id, channel_id, author_id, content) + VALUES ($1, $2, $3, $4, $5, $6) + ON CONFLICT (bot_id, message_id) + DO UPDATE SET + guild_id = EXCLUDED.guild_id, + channel_id = EXCLUDED.channel_id, + author_id = EXCLUDED.author_id, + content = EXCLUDED.content, + observed_at = NOW(); + "#, + ) + .bind(bot_id.get() as i64) + .bind(message_id.get() as i64) + .bind(guild_id.map(|id| id.get() as i64)) + .bind(channel_id.get() as i64) + .bind(author_id.map(|id| id.get() as i64)) + .bind(content) + .execute(pool) + .await?; + + Ok(()) +} + pub async fn mark_message_deleted( pool: &PgPool, bot_id: UserId, diff --git a/src/events/message_delete.rs b/src/events/message_delete.rs index e975703..c9abfe5 100644 --- a/src/events/message_delete.rs +++ b/src/events/message_delete.rs @@ -2,7 +2,9 @@ use serenity::model::prelude::*; use serenity::prelude::*; use crate::commands::logs_service; -use crate::db::{DbPoolKey, mark_message_deleted, mark_sent_mp_deleted_by_message}; +use crate::db::{ + DbPoolKey, get_observed_message, mark_message_deleted, mark_sent_mp_deleted_by_message, +}; pub async fn handle_message_delete( ctx: &Context, @@ -12,17 +14,31 @@ pub async fn handle_message_delete( ) { let bot_id = ctx.cache.current_user().id; - let (fallback_author_id, fallback_content) = + let (cache_author_id, cache_content) = if let Some(cached) = ctx.cache.message(channel_id, deleted_message_id) { (Some(cached.author.id), Some(cached.content.clone())) } else { (None, None) }; + let mut resolved_author_id = cache_author_id; + let mut resolved_content = cache_content; + if let Some(pool) = { let data = ctx.data.read().await; data.get::().cloned() } { + if let Ok(Some((db_author_id, db_content))) = + get_observed_message(&pool, bot_id, deleted_message_id).await + { + if resolved_author_id.is_none() { + resolved_author_id = db_author_id; + } + if resolved_content.is_none() { + resolved_content = Some(db_content); + } + } + let _ = mark_sent_mp_deleted_by_message(&pool, bot_id, channel_id, deleted_message_id).await; @@ -32,8 +48,8 @@ pub async fn handle_message_delete( guild_id, channel_id, deleted_message_id, - fallback_author_id, - fallback_content.clone(), + resolved_author_id, + resolved_content.clone(), ) .await; } @@ -43,8 +59,8 @@ pub async fn handle_message_delete( guild_id, channel_id, deleted_message_id, - fallback_author_id, - fallback_content, + resolved_author_id, + resolved_content, ) .await; } diff --git a/src/events/message_update.rs b/src/events/message_update.rs index 3477975..e55b0b5 100644 --- a/src/events/message_update.rs +++ b/src/events/message_update.rs @@ -2,6 +2,7 @@ use serenity::model::prelude::*; use serenity::prelude::*; use crate::commands::logs_service; +use crate::db::{DbPoolKey, get_observed_message, upsert_message_observed_partial}; pub async fn handle_message_update( ctx: &Context, @@ -9,7 +10,30 @@ pub async fn handle_message_update( new: Option, event: &MessageUpdateEvent, ) { - let before = old_if_available.as_ref().map(|m| m.content.clone()); + let bot_id = ctx.cache.current_user().id; + let pool = { + let data = ctx.data.read().await; + data.get::().cloned() + }; + + let db_fallback = if let Some(pool) = pool.as_ref() { + get_observed_message(pool, bot_id, event.id) + .await + .ok() + .flatten() + } else { + None + }; + + let db_author_id = db_fallback + .as_ref() + .and_then(|(author_id, _)| author_id.clone()); + let db_content = db_fallback.as_ref().map(|(_, content)| content.clone()); + + let before = old_if_available + .as_ref() + .map(|m| m.content.clone()) + .or_else(|| db_content.clone()); let after = new .as_ref() .map(|m| m.content.clone()) @@ -17,7 +41,22 @@ pub async fn handle_message_update( let author_id = old_if_available .as_ref() .map(|m| m.author.id) - .or_else(|| new.as_ref().map(|m| m.author.id)); + .or_else(|| new.as_ref().map(|m| m.author.id)) + .or_else(|| event.author.as_ref().map(|u| u.id)) + .or(db_author_id); + + if let (Some(pool), Some(after_content)) = (pool.as_ref(), after.as_ref()) { + let _ = upsert_message_observed_partial( + pool, + bot_id, + event.id, + event.guild_id, + event.channel_id, + author_id, + after_content, + ) + .await; + } logs_service::on_message_edited( ctx,