OperServ: STATS enforcement overview
STATS reports the live counts OperServ holds — AKILL and SQLINE bans by kind, and services ignores — as a quick at-a-glance check. Read-only, so any operator can run it.
This commit is contained in:
parent
ab5dc2e931
commit
f9aea30f81
3 changed files with 18 additions and 1 deletions
|
|
@ -17,6 +17,8 @@ mod kick;
|
||||||
mod mode;
|
mod mode;
|
||||||
#[path = "ignore.rs"]
|
#[path = "ignore.rs"]
|
||||||
mod ignore;
|
mod ignore;
|
||||||
|
#[path = "stats.rs"]
|
||||||
|
mod stats;
|
||||||
|
|
||||||
pub struct OperServ {
|
pub struct OperServ {
|
||||||
pub uid: String,
|
pub uid: String,
|
||||||
|
|
@ -48,6 +50,7 @@ impl Service for OperServ {
|
||||||
Some(cmd) if cmd.eq_ignore_ascii_case("KICK") => kick::handle(me, from, args, ctx, net),
|
Some(cmd) if cmd.eq_ignore_ascii_case("KICK") => kick::handle(me, from, args, ctx, net),
|
||||||
Some(cmd) if cmd.eq_ignore_ascii_case("MODE") => mode::handle(me, from, args, ctx, net),
|
Some(cmd) if cmd.eq_ignore_ascii_case("MODE") => mode::handle(me, from, args, ctx, net),
|
||||||
Some(cmd) if cmd.eq_ignore_ascii_case("IGNORE") => ignore::handle(me, from, args, ctx, db),
|
Some(cmd) if cmd.eq_ignore_ascii_case("IGNORE") => ignore::handle(me, from, args, ctx, db),
|
||||||
|
Some(cmd) if cmd.eq_ignore_ascii_case("STATS") => stats::handle(me, from, db, ctx),
|
||||||
Some(cmd) if cmd.eq_ignore_ascii_case("HELP") => help(me, from, ctx),
|
Some(cmd) if cmd.eq_ignore_ascii_case("HELP") => help(me, from, ctx),
|
||||||
None => help(me, from, ctx),
|
None => help(me, from, ctx),
|
||||||
Some(other) => ctx.notice(me, from.uid, format!("I don't know \x02{other}\x02. Try \x02HELP\x02.")),
|
Some(other) => ctx.notice(me, from.uid, format!("I don't know \x02{other}\x02. Try \x02HELP\x02.")),
|
||||||
|
|
@ -56,5 +59,5 @@ impl Service for OperServ {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx) {
|
fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx) {
|
||||||
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services).");
|
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services), \x02STATS\x02 (enforcement overview).");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
operserv/src/stats.rs
Normal file
12
operserv/src/stats.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use fedserv_api::{Sender, ServiceCtx, Store};
|
||||||
|
|
||||||
|
// STATS: an at-a-glance summary of the enforcement state OperServ holds — how
|
||||||
|
// many network bans of each kind and how many services ignores are live.
|
||||||
|
// Read-only, so any operator may run it (the module is already oper-gated).
|
||||||
|
pub fn handle(me: &str, from: &Sender, db: &mut dyn Store, ctx: &mut ServiceCtx) {
|
||||||
|
let akills = db.akills();
|
||||||
|
let glines = akills.iter().filter(|a| a.kind == "G").count();
|
||||||
|
let qlines = akills.iter().filter(|a| a.kind == "Q").count();
|
||||||
|
let ignores = db.ignores().len();
|
||||||
|
ctx.notice(me, from.uid, format!("Live network bans: \x02{glines}\x02 AKILL, \x02{qlines}\x02 SQLINE. Services ignores: \x02{ignores}\x02."));
|
||||||
|
}
|
||||||
|
|
@ -3947,6 +3947,8 @@ mod tests {
|
||||||
assert!(out.iter().any(|a| matches!(a, NetAction::AddLine { kind, mask, .. } if kind == "Q" && mask == "*bot*")), "Q-line added: {out:?}");
|
assert!(out.iter().any(|a| matches!(a, NetAction::AddLine { kind, mask, .. } if kind == "Q" && mask == "*bot*")), "Q-line added: {out:?}");
|
||||||
// A rejected @-shaped mask (that belongs to AKILL, not SQLINE).
|
// A rejected @-shaped mask (that belongs to AKILL, not SQLINE).
|
||||||
assert!(os(&mut e, "000AAAAAS", "SQLINE ADD a@b spam").iter().any(|a| matches!(a, NetAction::Notice { text, .. } if text.contains("valid"))), "nick mask rejects user@host");
|
assert!(os(&mut e, "000AAAAAS", "SQLINE ADD a@b spam").iter().any(|a| matches!(a, NetAction::Notice { text, .. } if text.contains("valid"))), "nick mask rejects user@host");
|
||||||
|
// STATS reflects the live ban counts.
|
||||||
|
assert!(os(&mut e, "000AAAAAS", "STATS").iter().any(|a| matches!(a, NetAction::Notice { text, .. } if text.contains("1") && text.contains("SQLINE"))), "stats shows the sqline count");
|
||||||
|
|
||||||
// GLOBAL fans out to every user via the $* server glob.
|
// GLOBAL fans out to every user via the $* server glob.
|
||||||
let out = os(&mut e, "000AAAAAS", "GLOBAL rebooting in 5");
|
let out = os(&mut e, "000AAAAAS", "GLOBAL rebooting in 5");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue