Add centralized HELP with per-command subcommands

New echo-api primitive: a HelpEntry table plus a shared help() renderer.
HELP lists a service's commands; HELP <command> prints that command's
detail. Every service routes its HELP arm through the one renderer, so
the shape is identical across the network. Wired into NickServ and the
six newer services so far.
This commit is contained in:
Jean Chevronnet 2026-07-14 17:12:14 +00:00
parent 973d2826de
commit c9e392630b
No known key found for this signature in database
8 changed files with 137 additions and 14 deletions

View file

@ -1130,6 +1130,37 @@ fn glob_match(pattern: &str, text: &str) -> bool {
pi == p.len()
}
// One command's help: its name, a one-line summary shown in the command list,
// and the detail shown for `HELP <command>` (newline-separated lines).
pub struct HelpEntry {
pub cmd: &'static str,
pub summary: &'static str,
pub detail: &'static str,
}
// Centralized HELP for a service: `HELP` lists the commands, `HELP <command>`
// prints that command's detail. Every service routes its HELP command here, so
// the shape is identical across every service on the network.
pub fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx, blurb: &str, topics: &[HelpEntry], topic: Option<&str>) {
match topic {
None => {
ctx.notice(me, from.uid, blurb);
for e in topics {
ctx.notice(me, from.uid, format!(" \x02{}\x02 {}", e.cmd, e.summary));
}
ctx.notice(me, from.uid, "Type \x02HELP <command>\x02 for detail on one command.");
}
Some(t) => match topics.iter().find(|e| e.cmd.eq_ignore_ascii_case(t)) {
Some(e) => {
for line in e.detail.lines() {
ctx.notice(me, from.uid, line);
}
}
None => ctx.notice(me, from.uid, format!("No help for \x02{}\x02. Type \x02HELP\x02 for the command list.", t.to_ascii_uppercase())),
},
}
}
// Format a Unix timestamp (seconds) as "YYYY-MM-DD HH:MM:SS UTC", using Howard
// Hinnant's civil-from-days algorithm so no date crate is needed.
pub fn human_time(ts: u64) -> String {