phoebe/services/phoebe-discord/src/handler.rs

148 lines
4.5 KiB
Rust

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<Context>,
}
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::<Vec<_>>();
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<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");
}
async fn message_update(
&self,
ctx: Context,
_old_if_available: Option<Message>,
new: Option<Message>,
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");
}
}
}