mail4agent_messenger_shell/cli/
node_client.rs1use crate::{
17 load_env_file_named, NodeClient, ShellError, LEADER_SOCK_ENV,
18};
19use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
20
21fn env_secs(key: &str, default: u64) -> u64 {
22 std::env::var(key)
23 .ok()
24 .and_then(|value| value.parse().ok())
25 .unwrap_or(default)
26}
27
28fn now_ms() -> i64 {
29 SystemTime::now()
30 .duration_since(UNIX_EPOCH)
31 .map(|elapsed| elapsed.as_millis() as i64)
32 .unwrap_or(0)
33}
34
35pub fn run(_args: Vec<String>) {
36 load_env_file_named("node-client.env");
37
38 let mut client = match NodeClient::from_env() {
39 Ok(client) => client,
40 Err(err) => fail(&err),
41 };
42
43 match client.listen_for_sends_from_env() {
44 Ok(path) => println!("send socket {}", path.display()),
45 Err(err) => eprintln!("send socket not opened: {err}"),
46 }
47
48 let nick = client.nick().unwrap_or("(unknown)");
49 println!("node open ok nick={nick}");
50 println!("wake=acp sock_set=1");
51 println!("push+drive loop; replies via m4a-send on the send socket");
52
53 let drive_secs = env_secs("M4A_DRIVE_SECS", 15);
54 let run_secs = env_secs("M4A_RUN_SECS", 0);
55 let started = Instant::now();
56 let mut seen_wakes = 0usize;
57
58 loop {
59 let report = client.tick(now_ms(), drive_secs);
60 for event_id in &report.pushed {
61 println!("push event={event_id}");
62 }
63 for room in &report.joined {
64 println!("joined room={room}");
65 }
66 for (from, to, reply) in &report.sent {
67 match (&reply.event_id, &reply.error) {
68 (_, Some(err)) => println!("send {from} -> {to} failed: {err}"),
69 (Some(event_id), None) => println!("send {from} -> {to} event={event_id}"),
70 (None, None) => println!("send {from} -> {to} ok"),
71 }
72 }
73 for err in &report.errors {
74 eprintln!("drive: {err}");
75 }
76 if let Some(note) = &report.wake_note {
77 if seen_wakes == 0 || client.wake_note() != Some(note.as_str()) {
79 eprintln!("wake note: {note}");
80 }
81 seen_wakes = seen_wakes.saturating_add(1);
82 }
83 if run_secs > 0 && started.elapsed().as_secs() >= run_secs {
84 println!("run time reached; exiting");
85 break;
86 }
87 std::thread::sleep(Duration::from_millis(250));
88 }
89}
90
91fn fail(err: &ShellError) -> ! {
92 eprintln!("node open failed: {err}");
94 if matches!(err, ShellError::NodeRoutine) {
95 eprintln!(
96 "node client refuses M4A_ROUTINE_URL / M4A_ROUTINE_BEARER; webhooks are for m4a-web-client only"
97 );
98 }
99 let _ = LEADER_SOCK_ENV;
100 std::process::exit(1);
101}