use std::sync::Arc; use async_trait::async_trait; use log::{info, debug}; use serde::Deserialize; use serenity::{prelude::*, model::prelude::*, http::Http}; use crate::{linkmap::Linkmap, supervisor::{Task, TaskResult, Id, Sender, Receiver}}; use crate::supervisor::Message as BMessage; use super::DiscordConfig; #[derive(Debug, Deserialize)] pub struct DiscordTask { config: DiscordConfig, links: Arc>, } #[async_trait] impl Task for DiscordTask { async fn start(&self, id: Id, tx: Sender, mut rx: Receiver) -> TaskResult { let handler = Handler { links: self.links.clone(), tx, id, }; let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; debug!("{id}: building Discord client"); let mut client = Client::builder(&self.config.token, intents) .event_handler(handler) .await?; let http = client.cache_and_http.http.clone(); let bridge_handler = async move { loop { let msg = rx.recv().await?; handle_bridge_msg(msg, id, &self.links, http.as_ref()).await?; } }; debug!("{id}: built client, starting"); tokio::select! { result = bridge_handler => result, result = client.start() => result.map_err(|e| e.into()), } } } async fn handle_bridge_msg(msg: BMessage, id: Id, links: &Linkmap, http: &Http) -> TaskResult { let content = format!("<{}> {}", msg.author, msg.content); for channel in links.get_channels(&msg.link) { if msg.origin.0 == id && msg.origin.1 == channel.to_string() { continue } debug!("{id}: bridging message from {:?}/{:?} to {channel:?}", msg.origin, msg.link); channel.say(http, &content).await?; } Ok(()) } struct Handler { id: Id, links: Arc>, tx: Sender, } #[async_trait] impl EventHandler for Handler { async fn message(&self, ctx: Context, msg: Message) { if msg.author.id == ctx.cache.current_user_id() { return } let Some(link) = self.links.get_link(&msg.channel_id) else { return }; let author = msg.author.nick_in(ctx.http, msg.guild_id.expect("failed to get guild ID")) .await.unwrap_or(msg.author.name); debug!("{}: broadcasting message from {:?} to {:?}", self.id, msg.channel_id, link); self.tx.send(BMessage { origin: (self.id, msg.channel_id.to_string()), link: link.clone(), author, content: msg.content, }).expect("failed to broadcast message"); } async fn ready(&self, _: Context, _: Ready) { info!("{}: ready", self.id); } }