From f6127411d71951ae000ebd5b0c90047e500fc728 Mon Sep 17 00:00:00 2001 From: michel Date: Sun, 19 Jul 2026 13:32:53 +0200 Subject: [PATCH] feat(cli): add 'message' subcommand to send private messages `soulseek-rs message ` connects, logs in, sends a private message and exits. Smoke-tested against a local soulfind server. --- soulseek-rs/src/cli.rs | 9 +++++++++ soulseek-rs/src/main.rs | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/soulseek-rs/src/cli.rs b/soulseek-rs/src/cli.rs index 6cf4336..0c1d3cf 100644 --- a/soulseek-rs/src/cli.rs +++ b/soulseek-rs/src/cli.rs @@ -89,6 +89,15 @@ pub enum Commands { )] max_concurrent_downloads: usize, }, + + /// Send a private message to another user + Message { + /// Username of the recipient + username: String, + + /// Message text to send + message: String, + }, } pub fn parse_server_address(server: &str) -> color_eyre::Result<(String, u16)> { diff --git a/soulseek-rs/src/main.rs b/soulseek-rs/src/main.rs index f1bc0c6..0037de5 100644 --- a/soulseek-rs/src/main.rs +++ b/soulseek-rs/src/main.rs @@ -89,6 +89,21 @@ fn main() -> Result<()> { }; search_and_download(config) } + Some(Commands::Message { + username: recipient, + message, + }) => send_private_message( + &ClientSettings { + username, + password, + server_address: PeerAddress::new(server_host, server_port), + enable_listen: !cli.disable_listener, + listen_port: cli.listener_port, + shared_directory, + }, + &recipient, + &message, + ), None => { use ratatui::crossterm::{ event::EnableMouseCapture, @@ -139,6 +154,34 @@ fn main() -> Result<()> { } } +fn send_private_message( + settings: &ClientSettings, + recipient: &str, + message: &str, +) -> Result<()> { + let mut client = Client::with_settings(settings.clone()); + client + .connect() + .map_err(|e| color_eyre::eyre::eyre!("Failed to connect: {}", e))?; + if !client + .login() + .map_err(|e| color_eyre::eyre::eyre!("Failed to login: {}", e))? + { + return Err(color_eyre::eyre::eyre!("Login rejected by server")); + } + + client + .send_private_message(recipient, message) + .map_err(|e| color_eyre::eyre::eyre!("Failed to send message: {}", e))?; + + // The send is dispatched asynchronously; give the server actor a moment to + // flush it to the socket before we drop the client and exit. + std::thread::sleep(Duration::from_millis(500)); + + println!("✉️ Message sent to {recipient}"); + Ok(()) +} + fn search_and_download(config: SearchConfig) -> Result<()> { if config.verbose > 0 { println!(