Compare commits
No commits in common. "d8cdc2bb1194814bba0c970ddee0f18a2d541716" and "6a0e2fd7d1e97ffc5c7600b07b20b95f17e1d5c3" have entirely different histories.
d8cdc2bb11
...
6a0e2fd7d1
14 changed files with 156 additions and 459 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
34
src/cli.rs
34
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
|
||||
|
|
|
@ -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) }
|
||||
|
|
33
src/main.rs
33
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
mod channel;
|
||||
mod client;
|
||||
mod event;
|
||||
|
||||
pub use channel::Channel;
|
||||
pub use client::Client;
|
||||
pub use event::{EventType, Event};
|
||||
pub use client::Client;
|
119
src/parameter.rs
119
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))
|
||||
}
|
||||
}
|
103
src/response.rs
103
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,
|
||||
}
|
||||
}
|
||||
}
|
10
src/utils.rs
10
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)
|
||||
])
|
||||
}
|
||||
}
|
|
@ -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."))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue