paramaters

This commit is contained in:
MeexReay 2025-02-10 14:22:34 +03:00
parent 07c62f4ad1
commit 4d1cad9520
7 changed files with 289 additions and 46 deletions

127
Cargo.lock generated
View File

@ -11,6 +11,56 @@ dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.95"
@ -27,6 +77,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
name = "bRAC"
version = "0.1.0+1.99.2"
dependencies = [
"clap",
"colored",
"crossterm",
"homedir",
@ -61,6 +112,52 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clap"
version = "4.5.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "colored"
version = "3.0.0"
@ -129,6 +226,12 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "homedir"
version = "0.3.4"
@ -151,6 +254,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.14"
@ -231,6 +340,12 @@ dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -446,6 +561,12 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.98"
@ -463,6 +584,12 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"

View File

@ -11,4 +11,5 @@ lazy_static = "1.5.0"
crossterm = "0.28.1"
serde = { version = "1.0.217", features = ["serde_derive"] }
serde_yml = "0.0.12"
homedir = "0.3.4"
homedir = "0.3.4"
clap = { version = "4.5.28", features = ["derive"] }

View File

@ -30,11 +30,37 @@ cargo build --release # build release (target/release/bRAC)
cargo run # run (builds and runs bRAC itself)
```
## config
```yml
host: meex.lol:11234 # server host
name: null # user name
message_format: 리㹰<{name}> {text} # message format
update_time: 50 # update chat interval
max_messages: 100 # chat messages limit
```
## command args
```
-p, --config-path Print config path
-H, --host <HOST> Use specified host
-n, --name <NAME> Use specified name
-F, --message-format <MESSAGE_FORMAT> Use specified message format
-r, --read-messages Print unformatted messages from chat and exit
-s, --send-message <MESSAGE> Send unformatted message to chat and exit
-f, --disable-formatting Disable message formatting and sanitizing
-c, --disable-commands Disable slash commands
-i, --disable-ip-hiding Disable ip hiding
-h, --help Print help
-V, --version Print version
```
## commands
`/help` - show help message \
`/clear` - clear chat \
`/spam *args` - spam with text \
`/help` - show help message
`/spam *args` - spam with text
## colored usernames

View File

@ -16,8 +16,8 @@ pub struct Config {
}
pub fn load_config(path: PathBuf) -> Config {
println!("Config path: {}", path.to_string_lossy());
println!("Loading config...");
// println!("Config path: {}", path.to_string_lossy());
// println!("Loading config...");
let config = if !fs::exists(&path).unwrap_or_default() {
let host = get_input("Host (default: meex.lol:11234) > ").unwrap_or("meex.lol:11234".to_string());
@ -41,7 +41,7 @@ pub fn load_config(path: PathBuf) -> Config {
serde_yml::from_str(config).expect("Config load error")
};
println!("Config loaded successfully!");
// println!("Config loaded successfully!");
config
}

View File

@ -1,25 +1,20 @@
use std::{
error::Error, io::{stdin, stdout, BufRead, Write}, sync::{Arc, RwLock}, thread, time::{Duration, SystemTime, UNIX_EPOCH}
error::Error, io::{stdin, stdout, BufRead, Write}, sync::{Arc, RwLock}
};
use colored::Color;
use config::{get_config_path, load_config, Config};
use rac::{run_recv_loop, send_message};
use rac::{read_messages, run_recv_loop, send_message};
use rand::random;
use regex::Regex;
use lazy_static::lazy_static;
use term::run_main_loop;
use clap::Parser;
const ADVERTISEMENT: &str = "\r\x1B[1A use bRAC client! https://github.com/MeexReay/bRAC \x1B[1B";
const ADVERTISEMENT_ENABLED: bool = false;
mod config;
mod term;
mod rac;
lazy_static! {
static ref DATE_REGEX: Regex = Regex::new(r"\[(.*?)\] (.*)").unwrap();
static ref COLORED_USERNAMES: Vec<(Regex, Color)> = vec![
@ -31,6 +26,10 @@ lazy_static! {
}
mod config;
mod term;
mod rac;
fn get_input(prompt: &str) -> Option<String> {
let mut out = stdout().lock();
@ -47,31 +46,86 @@ fn get_input(prompt: &str) -> Option<String> {
}
}
fn on_command(config: Arc<Config>, host: &str, command: &str) -> Result<(), Box<dyn Error>> {
fn on_command(config: Arc<Config>, host: &str, disable_hiding_ip: bool, command: &str) -> Result<(), Box<dyn Error>> {
let command = command.trim_start_matches("/");
let (command, args) = command.split_once(" ").unwrap_or((&command, ""));
let args = args.split(" ").collect::<Vec<&str>>();
if command == "clear" {
send_message(host, &format!("\r\x1B[1A{}", " ".repeat(64)).repeat(config.max_messages))?;
send_message(host, &format!("\r\x1B[1A{}", " ".repeat(64)).repeat(config.max_messages), disable_hiding_ip)?;
// *input.write().unwrap() = "/ заспамлено)))".to_string();
} else if command == "spam" {
send_message(host, &format!("\r\x1B[1A{}{}", args.join(" "), " ".repeat(10)).repeat(config.max_messages))?;
send_message(host, &format!("\r\x1B[1A{}{}", args.join(" "), " ".repeat(10)).repeat(config.max_messages), disable_hiding_ip)?;
// *input.write().unwrap() = "/ заспамлено)))".to_string();
} else if command == "help" {
write!(stdout(), "/clear - clear console; /spam *args - spam console with text; /help - show help message")?;
write!(stdout(), "Help message:\r
/clear - clear console\r
/spam *args - spam console with text\r
/help - show help message\r
\r
Press enter to close")?;
stdout().flush()?;
}
Ok(())
}
fn main() {
let start_time = SystemTime::now();
let config = load_config(get_config_path());
let name = match config.name.clone() {
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Print config path
#[arg(short='p', long)]
config_path: bool,
/// Use specified host
#[arg(short='H', long)]
host: Option<String>,
/// Use specified name
#[arg(short, long)]
name: Option<String>,
/// Use specified message format
#[arg(short='F', long)]
message_format: Option<String>,
/// Print unformatted messages from chat and exit
#[arg(short, long)]
read_messages: bool,
/// Send unformatted message to chat and exit
#[arg(short, long, value_name="MESSAGE")]
send_message: Option<String>,
/// Disable message formatting and sanitizing
#[arg(short='f', long)]
disable_formatting: bool,
/// Disable slash commands
#[arg(short='c', long)]
disable_commands: bool,
/// Disable ip hiding
#[arg(short='i', long)]
disable_ip_hiding: bool,
}
fn main() {
let args = Args::parse();
let config_path = get_config_path();
if args.config_path {
print!("{}", config_path.to_string_lossy());
return;
}
// let start_time = SystemTime::now();
let mut config = load_config(config_path);
let name = match args.name.clone().or(config.name.clone()) {
Some(i) => i,
None => {
let anon_name = format!("Anon#{:X}", random::<u16>());
@ -79,15 +133,40 @@ fn main() {
},
};
if let Some(host) = args.host {
config.host = host;
}
if let Some(message_format) = args.message_format {
config.message_format = message_format;
}
let disable_hiding_ip = args.disable_ip_hiding;
if let Some(message) = args.send_message {
send_message(&config.host, &message, disable_hiding_ip).expect("Error sending message");
return;
}
if args.read_messages {
print!("{}", read_messages(&config.host).expect("Error reading messages"));
return;
}
let disable_formatting = args.disable_formatting;
let disable_commands = args.disable_commands;
let messages = Arc::new(RwLock::new(String::new()));
let input = Arc::new(RwLock::new(String::new()));
let config = Arc::new(config);
let elapsed = start_time.elapsed().unwrap().as_millis();
if elapsed < 1500 {
thread::sleep(Duration::from_millis((1500 - elapsed) as u64));
}
run_recv_loop(config.clone(), config.host.clone(), messages.clone(), input.clone());
run_main_loop(config.clone(), messages.clone(), input.clone(), config.host.clone(), name.clone());
// let elapsed = start_time.elapsed().unwrap().as_millis();
// if elapsed < 1500 {
// thread::sleep(Duration::from_millis((1500 - elapsed) as u64));
// }
run_recv_loop(config.clone(), config.host.clone(), messages.clone(), input.clone(), disable_formatting);
run_main_loop(config.clone(), messages.clone(), input.clone(), config.host.clone(), name.clone(), disable_formatting, disable_commands, disable_hiding_ip);
}

View File

@ -2,12 +2,17 @@ use std::{error::Error, io::{Read, Write}, net::TcpStream, sync::{Arc, RwLock},
use crate::{config::Config, term::print_console, ADVERTISEMENT, ADVERTISEMENT_ENABLED};
pub fn send_message(host: &str, message: &str) -> Result<(), Box<dyn Error>> {
pub fn send_message(host: &str, message: &str, disable_hiding_ip: bool) -> Result<(), Box<dyn Error>> {
let mut stream = TcpStream::connect(host)?;
stream.write_all(&[0x01])?;
let data = format!("\r\x07{}{}{}",
let data = format!("{}{}{}{}",
if disable_hiding_ip {
"\r\x07"
} else {
""
},
message,
if message.chars().count() < 39 {
if !disable_hiding_ip && message.chars().count() < 39 {
" ".repeat(39-message.chars().count())
} else {
String::new()
@ -28,7 +33,7 @@ fn skip_null(stream: &mut TcpStream) -> Result<Vec<u8>, Box<dyn Error>> {
}
}
fn read_messages(host: &str) -> Result<String, Box<dyn Error>> {
pub fn read_messages(host: &str) -> Result<String, Box<dyn Error>> {
let mut stream = TcpStream::connect(host)?;
stream.write_all(&[0x00])?;
@ -67,23 +72,23 @@ fn read_messages(host: &str) -> Result<String, Box<dyn Error>> {
Ok(packet_data)
}
fn recv_loop(config: Arc<Config>, host: &str, cache: Arc<RwLock<String>>, input: Arc<RwLock<String>>) -> Result<(), Box<dyn Error>> {
fn recv_loop(config: Arc<Config>, host: &str, cache: Arc<RwLock<String>>, input: Arc<RwLock<String>>, disable_formatting: bool) -> Result<(), Box<dyn Error>> {
while let Ok(data) = read_messages(host) {
if data == cache.read().unwrap().clone() {
continue
}
*cache.write().unwrap() = data;
print_console(config.clone(), &cache.read().unwrap(), &input.read().unwrap())?;
print_console(config.clone(), &cache.read().unwrap(), &input.read().unwrap(), disable_formatting)?;
thread::sleep(Duration::from_millis(config.update_time as u64));
}
Ok(())
}
pub fn run_recv_loop(config: Arc<Config>, host: String, messages: Arc<RwLock<String>>, input: Arc<RwLock<String>>) {
pub fn run_recv_loop(config: Arc<Config>, host: String, messages: Arc<RwLock<String>>, input: Arc<RwLock<String>>, disable_formatting: bool) {
thread::spawn({
move || {
let _ = recv_loop(config.clone(), &host, messages, input);
let _ = recv_loop(config.clone(), &host, messages, input, disable_formatting);
println!("Connection closed");
}
});

View File

@ -6,14 +6,18 @@ use regex::Regex;
use crate::{config::Config, on_command, rac::send_message, ADVERTISEMENT, COLORED_USERNAMES, DATE_REGEX};
pub fn print_console(config: Arc<Config>, messages: &str, input: &str) -> Result<(), Box<dyn Error>> {
pub fn print_console(config: Arc<Config>, messages: &str, input: &str, disable_formatting: bool) -> Result<(), Box<dyn Error>> {
let mut messages = messages.split("\n")
.map(|o| o.to_string())
.collect::<Vec<String>>();
messages.reverse();
messages.truncate(config.max_messages);
messages.reverse();
let messages: Vec<String> = messages.into_iter().filter_map(format_message).collect();
let messages: Vec<String> = if disable_formatting {
messages
} else {
messages.into_iter().filter_map(format_message).collect()
};
let text = format!(
"{}{}\n> {}",
"\n".repeat(config.max_messages - messages.len()),
@ -80,7 +84,7 @@ fn find_username_color(message: &str) -> Option<(String, String, Color)> {
None
}
fn poll_events(config: Arc<Config>, input: Arc<RwLock<String>>, messages: Arc<RwLock<String>>, host: String, name: String) {
fn poll_events(config: Arc<Config>, input: Arc<RwLock<String>>, messages: Arc<RwLock<String>>, host: String, name: String, disable_formatting: bool, disable_commands: bool, disable_hiding_ip: bool) {
loop {
if !event::poll(Duration::from_millis(50)).unwrap_or(false) { continue }
@ -106,17 +110,18 @@ fn poll_events(config: Arc<Config>, input: Arc<RwLock<String>>, messages: Arc<Rw
stdout().lock().flush().unwrap();
input.write().unwrap().clear();
if message.starts_with("/") {
on_command(config.clone(), &host, &message).expect("Error on command");
if message.starts_with("/") && !disable_commands {
on_command(config.clone(), &host, disable_hiding_ip, &message).expect("Error on command");
} else {
let message = config.message_format.replace("{name}", &name).replace("{text}", &message);
send_message(&host, &message).expect("Error sending message");
send_message(&host, &message, disable_hiding_ip).expect("Error sending message");
}
} else {
print_console(
config.clone(),
&messages.read().unwrap(),
&input.read().unwrap()
&input.read().unwrap(),
disable_formatting
).expect("Error printing console");
}
}
@ -149,7 +154,7 @@ fn poll_events(config: Arc<Config>, input: Arc<RwLock<String>>, messages: Arc<Rw
}
}
pub fn run_main_loop(config: Arc<Config>, messages: Arc<RwLock<String>>, input: Arc<RwLock<String>>, host: String, name: String) {
pub fn run_main_loop(config: Arc<Config>, messages: Arc<RwLock<String>>, input: Arc<RwLock<String>>, host: String, name: String, disable_formatting: bool, disable_commands: bool, disable_hiding_ip: bool) {
enable_raw_mode().unwrap();
// thread::spawn({
@ -167,5 +172,5 @@ pub fn run_main_loop(config: Arc<Config>, messages: Arc<RwLock<String>>, input:
// }
// });
poll_events(config, input.clone(), messages.clone(), host, name);
poll_events(config, input.clone(), messages.clone(), host, name, disable_formatting, disable_commands, disable_hiding_ip);
}