aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2022-10-06 13:20:12 +0200
committerMax Audron <audron@cocaine.farm>2022-10-06 13:21:03 +0200
commit43a3f6e69098ab8dc7b2a0cf628417a8945e09d4 (patch)
treed88acb01613c3817d5001cc18500a58ecc8de108
parentfix derive macros (diff)
implement sync for IrcChannel
-rw-r--r--src/message/signalproxy/objects/ircchannel.rs215
-rw-r--r--src/session/mod.rs51
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 => (),
}