Init
This commit is contained in:
commit
13abafae9d
12 changed files with 1095 additions and 0 deletions
119
src/cli.rs
Normal file
119
src/cli.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use clap::{Parser, Subcommand, Args};
|
||||
use telnet::Telnet;
|
||||
use crate::parameter::{Parameter, ParameterList};
|
||||
use crate::response_classes::{ResponseChannel, ResponseClient};
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
Channels(ChannelsArgs),
|
||||
Clients,
|
||||
Fetch(FetchArgs),
|
||||
Move(MoveArgs),
|
||||
Update(UpdateArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ChannelsArgs {
|
||||
#[arg(long)]
|
||||
pub spacers: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct FetchArgs {
|
||||
#[arg(long)]
|
||||
strict_client: bool,
|
||||
client: String,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct MoveArgs {
|
||||
#[arg(long)]
|
||||
strict_client: bool,
|
||||
#[arg(long)]
|
||||
strict_channel: bool,
|
||||
channel: String,
|
||||
client: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct UpdateArgs {
|
||||
#[arg(long, short)]
|
||||
name: Option<String>,
|
||||
#[arg(long, short)]
|
||||
away: Option<String>,
|
||||
#[arg(long, short)]
|
||||
back: bool,
|
||||
#[arg(long, short)]
|
||||
microphone: Option<bool>,
|
||||
#[arg(long, short)]
|
||||
speakers: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
impl FetchArgs {
|
||||
pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> {
|
||||
utils::find_client(connection, &self.client, self.strict_client)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MoveArgs {
|
||||
pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> {
|
||||
utils::find_channel(connection, &self.channel, self.strict_channel)
|
||||
}
|
||||
pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> {
|
||||
match &self.client {
|
||||
Some(client) => {
|
||||
utils::find_client(connection, client, self.strict_client)
|
||||
}
|
||||
None => {
|
||||
match utils::find_self(connection) {
|
||||
Ok(client) => Ok(Some(client)),
|
||||
Err(msg) => Err(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateArgs {
|
||||
pub fn to_parameter_list(&self) -> ParameterList {
|
||||
let mut params: ParameterList = Vec::new();
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
params.push(Parameter::new(String::from("client_nickname"), name.clone()));
|
||||
}
|
||||
|
||||
if let Some(away) = &self.away {
|
||||
params.push(Parameter::new(String::from("client_away_message"), away.clone()));
|
||||
params.push(Parameter::new(String::from("client_away"), String::from("1")));
|
||||
}
|
||||
if self.back {
|
||||
params.push(Parameter::new(String::from("client_away"), String::from("0")));
|
||||
}
|
||||
|
||||
if let Some(microphone) = self.microphone {
|
||||
let muted = u8::from(!microphone).to_string();
|
||||
params.push(Parameter::new(String::from("client_input_muted"), muted));
|
||||
}
|
||||
if let Some(speakers) = self.speakers {
|
||||
let muted = u8::from(!speakers).to_string();
|
||||
params.push(Parameter::new(String::from("client_output_muted"), muted));
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() -> Commands {
|
||||
Cli::parse().command
|
||||
}
|
107
src/commands.rs
Normal file
107
src/commands.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use telnet::Telnet;
|
||||
use telnet::Event::Data;
|
||||
use crate::parameter::ParameterList;
|
||||
|
||||
use crate::response::Response;
|
||||
|
||||
fn to_single_response(resp: Response) -> Response {
|
||||
match resp {
|
||||
Response::DataList(list) => Response::Data(list[0].clone()),
|
||||
_ => resp
|
||||
}
|
||||
}
|
||||
|
||||
fn read_part(connection: &mut Telnet) -> Result<String, String> {
|
||||
match connection.read() {
|
||||
Ok(event) => {
|
||||
match event {
|
||||
Data(bytes) => Ok(String::from_utf8(bytes.to_vec())
|
||||
.map_err(|_| "Teamspeak returned a badly formatted response.")?),
|
||||
_ => {
|
||||
Err(String::from("Received unknown event from Teamspeak."))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
Err(String::from("Failed to read from Teamspeak."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_response_buffer(connection: &mut Telnet, buffer: &mut String) -> Result<(String, String), String> {
|
||||
loop {
|
||||
buffer.push_str(&read_part(connection)?);
|
||||
match buffer.split_once("\n\r") {
|
||||
None => {}
|
||||
Some((response, remaining)) => {
|
||||
return Ok((String::from(response), String::from(remaining)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_command(connection: &mut Telnet, command: &str, skip_ok: bool) -> Result<Response, String> {
|
||||
connection.write(command.as_bytes()).map_err(|_| "Failed to write to Teamspeak.")?;
|
||||
|
||||
read_response(connection, skip_ok, String::new())
|
||||
}
|
||||
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) {
|
||||
Ok(resp) => {
|
||||
Ok(resp)
|
||||
}
|
||||
Err(err) => {
|
||||
if err.is_error_okay() {
|
||||
if skip_ok {
|
||||
read_response(connection, skip_ok, buffer)
|
||||
} else {
|
||||
// empty Ok response
|
||||
Ok(Response::Data(Vec::new()))
|
||||
}
|
||||
} else {
|
||||
Err(format!("Received error response from Teamspeak: {} ({})", err.msg, err.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn login(connection: &mut Telnet, apikey: &str) -> Result<Response, String> {
|
||||
send_command(connection, &format!("auth apikey={}\n", apikey), false)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_name(connection: &mut Telnet, name: &str) -> Result<Response, String> {
|
||||
send_command(connection, &format!("clientupdate client_nickname={}\n", name), true)
|
||||
}
|
||||
|
||||
pub fn channellist(connection: &mut Telnet) -> Result<Response, String> {
|
||||
send_command(connection, "channellist\n", true)
|
||||
}
|
||||
|
||||
pub fn clientlist(connection: &mut Telnet) -> Result<Response, String> {
|
||||
send_command(connection, "clientlist\n", true)
|
||||
}
|
||||
|
||||
pub fn whoami(connection: &mut Telnet) -> Result<Response, String> {
|
||||
send_command(connection, "whoami\n", true).map(to_single_response)
|
||||
}
|
||||
|
||||
pub fn clientmove(connection: &mut Telnet, cid: &i32, clid_list: Vec<&i32>) -> Result<Response, String> {
|
||||
let clid_str = clid_list
|
||||
.iter()
|
||||
.map(|clid| format!("clid={}", clid))
|
||||
.collect::<Vec<String>>()
|
||||
.join("|");
|
||||
send_command(connection, &format!("clientmove cid={} {}\n", cid, clid_str), false)
|
||||
}
|
||||
|
||||
pub fn clientupdate(connection: &mut Telnet, parameters: &ParameterList) -> Result<Response, String> {
|
||||
let parameters_str = parameters
|
||||
.iter()
|
||||
.map(|param| format!("{}={}", param.name, param.value))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
send_command(connection, &format!("clientupdate {}\n", parameters_str), false)
|
||||
}
|
114
src/main.rs
Normal file
114
src/main.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
mod response;
|
||||
mod utils;
|
||||
mod commands;
|
||||
mod parameter;
|
||||
mod response_classes;
|
||||
mod cli;
|
||||
|
||||
use std::process::exit;
|
||||
use telnet::Telnet;
|
||||
use crate::cli::Commands;
|
||||
|
||||
fn main() {
|
||||
|
||||
let cli = cli::init();
|
||||
|
||||
let connection = Telnet::connect(("127.0.0.1", 25639), 512 * 1024);
|
||||
if connection.is_err() {
|
||||
println!("Failed to connect to Teamspeak.");
|
||||
exit(1);
|
||||
}
|
||||
let mut connection = connection.unwrap();
|
||||
|
||||
utils::skip_welcome(&mut connection);
|
||||
utils::login(&mut connection);
|
||||
|
||||
// You can check for the existence of subcommands, and if found use their
|
||||
// matches just as you would the top level cmd
|
||||
match &cli {
|
||||
Commands::Channels(args) => {
|
||||
match utils::get_channels(&mut connection, args.spacers) {
|
||||
Ok(channels) => {
|
||||
for channel in channels {
|
||||
println!("{}", channel.channel_name);
|
||||
}
|
||||
}
|
||||
Err(msg) => {
|
||||
println!("Failed to get channels: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Commands::Clients => {
|
||||
match utils::get_clients(&mut connection) {
|
||||
Ok(clients) => {
|
||||
for client in clients {
|
||||
println!("{}", client.client_nickname);
|
||||
}
|
||||
}
|
||||
Err(msg) => {
|
||||
println!("Failed to get clients: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Commands::Fetch(args) => {
|
||||
let client = args.client(&mut connection).unwrap_or_else(|err| {
|
||||
println!("Failed to find client for move: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find client for move.");
|
||||
exit(1);
|
||||
});
|
||||
|
||||
match utils::fetch_client(&mut connection, &[client]) {
|
||||
Ok(resp) => println!("Successfully fetched client: {}", resp),
|
||||
Err(msg) => {
|
||||
println!("Failed to fetch client: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Commands::Move(args) => {
|
||||
let channel = args.channel(&mut connection).unwrap_or_else(|err| {
|
||||
println!("Failed to find channel for move: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find channel for move.");
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let client = args.client(&mut connection).unwrap_or_else(|err| {
|
||||
println!("Failed to find client for move: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find client for move.");
|
||||
exit(1);
|
||||
});
|
||||
|
||||
match utils::move_client(&mut connection, &channel, &[client]) {
|
||||
Ok(resp) => println!("Successfully moved client: {}", resp),
|
||||
Err(msg) => {
|
||||
println!("Failed to move client: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Commands::Update(args) => {
|
||||
match utils::update_client(&mut connection, &args.to_parameter_list()) {
|
||||
Ok(_) => println!("Successfully updated client."),
|
||||
Err(msg) => {
|
||||
println!("Failed to update client: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
src/parameter.rs
Normal file
87
src/parameter.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parameter {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn parameter_list_find(param_lists: &Vec<ParameterList>, name: &str, value: &str, strict: bool) -> Option<ParameterList> {
|
||||
for params in param_lists {
|
||||
for param in params {
|
||||
if param.name != name {
|
||||
continue;
|
||||
}
|
||||
if param.value != value && strict {
|
||||
continue;
|
||||
}
|
||||
if !param.value.contains(value) && !strict {
|
||||
continue;
|
||||
}
|
||||
return Some(params.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn parameter_parse(params_str: &str) -> ParameterList {
|
||||
let parts: Vec<&str> = params_str.split(' ').collect();
|
||||
|
||||
let mut response_params = Vec::new();
|
||||
parts.iter().for_each(|part| {
|
||||
response_params.push(Parameter::from(part.split_once('=').unwrap()));
|
||||
});
|
||||
|
||||
response_params
|
||||
}
|
||||
|
||||
impl From<(&str, &str)> for Parameter {
|
||||
fn from(param: (&str, &str)) -> Parameter {
|
||||
Parameter::new(String::from(param.0), String::from(param.1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parameter {
|
||||
pub fn new(name: String, value: String) -> Parameter {
|
||||
let value = value
|
||||
.replace("\\s", " ")
|
||||
.replace("\\p", "|");
|
||||
|
||||
Parameter {
|
||||
name,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i32(&self, default: i32) -> i32 {
|
||||
self.value.parse::<i32>().unwrap_or(default)
|
||||
}
|
||||
}
|
||||
|
||||
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(String::from(""), String::from(""))
|
||||
}
|
||||
}
|
93
src/response.rs
Normal file
93
src/response.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use std::fmt::{Debug, Display, Formatter};
|
||||
use crate::parameter::*;
|
||||
|
||||
pub enum Response {
|
||||
Data(ParameterList),
|
||||
DataList(Vec<ParameterList>),
|
||||
}
|
||||
|
||||
pub struct ResponseError {
|
||||
pub id: i32,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Response {
|
||||
type Error = ResponseError;
|
||||
|
||||
fn try_from(response_str: String) -> Result<Self, ResponseError> {
|
||||
let mut response_str = response_str.trim_end_matches("\n\r");
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Response {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Response::Data(params) => {
|
||||
write!(f, "Data:")?;
|
||||
write!(f, "{:?};", params)?;
|
||||
Ok(())
|
||||
}
|
||||
Response::DataList(params_list) => {
|
||||
write!(f, "DataList:")?;
|
||||
for params in params_list {
|
||||
write!(f, "{:?};", params)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Response {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Response::Data(params) => {
|
||||
for param in params {
|
||||
write!(f, "{};", param)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Response::DataList(params_list) => {
|
||||
for params in params_list {
|
||||
write!(f, "[")?;
|
||||
for param in params {
|
||||
write!(f, "{};", param)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError {
|
||||
pub fn is_error_okay(&self) -> bool {
|
||||
self.id == 0
|
||||
}
|
||||
|
||||
fn create_error(params: &Vec<Parameter>) -> ResponseError {
|
||||
ResponseError {
|
||||
id: parameter_find(params, "id")
|
||||
.unwrap_or_else(|| Parameter::new(String::from("id"), String::from("-1")))
|
||||
.to_i32(-1),
|
||||
msg: parameter_find(params, "msg")
|
||||
.unwrap_or_else(|| Parameter::new(String::from("msg"), String::from("Unknown error.")))
|
||||
.value,
|
||||
}
|
||||
}
|
||||
}
|
106
src/response_classes.rs
Normal file
106
src/response_classes.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use crate::parameter::{parameter_find, ParameterList};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResponseChannel {
|
||||
pub cid: i32,
|
||||
pub pid: i32,
|
||||
pub channel_order: i32,
|
||||
pub channel_name: String,
|
||||
pub channel_topic: String,
|
||||
pub channel_flag_are_subscribed: bool,
|
||||
}
|
||||
|
||||
impl From<ParameterList> for ResponseChannel {
|
||||
fn from(value: ParameterList) -> Self {
|
||||
ResponseChannel {
|
||||
cid: parameter_find(&value, "cid")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
pid: parameter_find(&value, "pid")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
channel_order: parameter_find(&value, "channel_order")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
channel_name: parameter_find(&value, "channel_name")
|
||||
.unwrap_or_default()
|
||||
.value,
|
||||
channel_topic: parameter_find(&value, "channel_topic")
|
||||
.unwrap_or_default()
|
||||
.value,
|
||||
channel_flag_are_subscribed: parameter_find(&value, "channel_flag_are_subscribed")
|
||||
.unwrap_or_default()
|
||||
.to_i32(0) == 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseChannel {
|
||||
pub fn is_spacer(&self) -> bool {
|
||||
self.channel_name.starts_with("[spacer")
|
||||
|| self.channel_name.starts_with("[*spacer")
|
||||
|| self.channel_name.starts_with("[lspacer")
|
||||
|| self.channel_name.starts_with("[cspacer")
|
||||
|| self.channel_name.starts_with("[rspacer")
|
||||
}
|
||||
|
||||
fn list_find_next(list: &mut Vec<ResponseChannel>, order: i32, pid: i32) -> Option<ResponseChannel> {
|
||||
let index = list.iter().position(|a| a.channel_order == order && a.pid == pid)?;
|
||||
Some(list.swap_remove(index))
|
||||
}
|
||||
|
||||
pub fn sort_list(mut list: Vec<ResponseChannel>) -> Vec<ResponseChannel> {
|
||||
let mut list_sorted: Vec<ResponseChannel> = Vec::new();
|
||||
let mut pids: Vec<i32> = Vec::new();
|
||||
let mut pid = 0;
|
||||
let mut order = 0;
|
||||
|
||||
while !list.is_empty() {
|
||||
match ResponseChannel::list_find_next(&mut list, order, pid) {
|
||||
None => {
|
||||
order = pid;
|
||||
pid = pids.pop().unwrap_or(0);
|
||||
}
|
||||
Some(elem) => {
|
||||
pids.push(pid);
|
||||
pid = elem.cid;
|
||||
order = 0;
|
||||
list_sorted.push(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list_sorted
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResponseClient {
|
||||
pub cid: i32,
|
||||
pub clid: i32,
|
||||
pub client_database_id: i32,
|
||||
pub client_nickname: String,
|
||||
pub client_type: i32,
|
||||
}
|
||||
|
||||
impl From<ParameterList> for ResponseClient {
|
||||
fn from(value: ParameterList) -> Self {
|
||||
ResponseClient {
|
||||
cid: parameter_find(&value, "cid")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
clid: parameter_find(&value, "clid")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
client_database_id: parameter_find(&value, "client_database_id")
|
||||
.unwrap_or_default()
|
||||
.to_i32(-1),
|
||||
client_nickname: parameter_find(&value, "client_nickname")
|
||||
.unwrap_or_default()
|
||||
.value,
|
||||
client_type: parameter_find(&value, "channel_topic")
|
||||
.unwrap_or_default()
|
||||
.to_i32(0),
|
||||
}
|
||||
}
|
||||
}
|
179
src/utils.rs
Normal file
179
src/utils.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
use std::process::exit;
|
||||
use telnet::Telnet;
|
||||
use std::time::Duration;
|
||||
use telnet::Event::TimedOut;
|
||||
|
||||
use crate::{commands, parameter};
|
||||
use crate::parameter::ParameterList;
|
||||
use crate::response::Response;
|
||||
use crate::response_classes::{ResponseChannel, ResponseClient};
|
||||
|
||||
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(¶meter_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(¶meter_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)
|
||||
}
|
||||
}
|
||||
|
||||
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(¶ms, "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(¶meter_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 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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue