use phoebe::{get_message_link_id, mid_chat::*, prelude::*}; use serenity::{ client::{Context, EventHandler}, model::prelude::*, }; use tracing::debug; use crate::discord_reference; pub struct DiscordHandler { pub core_db: SqlitePool, pub discord_media_db: SqlitePool, pub chat_event_tx: ChatEventSender, pub ctx_tx: tokio::sync::mpsc::UnboundedSender, } impl DiscordHandler { async fn get_author(&self, ctx: &Context, message: &Message) -> ChatAuthor { let display_name = message .author_nick(ctx) .await .unwrap_or_else(|| message.author.name.clone()); async fn tag_color(ctx: &Context, message: &Message) -> Option<[u8; 3]> { let color = message.member(ctx).await.ok()?.colour(ctx).await?; Some([color.r(), color.b(), color.g()]) } let display_color = tag_color(ctx, message).await; let avatar_url = message .author .static_avatar_url() .unwrap_or_else(|| message.author.default_avatar_url()); ChatAuthor { reference: discord_reference(message.author.id), display_name, display_color, avatar: ChatAttachment::Online { media_type: None, url: avatar_url, }, } } } #[async_trait] impl EventHandler for DiscordHandler { async fn ready(&self, ctx: Context, _ready: Ready) { debug!("Discord signalled ready!"); let _ = self.ctx_tx.send(ctx); } async fn message(&self, ctx: Context, message: Message) { let origin = ChatMessageReference::new(discord_reference(message.channel_id), message.id); // skip messages linked to ones we have already seen if let Ok(mut conn) = self.core_db.acquire().await { if get_message_link_id(&mut conn, &origin).await.is_ok() { return; } } let author = self.get_author(&ctx, &message).await; let content = discord_message_format::parse(&message.content); let content = super::chat_conv::convert(&content); let replies_to = message .referenced_message .as_ref() .map(|m| ChatMessageReference::new(discord_reference(m.channel_id), m.id)); let attachments = message .attachments .into_iter() .map(|a| ChatAttachment::Online { media_type: a.content_type, url: a.url, }) .collect::>(); let chat_message = ChatMessage { origin, author, content, attachments, replying: replies_to, }; let _ = self .chat_event_tx .send(ChatEvent::NewMessage(Box::new(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, ) { 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"); } async fn message_update( &self, ctx: Context, _old_if_available: Option, new: Option, event: MessageUpdateEvent, ) { if let Ok(new_message) = { if let Some(m) = new { Ok(m) } else { event.channel_id.message(&ctx, event.id).await } } { let origin = ChatMessageReference::new( discord_reference(new_message.channel_id), new_message.id, ); let author = self.get_author(&ctx, &new_message).await; let content = discord_message_format::parse(&new_message.content); let content = super::chat_conv::convert(&content); let edit = ChatMessageEdit { origin: origin.clone(), author, new_content: content, }; let _ = self .chat_event_tx .send(ChatEvent::EditMessage(origin, Box::new(edit))) .expect("Failed to dispatch incoming Discord chat message update"); } } }