botserv: op assigned bots on join; statserv: persist counters across restart
Some checks failed
CI / check (push) Failing after 3m48s

Problem 1: an assigned bot joined via IJOIN with no status mode, so it had no
+o. Join it with the 4-param IJOIN form (ts 1, mode o) so it's opped, matching
Anope's default bot modes (+o), and exempt bots from SECUREOPS/RESTRICTED so the
op is never stripped.

Problem 2: the shared stat counters lived only in memory, so a restart wiped
them. Snapshot them to the log (StatsSet event, folded into NetData + compaction)
periodically and on shutdown, and reseed the live registry from the log at
startup. Live gauges (online counts) stay re-derived, not stored.
This commit is contained in:
Jean Chevronnet 2026-07-16 17:32:20 +00:00
parent fd455922a5
commit 8a9b6a37ca
No known key found for this signature in database
10 changed files with 85 additions and 10 deletions

View file

@ -317,9 +317,12 @@ impl Protocol for InspIrcd {
NetAction::QuitUser { uid, reason } => vec![format!(":{} QUIT :{}", uid, reason)],
NetAction::ServiceJoin { uid, channel } => {
// IJOIN needs a membid (`IJOIN <chan> <membid>`); without it the
// ircd rejects the whole link with "Insufficient parameters".
// ircd rejects the whole link with "Insufficient parameters". The
// trailing "1 o" is the 4-param form (ts, status modes): the bot
// joins opped, matching Anope's default bot modes (+o). ts 1 keeps
// it below the channel TS so the op is always applied.
self.membid += 1;
vec![format!(":{} IJOIN {} {}", uid, channel, self.membid)]
vec![format!(":{} IJOIN {} {} 1 o", uid, channel, self.membid)]
}
NetAction::ServicePart { uid, channel } => vec![format!(":{} PART {}", uid, channel)],
// ENCAP the target's server: CHGHOST <uid> <newhost>, to set a vhost.
@ -681,9 +684,9 @@ mod tests {
fn service_join_ijoin_includes_membid() {
let mut p = proto();
let a = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#taverne".into() });
assert_eq!(a, vec![":00DB00000 IJOIN #taverne 1".to_string()]);
assert_eq!(a, vec![":00DB00000 IJOIN #taverne 1 1 o".to_string()], "joins opped with a membid");
// membid is monotonic across joins
let b = p.serialize(&NetAction::ServiceJoin { uid: "00DB00000".into(), channel: "#quizz".into() });
assert_eq!(b, vec![":00DB00000 IJOIN #quizz 2".to_string()]);
assert_eq!(b, vec![":00DB00000 IJOIN #quizz 2 1 o".to_string()]);
}
}