use async_trait::async_trait; use futures::StreamExt; use irc::{client::{Client, prelude::Config}, proto::{Command, Message}}; use log::debug; use serde::Deserialize; use tokio::select; use crate::{supervisor::{Id, TaskResult, Sender, Task, Receiver}, linkmap::Linkmap}; use crate::supervisor::Message as BMessage; use super::IrcConfig; #[derive(Debug, Deserialize)] pub struct IrcTask { config: IrcConfig, links: Linkmap, } impl IrcTask { async fn handle_bridge_msg(&self, id: Id, client: &mut Client, msg: BMessage) -> TaskResult { for channel in self.links.get_channels(&msg.link) { if msg.origin.0 == id && &msg.origin.1 == channel { continue } debug!("{id}: bridging message from {:?}/{:?} to {channel:?}", msg.origin, msg.link); client.send_privmsg(channel, format!("<{}> {}", msg.author, msg.content))?; } Ok(()) } async fn handle_irc_msg(&self, id: Id, tx: &mut Sender, msg: Message) -> TaskResult { if let Command::PRIVMSG(channel, message) = &msg.command { let Some(link) = self.links.get_link(channel) else { return Ok(()) }; let Some(author) = msg.source_nickname() else { return Ok(()) }; debug!("{id}: broadcasting message from {:?} to {:?}", channel, link); tx.send(BMessage { origin: (id, channel.clone()), link: link.clone(), author: author.to_owned(), content: message.clone(), })?; } Ok(()) } } #[async_trait] impl Task for IrcTask { async fn start(&self, id: Id, mut tx: Sender, mut rx: Receiver) -> TaskResult { let config = Config { server: Some(self.config.server.clone()), port: Some(self.config.port), nickname: Some(self.config.nick.clone()), alt_nicks: self.config.alt_nicks.clone(), use_tls: Some(self.config.tls), channels: self.links.iter_channels().cloned().collect(), ..Config::default() }; debug!("{id}: building IRC client"); let mut client = Client::from_config(config).await?; debug!("{id}: identifying"); client.identify()?; let mut stream = client.stream()?; debug!("{id}: entering event loop"); loop { select! { bridge_msg = rx.recv() => self.handle_bridge_msg(id, &mut client, bridge_msg?).await?, irc_msg = stream.next() => match irc_msg { Some(msg) => self.handle_irc_msg(id, &mut tx, msg?).await?, None => return Err("Lost connection to IRC server".into()), } }; } } }