Implement message deletion

main
Charlotte Som 2022-04-17 13:03:21 +01:00
parent 4c37ec5f36
commit cee7a551d1
7 changed files with 90 additions and 9 deletions

View File

@ -1,6 +1,7 @@
use crate::ChatMessage; use crate::{ChatMessage, ChatMessageReference};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ChatEvent { pub enum ChatEvent {
NewMessage(ChatMessage), NewMessage(Box<ChatMessage>),
DeleteMessage(ChatMessageReference),
} }

View File

@ -1,12 +1,13 @@
use color_eyre::Result; use color_eyre::Result;
use futures::{future, StreamExt};
use tracing::info; use tracing::info;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use phoebe::{ use phoebe::{
get_linked_channels, link_messages, get_linked_channels, get_linked_messages, link_messages,
prelude::{ChatEvent, SqlitePool}, prelude::{ChatEvent, SqlitePool},
service::Service, service::Service,
DynServiceLookup, unlink_message, DynServiceLookup,
}; };
async fn handle_events( async fn handle_events(
@ -24,7 +25,7 @@ async fn handle_events(
while let Ok(event) = rx.recv().await { while let Ok(event) = rx.recv().await {
match event { match event {
phoebe::prelude::ChatEvent::NewMessage(message) => { ChatEvent::NewMessage(message) => {
let linked_channels = let linked_channels =
get_linked_channels(&mut conn, dyn_service, &message.origin.channel).await; get_linked_channels(&mut conn, dyn_service, &message.origin.channel).await;
@ -45,6 +46,33 @@ async fn handle_events(
} }
} }
} }
ChatEvent::DeleteMessage(origin) => {
let messages = if let Ok(message_stream) =
get_linked_messages(&mut conn, dyn_service, &origin).await
{
message_stream
.filter(|r| future::ready(r.channel.service == service.tag()))
.collect::<Vec<_>>()
.await
} else {
vec![]
};
if !messages.is_empty() {
if let Err(e) = unlink_message(&mut conn, &origin).await {
tracing::error!("Failed to unlink origin message: {e}");
}
for message in messages {
if service.delete_message(&message).await {
if let Err(e) = unlink_message(&mut conn, &message).await {
tracing::error!("Failed to unlink related message: {e}");
}
}
}
}
}
} }
} }
} }
@ -78,7 +106,7 @@ async fn main() -> Result<()> {
.into_iter() .into_iter()
.map(|srv| tokio::spawn(handle_events(dyn_service, db.clone(), srv, tx.subscribe()))); .map(|srv| tokio::spawn(handle_events(dyn_service, db.clone(), srv, tx.subscribe())));
let _ = futures::future::join_all(handles).await; let _ = future::join_all(handles).await;
Ok(()) Ok(())
} }

View File

@ -92,6 +92,27 @@ pub async fn link_messages(
Ok(()) Ok(())
} }
pub async fn unlink_message(
conn: &mut SqliteConnection,
message: &ChatMessageReference,
) -> sqlx::Result<()> {
let service = &message.channel.service;
let channel = &message.channel.id;
let message_id = &message.message_id;
let query = sqlx::query!(
"DELETE FROM messages WHERE service = ? AND channel = ? AND message = ?",
service,
channel,
message_id
);
if query.execute(&mut *conn).await?.rows_affected() == 0 {
return Err(sqlx::Error::RowNotFound);
}
Ok(())
}
pub async fn get_message_link_id( pub async fn get_message_link_id(
conn: &mut SqliteConnection, conn: &mut SqliteConnection,
message: &ChatMessageReference, message: &ChatMessageReference,

View File

@ -3,9 +3,11 @@ use mid_chat::{ChatMessage, ChatMessageReference, ChatReference};
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait Service { pub trait Service {
fn tag(&self) -> &'static str; fn tag(&self) -> &'static str;
async fn send_chat_message( async fn send_chat_message(
&mut self, &mut self,
source: &ChatMessage, source: &ChatMessage,
destination_channel: ChatReference, destination_channel: ChatReference,
) -> Vec<ChatMessageReference>; ) -> Vec<ChatMessageReference>;
async fn delete_message(&mut self, message: &ChatMessageReference) -> bool;
} }

View File

@ -91,7 +91,21 @@ impl EventHandler for DiscordHandler {
let _ = self let _ = self
.chat_event_tx .chat_event_tx
.send(ChatEvent::NewMessage(chat_message)) .send(ChatEvent::NewMessage(Box::new(chat_message)))
.expect("Failed to dispatch incoming Discord chat message"); .expect("Failed to dispatch incoming Discord chat message");
} }
async fn message_delete(
&self,
_ctx: Context,
channel_id: ChannelId,
deleted_message_id: MessageId,
_guild_id: Option<GuildId>,
) {
let origin = ChatMessageReference::new(discord_reference(channel_id), deleted_message_id);
let _ = self
.chat_event_tx
.send(ChatEvent::DeleteMessage(origin))
.expect("Failed to dispatch incoming Discord chat message deletion");
}
} }

View File

@ -72,6 +72,10 @@ pub async fn setup(
#[async_trait] #[async_trait]
impl Service for DiscordService { impl Service for DiscordService {
fn tag(&self) -> &'static str {
"discord"
}
async fn send_chat_message( async fn send_chat_message(
&mut self, &mut self,
source: &ChatMessage, source: &ChatMessage,
@ -85,7 +89,8 @@ impl Service for DiscordService {
.collect() .collect()
} }
fn tag(&self) -> &'static str { async fn delete_message(&mut self, message: &ChatMessageReference) -> bool {
"discord" assert_eq!(message.channel.service, "discord");
sender::delete_discord_message(self, message).await.is_ok()
} }
} }

View File

@ -161,3 +161,13 @@ pub async fn send_discord_message(
sent_message.id, sent_message.id,
)) ))
} }
pub async fn delete_discord_message(
discord: &mut DiscordService,
message: &ChatMessageReference,
) -> Result<()> {
let channel_id = message.channel.id.parse::<ChannelId>()?;
let message_id: MessageId = message.message_id.parse::<u64>()?.into();
channel_id.delete_message(&discord.ctx, message_id).await?;
Ok(())
}