diff --git a/Cargo.lock b/Cargo.lock index d658b69..edd51d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,12 +102,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - [[package]] name = "proc-macro2" version = "1.0.69" @@ -126,43 +120,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "serde" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "strsim" version = "0.10.0" @@ -182,11 +139,9 @@ dependencies = [ [[package]] name = "teamspeak-query-lib" -version = "0.1.1" +version = "0.1.0" dependencies = [ "clap", - "serde", - "serde_json", "telnet", ] diff --git a/Cargo.toml b/Cargo.toml index a39f88b..d43dbcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,3 @@ edition = "2021" [dependencies] telnet = "0.2" clap = { version = "4.4", features = ["derive"] } - -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/src/cli.rs b/src/cli.rs index 9cfdb0d..f518b41 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,8 @@ use clap::{Args, Parser, Subcommand}; use telnet::Telnet; -use crate::parameter::ParameterList; -use crate::models::{Channel, EventType}; +use crate::parameter::{Parameter, ParameterList}; +use crate::models::Channel; use crate::models::Client; use crate::utils::SendTextMessageTarget; use crate::wrappers; @@ -23,7 +23,6 @@ pub enum Commands { Message(MessageArgs), Move(MoveArgs), Update(UpdateArgs), - Events(EventArgs), } #[derive(Args)] @@ -79,11 +78,6 @@ pub struct UpdateArgs { speakers: Option<bool>, } -#[derive(Args)] -pub struct EventArgs { - pub event: Vec<EventType>, -} - impl FetchArgs { pub fn want_channel(&self) -> bool { self.channel.is_some() @@ -95,14 +89,14 @@ impl FetchArgs { pub fn channel(&self, connection: &mut Telnet) -> Result<Option<Channel>, String> { if let Some(channel) = &self.channel { - wrappers::find_channel(connection, "channel_name", channel, self.strict_channel) + wrappers::find_channel(connection, channel, self.strict_channel) } else { Err("No channel specified.".to_string()) } } pub fn client(&self, connection: &mut Telnet) -> Result<Option<Client>, String> { if let Some(client) = &self.client { - wrappers::find_client(connection, "client_nickname", client, self.strict_client) + wrappers::find_client(connection, client, self.strict_client) } else { Err("No client specified.".to_string()) } @@ -114,7 +108,7 @@ impl MessageArgs { if self.server { Ok(SendTextMessageTarget::Server) } else if let Some(client) = &self.client { - if let Some(client) = wrappers::find_client(connection, "client_nickname", client, self.strict_client)? { + if let Some(client) = wrappers::find_client(connection, client, self.strict_client)? { return Ok(SendTextMessageTarget::Client(client)); } return Err("Could not find client.".to_string()); @@ -126,12 +120,12 @@ impl MessageArgs { impl MoveArgs { pub fn channel(&self, connection: &mut Telnet) -> Result<Option<Channel>, String> { - wrappers::find_channel(connection, "channel_name", &self.channel, self.strict_channel) + wrappers::find_channel(connection, &self.channel, self.strict_channel) } pub fn client(&self, connection: &mut Telnet) -> Result<Option<Client>, String> { match &self.client { Some(client) => { - wrappers::find_client(connection, "client_nickname", client, self.strict_client) + wrappers::find_client(connection, client, self.strict_client) } None => { match wrappers::find_self(connection) { @@ -145,25 +139,25 @@ impl MoveArgs { impl UpdateArgs { pub fn to_parameter_list(&self) -> ParameterList { - let mut params: ParameterList = ParameterList::new(); + let mut params: ParameterList = Vec::new(); if let Some(name) = &self.name { - params.insert(String::from("client_nickname"), name.clone()); + params.push(Parameter::new("client_nickname", name)); } if let Some(away) = &self.away { - params.insert(String::from("client_away_message"), away.clone()); - params.insert(String::from("client_away"), String::from("1")); + params.push(Parameter::new("client_away_message", away)); + params.push(Parameter::new("client_away", "1")); } if self.back { - params.insert(String::from("client_away"), String::from("0")); + params.push(Parameter::new("client_away", "0")); } if let Some(microphone) = self.microphone { - params.insert(String::from("client_input_muted"), u8::from(!microphone).to_string()); + params.push(Parameter::new("client_input_muted", &u8::from(!microphone).to_string())); } if let Some(speakers) = self.speakers { - params.insert(String::from("client_output_muted"), u8::from(!speakers).to_string()); + params.push(Parameter::new("client_output_muted", &u8::from(!speakers).to_string())); } params diff --git a/src/commands.rs b/src/commands.rs index 204e455..a448c55 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,9 +1,8 @@ use crate::utils::SendTextMessageTarget; use telnet::Event::Data; use telnet::Telnet; -use crate::models::EventType; -use crate::parameter::{Parameter, parameters_to_string, ParameterList, parameter_to_string, parameter_list_to_string}; +use crate::parameter::{Parameter, ParameterList}; use crate::response::Response; fn to_single_response(resp: Response) -> Response { @@ -49,7 +48,7 @@ fn send_command(connection: &mut Telnet, command: &str, skip_ok: bool) -> Result read_response(connection, skip_ok, String::new()) } -pub fn read_response(connection: &mut Telnet, skip_ok: bool, mut buffer: String) -> Result<Response, String> { +fn read_response(connection: &mut Telnet, skip_ok: bool, mut buffer: String) -> Result<Response, String> { let (response_str, buffer) = read_response_buffer(connection, &mut buffer)?; match Response::try_from(response_str) { @@ -94,22 +93,17 @@ pub fn whoami(connection: &mut Telnet) -> Result<Response, String> { pub fn clientmove(connection: &mut Telnet, cid: &i32, clid_list: Vec<&i32>) -> Result<Response, String> { let clid_param_list = clid_list .into_iter() - .map(|clid| (String::from("clid"), clid.to_string())) + .map(|clid| Parameter::new("clid", &clid.to_string())) .collect::<Vec<Parameter>>(); - let clid_str = parameters_to_string(clid_param_list, "|"); + let clid_str = Parameter::list_to_string_sep(clid_param_list, "|"); send_command(connection, &format!("clientmove cid={} {}", cid, clid_str), false) } pub fn clientupdate(connection: &mut Telnet, parameters: ParameterList) -> Result<Response, String> { - let parameters_str = parameter_list_to_string(parameters, " "); + let parameters_str = Parameter::list_to_string(parameters); send_command(connection, &format!("clientupdate {}", parameters_str), false) } pub fn sendtextmessage(connection: &mut Telnet, target: SendTextMessageTarget, msg: String) -> Result<Response, String> { - let msg = parameter_to_string((String::from("msg"), msg)); - send_command(connection, &format!("sendtextmessage {} {}", msg, String::from(target)), false) -} - -pub fn clientnotifyregister(connection: &mut Telnet, schandlerid: u32, event: EventType) -> Result<Response, String> { - send_command(connection, &format!("clientnotifyregister schandlerid={} event={}", schandlerid, String::from(&event)), false) -} + let msg = String::from(Parameter::new("msg", &msg)); + send_command(connection, &format!("sendtextmessage {} {}", msg, String::from(target)), false) } diff --git a/src/main.rs b/src/main.rs index 6af36ac..f4d735b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,8 @@ use std::process::exit; use telnet::Telnet; use crate::cli::Commands; -use crate::models::{Channel, Event}; +use crate::models::Channel; use crate::models::Client; -use crate::response::Response; mod wrappers; mod commands; @@ -153,35 +152,5 @@ fn main() { } } } - - Commands::Events(args) => { - for event in args.event { - if commands::clientnotifyregister(&mut connection, 1, event).is_err() { - println!("Failed to register event listener."); - exit(1); - } - } - - loop { - match commands::read_response(&mut connection, true, String::new()) { - Ok(response) => { - if let Response::Event(event_type, params) = response { - - let event = Event::new(&mut connection, event_type, params); - match serde_json::to_string(&event) { - Ok(json) => println!("{}", json), - Err(_) => { - // TODO: Handle error - } - } - - } - } - Err(_) => { - // TODO: Handle error - } - } - } - } } } \ No newline at end of file diff --git a/src/models/channel.rs b/src/models/channel.rs index ed7f5f7..bc0d928 100644 --- a/src/models/channel.rs +++ b/src/models/channel.rs @@ -1,9 +1,8 @@ use std::fmt::{Display, Formatter}; -use serde::{Deserialize, Serialize}; use crate::parameter::{parameter_find, ParameterList}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug)] pub struct Channel { pub cid: i32, pub pid: i32, @@ -24,20 +23,22 @@ impl From<ParameterList> for Channel { Channel { cid: parameter_find(&value, "cid") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), pid: parameter_find(&value, "pid") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), channel_order: parameter_find(&value, "channel_order") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), channel_name: parameter_find(&value, "channel_name") - .unwrap_or_default(), + .unwrap_or_default() + .value, channel_topic: parameter_find(&value, "channel_topic") - .unwrap_or_default(), + .unwrap_or_default() + .value, channel_flag_are_subscribed: parameter_find(&value, "channel_flag_are_subscribed") .unwrap_or_default() - .parse::<i32>().unwrap_or(0) == 1, + .to_i32(0) == 1, } } } @@ -80,3 +81,4 @@ impl Channel { list_sorted } } + diff --git a/src/models/client.rs b/src/models/client.rs index fe38e06..25405e0 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -1,9 +1,8 @@ use std::fmt::{Display, Formatter}; -use serde::{Deserialize, Serialize}; use crate::parameter::{parameter_find, ParameterList}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug)] pub struct Client { pub cid: i32, pub clid: i32, @@ -23,18 +22,19 @@ impl From<ParameterList> for Client { Client { cid: parameter_find(&value, "cid") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), clid: parameter_find(&value, "clid") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), client_database_id: parameter_find(&value, "client_database_id") .unwrap_or_default() - .parse::<i32>().unwrap_or(-1), + .to_i32(-1), client_nickname: parameter_find(&value, "client_nickname") - .unwrap_or_default(), + .unwrap_or_default() + .value, client_type: parameter_find(&value, "channel_topic") .unwrap_or_default() - .parse::<i32>().unwrap_or(0), + .to_i32(0), } } } \ No newline at end of file diff --git a/src/models/event.rs b/src/models/event.rs deleted file mode 100644 index 1d383c8..0000000 --- a/src/models/event.rs +++ /dev/null @@ -1,193 +0,0 @@ -use std::fmt::{Display, Formatter}; -use std::str::FromStr; - -use serde::{Deserialize, Serialize}; -use telnet::Telnet; - -use crate::models::{Channel, Client}; -use crate::parameter::{parameter_find, ParameterList}; -use crate::wrappers; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum EventType { - Any, - ChannelList, - ChannelListFinished, - NotifyTalkStatusChange, - NotifyMessage, - NotifyMessageList, - NotifyComplainList, - NotifyBanList, - NotifyClientMoved, - NotifyClientLeftView, - NotifyClientEnterView, - NotifyClientPoke, - NotifyClientChatClosed, - NotifyClientChatComposing, - NotifyClientUpdated, - NotifyClientChannelGroupChanged, - NotifyClientIds, - NotifyClientDBIDFromUid, - NotifyClientNameFromUid, - NotifyClientNameFromDBID, - NotifyClientUidFromClid, - NotifyConnectionInfo, - NotifyChannelCreated, - NotifyChannelEdited, - NotifyChannelDeleted, - NotifyChannelMoved, - NotifyServerEdited, - NotifyServerUpdated, - NotifyTextMessage, - NotifyCurrentServerConnectionChanged, - NotifyConnectStatusChange, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Event { - pub event_type: EventType, - pub params: ParameterList, - pub channel: Option<Channel>, - pub client: Option<Client>, -} - -impl TryFrom<&str> for EventType { - type Error = String; - - //noinspection SpellCheckingInspection - fn try_from(event_type: &str) -> Result<Self, Self::Error> { - match event_type.to_lowercase().as_str() { - "any" => Ok(EventType::Any), - "channellist" => Ok(EventType::ChannelList), - "channellistfinished" => Ok(EventType::ChannelListFinished), - "notifytalkstatuschange" => Ok(EventType::NotifyTalkStatusChange), - "notifymessage" => Ok(EventType::NotifyMessage), - "notifymessagelist" => Ok(EventType::NotifyMessageList), - "notifycomplainlist" => Ok(EventType::NotifyComplainList), - "notifybanlist" => Ok(EventType::NotifyBanList), - "notifyclientmoved" => Ok(EventType::NotifyClientMoved), - "notifyclientleftview" => Ok(EventType::NotifyClientLeftView), - "notifycliententerview" => Ok(EventType::NotifyClientEnterView), - "notifyclientpoke" => Ok(EventType::NotifyClientPoke), - "notifyclientchatclosed" => Ok(EventType::NotifyClientChatClosed), - "notifyclientchatcomposing" => Ok(EventType::NotifyClientChatComposing), - "notifyclientupdated" => Ok(EventType::NotifyClientUpdated), - "notifyclientchannelgroupchanged" => Ok(EventType::NotifyClientChannelGroupChanged), - "notifyclientids" => Ok(EventType::NotifyClientIds), - "notifyclientdbidfromuid" => Ok(EventType::NotifyClientDBIDFromUid), - "notifyclientnamefromuid" => Ok(EventType::NotifyClientNameFromUid), - "notifyclientnamefromdbid" => Ok(EventType::NotifyClientNameFromDBID), - "notifyclientuidfromclid" => Ok(EventType::NotifyClientUidFromClid), - "notifyconnectioninfo" => Ok(EventType::NotifyConnectionInfo), - "notifychannelcreated" => Ok(EventType::NotifyChannelCreated), - "notifychanneledited" => Ok(EventType::NotifyChannelEdited), - "notifychanneldeleted" => Ok(EventType::NotifyChannelDeleted), - "notifychannelmoved" => Ok(EventType::NotifyChannelMoved), - "notifyserveredited" => Ok(EventType::NotifyServerEdited), - "notifyserverupdated" => Ok(EventType::NotifyServerUpdated), - "notifytextmessage" => Ok(EventType::NotifyTextMessage), - "notifycurrentserverconnectionchanged" => { - Ok(EventType::NotifyCurrentServerConnectionChanged) - } - "notifyconnectstatuschange" => Ok(EventType::NotifyConnectStatusChange), - _ => Err(format!("Unknown event type: {}", event_type)), - } - } -} - -impl FromStr for EventType { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - EventType::try_from(s) - } -} - -impl From<&EventType> for String { - //noinspection SpellCheckingInspection - fn from(value: &EventType) -> Self { - match value { - EventType::Any => String::from("any"), - EventType::ChannelList => String::from("channellist"), - EventType::ChannelListFinished => String::from("channellistfinished"), - EventType::NotifyTalkStatusChange => String::from("notifytalkstatuschange"), - EventType::NotifyMessage => String::from("notifymessage"), - EventType::NotifyMessageList => String::from("notifymessagelist"), - EventType::NotifyComplainList => String::from("notifycomplainlist"), - EventType::NotifyBanList => String::from("notifybanlist"), - EventType::NotifyClientMoved => String::from("notifyclientmoved"), - EventType::NotifyClientLeftView => String::from("notifyclientleftview"), - EventType::NotifyClientEnterView => String::from("notifycliententerview"), - EventType::NotifyClientPoke => String::from("notifyclientpoke"), - EventType::NotifyClientChatClosed => String::from("notifyclientchatclosed"), - EventType::NotifyClientChatComposing => String::from("notifyclientchatcomposing"), - EventType::NotifyClientChannelGroupChanged => String::from("notifyclientchannelgroupchanged"), - EventType::NotifyClientUpdated => String::from("notifyclientupdated"), - EventType::NotifyClientIds => String::from("notifyclientids"), - EventType::NotifyClientDBIDFromUid => String::from("notifyclientdbidfromuid"), - EventType::NotifyClientNameFromUid => String::from("notifyclientnamefromuid"), - EventType::NotifyClientNameFromDBID => String::from("notifyclientnamefromdbid"), - EventType::NotifyClientUidFromClid => String::from("notifyclientuidfromclid"), - EventType::NotifyConnectionInfo => String::from("notifyconnectioninfo"), - EventType::NotifyChannelCreated => String::from("notifychannelcreated"), - EventType::NotifyChannelEdited => String::from("notifychanneledited"), - EventType::NotifyChannelDeleted => String::from("notifychanneldeleted"), - EventType::NotifyChannelMoved => String::from("notifychannelmoved"), - EventType::NotifyServerEdited => String::from("notifyserveredited"), - EventType::NotifyServerUpdated => String::from("notifyserverupdated"), - EventType::NotifyTextMessage => String::from("notifytextmessage"), - EventType::NotifyCurrentServerConnectionChanged => { - String::from("notifycurrentserverconnectionchanged") - } - EventType::NotifyConnectStatusChange => String::from("notifyconnectstatuschange"), - } - } -} - -impl Display for EventType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let event_str: String = self.into(); - write!(f, "{}", event_str) - } -} - -impl EventType { - pub fn get_channel(&self, connection: &mut Telnet, params: &ParameterList) -> Option<Channel> { - match self { - EventType::NotifyClientMoved => { - let id = parameter_find(params, "ctid")? - .parse::<i32>().unwrap_or(0); - wrappers::find_channel(connection, "cid", &id.to_string(), true).unwrap().or(None) - } - _ => None, - } - } - - pub fn get_client(&self, connection: &mut Telnet, params: &ParameterList) -> Option<Client> { - match self { - EventType::NotifyClientMoved => { - let id = parameter_find(params, "clid")? - .parse::<i32>().unwrap_or(0); - wrappers::find_client(connection, "clid", &id.to_string(), true).unwrap().or(None) - } - EventType::NotifyClientEnterView => { - Some(Client::from(params.clone())) - } - _ => None, - } - } -} - -impl Event { - pub fn new(connection: &mut Telnet, event_type: EventType, params: ParameterList) -> Event { - let channel = event_type.get_channel(connection, ¶ms); - let client = event_type.get_client(connection, ¶ms); - - Event { - event_type, - params, - channel, - client, - } - } -} \ No newline at end of file diff --git a/src/models/mod.rs b/src/models/mod.rs index 6d919ab..df71696 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,7 +1,5 @@ mod channel; mod client; -mod event; pub use channel::Channel; -pub use client::Client; -pub use event::{EventType, Event}; \ No newline at end of file +pub use client::Client; \ No newline at end of file diff --git a/src/parameter.rs b/src/parameter.rs index 6424ecb..2c7623e 100644 --- a/src/parameter.rs +++ b/src/parameter.rs @@ -1,38 +1,36 @@ -use std::collections::HashMap; +use std::fmt::{Debug, Display, Formatter}; use crate::utils::{decode_value, encode_value}; -pub type ParameterList = HashMap<String, String>; -pub type Parameter = (String, String); - -pub fn parameter_find(params: &ParameterList, name: &str) -> Option<String> { - params.get(name).map(|value| value.clone()) +#[derive(Clone)] +pub struct Parameter { + pub name: String, + pub value: String, } -pub fn parameter_list_has(params: &ParameterList, key: &str, value: &str, strict: bool) -> bool { - if let Some(check_value) = params.get(key) { - if check_value == value && strict { - return true; - } - if check_value.contains(value) && !strict { - return true; +pub type ParameterList = Vec<Parameter>; + +pub fn parameter_find(params: &Vec<Parameter>, name: &str) -> Option<Parameter> { + for param in params { + if param.name == name { + return Some(param.clone()); } } - return false; + None } -pub fn parameter_list_find(param_lists: &Vec<ParameterList>, key: &str, value: &str, strict: bool) -> Option<ParameterList> { +pub fn parameter_list_find(param_lists: &Vec<ParameterList>, name: &str, value: &str, strict: bool) -> Option<ParameterList> { for params in param_lists { - if parameter_list_has(params, key, value, strict) { + if params.iter().any(|param| param.is(name, value, strict)) { return Some(params.clone()); } } None } -pub fn parameter_list_find_all(param_lists: &Vec<ParameterList>, key: &str, value: &str, strict: bool) -> Vec<ParameterList> { +pub fn parameter_list_find_all(param_lists: &Vec<ParameterList>, name: &str, value: &str, strict: bool) -> Vec<ParameterList> { let mut found = Vec::new(); for params in param_lists { - if parameter_list_has(params, key, value, strict) { + if params.iter().any(|param| param.is(name, value, strict)) { found.push(params.clone()) } } @@ -42,31 +40,82 @@ pub fn parameter_list_find_all(param_lists: &Vec<ParameterList>, key: &str, valu pub fn parameter_parse(params_str: &str) -> ParameterList { let parts: Vec<&str> = params_str.split(' ').collect(); - let mut response_params = ParameterList::new(); + let mut response_params = Vec::new(); parts.iter().for_each(|part| { - let (key, value) = part.split_once('=').unwrap_or((part, "1")); - response_params.insert(key.to_string(), decode_value(value)); + response_params.push(Parameter::from(part.split_once('=').unwrap())); }); response_params } -pub fn parameter_to_string(param: Parameter) -> String { - format!("{}={}", param.0, encode_value(¶m.1)) +impl From<(&str, &str)> for Parameter { + fn from(param: (&str, &str)) -> Parameter { + Parameter::new(param.0, param.1) + } } -pub fn parameter_list_to_string(parameter_list: ParameterList, sep: &str) -> String { - parameter_list - .into_iter() - .map(|param| parameter_to_string(param)) - .collect::<Vec<String>>() - .join(sep) +impl Parameter { + pub fn new(name: &str, value: &str) -> Parameter { + Parameter { + name: String::from(name), + value: decode_value(value) + } + } + + pub fn is(&self, name: &str, value: &str, strict: bool) -> bool { + if self.name != name { + return false; + } + if self.value != value && strict { + return false; + } + if !self.value.contains(value) && !strict { + return false; + } + true + } + + pub fn to_i32(&self, default: i32) -> i32 { + self.value.parse::<i32>().unwrap_or(default) + } + + pub fn list_to_string(parameter_list: ParameterList) -> String { + parameter_list + .into_iter() + .map(String::from) + .collect::<Vec<String>>() + .join(" ") + } + + pub fn list_to_string_sep(parameter_list: ParameterList, sep: &str) -> String { + parameter_list + .into_iter() + .map(String::from) + .collect::<Vec<String>>() + .join(sep) + } } -pub fn parameters_to_string(parameters: Vec<Parameter>, sep: &str) -> String { - parameters - .into_iter() - .map(|param| parameter_to_string(param)) - .collect::<Vec<String>>() - .join(sep) +impl Display for Parameter { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}={}", self.name, self.value) + } +} + +impl Debug for Parameter { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}={}", self.name, self.value) + } +} + +impl Default for Parameter { + fn default() -> Self { + Parameter::new("", "") + } +} + +impl From<Parameter> for String { + fn from(value: Parameter) -> Self { + format!("{}={}", value.name, encode_value(&value.value)) + } } \ No newline at end of file diff --git a/src/response.rs b/src/response.rs index aed60d2..d4c5a00 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,5 +1,4 @@ use std::fmt::{Debug, Display, Formatter}; -use crate::models::EventType; use crate::parameter::*; @@ -7,14 +6,6 @@ pub enum Response { Ok, Data(ParameterList), DataList(Vec<ParameterList>), - Event(EventType, ParameterList), -} - -pub enum ResponseType { - Error, - Event(EventType), - Data, - Unknown, } pub struct ResponseError { @@ -22,66 +13,23 @@ pub struct ResponseError { pub msg: String, } -fn split_type_and_params(response_str: &str) -> Result<(ResponseType, &str), String> { - if let Some(first_space) = response_str.find(' ') { - if let Some(first_equal) = response_str.find('=') { - if first_equal > first_space { - // The first word is not a parameter. Error or Event? - let (type_str, params) = response_str.split_once(' ').unwrap(); // We found a space, so this is safe. - let response_type = get_response_type(type_str)?; - return Ok((response_type, params)); - } - } - } - Ok((ResponseType::Data, response_str)) -} - -fn get_response_type(type_str: &str) -> Result<ResponseType, String> { - if type_str == "error" { - return Ok(ResponseType::Error); - } - if type_str.starts_with("notify") || type_str == "channellist" || type_str == "channellistfinished" { - let event_type = EventType::try_from(type_str)?; - return Ok(ResponseType::Event(event_type)); - } - - return Ok(ResponseType::Unknown); -} - impl TryFrom<String> for Response { type Error = ResponseError; fn try_from(response_str: String) -> Result<Self, ResponseError> { - let response_str = response_str.trim_end_matches("\n\r"); + let mut response_str = response_str.trim_end_matches("\n\r"); - let (rsp_type, rsp_params) = split_type_and_params(&response_str) - .map_err(|err| ResponseError { - id: -1, - msg: err, - })?; - - let mut parameter_lists: Vec<ParameterList> = Vec::new(); - for response_entry in rsp_params.split('|') { - let response_params = parameter_parse(response_entry); - parameter_lists.push(response_params); - } - - return match rsp_type { - ResponseType::Error => { - Err(ResponseError::create_error(¶meter_lists[0])) - } - ResponseType::Event(event_type) => { - Ok(Response::Event(event_type, parameter_lists[0].clone())) - } - ResponseType::Data => { - Ok(Response::DataList(parameter_lists)) - } - ResponseType::Unknown => { - Err(ResponseError { - id: -1, - msg: "Unknown response type.".to_string(), - }) + if response_str.starts_with("error ") { + response_str = response_str.trim_start_matches("error "); + let response_params = parameter_parse(response_str); + Err(ResponseError::create_error(&response_params)) + } else { + let mut parameter_lists: Vec<ParameterList> = Vec::new(); + for response_entry in response_str.split('|') { + let response_params = parameter_parse(response_entry); + parameter_lists.push(response_params); } + Ok(Response::DataList(parameter_lists)) } } } @@ -104,11 +52,6 @@ impl Debug for Response { } Ok(()) } - Response::Event(event, params) => { - write!(f, "Event: {:?}", event)?; - write!(f, "{:?};", params)?; - Ok(()) - } } } } @@ -120,28 +63,21 @@ impl Display for Response { write!(f, "Ok") } Response::Data(params) => { - for param in params.clone() { - write!(f, "{};", parameter_to_string(param))?; + for param in params { + write!(f, "{};", param)?; } Ok(()) } Response::DataList(params_list) => { for params in params_list { write!(f, "[")?; - for param in params.clone() { - write!(f, "{};", parameter_to_string(param))?; + for param in params { + write!(f, "{};", param)?; } write!(f, "]")?; } Ok(()) } - Response::Event(event, params) => { - write!(f, "Event: {}", event)?; - for param in params.clone() { - write!(f, "{};", parameter_to_string(param))?; - } - Ok(()) - } } } } @@ -151,13 +87,14 @@ impl ResponseError { self.id == 0 } - fn create_error(params: &ParameterList) -> ResponseError { + fn create_error(params: &Vec<Parameter>) -> ResponseError { ResponseError { id: parameter_find(params, "id") - .unwrap_or_else(|| String::from("-1")) - .parse::<i32>().unwrap_or(-1), + .unwrap_or_else(|| Parameter::new("id", "-1")) + .to_i32(-1), msg: parameter_find(params, "msg") - .unwrap_or_else(|| String::from("Unknown error.")) + .unwrap_or_else(|| Parameter::new("msg", "Unknown error.")) + .value, } } } \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index e21547f..766eb23 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ use crate::models::Client; -use crate::parameter::parameters_to_string; +use crate::parameter::Parameter; pub fn decode_value(value: &str) -> String { value @@ -32,9 +32,9 @@ impl From<SendTextMessageTarget> for String { _ => String::from("0"), }; - parameters_to_string(vec![ - (String::from("targetmode"), String::from(target_mode)), - (String::from("target"), target) - ], " ") + Parameter::list_to_string(vec![ + Parameter::new("targetmode", target_mode), + Parameter::new("target", &target) + ]) } } \ No newline at end of file diff --git a/src/wrappers.rs b/src/wrappers.rs index 9c50680..0effadc 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -57,10 +57,10 @@ pub fn get_channels(connection: &mut Telnet, spacers: bool) -> Result<Vec<Channe } } -pub fn find_channel(connection: &mut Telnet, key: &str, value: &str, strict: bool) -> Result<Option<Channel>, String> { +pub fn find_channel(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<Channel>, String> { match commands::channellist(connection)? { Response::DataList(parameter_lists) => { - match parameter::parameter_list_find(¶meter_lists, key, value, strict) { + match parameter::parameter_list_find(¶meter_lists, "channel_name", name, strict) { Some(params) => { Ok(Some(Channel::from(params))) } @@ -88,10 +88,10 @@ pub fn get_clients(connection: &mut Telnet) -> Result<Vec<Client>, String> { } } -pub fn find_client(connection: &mut Telnet, key: &str, value: &str, strict: bool) -> Result<Option<Client>, String> { +pub fn find_client(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<Client>, String> { match commands::clientlist(connection)? { Response::DataList(parameter_lists) => { - match parameter::parameter_list_find(¶meter_lists, key, value, strict) { + match parameter::parameter_list_find(¶meter_lists, "client_nickname", name, strict) { Some(params) => { Ok(Some(Client::from(params))) } @@ -122,7 +122,7 @@ fn get_self_clid(connection: &mut Telnet) -> Result<String, String> { Response::Data(params) => { match parameter::parameter_find(¶ms, "clid") { None => Err(String::from("Could not find clid in models from Teamspeak.")), - Some(param) => Ok(param) + Some(param) => Ok(param.value) } } _ => Err(String::from("Received unexpected models from Teamspeak for whoami.")) diff --git a/ts-control b/ts-control index 5f26108..3ce6769 100755 --- a/ts-control +++ b/ts-control @@ -8,8 +8,7 @@ away not away back message -message-user -events-move" +message-user" _ts_control_get_entity() { entity=$(_ts_control_single_or_dmenu "$(teamspeak-query-lib "$1s")" "$2") @@ -98,8 +97,4 @@ case $action in message=$(_ts_control_get_message "$3") teamspeak-query-lib message --strict-client --client "$user" "$message" ;; - "events-move") - teamspeak-query-lib events NotifyClientMoved NotifyClientEnterView \ - | jq -r --unbuffered '.client.client_nickname + " joined " + .channel.channel_name // "the server"' \ - | xargs -I{} notify-send "TS3 movement" "{}" esac