abridged/src/bridge_irc/bridge.rs

84 lines
2.3 KiB
Rust

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<String>,
}
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(),
avatar: None,
})?;
}
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()),
}
};
}
}
}