diff options
| author | Max Audron <audron@cocaine.farm> | 2022-10-06 13:20:12 +0200 |
|---|---|---|
| committer | Max Audron <audron@cocaine.farm> | 2022-10-06 13:21:03 +0200 |
| commit | 43a3f6e69098ab8dc7b2a0cf628417a8945e09d4 (patch) | |
| tree | d88acb01613c3817d5001cc18500a58ecc8de108 | |
| parent | fix derive macros (diff) | |
implement sync for IrcChannel
| -rw-r--r-- | src/message/signalproxy/objects/ircchannel.rs | 215 | ||||
| -rw-r--r-- | src/session/mod.rs | 51 |
2 files changed, 256 insertions, 10 deletions
diff --git a/src/message/signalproxy/objects/ircchannel.rs b/src/message/signalproxy/objects/ircchannel.rs index a977a2e..bbdc600 100644 --- a/src/message/signalproxy/objects/ircchannel.rs +++ b/src/message/signalproxy/objects/ircchannel.rs @@ -1,30 +1,210 @@ use std::collections::HashMap; -use crate::message::NetworkMap; +#[cfg(feature = "server")] +use libquassel_derive::sync; +use libquassel_derive::Setters; +use log::{error, warn}; + +use crate::message::{NetworkMap, Syncable, Class}; use crate::primitive::{StringList, Variant, VariantList, VariantMap}; +use super::ChannelModeType; + #[allow(dead_code)] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Setters)] pub struct IrcChannel { + /// Modes that add or remove items from a list, like commonly +b for the banlist. + /// + /// Always require a parameter from server to client. + /// Clients can request the whole list by leaving the parameter empty + #[setter(skip)] pub channel_modes_a: HashMap<char, StringList>, + + /// Modes that take a parameter as setting and require it when setting or removing the mode. + #[setter(skip)] pub channel_modes_b: HashMap<char, String>, + + /// Modes that take a parameter as setting, but only require it when setting the mode. + #[setter(skip)] pub channel_modes_c: HashMap<char, String>, + + /// Modes without a parameter. + #[setter(skip)] pub channel_modes_d: String, + // pub channel_modes: HashMap<char, ChannelMode>, + #[setter(skip)] pub user_modes: HashMap<String, String>, + #[setter(skip)] pub name: String, + pub topic: String, pub password: String, pub encrypted: bool, } -// #[derive(Debug, Clone, PartialEq)] -// pub enum ChannelMode { -// A(char, StringList), -// B(char, String), -// C(char, String), -// D(char), -// } +// TODO keep user modes sorted +impl IrcChannel { + pub fn add_channel_mode(&mut self, mode_type: ChannelModeType, mode: char, value: String) { + match mode_type { + ChannelModeType::NotAChanmode => (), + ChannelModeType::AChanmode => { + self.channel_modes_a.insert(mode, vec![value]); + }, + ChannelModeType::BChanmode => { + self.channel_modes_b.insert(mode, value); + }, + ChannelModeType::CChanmode => { + self.channel_modes_c.insert(mode, value); + }, + ChannelModeType::DChanmode => { + if ! self.channel_modes_d.contains(mode) { + self.channel_modes_d.push(mode); + }; + }, + }; + } + pub fn remove_channel_mode(&mut self, mode_type: ChannelModeType, mode: char, value: String) { + match mode_type { + ChannelModeType::NotAChanmode => (), + ChannelModeType::AChanmode => { + self.channel_modes_a.remove(&mode); + }, + ChannelModeType::BChanmode => { + self.channel_modes_b.remove(&mode); + }, + ChannelModeType::CChanmode => { + self.channel_modes_c.remove(&mode); + }, + ChannelModeType::DChanmode => { + if self.channel_modes_d.contains(mode) { + self.channel_modes_d = self.channel_modes_d.chars().filter(|c| *c != mode).collect(); + }; + }, + } + } + + // TODO add user mode validation + /// Add one or more mode flags to a user + pub fn add_user_mode(&mut self, nick: String, mode: String) { + if let Some(user_modes) = self.user_modes.get_mut(&nick) { + mode.chars().for_each(|c| { + if !user_modes.contains(c) { + user_modes.push(c); + } + }); + } else { + self.user_modes.insert(nick.clone(), mode.clone()); + }; + + // We need to iterate over all the chars and send a sync for each one + // to stay compatible with quassels current behaviour + // TODO this might actually be dumb can IRC even into mutiple modes at once? + #[cfg(feature = "server")] + if let Some(user_modes) = self.user_modes.get(&nick) { + mode.chars().for_each(|c| { + if !user_modes.contains(c) { + sync!("addUserMode", [nick.clone(), c.to_string()]); + } + }); + }; + } + + /// Remove one or more mode flags from a user + pub fn remove_user_mode(&mut self, nick: String, mode: String) { + if let Some(user_modes) = self.user_modes.get_mut(&nick) { + mode.chars().for_each(|c| { + *user_modes = user_modes.replace(c, ""); + }); + } + + #[cfg(feature = "server")] + sync!("removeUserMode", [nick, mode]); + } + + pub fn join_irc_users(&mut self, nicks: StringList, modes: StringList) { + if nicks.len() != modes.len() { + error!("number of nicks does not match number of modes"); + } + + #[cfg(feature = "server")] + sync!("joinIrcUsers", [nicks.clone(), modes.clone()]); + + nicks + .into_iter() + .zip(modes) + .for_each(|(nick, mode)| self.add_user_mode(nick, mode)); + } + + pub fn part(&mut self, nick: String) { + match self.user_modes.remove(&nick) { + Some(_) => (), + None => warn!("tried to remove a user that is not joined to the channel"), + } + + if self.user_modes.len() == 0 + /* nick.is_me() */ + { + // TODO Clean up channel and delete + } + } + + pub fn set_user_modes(&mut self, nick: String, modes: String) { + #[cfg(feature = "server")] + sync!("setUserModes", [nick.clone(), modes.clone()]); + + *self.user_modes.entry(nick).or_default() = modes; + } +} + +#[cfg(feature = "client")] +impl crate::message::StatefulSyncableClient for IrcChannel { + fn sync_custom(&mut self, mut msg: crate::message::SyncMessage) + where + Self: Sized, + { + match msg.slot_name.as_str() { + // "addChannelMode" => { + // let mode: String = get_param!(msg); + // self.add_channel_mode(mode.chars().next().unwrap(), get_param!(msg)); + // } + // "removeChannelMode" => { + // let mode: String = get_param!(msg); + // self.remove_channel_mode(mode.chars().next().unwrap(), get_param!(msg)); + // } + "addUserMode" => self.add_user_mode(get_param!(msg), get_param!(msg)), + "removeUserMode" => self.remove_user_mode(get_param!(msg), get_param!(msg)), + "joinIrcUsers" => self.join_irc_users(get_param!(msg), get_param!(msg)), + "part" => self.part(get_param!(msg)), + "setEncrypted" => self.set_encrypted(get_param!(msg)), + "setPassword" => self.set_password(get_param!(msg)), + "setTopic" => self.set_topic(get_param!(msg)), + "setUserModes" => self.set_user_modes(get_param!(msg), get_param!(msg)), + _ => (), + } + } + + /// Not Implemented for this type + fn request_update(&mut self) + where + Self: Sized, + { + } +} + +#[cfg(feature = "server")] +impl crate::message::StatefulSyncableServer for IrcChannel { + /// Not Implemented for this type + fn request_update(&mut self, _param: <IrcChannel as NetworkMap>::Item) + where + Self: Sized, + { + } +} + +impl Syncable for IrcChannel { + const CLASS: Class = Class::IrcChannel; +} impl NetworkMap for Vec<IrcChannel> { type Item = VariantMap; @@ -341,4 +521,21 @@ mod tests { get_runtime() ) } + + #[test] + fn add_user_mode() { + let mut base = get_runtime(); + let mut res = get_runtime(); + res.user_modes = map! { s!("audron") => s!("oh"), s!("audron_") => s!("") }; + + base.add_user_mode(s!("audron"), s!("h")); + assert_eq!(res, base); + base.add_user_mode(s!("audron"), s!("o")); + assert_eq!(res, base); + + res.user_modes = + map! { s!("audron") => s!("oh"), s!("audron_") => s!(""), s!("test") => s!("h") }; + base.add_user_mode(s!("test"), s!("h")); + assert_eq!(res, base); + } } diff --git a/src/session/mod.rs b/src/session/mod.rs index 01e625d..0a305c7 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -45,7 +45,56 @@ pub trait SessionManager { Class::Network => (), Class::NetworkInfo => (), Class::NetworkConfig => (), - Class::IrcChannel => (), + Class::IrcChannel => { + let mut object_name = msg.object_name.split('/'); + let network_id: i32 = object_name.next().unwrap().parse().unwrap(); + let channel = object_name.next().unwrap(); + + debug!("Syncing IrcChannel {} in Network {:?}", channel, network_id); + + if let Some(network) = self.network(network_id) { + if network.irc_channels.get_mut(channel).is_none() { + warn!( + "Could not find IrcChannel {} in Network {:?}", + channel, network_id + ) + } else { + match msg.slot_name.as_str() { + "addChannelMode" => { + let mut msg = msg.clone(); + let mode: char = get_param!(msg); + let mode_type: ChannelModeType = + network.get_channel_mode_type(mode); + + network + .irc_channels + .get_mut(channel) + .unwrap() + .add_channel_mode(mode_type, mode, get_param!(msg)); + } + "removeChannelMode" => { + let mut msg = msg.clone(); + let mode: char = get_param!(msg); + let mode_type: ChannelModeType = + network.get_channel_mode_type(mode); + + network + .irc_channels + .get_mut(channel) + .unwrap() + .remove_channel_mode(mode_type, mode, get_param!(msg)); + } + _ => network + .irc_channels + .get_mut(channel) + .unwrap() + .sync(msg.clone()), + } + } + } else { + warn!("Could not find Network {:?}", network_id) + } + } Class::IrcUser => (), Class::Unknown => (), } |
