BotServ kickers: FLOOD and REPEAT
KICK <#channel> FLOOD ON [lines [secs]] kicks a user who sends too many lines too fast (default 6 in 10s); KICK <#channel> REPEAT ON [times] kicks one who repeats the same line (default 3, case-insensitive). These are stateful, so the engine keeps per-channel/per-user counters in an ephemeral map that is never event-logged. Hot-path conscious: a channel with no kickers costs one lookup and returns; a returning speaker allocates nothing (get_mut, not entry); repeat comparison is an allocation-free case-folding 64-bit hash. Counters are dropped when a user parts or quits, so the map stays bounded. Flood time is injectable for deterministic tests.
This commit is contained in:
parent
ed7da9c713
commit
43def8ee23
4 changed files with 264 additions and 12 deletions
|
|
@ -5,7 +5,7 @@ use fedserv_api::{Kicker, Sender, ServiceCtx, Store};
|
|||
// and the exemption DONTKICKOPS. Founder-or-admin.
|
||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
let (Some(&chan), Some(kind)) = (args.get(1), args.get(2)) else {
|
||||
ctx.notice(me, from.uid, "Syntax: KICK <#channel> <CAPS|BOLDS|COLORS|UNDERLINES|REVERSES|ITALICS|DONTKICKOPS> <ON|OFF> [params]");
|
||||
ctx.notice(me, from.uid, "Syntax: KICK <#channel> <CAPS|FLOOD|REPEAT|BOLDS|COLORS|UNDERLINES|REVERSES|ITALICS|DONTKICKOPS> <ON|OFF> [params]");
|
||||
return;
|
||||
};
|
||||
if !super::require_channel_admin(me, from, chan, ctx, db) {
|
||||
|
|
@ -20,7 +20,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
};
|
||||
|
||||
// CAPS carries optional length/percentage thresholds when turned on.
|
||||
// CAPS/FLOOD/REPEAT carry optional thresholds when turned on.
|
||||
if kind.eq_ignore_ascii_case("CAPS") {
|
||||
if on {
|
||||
let min = args.get(4).and_then(|s| s.parse::<u16>().ok()).unwrap_or(0);
|
||||
|
|
@ -34,6 +34,31 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
return;
|
||||
}
|
||||
if kind.eq_ignore_ascii_case("FLOOD") {
|
||||
if on {
|
||||
let lines = args.get(4).and_then(|s| s.parse::<u16>().ok()).unwrap_or(0);
|
||||
let secs = args.get(5).and_then(|s| s.parse::<u16>().ok()).unwrap_or(0);
|
||||
match db.set_flood_kicker(chan, lines, secs) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("The bot will now kick for \x02flooding\x02 in \x02{chan}\x02.")),
|
||||
Err(_) => reg_error(me, from, chan, ctx),
|
||||
}
|
||||
} else {
|
||||
toggle(me, from, chan, Kicker::Flood, false, ctx, db);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if kind.eq_ignore_ascii_case("REPEAT") {
|
||||
if on {
|
||||
let times = args.get(4).and_then(|s| s.parse::<u16>().ok()).unwrap_or(0);
|
||||
match db.set_repeat_kicker(chan, times) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("The bot will now kick for \x02repeating\x02 in \x02{chan}\x02.")),
|
||||
Err(_) => reg_error(me, from, chan, ctx),
|
||||
}
|
||||
} else {
|
||||
toggle(me, from, chan, Kicker::Repeat, false, ctx, db);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let kicker = match kind.to_ascii_uppercase().as_str() {
|
||||
"BOLDS" => Kicker::Bolds,
|
||||
|
|
@ -43,7 +68,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
"ITALICS" => Kicker::Italics,
|
||||
"DONTKICKOPS" => Kicker::DontKickOps,
|
||||
other => {
|
||||
ctx.notice(me, from.uid, format!("Unknown kicker \x02{other}\x02. Try CAPS, BOLDS, COLORS, UNDERLINES, REVERSES, ITALICS or DONTKICKOPS."));
|
||||
ctx.notice(me, from.uid, format!("Unknown kicker \x02{other}\x02. Try CAPS, FLOOD, REPEAT, BOLDS, COLORS, UNDERLINES, REVERSES, ITALICS or DONTKICKOPS."));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
@ -58,6 +83,8 @@ fn label(kicker: Kicker) -> &'static str {
|
|||
Kicker::Underlines => "underlines",
|
||||
Kicker::Reverses => "reverses",
|
||||
Kicker::Italics => "italics",
|
||||
Kicker::Flood => "flood",
|
||||
Kicker::Repeat => "repeat",
|
||||
Kicker::DontKickOps => "dontkickops",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue