From 870e9bf9adf0852b8a1136047d7a9f2144db10d8 Mon Sep 17 00:00:00 2001 From: sinavir Date: Fri, 11 Oct 2024 11:53:47 +0200 Subject: [PATCH] fixup! feat: multi-server support --- src/config.rs | 8 +++++++- src/main.rs | 25 +++++++++++++------------ src/transform.rs | 34 +++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/config.rs b/src/config.rs index b9e0bbd..922097a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,7 +11,7 @@ use toml_env::{initialize, Args, AutoMapEnvArgs, Logging}; pub struct BridgeConfig { pub irc: HashMap, pub signal: SignalConfig, - pub mapping: BiMap, + pub mapping: BiMap, } impl BridgeConfig { @@ -31,6 +31,12 @@ impl BridgeConfig { } } +#[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] +pub struct IrcTarget { + pub server: String, // TODO: Add a more meaningful type to enforce url format + pub channel: String, // Same (leading # ?) +} + #[derive(Debug, Deserialize, Serialize)] pub struct SignalConfig { pub media_dir: Box, diff --git a/src/main.rs b/src/main.rs index 3107797..4969f8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,13 +52,13 @@ async fn main() -> Result<(), String> { let senders_and_streams: HashMap> = join_all( config.irc.into_iter() .map(|(name, config)| async { - (name, init_irc_client(Client::from_config(config) + let irc_client = init_irc_client(Client::from_config(config) .await - .expect(&format!("Failed to parse configuration for {name}"))) - .await) + .expect(&format!("Failed to parse configuration for {name}"))).await; + (name, irc_client) }) // Filter out failed connections - .collect() + //.collect() ).await.into_iter().collect(); let filtered_senders_and_streams: Vec<(String, irc::client::Sender, ClientStream)> = senders_and_streams @@ -74,11 +74,11 @@ async fn main() -> Result<(), String> { }) .collect(); - let filtered_senders = HashMap::new(); - let filtered_streams = HashMap::new(); + let mut filtered_senders = HashMap::new(); + let mut filtered_streams = HashMap::new(); for (name, sender, stream) in filtered_senders_and_streams { - filtered_senders.insert(name, sender); + filtered_senders.insert(name.clone(), sender); filtered_streams.insert(name, stream); } @@ -106,7 +106,7 @@ async fn main() -> Result<(), String> { .await .map_err(|e| format!("failed to subscribe to signal msg: {e}"))?; log::debug!("Initialized Signal"); - Ok((signal_stream, signal_client)) + Ok::<_, String>((signal_stream, signal_client)) }; let (mut signal_stream, signal_client) = init_signal.await?; @@ -124,10 +124,10 @@ async fn main() -> Result<(), String> { // TODO: if only one of the future fails, we should not crash the rest of the threads but log a // very good error. - let irc_handlers = try_join_all(irc_streams.into_iter().map(|(name, stream)| { + let irc_handlers = try_join_all(irc_streams.into_iter().map(|(name, mut stream)| { log::info!("Listening for IRC messages for {name}"); - handle_irc(&mut stream, signal_client, &bridge) - }).collect()); + handle_irc(name, &mut stream, signal_client, &bridge) + })); try_join( handle_signal(irc_senders, &mut signal_stream, &bridge), @@ -138,6 +138,7 @@ async fn main() -> Result<(), String> { } async fn handle_irc( + server_id: String, stream: &mut ClientStream, signal_client: impl SubscriptionClientT + std::marker::Sync, bridge: &Bridge<'_>, @@ -149,7 +150,7 @@ async fn handle_irc( .transpose() .map_err(|e| format!("error while retrieving messages from irc: {e}"))? { - if let Some((groupid, msg)) = bridge.irc2signal(message) { + if let Some((groupid, msg)) = bridge.irc2signal(&server_id, message) { log::trace!("[SIGNAL SEND] {msg}"); signal_client .send( diff --git a/src/transform.rs b/src/transform.rs index 5ae520f..532e75e 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -1,6 +1,8 @@ use std::path::Path; use bimap::BiMap; +use crate::config::IrcTarget; + use irc::client::prelude::*; use serde_json::{Map, Value}; use tokio::fs::copy; @@ -11,7 +13,7 @@ struct SignalMessageBuilder<'a> { message: Option, attachments_urls: Vec, mentions: Vec<(usize, usize, String)>, // TODO Add type - target_channel: Option<&'a String>, + target_channel: Option<&'a str>, target_server: Option<&'a str>, } @@ -32,15 +34,29 @@ impl<'b> SignalMessageBuilder<'b> { self.mentions.extend_from_slice(mentions); } - pub fn target(&mut self, channel: Option<&'b String>) { + pub fn target(&mut self, target: Option<&'b IrcTarget>) { + match target { + Some(target) => { + self.channel(Some(&target.channel)); + self.server(Some(&target.server)); + }, + None => { + self.channel(None); + self.server(None); + }, + }; + } + + pub fn channel(&mut self, channel: Option<&'b str>) { self.target_channel = channel; } + pub fn server(&mut self, server: Option<&'b str>) { self.target_server = server; } - pub fn build(mut self) -> Option<(&'b str, &'b String, String)> + pub fn build(mut self) -> Option<(&'b str, &'b str, String)> { self.mentions.sort_by(|(pos1, _, _), (pos2, _, _)| pos1.cmp(pos2)); log::trace!("building the final message: {:?} {:?}", self.mentions, self.message); @@ -75,7 +91,7 @@ pub struct Bridge<'a> { media_dir: &'a Path, signalcli_dir: &'a Path, url_root: &'a str, - mapping: &'a BiMap, + mapping: &'a BiMap, } impl<'a> Bridge<'a> { @@ -83,7 +99,7 @@ impl<'a> Bridge<'a> { media_dir: &'a Path, signalcli_dir: &'a Path, url_root: &'a str, - mapping: &'a BiMap, + mapping: &'a BiMap, ) -> Self { Self { media_dir, @@ -137,12 +153,12 @@ impl<'a> Bridge<'a> { } if let Some(Value::Object(group_info)) = message.get("groupInfo") { if let Some(Value::String(groupid)) = group_info.get("groupId") { - result.target(self.mapping.get_by_right(groupid)); + result.target(self.mapping.get_by_left(groupid)); } } } - pub async fn signal2irc(&self, signal: Value) -> Option<(&str, &String, String)> { + pub async fn signal2irc(&self, signal: Value) -> Option<(&str, &str, String)> { let mut result: SignalMessageBuilder = SignalMessageBuilder::default(); if let Value::Object(map) = signal { if let Some(Value::Object(envelope)) = map.get("envelope") { @@ -162,7 +178,7 @@ impl<'a> Bridge<'a> { result.build() } - pub fn irc2signal(&self, message: Message) -> Option<(&String, String)> { + pub fn irc2signal(&self, server: &String, message: Message) -> Option<(&String, String)> { match (message.prefix, message.command) { (Some(Prefix::Nickname(from, _, _)), Command::PRIVMSG(channel, message)) => { Some((channel, format!("<{from}>: {message}"))) @@ -172,6 +188,6 @@ impl<'a> Bridge<'a> { } _ => None, } - .and_then(|(channel, e)| self.mapping.get_by_left(&channel).map(|c| (c, e))) + .and_then(|(channel, e)| self.mapping.get_by_right(&IrcTarget { server: *server, channel}).map(|c| (c, e))) } }