Refactor and add message command

This commit is contained in:
Tobias Reisinger 2023-11-26 22:30:59 +01:00
parent e87bed00a2
commit fa959136be
Signed by: serguzim
GPG key ID: 13AD60C237A28DFE
8 changed files with 370 additions and 263 deletions

View file

@ -4,7 +4,8 @@ use telnet::Telnet;
use crate::parameter::{Parameter, ParameterList}; use crate::parameter::{Parameter, ParameterList};
use crate::response::channel::ResponseChannel; use crate::response::channel::ResponseChannel;
use crate::response::client::ResponseClient; use crate::response::client::ResponseClient;
use crate::utils; use crate::utils::SendTextMessageTarget;
use crate::wrappers;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -19,6 +20,7 @@ pub enum Commands {
Channels(ChannelsArgs), Channels(ChannelsArgs),
Clients, Clients,
Fetch(FetchArgs), Fetch(FetchArgs),
Message(MessageArgs),
Move(MoveArgs), Move(MoveArgs),
Update(UpdateArgs), Update(UpdateArgs),
} }
@ -41,6 +43,17 @@ pub struct FetchArgs {
client: Option<String>, client: Option<String>,
} }
#[derive(Args)]
pub struct MessageArgs {
#[arg(long)]
strict_client: bool,
#[arg(long)]
client: Option<String>,
#[arg(long)]
server: bool,
pub message: String,
}
#[derive(Args)] #[derive(Args)]
pub struct MoveArgs { pub struct MoveArgs {
#[arg(long)] #[arg(long)]
@ -76,31 +89,46 @@ impl FetchArgs {
pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> { pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> {
if let Some(channel) = &self.channel { if let Some(channel) = &self.channel {
utils::find_channel(connection, channel, self.strict_channel) wrappers::find_channel(connection, channel, self.strict_channel)
} else { } else {
Err("No channel specified.".to_string()) Err("No channel specified.".to_string())
} }
} }
pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> { pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> {
if let Some(client) = &self.client { if let Some(client) = &self.client {
utils::find_client(connection, client, self.strict_client) wrappers::find_client(connection, client, self.strict_client)
} else { } else {
Err("No client specified.".to_string()) Err("No client specified.".to_string())
} }
} }
} }
impl MessageArgs {
pub fn target(&self, connection: &mut Telnet) -> Result<SendTextMessageTarget, String> {
if self.server {
Ok(SendTextMessageTarget::Server)
} else if let Some(client) = &self.client {
if let Some(client) = wrappers::find_client(connection, client, self.strict_client)? {
return Ok(SendTextMessageTarget::Client(client.cid));
}
return Err("Could not find client.".to_string());
} else {
Ok(SendTextMessageTarget::Channel)
}
}
}
impl MoveArgs { impl MoveArgs {
pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> { pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> {
utils::find_channel(connection, &self.channel, self.strict_channel) wrappers::find_channel(connection, &self.channel, self.strict_channel)
} }
pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> { pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> {
match &self.client { match &self.client {
Some(client) => { Some(client) => {
utils::find_client(connection, client, self.strict_client) wrappers::find_client(connection, client, self.strict_client)
} }
None => { None => {
match utils::find_self(connection) { match wrappers::find_self(connection) {
Ok(client) => Ok(Some(client)), Ok(client) => Ok(Some(client)),
Err(msg) => Err(msg) Err(msg) => Err(msg)
} }
@ -114,24 +142,22 @@ impl UpdateArgs {
let mut params: ParameterList = Vec::new(); let mut params: ParameterList = Vec::new();
if let Some(name) = &self.name { if let Some(name) = &self.name {
params.push(Parameter::new(String::from("client_nickname"), name.clone())); params.push(Parameter::new("client_nickname", name));
} }
if let Some(away) = &self.away { if let Some(away) = &self.away {
params.push(Parameter::new(String::from("client_away_message"), away.clone())); params.push(Parameter::new("client_away_message", away));
params.push(Parameter::new(String::from("client_away"), String::from("1"))); params.push(Parameter::new("client_away", "1"));
} }
if self.back { if self.back {
params.push(Parameter::new(String::from("client_away"), String::from("0"))); params.push(Parameter::new("client_away", "0"));
} }
if let Some(microphone) = self.microphone { if let Some(microphone) = self.microphone {
let muted = u8::from(!microphone).to_string(); params.push(Parameter::new("client_input_muted", &u8::from(!microphone).to_string()));
params.push(Parameter::new(String::from("client_input_muted"), muted));
} }
if let Some(speakers) = self.speakers { if let Some(speakers) = self.speakers {
let muted = u8::from(!speakers).to_string(); params.push(Parameter::new("client_output_muted", &u8::from(!speakers).to_string()));
params.push(Parameter::new(String::from("client_output_muted"), muted));
} }
params params

View file

@ -1,7 +1,8 @@
use crate::utils::SendTextMessageTarget;
use telnet::Event::Data; use telnet::Event::Data;
use telnet::Telnet; use telnet::Telnet;
use crate::parameter::ParameterList; use crate::parameter::{Parameter, ParameterList};
use crate::response::Response; use crate::response::Response;
fn to_single_response(resp: Response) -> Response { fn to_single_response(resp: Response) -> Response {
@ -41,6 +42,7 @@ fn read_response_buffer(connection: &mut Telnet, buffer: &mut String) -> Result<
} }
fn send_command(connection: &mut Telnet, command: &str, skip_ok: bool) -> Result<Response, String> { fn send_command(connection: &mut Telnet, command: &str, skip_ok: bool) -> Result<Response, String> {
let command = format!("{}\n\r", command);
connection.write(command.as_bytes()).map_err(|_| "Failed to write to Teamspeak.")?; connection.write(command.as_bytes()).map_err(|_| "Failed to write to Teamspeak.")?;
read_response(connection, skip_ok, String::new()) read_response(connection, skip_ok, String::new())
@ -68,40 +70,40 @@ fn read_response(connection: &mut Telnet, skip_ok: bool, mut buffer: String) ->
} }
pub fn login(connection: &mut Telnet, apikey: &str) -> Result<Response, String> { pub fn login(connection: &mut Telnet, apikey: &str) -> Result<Response, String> {
send_command(connection, &format!("auth apikey={}\n", apikey), false) send_command(connection, &format!("auth apikey={}", apikey), false)
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn set_name(connection: &mut Telnet, name: &str) -> Result<Response, String> { pub fn set_name(connection: &mut Telnet, name: &str) -> Result<Response, String> {
send_command(connection, &format!("clientupdate client_nickname={}\n", name), true) send_command(connection, &format!("clientupdate client_nickname={}", name), true)
} }
pub fn channellist(connection: &mut Telnet) -> Result<Response, String> { pub fn channellist(connection: &mut Telnet) -> Result<Response, String> {
send_command(connection, "channellist\n", true) send_command(connection, "channellist", true)
} }
pub fn clientlist(connection: &mut Telnet) -> Result<Response, String> { pub fn clientlist(connection: &mut Telnet) -> Result<Response, String> {
send_command(connection, "clientlist\n", true) send_command(connection, "clientlist", true)
} }
pub fn whoami(connection: &mut Telnet) -> Result<Response, String> { pub fn whoami(connection: &mut Telnet) -> Result<Response, String> {
send_command(connection, "whoami\n", true).map(to_single_response) send_command(connection, "whoami", true).map(to_single_response)
} }
pub fn clientmove(connection: &mut Telnet, cid: &i32, clid_list: Vec<&i32>) -> Result<Response, String> { pub fn clientmove(connection: &mut Telnet, cid: &i32, clid_list: Vec<&i32>) -> Result<Response, String> {
let clid_str = clid_list let clid_param_list = clid_list
.iter() .into_iter()
.map(|clid| format!("clid={}", clid)) .map(|clid| Parameter::new("clid", &clid.to_string()))
.collect::<Vec<String>>() .collect::<Vec<Parameter>>();
.join("|"); let clid_str = Parameter::list_to_string_sep(clid_param_list, "|");
send_command(connection, &format!("clientmove cid={} {}\n", cid, clid_str), false) send_command(connection, &format!("clientmove cid={} {}", cid, clid_str), false)
} }
pub fn clientupdate(connection: &mut Telnet, parameters: &ParameterList) -> Result<Response, String> { pub fn clientupdate(connection: &mut Telnet, parameters: ParameterList) -> Result<Response, String> {
let parameters_str = parameters let parameters_str = Parameter::list_to_string(parameters);
.iter() send_command(connection, &format!("clientupdate {}", parameters_str), false)
.map(|param| format!("{}={}", param.name, param.value))
.collect::<Vec<String>>()
.join(" ");
send_command(connection, &format!("clientupdate {}\n", parameters_str), false)
} }
pub fn sendtextmessage(connection: &mut Telnet, target: SendTextMessageTarget, msg: String) -> Result<Response, String> {
let msg = String::from(Parameter::new("msg", &msg));
send_command(connection, &format!("sendtextmessage {} {}", msg, String::from(target)), false) }

View file

@ -7,10 +7,11 @@ use crate::response::channel::ResponseChannel;
use crate::response::client::ResponseClient; use crate::response::client::ResponseClient;
mod response; mod response;
mod utils; mod wrappers;
mod commands; mod commands;
mod parameter; mod parameter;
mod cli; mod cli;
mod utils;
fn channel_or_exit(channel_res: Result<Option<ResponseChannel>, String>) -> ResponseChannel { fn channel_or_exit(channel_res: Result<Option<ResponseChannel>, String>) -> ResponseChannel {
channel_res.unwrap_or_else(|err| { channel_res.unwrap_or_else(|err| {
@ -44,14 +45,14 @@ fn main() {
} }
let mut connection = connection.unwrap(); let mut connection = connection.unwrap();
utils::skip_welcome(&mut connection); wrappers::skip_welcome(&mut connection);
utils::login(&mut connection); wrappers::login(&mut connection);
// You can check for the existence of subcommands, and if found use their // You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd // matches just as you would the top level cmd
match &cli { match cli {
Commands::Channels(args) => { Commands::Channels(args) => {
match utils::get_channels(&mut connection, args.spacers) { match wrappers::get_channels(&mut connection, args.spacers) {
Ok(channels) => { Ok(channels) => {
for channel in channels { for channel in channels {
println!("{}", channel.channel_name); println!("{}", channel.channel_name);
@ -65,7 +66,7 @@ fn main() {
} }
Commands::Clients => { Commands::Clients => {
match utils::get_clients(&mut connection) { match wrappers::get_clients(&mut connection) {
Ok(clients) => { Ok(clients) => {
for client in clients { for client in clients {
println!("{}", client.client_nickname); println!("{}", client.client_nickname);
@ -91,7 +92,7 @@ fn main() {
if args.want_client() { if args.want_client() {
let client = client_or_exit(args.client(&mut connection)); let client = client_or_exit(args.client(&mut connection));
match utils::fetch_client(&mut connection, &[client]) { match wrappers::fetch_client(&mut connection, &[client]) {
Ok(_) => println!("Successfully fetched client."), Ok(_) => println!("Successfully fetched client."),
Err(msg) => { Err(msg) => {
println!("Failed to fetch client: {}", msg); println!("Failed to fetch client: {}", msg);
@ -103,7 +104,7 @@ fn main() {
if args.want_channel() { if args.want_channel() {
let channel = channel_or_exit(args.channel(&mut connection)); let channel = channel_or_exit(args.channel(&mut connection));
match utils::fetch_channel(&mut connection, channel) { match wrappers::fetch_channel(&mut connection, channel) {
Ok(_) => println!("Successfully fetched channel."), Ok(_) => println!("Successfully fetched channel."),
Err(msg) => { Err(msg) => {
println!("Failed to fetch channel: {}", msg); println!("Failed to fetch channel: {}", msg);
@ -113,11 +114,26 @@ fn main() {
} }
} }
Commands::Message(args) => {
let target = args.target(&mut connection).unwrap_or_else(|err| {
println!("Failed to get message target: {}", err);
exit(1);
});
match wrappers::send_text_message(&mut connection, target, args.message) {
Ok(_) => println!("Successfully sent message."),
Err(msg) => {
println!("Failed to send message: {}", msg);
exit(1);
}
}
}
Commands::Move(args) => { Commands::Move(args) => {
let channel = channel_or_exit(args.channel(&mut connection)); let channel = channel_or_exit(args.channel(&mut connection));
let client = client_or_exit(args.client(&mut connection)); let client = client_or_exit(args.client(&mut connection));
match utils::move_client(&mut connection, &channel, &[client]) { match wrappers::move_client(&mut connection, &channel, &[client]) {
Ok(resp) => println!("Successfully moved client: {}", resp), Ok(resp) => println!("Successfully moved client: {}", resp),
Err(msg) => { Err(msg) => {
println!("Failed to move client: {}", msg); println!("Failed to move client: {}", msg);
@ -127,7 +143,7 @@ fn main() {
} }
Commands::Update(args) => { Commands::Update(args) => {
match utils::update_client(&mut connection, &args.to_parameter_list()) { match wrappers::update_client(&mut connection, args.to_parameter_list()) {
Ok(_) => println!("Successfully updated client."), Ok(_) => println!("Successfully updated client."),
Err(msg) => { Err(msg) => {
println!("Failed to update client: {}", msg); println!("Failed to update client: {}", msg);

View file

@ -1,4 +1,5 @@
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use crate::utils::{decode_value, encode_value};
#[derive(Clone)] #[derive(Clone)]
pub struct Parameter { pub struct Parameter {
@ -49,19 +50,15 @@ pub fn parameter_parse(params_str: &str) -> ParameterList {
impl From<(&str, &str)> for Parameter { impl From<(&str, &str)> for Parameter {
fn from(param: (&str, &str)) -> Parameter { fn from(param: (&str, &str)) -> Parameter {
Parameter::new(String::from(param.0), String::from(param.1)) Parameter::new(param.0, param.1)
} }
} }
impl Parameter { impl Parameter {
pub fn new(name: String, value: String) -> Parameter { pub fn new(name: &str, value: &str) -> Parameter {
let value = value
.replace("\\s", " ")
.replace("\\p", "|");
Parameter { Parameter {
name, name: String::from(name),
value, value: decode_value(value)
} }
} }
@ -81,6 +78,22 @@ impl Parameter {
pub fn to_i32(&self, default: i32) -> i32 { pub fn to_i32(&self, default: i32) -> i32 {
self.value.parse::<i32>().unwrap_or(default) 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)
}
} }
impl Display for Parameter { impl Display for Parameter {
@ -97,6 +110,12 @@ impl Debug for Parameter {
impl Default for Parameter { impl Default for Parameter {
fn default() -> Self { fn default() -> Self {
Parameter::new(String::from(""), String::from("")) Parameter::new("", "")
}
}
impl From<Parameter> for String {
fn from(value: Parameter) -> Self {
format!("{}={}", value.name, encode_value(&value.value))
} }
} }

View file

@ -93,10 +93,10 @@ impl ResponseError {
fn create_error(params: &Vec<Parameter>) -> ResponseError { fn create_error(params: &Vec<Parameter>) -> ResponseError {
ResponseError { ResponseError {
id: parameter_find(params, "id") id: parameter_find(params, "id")
.unwrap_or_else(|| Parameter::new(String::from("id"), String::from("-1"))) .unwrap_or_else(|| Parameter::new("id", "-1"))
.to_i32(-1), .to_i32(-1),
msg: parameter_find(params, "msg") msg: parameter_find(params, "msg")
.unwrap_or_else(|| Parameter::new(String::from("msg"), String::from("Unknown error."))) .unwrap_or_else(|| Parameter::new("msg", "Unknown error."))
.value, .value,
} }
} }

View file

@ -1,208 +1,39 @@
use std::process::exit; use crate::parameter::Parameter;
use std::time::Duration;
use telnet::Event::TimedOut; pub fn decode_value(value: &str) -> String {
use telnet::Telnet; value
.replace("\\s", " ")
use crate::{commands, parameter}; .replace("\\p", "|")
use crate::parameter::{parameter_list_find_all, ParameterList};
use crate::response::channel::ResponseChannel;
use crate::response::client::ResponseClient;
use crate::response::Response;
pub fn skip_welcome(connection: &mut Telnet) {
loop {
let event_result = connection.read_timeout(Duration::from_millis(100));
match event_result {
Ok(event) => {
if let TimedOut = event {
break;
}
}
Err(_) => println!("Failed to read from Teamspeak."),
}
}
} }
pub fn login(connection: &mut Telnet) { pub fn encode_value(value: &str) -> String {
// read api key from environment variable value
let apikey = std::env::var("TS3_CLIENT_API_KEY").unwrap_or_else(|_| { .replace(' ', "\\s")
println!("No API key found in environment variable TS3_CLIENT_API_KEY."); .replace('|', "\\p")
exit(1);
});
match commands::login(connection, &apikey) {
Ok(_) => {}
Err(msg) => {
println!("Failed to authenticate with Teamspeak: {}", msg);
exit(1);
}
}
} }
pub fn get_channels(connection: &mut Telnet, spacers: bool) -> Result<Vec<ResponseChannel>, String> { pub enum SendTextMessageTarget {
match commands::channellist(connection) { Client(i32),
Ok(response) => { Channel,
match response { Server,
Response::DataList(parameter_lists) => {
let channels: Vec<ResponseChannel> = parameter_lists.iter()
.map(|params| ResponseChannel::from(params.clone()))
.collect();
let mut channels = ResponseChannel::sort_list(channels);
if !spacers {
channels.retain(|c| !c.is_spacer());
}
Ok(channels)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
} }
pub fn find_channel(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<ResponseChannel>, String> { impl From<SendTextMessageTarget> for String {
match commands::channellist(connection) { fn from(value: SendTextMessageTarget) -> Self {
Ok(response) => { let target_mode = match value {
match response { SendTextMessageTarget::Client(_) => "1",
Response::DataList(parameter_lists) => { SendTextMessageTarget::Channel => "2",
match parameter::parameter_list_find(&parameter_lists, "channel_name", name, strict) { SendTextMessageTarget::Server => "3",
Some(params) => { };
Ok(Some(ResponseChannel::from(params)))
} let target = match value {
None => { SendTextMessageTarget::Client(id) => id.to_string(),
Ok(None) _ => String::from("0"),
} };
}
} Parameter::list_to_string(vec![
_ => Err(String::from("Received unexpected response from Teamspeak.")) Parameter::new("targetmode", target_mode),
} Parameter::new("target", &target)
} ])
Err(msg) => Err(msg)
} }
} }
pub fn get_clients(connection: &mut Telnet) -> Result<Vec<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
let mut clients: Vec<ResponseClient> = parameter_lists.iter()
.map(|params| ResponseClient::from(params.clone()))
.collect();
clients.sort_by(|a, b| a.client_nickname.cmp(&b.client_nickname));
Ok(clients)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn find_client(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
match parameter::parameter_list_find(&parameter_lists, "client_nickname", name, strict) {
Some(params) => {
Ok(Some(ResponseClient::from(params)))
}
None => {
Ok(None)
}
}
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn get_channel_clients(connection: &mut Telnet, channel: &ResponseChannel) -> Result<Vec<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
let mut clients: Vec<ResponseClient> = Vec::new();
for client_params in parameter_list_find_all(&parameter_lists, "cid", &channel.cid.to_string(), true) {
clients.push(ResponseClient::from(client_params));
}
Ok(clients)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
fn get_self_clid(connection: &mut Telnet) -> Result<String, String> {
match commands::whoami(connection) {
Ok(response) => {
match response {
Response::Data(params) => {
match parameter::parameter_find(&params, "clid") {
None => Err(String::from("Could not find clid in response from Teamspeak.")),
Some(param) => Ok(param.value)
}
}
_ => Err(String::from("Received unexpected response from Teamspeak for whoami."))
}
}
Err(msg) => Err(msg)
}
}
pub fn find_self(connection: &mut Telnet) -> Result<ResponseClient, String> {
let clid = get_self_clid(connection)?;
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
match parameter::parameter_list_find(&parameter_lists, "clid", &clid, false) {
Some(params) => {
Ok(ResponseClient::from(params))
}
None => {
Err(String::from("Could not find self in response from Teamspeak."))
}
}
}
_ => Err(String::from("Received unexpected response from Teamspeak for clientlist."))
}
}
Err(msg) => Err(msg)
}
}
pub fn fetch_client(connection: &mut Telnet, clients: &[ResponseClient]) -> Result<Response, String> {
let cid = find_self(connection)?.cid;
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &cid, clid_list)
}
pub fn fetch_channel(connection: &mut Telnet, channel: ResponseChannel) -> Result<Response, String> {
let cid = find_self(connection)?.cid;
let clients = get_channel_clients(connection, &channel)?;
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &cid, clid_list)
}
pub fn move_client(connection: &mut Telnet, channel: &ResponseChannel, clients: &[ResponseClient]) -> Result<Response, String> {
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &channel.cid, clid_list)
}
pub fn update_client(connection: &mut Telnet, parameters: &ParameterList) -> Result<Response, String> {
commands::clientupdate(connection, parameters)
}

213
src/wrappers.rs Normal file
View file

@ -0,0 +1,213 @@
use std::process::exit;
use std::time::Duration;
use telnet::Event::TimedOut;
use telnet::Telnet;
use crate::{commands, parameter};
use crate::parameter::{parameter_list_find_all, ParameterList};
use crate::response::channel::ResponseChannel;
use crate::response::client::ResponseClient;
use crate::response::Response;
use crate::utils::SendTextMessageTarget;
pub fn skip_welcome(connection: &mut Telnet) {
loop {
let event_result = connection.read_timeout(Duration::from_millis(100));
match event_result {
Ok(event) => {
if let TimedOut = event {
break;
}
}
Err(_) => println!("Failed to read from Teamspeak."),
}
}
}
pub fn login(connection: &mut Telnet) {
// read api key from environment variable
let apikey = std::env::var("TS3_CLIENT_API_KEY").unwrap_or_else(|_| {
println!("No API key found in environment variable TS3_CLIENT_API_KEY.");
exit(1);
});
match commands::login(connection, &apikey) {
Ok(_) => {}
Err(msg) => {
println!("Failed to authenticate with Teamspeak: {}", msg);
exit(1);
}
}
}
pub fn get_channels(connection: &mut Telnet, spacers: bool) -> Result<Vec<ResponseChannel>, String> {
match commands::channellist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
let channels: Vec<ResponseChannel> = parameter_lists.iter()
.map(|params| ResponseChannel::from(params.clone()))
.collect();
let mut channels = ResponseChannel::sort_list(channels);
if !spacers {
channels.retain(|c| !c.is_spacer());
}
Ok(channels)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn find_channel(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<ResponseChannel>, String> {
match commands::channellist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
match parameter::parameter_list_find(&parameter_lists, "channel_name", name, strict) {
Some(params) => {
Ok(Some(ResponseChannel::from(params)))
}
None => {
Ok(None)
}
}
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn get_clients(connection: &mut Telnet) -> Result<Vec<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
let mut clients: Vec<ResponseClient> = parameter_lists.iter()
.map(|params| ResponseClient::from(params.clone()))
.collect();
clients.sort_by(|a, b| a.client_nickname.cmp(&b.client_nickname));
Ok(clients)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn find_client(connection: &mut Telnet, name: &str, strict: bool) -> Result<Option<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
match parameter::parameter_list_find(&parameter_lists, "client_nickname", name, strict) {
Some(params) => {
Ok(Some(ResponseClient::from(params)))
}
None => {
Ok(None)
}
}
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
pub fn get_channel_clients(connection: &mut Telnet, channel: &ResponseChannel) -> Result<Vec<ResponseClient>, String> {
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
let mut clients: Vec<ResponseClient> = Vec::new();
for client_params in parameter_list_find_all(&parameter_lists, "cid", &channel.cid.to_string(), true) {
clients.push(ResponseClient::from(client_params));
}
Ok(clients)
}
_ => Err(String::from("Received unexpected response from Teamspeak."))
}
}
Err(msg) => Err(msg)
}
}
fn get_self_clid(connection: &mut Telnet) -> Result<String, String> {
match commands::whoami(connection) {
Ok(response) => {
match response {
Response::Data(params) => {
match parameter::parameter_find(&params, "clid") {
None => Err(String::from("Could not find clid in response from Teamspeak.")),
Some(param) => Ok(param.value)
}
}
_ => Err(String::from("Received unexpected response from Teamspeak for whoami."))
}
}
Err(msg) => Err(msg)
}
}
pub fn find_self(connection: &mut Telnet) -> Result<ResponseClient, String> {
let clid = get_self_clid(connection)?;
match commands::clientlist(connection) {
Ok(response) => {
match response {
Response::DataList(parameter_lists) => {
match parameter::parameter_list_find(&parameter_lists, "clid", &clid, false) {
Some(params) => {
Ok(ResponseClient::from(params))
}
None => {
Err(String::from("Could not find self in response from Teamspeak."))
}
}
}
_ => Err(String::from("Received unexpected response from Teamspeak for clientlist."))
}
}
Err(msg) => Err(msg)
}
}
pub fn fetch_client(connection: &mut Telnet, clients: &[ResponseClient]) -> Result<Response, String> {
let cid = find_self(connection)?.cid;
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &cid, clid_list)
}
pub fn fetch_channel(connection: &mut Telnet, channel: ResponseChannel) -> Result<Response, String> {
let cid = find_self(connection)?.cid;
let clients = get_channel_clients(connection, &channel)?;
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &cid, clid_list)
}
pub fn move_client(connection: &mut Telnet, channel: &ResponseChannel, clients: &[ResponseClient]) -> Result<Response, String> {
let clid_list: Vec<&i32> = clients.iter().map(|c| &c.clid).collect();
commands::clientmove(connection, &channel.cid, clid_list)
}
pub fn update_client(connection: &mut Telnet, parameters: ParameterList) -> Result<Response, String> {
commands::clientupdate(connection, parameters)
}
pub fn send_text_message(connection: &mut Telnet, target: SendTextMessageTarget, msg: String) -> Result<Response, String> {
commands::sendtextmessage(connection, target, msg)
}

View file

@ -9,7 +9,7 @@ back"
action=$(echo "$actions" | $DMENU) action=$(echo "$actions" | $DMENU)
ts_control_move_self() { _ts_control_move_self() {
channel=$(teamspeak-query-lib channels | $DMENU) channel=$(teamspeak-query-lib channels | $DMENU)
if [ -z "$channel" ]; then if [ -z "$channel" ]; then
return 1 return 1
@ -18,7 +18,7 @@ ts_control_move_self() {
return 0 return 0
} }
ts_control_fetch() { _ts_control_fetch() {
target=$(teamspeak-query-lib "$1s" | $DMENU) target=$(teamspeak-query-lib "$1s" | $DMENU)
if [ -z "$target" ]; then if [ -z "$target" ]; then
exit 1 exit 1
@ -28,13 +28,13 @@ ts_control_fetch() {
case $action in case $action in
"move") "move")
ts_control_move_self _ts_control_move_self
;; ;;
"fetch-client") "fetch-client")
ts_control_fetch client _ts_control_fetch client
;; ;;
"fetch-channel") "fetch-channel")
ts_control_fetch channel _ts_control_fetch channel
;; ;;
"not away") "not away")
teamspeak-query-lib move "Not Away From Keyboard" teamspeak-query-lib move "Not Away From Keyboard"