whois: track remote users' umodes from UID; show 335 (is a bot) for remote +B services/bots and 379 modes to opers

This commit is contained in:
Jean Chevronnet 2026-08-15 23:50:07 +00:00
parent 7d57e4ed34
commit 91022cffe8
2 changed files with 22 additions and 3 deletions

View file

@ -135,6 +135,18 @@ impl Command for Whois {
&format!("{} {a} :is logged in as", ru.nick), &format!("{} {a} :is logged in as", ru.nick),
); );
} }
// 335: mark remote bots (+B), e.g. services / BotServ bots
if ru.modes.contains('B') {
s.numeric(uid, RPL_WHOISBOT, &format!("{} :is a bot", ru.nick));
}
// 379: a remote user's modes — opers only (self is impossible here)
if s.is_oper(uid) && !ru.modes.is_empty() {
s.numeric(
uid,
RPL_WHOISMODES,
&format!("{} :is using modes +{}", ru.nick, ru.modes),
);
}
s.numeric( s.numeric(
uid, uid,
RPL_ENDOFWHOIS, RPL_ENDOFWHOIS,

View file

@ -55,9 +55,10 @@ pub struct RemoteUser {
pub host: String, pub host: String,
pub realname: String, pub realname: String,
pub account: Option<String>, pub account: Option<String>,
pub ip: String, // client IP (for network-wide clone limits); "" if a peer omitted it pub ip: String, // client IP (for network-wide clone limits); "" if a peer omitted it
pub sid: String, // origin server id pub modes: String, // user mode letters (no leading '+'); e.g. services wear "iHB"
pub via: Uid, // local link uid it is reached through pub sid: String, // origin server id
pub via: Uid, // local link uid it is reached through
} }
impl RemoteUser { impl RemoteUser {
@ -866,6 +867,11 @@ impl Server {
let host = msg.params[4].clone(); // displayed host let host = msg.params[4].clone(); // displayed host
let ident = msg.params[6].clone(); // displayed ident let ident = msg.params[6].clone(); // displayed ident
let ip = msg.params[7].clone(); let ip = msg.params[7].clone();
let modes = msg
.params
.get(9)
.map(|m| m.trim_start_matches('+').to_string())
.unwrap_or_default();
let realname = msg.params.last().cloned().unwrap_or_default(); let realname = msg.params.last().cloned().unwrap_or_default();
// nick collision: a local holder is killed (both sides do this, so both // nick collision: a local holder is killed (both sides do this, so both
// vanish deterministically); an existing remote holder simply wins. // vanish deterministically); an existing remote holder simply wins.
@ -889,6 +895,7 @@ impl Server {
realname, realname,
account: None, account: None,
ip, ip,
modes,
sid, sid,
via, via,
}, },