move images to assets/

This commit is contained in:
MeexReay 2025-04-17 22:56:44 +03:00
parent 67a48b74f9
commit 47baed7d6f
9 changed files with 260 additions and 261 deletions

5
Cargo.lock generated
View File

@ -1054,13 +1054,12 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.0" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
"zerocopy 0.8.17",
] ]
[[package]] [[package]]

View File

@ -4,7 +4,7 @@ version = "0.1.3+2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
rand = "0.9.0" rand = "0.9.1"
regex = "1.11.1" regex = "1.11.1"
colored = "3.0.0" colored = "3.0.0"
lazy_static = "1.5.0" lazy_static = "1.5.0"

View File

@ -20,7 +20,7 @@ better RAC client
- RACS compatible (--enable-ssl or in --configure enable SSL) - RACS compatible (--enable-ssl or in --configure enable SSL)
- chunked reading messages - chunked reading messages
![screenshot](image.png) ![screenshot](assets/image.png)
## how to run ## how to run

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -58,7 +58,7 @@ pub fn recv_tick(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
) { ) {
Ok(Some((messages, size))) => { Ok(Some((messages, size))) => {
let messages: Vec<String> = if ctx.disable_formatting { let messages: Vec<String> = if ctx.disable_formatting {
messages messages
} else { } else {
messages.into_iter().flat_map(|o| format_message(ctx.enable_ip_viewing, o)).collect() messages.into_iter().flat_map(|o| format_message(ctx.enable_ip_viewing, o)).collect()
}; };
@ -179,7 +179,7 @@ fn build_menu(_: Arc<Context>, app: &Application) {
.comments("better RAC client") .comments("better RAC client")
.website("https://github.com/MeexReay/bRAC") .website("https://github.com/MeexReay/bRAC")
.website_label("source code") .website_label("source code")
.logo(&Texture::for_pixbuf(&load_pixbuf(include_bytes!("../../icon.png")))) .logo(&Texture::for_pixbuf(&load_pixbuf(include_bytes!("../../assets/icon.png"))))
.build() .build()
.present(); .present();
} }
@ -222,17 +222,17 @@ fn build_ui(ctx: Arc<Context>, app: &Application) -> UiModel {
let fixed = Fixed::new(); let fixed = Fixed::new();
fixed.set_can_target(false); fixed.set_can_target(false);
let konata = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../konata.png"))); let konata = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../assets/konata.png")));
konata.set_size_request(174, 127); konata.set_size_request(174, 127);
fixed.put(&konata, 325.0, 4.0); fixed.put(&konata, 325.0, 4.0);
let logo = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../logo.gif"))); let logo = Picture::for_pixbuf(&load_pixbuf(include_bytes!("../../assets/logo.gif")));
logo.set_size_request(152, 64); logo.set_size_request(152, 64);
let logo_anim = PixbufAnimation::from_stream( let logo_anim = PixbufAnimation::from_stream(
&MemoryInputStream::from_bytes( &MemoryInputStream::from_bytes(
&glib::Bytes::from(include_bytes!("../../logo.gif")) &glib::Bytes::from(include_bytes!("../../assets/logo.gif"))
), ),
None::<&gio::Cancellable> None::<&gio::Cancellable>
).unwrap().iter(Some(SystemTime::now())); ).unwrap().iter(Some(SystemTime::now()));

View File

@ -1,252 +1,252 @@
use std::{str::FromStr, sync::{Arc, RwLock}}; use std::{str::FromStr, sync::{Arc, RwLock}};
#[allow(unused_imports)] #[allow(unused_imports)]
use std::{env, fs, path::{Path, PathBuf}, thread, time::Duration}; use std::{env, fs, path::{Path, PathBuf}, thread, time::Duration};
use colored::Colorize; use colored::Colorize;
use rand::random; use rand::random;
use serde_yml; use serde_yml;
use clap::Parser; use clap::Parser;
use crate::chat::ChatContext; use crate::chat::ChatContext;
use super::util::get_input; use super::util::get_input;
const MESSAGE_FORMAT: &str = "\u{B9AC}\u{3E70}<{name}> {text}"; const MESSAGE_FORMAT: &str = "\u{B9AC}\u{3E70}<{name}> {text}";
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
pub struct Config { pub struct Config {
#[serde(default = "default_host")] #[serde(default = "default_host")]
pub host: String, pub host: String,
#[serde(default)] #[serde(default)]
pub name: Option<String>, pub name: Option<String>,
#[serde(default = "default_message_format")] #[serde(default = "default_message_format")]
pub message_format: String, pub message_format: String,
#[serde(default = "default_update_time")] #[serde(default = "default_update_time")]
pub update_time: usize, pub update_time: usize,
#[serde(default = "default_max_messages")] #[serde(default = "default_max_messages")]
pub max_messages: usize, pub max_messages: usize,
#[serde(default)] #[serde(default)]
pub enable_ip_viewing: bool, pub enable_ip_viewing: bool,
#[serde(default)] #[serde(default)]
pub disable_ip_hiding: bool, pub disable_ip_hiding: bool,
#[serde(default)] #[serde(default)]
pub enable_auth: bool, pub enable_auth: bool,
#[serde(default)] #[serde(default)]
pub enable_ssl: bool, pub enable_ssl: bool,
#[serde(default)] #[serde(default)]
pub enable_chunked: bool, pub enable_chunked: bool,
} }
fn default_max_messages() -> usize { 200 } fn default_max_messages() -> usize { 200 }
fn default_update_time() -> usize { 50 } fn default_update_time() -> usize { 50 }
fn default_host() -> String { "meex.lol:11234".to_string() } fn default_host() -> String { "meex.lol:11234".to_string() }
fn default_message_format() -> String { MESSAGE_FORMAT.to_string() } fn default_message_format() -> String { MESSAGE_FORMAT.to_string() }
fn ask_usize(name: impl ToString, default: usize) -> usize { fn ask_usize(name: impl ToString, default: usize) -> usize {
get_input(format!("{} (default: {}) {} ", name.to_string().bold(), default, ">".bold()).bright_yellow()) get_input(format!("{} (default: {}) {} ", name.to_string().bold(), default, ">".bold()).bright_yellow())
.and_then(|o| o.parse().ok()).unwrap_or(default) .and_then(|o| o.parse().ok()).unwrap_or(default)
} }
fn ask_string(name: impl ToString, default: impl ToString + Clone) -> String { fn ask_string(name: impl ToString, default: impl ToString + Clone) -> String {
ask_string_option(name, default.clone()).unwrap_or(default.to_string()) ask_string_option(name, default.clone()).unwrap_or(default.to_string())
} }
fn ask_string_option(name: impl ToString, default: impl ToString) -> Option<String> { fn ask_string_option(name: impl ToString, default: impl ToString) -> Option<String> {
let default = default.to_string(); let default = default.to_string();
get_input(format!("{} (default: {}) {} ", name.to_string().bold(), default, ">".bold()).bright_yellow()) get_input(format!("{} (default: {}) {} ", name.to_string().bold(), default, ">".bold()).bright_yellow())
} }
fn ask_bool(name: impl ToString, default: bool) -> bool { fn ask_bool(name: impl ToString, default: bool) -> bool {
get_input(format!("{} (Y/N, default: {}) {} ", name.to_string().bold(), if default { "Y" } else { "N" }, ">".bold()).bright_yellow()) get_input(format!("{} (Y/N, default: {}) {} ", name.to_string().bold(), if default { "Y" } else { "N" }, ">".bold()).bright_yellow())
.map(|o| o.to_lowercase() != "n") .map(|o| o.to_lowercase() != "n")
.unwrap_or(default) .unwrap_or(default)
} }
pub fn configure(path: PathBuf) -> Config { pub fn configure(path: PathBuf) -> Config {
println!("{}", "To configure the client, please answer a few questions. It won't take long.".yellow()); println!("{}", "To configure the client, please answer a few questions. It won't take long.".yellow());
println!("{}", "You can reconfigure client in any moment via `bRAC --configure`".yellow()); println!("{}", "You can reconfigure client in any moment via `bRAC --configure`".yellow());
println!("{}", format!("Config stores in path `{}`", path.to_string_lossy()).yellow()); println!("{}", format!("Config stores in path `{}`", path.to_string_lossy()).yellow());
println!(); println!();
let host = ask_string("Host", default_host()); let host = ask_string("Host", default_host());
let name = ask_string_option("Name", "ask every time"); let name = ask_string_option("Name", "ask every time");
let update_time = ask_usize("Update interval", default_update_time()); let update_time = ask_usize("Update interval", default_update_time());
let max_messages = ask_usize("Max messages", default_max_messages()); let max_messages = ask_usize("Max messages", default_max_messages());
let message_format = ask_string("Message format", default_message_format()); let message_format = ask_string("Message format", default_message_format());
let enable_ip_viewing = ask_bool("Enable users IP viewing?", true); let enable_ip_viewing = ask_bool("Enable users IP viewing?", true);
let disable_ip_hiding = ask_bool("Enable your IP viewing?", false); let disable_ip_hiding = ask_bool("Enable your IP viewing?", false);
let enable_auth = ask_bool("Enable auth-mode?", false); let enable_auth = ask_bool("Enable auth-mode?", false);
let enable_ssl = ask_bool("Enable SSL?", false); let enable_ssl = ask_bool("Enable SSL?", false);
let enable_chunked = ask_bool("Enable chunked reading?", true); let enable_chunked = ask_bool("Enable chunked reading?", true);
let config = Config { let config = Config {
host, host,
name, name,
message_format, message_format,
update_time, update_time,
max_messages, max_messages,
enable_ip_viewing, enable_ip_viewing,
disable_ip_hiding, disable_ip_hiding,
enable_auth, enable_auth,
enable_ssl, enable_ssl,
enable_chunked enable_chunked
}; };
let config_text = serde_yml::to_string(&config).expect("Config save error"); let config_text = serde_yml::to_string(&config).expect("Config save error");
fs::create_dir_all(&path.parent().expect("Config save error")).expect("Config save error"); fs::create_dir_all(&path.parent().expect("Config save error")).expect("Config save error");
fs::write(&path, config_text).expect("Config save error"); fs::write(&path, config_text).expect("Config save error");
println!(); println!();
println!("{}", "Config saved! You can reconfigure it in any moment via `bRAC --configure`".yellow()); println!("{}", "Config saved! You can reconfigure it in any moment via `bRAC --configure`".yellow());
config config
} }
pub fn load_config(path: PathBuf) -> Config { pub fn load_config(path: PathBuf) -> Config {
if !fs::exists(&path).unwrap_or_default() { if !fs::exists(&path).unwrap_or_default() {
let config = configure(path.clone()); let config = configure(path.clone());
thread::sleep(Duration::from_secs(4)); thread::sleep(Duration::from_secs(4));
config config
} else { } else {
let config = &fs::read_to_string(&path).expect("Config load error"); let config = &fs::read_to_string(&path).expect("Config load error");
serde_yml::from_str(config).expect("Config load error") serde_yml::from_str(config).expect("Config load error")
} }
} }
pub fn get_config_path() -> PathBuf { pub fn get_config_path() -> PathBuf {
let mut config_dir = PathBuf::from_str(".").unwrap(); let mut config_dir = PathBuf::from_str(".").unwrap();
#[cfg(all(feature = "homedir", not(target_os = "windows")))] #[cfg(all(feature = "homedir", not(target_os = "windows")))]
if let Some(dir) = { if let Some(dir) = {
let home_dir = { let home_dir = {
use homedir::my_home; use homedir::my_home;
my_home().ok().flatten() my_home().ok().flatten()
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let config_dir = { let config_dir = {
let home_dir = home_dir.map(|o| o.join("bRAC")); let home_dir = home_dir.map(|o| o.join("bRAC"));
home_dir.map(|o| o.join(".config")) home_dir.map(|o| o.join(".config"))
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let config_dir = { let config_dir = {
let home_dir = home_dir.map(|o| o.join("bRAC")); let home_dir = home_dir.map(|o| o.join("bRAC"));
home_dir.map(|o| o.join(".config")) home_dir.map(|o| o.join(".config"))
}; };
config_dir config_dir
} { } {
config_dir = dir; config_dir = dir;
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
if let Some(dir) = { if let Some(dir) = {
env::var("APPDATA") env::var("APPDATA")
.ok() .ok()
.and_then(|o| Some(PathBuf::from_str(&o).ok()?.join("bRAC"))) .and_then(|o| Some(PathBuf::from_str(&o).ok()?.join("bRAC")))
} { } {
config_dir = dir; config_dir = dir;
} }
config_dir.join("config.yml") config_dir.join("config.yml")
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct Args { pub struct Args {
/// Print config path /// Print config path
#[arg(short='p', long)] #[arg(short='p', long)]
pub config_path: bool, pub config_path: bool,
/// Use specified host /// Use specified host
#[arg(short='H', long)] #[arg(short='H', long)]
pub host: Option<String>, pub host: Option<String>,
/// Use specified name /// Use specified name
#[arg(short='n', long)] #[arg(short='n', long)]
pub name: Option<String>, pub name: Option<String>,
/// Use specified message format /// Use specified message format
#[arg(short='F', long)] #[arg(short='F', long)]
pub message_format: Option<String>, pub message_format: Option<String>,
/// Print unformatted messages from chat and exit /// Print unformatted messages from chat and exit
#[arg(short='r', long)] #[arg(short='r', long)]
pub read_messages: bool, pub read_messages: bool,
/// Send unformatted message to chat and exit /// Send unformatted message to chat and exit
#[arg(short='s', long, value_name="MESSAGE")] #[arg(short='s', long, value_name="MESSAGE")]
pub send_message: Option<String>, pub send_message: Option<String>,
/// Disable message formatting and sanitizing /// Disable message formatting and sanitizing
#[arg(short='f', long)] #[arg(short='f', long)]
pub disable_formatting: bool, pub disable_formatting: bool,
/// Disable slash commands /// Disable slash commands
#[arg(short='c', long)] #[arg(short='c', long)]
pub disable_commands: bool, pub disable_commands: bool,
/// Disable ip hiding /// Disable ip hiding
#[arg(short='i', long)] #[arg(short='i', long)]
pub disable_ip_hiding: bool, pub disable_ip_hiding: bool,
/// Enable users IP viewing /// Enable users IP viewing
#[arg(short='v', long)] #[arg(short='v', long)]
pub enable_users_ip_viewing: bool, pub enable_users_ip_viewing: bool,
/// Configure client /// Configure client
#[arg(short='C', long)] #[arg(short='C', long)]
pub configure: bool, pub configure: bool,
/// Enable authentication /// Enable authentication
#[arg(short='a', long)] #[arg(short='a', long)]
pub enable_auth: bool, pub enable_auth: bool,
/// Enable SSL /// Enable SSL
#[arg(short='S', long)] #[arg(short='S', long)]
pub enable_ssl: bool, pub enable_ssl: bool,
/// Enable chunked reading /// Enable chunked reading
#[arg(short='u', long)] #[arg(short='u', long)]
pub enable_chunked: bool, pub enable_chunked: bool,
} }
pub struct Context { pub struct Context {
pub chat: Arc<RwLock<Option<Arc<ChatContext>>>>, pub chat: Arc<RwLock<Option<Arc<ChatContext>>>>,
pub host: String, pub host: String,
pub name: String, pub name: String,
pub disable_formatting: bool, pub disable_formatting: bool,
pub disable_commands: bool, pub disable_commands: bool,
pub disable_hiding_ip: bool, pub disable_hiding_ip: bool,
pub message_format: String, pub message_format: String,
pub update_time: usize, pub update_time: usize,
pub max_messages: usize, pub max_messages: usize,
pub enable_ip_viewing: bool, pub enable_ip_viewing: bool,
pub enable_auth: bool, pub enable_auth: bool,
pub enable_ssl: bool, pub enable_ssl: bool,
pub enable_chunked: bool, pub enable_chunked: bool,
} }
impl Context { impl Context {
pub fn new(config: &Config, args: &Args) -> Context { pub fn new(config: &Config, args: &Args) -> Context {
Context { Context {
chat: Arc::new(RwLock::new(None)), chat: Arc::new(RwLock::new(None)),
message_format: args.message_format.clone().unwrap_or(config.message_format.clone()), message_format: args.message_format.clone().unwrap_or(config.message_format.clone()),
host: args.host.clone().unwrap_or(config.host.clone()), host: args.host.clone().unwrap_or(config.host.clone()),
name: args.name.clone().or(config.name.clone()).unwrap_or_else(|| ask_string("Name", format!("Anon#{:X}", random::<u16>()))), name: args.name.clone().or(config.name.clone()).unwrap_or_else(|| ask_string("Name", format!("Anon#{:X}", random::<u16>()))),
disable_formatting: args.disable_formatting, disable_formatting: args.disable_formatting,
disable_commands: args.disable_commands, disable_commands: args.disable_commands,
disable_hiding_ip: args.disable_ip_hiding, disable_hiding_ip: args.disable_ip_hiding,
update_time: config.update_time, update_time: config.update_time,
max_messages: config.max_messages, max_messages: config.max_messages,
enable_ip_viewing: args.enable_users_ip_viewing || config.enable_ip_viewing, enable_ip_viewing: args.enable_users_ip_viewing || config.enable_ip_viewing,
enable_auth: args.enable_auth || config.enable_auth, enable_auth: args.enable_auth || config.enable_auth,
enable_ssl: args.enable_ssl || config.enable_ssl, enable_ssl: args.enable_ssl || config.enable_ssl,
enable_chunked: args.enable_chunked || config.enable_chunked, enable_chunked: args.enable_chunked || config.enable_chunked,
} }
} }
pub fn chat(&self) -> Arc<ChatContext> { pub fn chat(&self) -> Arc<ChatContext> {
self.chat.read().unwrap().clone().unwrap() self.chat.read().unwrap().clone().unwrap()
} }
} }