use log::info; use serenity::{async_trait, model::prelude::*, prelude::*}; use tokio::sync::mpsc; use crate::{ channels::ChannelReference, message_ast::{self, format_discord}, messages::{MessageAuthor, MessageEvent, MessageReference, SentMessage}, }; pub use serenity::client::Context; pub use serenity::model::id::ChannelId; impl From<&Message> for MessageReference { fn from(message: &Message) -> Self { Self::Discord(message.channel_id.0, message.id.0) } } struct DiscordHandler { ctx_tx: mpsc::UnboundedSender, event_tx: mpsc::UnboundedSender, } #[async_trait] impl EventHandler for DiscordHandler { async fn ready(&self, ctx: Context, _ready: Ready) { let _ = self.ctx_tx.send(ctx); info!("Discord ready!"); // TODO: Scan for channels to link } async fn message(&self, ctx: Context, message: Message) { if message.author.id == ctx.cache.current_user_id().await { return; } if let Some(target) = message.content.strip_prefix("phoebe!link ") { if message .member(&ctx) .await .unwrap() .roles(&ctx) .await .unwrap() .iter() .any(|r| r.name == "Phoebe") { let _ = self.event_tx.send(MessageEvent::AdminLinkChannels(vec![ ChannelReference::Discord(message.channel_id.0), ChannelReference::Matrix(target.to_string()), ])); message.reply(&ctx, "Linking with matrix.").await.unwrap(); return; } } let message_ref = MessageReference::from(&message); let content = discord_message_format::parse(&message.content); let content = message_ast::convert_discord(&content); let replies_to = message .referenced_message .as_ref() .map(|m| MessageReference::from(m.as_ref())); let _ = self.event_tx.send(MessageEvent::Send(SentMessage { source: message_ref, content, author: MessageAuthor { display_name: message .author_nick(&ctx.http) .await .unwrap_or(message.author.name), }, replies_to, })); } } pub async fn forward_to_discord( discord_ctx: &Context, channel: ChannelId, message: &SentMessage, reply: Option<(u64, u64)>, ) -> Option { channel .send_message(&discord_ctx.http, |m| { let b = m.content(format_discord(&message.content)); if let Some((channel_id, message_id)) = reply { b.reference_message((ChannelId(channel_id), MessageId(message_id))) } else { b } }) .await .as_ref() .ok() .map(MessageReference::from) } pub async fn create_discord_client( ctx_tx: mpsc::UnboundedSender, message_tx: mpsc::UnboundedSender, token: &str, ) -> Client { let handler = DiscordHandler { ctx_tx, event_tx: message_tx, }; info!("Discord logging in…"); let client = Client::builder(token) .event_handler(handler) .await .expect("Failed to create discord client"); info!("Discord starting…"); client }