mail4agent_messenger_shell/cli/
send.rs1use crate::{
15 load_env_file, send_sock_path, send_via_socket, MachineClient, SendReply, SendRequest,
16 MAX_SEND_BYTES, STORE_ROOT_ENV,
17};
18use std::io::Read;
19use std::path::PathBuf;
20use std::time::Duration;
21
22const USAGE: &str =
23 "usage: m4a-send --as <your nick> --to <nick> <text...> (text '-' or none: read stdin)";
24
25fn fail(code: i32, message: &str) -> ! {
26 eprintln!("m4a-send: {message}");
27 std::process::exit(code);
28}
29
30pub fn run(args: Vec<String>) {
31 let mut as_nick = None;
32 let mut to = None;
33 let mut words: Vec<String> = Vec::new();
34 let mut args = args.clone().into_iter();
35 while let Some(arg) = args.next() {
36 match arg.as_str() {
37 "--as" => as_nick = args.next(),
38 "--to" => to = args.next(),
39 "-h" | "--help" => {
40 println!("{USAGE}");
41 return;
42 }
43 "--" => words.extend(args.by_ref()),
44 _ => words.push(arg),
45 }
46 }
47 let (Some(as_nick), Some(to)) = (as_nick, to) else {
48 fail(2, USAGE);
49 };
50 let text = if words.is_empty() || words == ["-"] {
51 let mut text = String::new();
52 if std::io::stdin().read_to_string(&mut text).is_err() {
53 fail(2, "stdin is not text");
54 }
55 text.trim_end_matches('\n').to_string()
56 } else {
57 words.join(" ")
58 };
59 if text.trim().is_empty() {
60 fail(2, "text is empty");
61 }
62 if text.len() > MAX_SEND_BYTES {
63 fail(2, &format!("text is longer than {MAX_SEND_BYTES} bytes"));
64 }
65 load_env_file();
66 let get = |key: &str| std::env::var(key).ok().filter(|value| !value.is_empty());
67 let Some(store_root) = get(STORE_ROOT_ENV).map(PathBuf::from) else {
68 fail(1, "M4A_STORE_ROOT is not set (environment or env file)");
69 };
70 let request = SendRequest {
71 as_nick: as_nick.clone(),
72 to: to.clone(),
73 text: text.clone(),
74 };
75 let sock = send_sock_path(get, &store_root);
76 let reply = match send_via_socket(&sock, &request, Duration::from_secs(180)) {
77 Ok(reply) => reply,
78 Err(_) => {
79 let mut client = match MachineClient::from_env_for(&as_nick) {
81 Ok(client) => client,
82 Err(err) => fail(
83 1,
84 &format!("no running client and opening {as_nick} failed: {err}"),
85 ),
86 };
87 client.send_blocking(&as_nick, &to, &text, Duration::from_secs(120))
88 }
89 };
90 print_reply(&reply);
91}
92
93fn print_reply(reply: &SendReply) {
94 if reply.ok {
95 println!(
96 "sent room={} event={}",
97 reply.room.as_deref().unwrap_or(""),
98 reply.event_id.as_deref().unwrap_or("")
99 );
100 } else {
101 fail(1, reply.error.as_deref().unwrap_or("send failed"));
102 }
103}