Add fetch-channel command
This commit is contained in:
parent
970c1ee2c2
commit
244a7073fe
9 changed files with 183 additions and 54 deletions
8
Makefile
Normal file
8
Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
.PHONY: run
|
||||
|
||||
build:
|
||||
@cargo build
|
||||
|
||||
run: build
|
||||
@PATH=$(PWD)/target/debug:$(PATH) ./ts-control
|
||||
|
30
src/cli.rs
30
src/cli.rs
|
@ -31,7 +31,12 @@ pub struct ChannelsArgs {
|
|||
pub struct FetchArgs {
|
||||
#[arg(long)]
|
||||
strict_client: bool,
|
||||
client: String,
|
||||
#[arg(long)]
|
||||
strict_channel: bool,
|
||||
#[arg(long)]
|
||||
channel: Option<String>,
|
||||
#[arg(long)]
|
||||
client: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -58,12 +63,29 @@ pub struct UpdateArgs {
|
|||
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)
|
||||
pub fn want_channel(&self) -> bool {
|
||||
self.channel.is_some()
|
||||
}
|
||||
|
||||
pub fn want_client(&self) -> bool {
|
||||
self.client.is_some()
|
||||
}
|
||||
|
||||
pub fn channel(&self, connection: &mut Telnet) -> Result<Option<ResponseChannel>, String> {
|
||||
if let Some(channel) = &self.channel {
|
||||
utils::find_channel(connection, channel, self.strict_channel)
|
||||
} else {
|
||||
Err("No channel specified.".to_string())
|
||||
}
|
||||
}
|
||||
pub fn client(&self, connection: &mut Telnet) -> Result<Option<ResponseClient>, String> {
|
||||
if let Some(client) = &self.client {
|
||||
utils::find_client(connection, client, self.strict_client)
|
||||
} else {
|
||||
Err("No client specified.".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MoveArgs {
|
||||
|
|
|
@ -57,8 +57,7 @@ fn read_response(connection: &mut Telnet, skip_ok: bool, mut buffer: String) ->
|
|||
if skip_ok {
|
||||
read_response(connection, skip_ok, buffer)
|
||||
} else {
|
||||
// empty Ok response
|
||||
Ok(Response::Data(Vec::new()))
|
||||
Ok(Response::Ok)
|
||||
}
|
||||
} else {
|
||||
Err(format!("Received error response from Teamspeak: {} ({})", err.msg, err.id))
|
||||
|
|
82
src/main.rs
82
src/main.rs
|
@ -8,6 +8,29 @@ mod cli;
|
|||
use std::process::exit;
|
||||
use telnet::Telnet;
|
||||
use crate::cli::Commands;
|
||||
use crate::response_classes::{ResponseChannel, ResponseClient};
|
||||
|
||||
fn channel_or_exit(channel_res: Result<Option<ResponseChannel>, String>) -> ResponseChannel {
|
||||
channel_res.unwrap_or_else(|err| {
|
||||
println!("Failed to find channel: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find channel.");
|
||||
exit(1);
|
||||
})
|
||||
}
|
||||
|
||||
fn client_or_exit(client_res: Result<Option<ResponseClient>, String>) -> ResponseClient {
|
||||
client_res.unwrap_or_else(|err| {
|
||||
println!("Failed to find client: {}", err);
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find client.");
|
||||
exit(1);
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
|
@ -55,42 +78,43 @@ fn main() {
|
|||
}
|
||||
|
||||
Commands::Fetch(args) => {
|
||||
let client = args.client(&mut connection).unwrap_or_else(|err| {
|
||||
println!("Failed to find client for move: {}", err);
|
||||
if args.want_client() && args.want_channel() {
|
||||
println!("Fetching both clients and channels is not supported.");
|
||||
exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
println!("Failed to find client for move.");
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
if !args.want_client() && !args.want_channel() {
|
||||
println!("No clients or channels specified.");
|
||||
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);
|
||||
if args.want_client() {
|
||||
let client = client_or_exit(args.client(&mut connection));
|
||||
|
||||
match utils::fetch_client(&mut connection, &[client]) {
|
||||
Ok(_) => println!("Successfully fetched client."),
|
||||
Err(msg) => {
|
||||
println!("Failed to fetch client: {}", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args.want_channel() {
|
||||
let channel = channel_or_exit(args.channel(&mut connection));
|
||||
|
||||
match utils::fetch_channel(&mut connection, channel) {
|
||||
Ok(_) => println!("Successfully fetched channel."),
|
||||
Err(msg) => {
|
||||
println!("Failed to fetch channel: {}", 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);
|
||||
});
|
||||
let channel = channel_or_exit(args.channel(&mut connection));
|
||||
let client = client_or_exit(args.client(&mut connection));
|
||||
|
||||
match utils::move_client(&mut connection, &channel, &[client]) {
|
||||
Ok(resp) => println!("Successfully moved client: {}", resp),
|
||||
|
|
|
@ -18,21 +18,21 @@ pub fn parameter_find(params: &Vec<Parameter>, name: &str) -> Option<Parameter>
|
|||
}
|
||||
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;
|
||||
}
|
||||
if params.iter().position(|param| param.is(name, value, strict)).is_some() {
|
||||
return Some(params.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
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 params.iter().position(|param| param.is(name, value, strict)).is_some() {
|
||||
found.push(params.clone())
|
||||
}
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
pub fn parameter_parse(params_str: &str) -> ParameterList {
|
||||
let parts: Vec<&str> = params_str.split(' ').collect();
|
||||
|
@ -63,6 +63,21 @@ impl Parameter {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is(&self, name: &str, value: &str, strict: bool) -> bool {
|
||||
if self.name != name {
|
||||
false
|
||||
}
|
||||
else if self.value != value && strict {
|
||||
false
|
||||
}
|
||||
else if !self.value.contains(value) && !strict {
|
||||
false
|
||||
}
|
||||
else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i32(&self, default: i32) -> i32 {
|
||||
self.value.parse::<i32>().unwrap_or(default)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::fmt::{Debug, Display, Formatter};
|
|||
use crate::parameter::*;
|
||||
|
||||
pub enum Response {
|
||||
Ok,
|
||||
Data(ParameterList),
|
||||
DataList(Vec<ParameterList>),
|
||||
}
|
||||
|
@ -36,6 +37,9 @@ impl TryFrom<String> for Response {
|
|||
impl Debug for Response {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Response::Ok => {
|
||||
write!(f, "Ok")
|
||||
}
|
||||
Response::Data(params) => {
|
||||
write!(f, "Data:")?;
|
||||
write!(f, "{:?};", params)?;
|
||||
|
@ -48,13 +52,16 @@ impl Debug for Response {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Response {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Response::Ok => {
|
||||
write!(f, "Ok")
|
||||
}
|
||||
Response::Data(params) => {
|
||||
for param in params {
|
||||
write!(f, "{};", param)?;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use crate::parameter::{parameter_find, ParameterList};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -10,6 +11,12 @@ pub struct ResponseChannel {
|
|||
pub channel_flag_are_subscribed: bool,
|
||||
}
|
||||
|
||||
impl Display for ResponseChannel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} ({})", self.channel_name, self.cid)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParameterList> for ResponseChannel {
|
||||
fn from(value: ParameterList) -> Self {
|
||||
ResponseChannel {
|
||||
|
@ -83,6 +90,12 @@ pub struct ResponseClient {
|
|||
pub client_type: i32,
|
||||
}
|
||||
|
||||
impl Display for ResponseClient {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} ({})", self.client_nickname, self.clid)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParameterList> for ResponseClient {
|
||||
fn from(value: ParameterList) -> Self {
|
||||
ResponseClient {
|
||||
|
|
29
src/utils.rs
29
src/utils.rs
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
|||
use telnet::Event::TimedOut;
|
||||
|
||||
use crate::{commands, parameter};
|
||||
use crate::parameter::ParameterList;
|
||||
use crate::parameter::{parameter_list_find_all, ParameterList};
|
||||
use crate::response::Response;
|
||||
use crate::response_classes::{ResponseChannel, ResponseClient};
|
||||
|
||||
|
@ -121,6 +121,24 @@ pub fn find_client(connection: &mut Telnet, name: &str, strict: bool) -> Result<
|
|||
}
|
||||
}
|
||||
|
||||
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(¶meter_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) => {
|
||||
|
@ -168,6 +186,15 @@ pub fn fetch_client(connection: &mut Telnet, clients: &[ResponseClient]) -> Resu
|
|||
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();
|
||||
|
||||
|
|
28
ts-control
28
ts-control
|
@ -1,6 +1,13 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
action=$(printf "move\nfetch\naway\nnot away\nback" | $DMENU)
|
||||
actions="move
|
||||
fetch-client
|
||||
fetch-channel
|
||||
away
|
||||
not away
|
||||
back"
|
||||
action=$(echo "$actions" | $DMENU)
|
||||
|
||||
|
||||
ts_control_move_self() {
|
||||
channel=$(teamspeak-query-lib channels | $DMENU)
|
||||
|
@ -11,16 +18,23 @@ ts_control_move_self() {
|
|||
return 0
|
||||
}
|
||||
|
||||
ts_control_fetch() {
|
||||
target=$(teamspeak-query-lib "$1s" | $DMENU)
|
||||
if [ -z "$target" ]; then
|
||||
exit 1
|
||||
fi
|
||||
teamspeak-query-lib fetch "--strict-$1" "--$1" "$target"
|
||||
}
|
||||
|
||||
case $action in
|
||||
"move")
|
||||
ts_control_move_self
|
||||
;;
|
||||
"fetch")
|
||||
client=$(teamspeak-query-lib clients | $DMENU)
|
||||
if [ -z "$client" ]; then
|
||||
exit 1
|
||||
fi
|
||||
teamspeak-query-lib fetch --strict-client "$client"
|
||||
"fetch-client")
|
||||
ts_control_fetch client
|
||||
;;
|
||||
"fetch-channel")
|
||||
ts_control_fetch channel
|
||||
;;
|
||||
"not away")
|
||||
teamspeak-query-lib move "Not Away From Keyboard"
|
||||
|
|
Loading…
Reference in a new issue