Harden votekick: dedup votes, require member targets, fix trigger refs
This commit is contained in:
parent
a7f13098e3
commit
e4a2e76903
2 changed files with 14 additions and 3 deletions
|
|
@ -157,7 +157,8 @@ impl Engine {
|
||||||
}
|
}
|
||||||
e.last_fired = now;
|
e.last_fired = now;
|
||||||
let mut resp = e.response.clone();
|
let mut resp = e.response.clone();
|
||||||
for i in 1..caps.len() {
|
// High group numbers first, so `$1` doesn't rewrite the prefix of `$10`.
|
||||||
|
for i in (1..caps.len()).rev() {
|
||||||
resp = resp.replace(&format!("${i}"), caps.get(i).map_or("", |m| m.as_str()));
|
resp = resp.replace(&format!("${i}"), caps.get(i).map_or("", |m| m.as_str()));
|
||||||
}
|
}
|
||||||
response = Some(resp.replace("$nick", &nick));
|
response = Some(resp.replace("$nick", &nick));
|
||||||
|
|
@ -220,8 +221,13 @@ impl Engine {
|
||||||
let (Some(botnick), true) = (botnick, threshold > 0) else { return None };
|
let (Some(botnick), true) = (botnick, threshold > 0) else { return None };
|
||||||
let botuid = self.network.uid_by_nick(&botnick).map(str::to_string)?;
|
let botuid = self.network.uid_by_nick(&botnick).map(str::to_string)?;
|
||||||
let Some(target_uid) = self.network.uid_by_nick(target_nick).map(str::to_string) else {
|
let Some(target_uid) = self.network.uid_by_nick(target_nick).map(str::to_string) else {
|
||||||
return Some(vec![NetAction::Privmsg { from: botuid, to: channel.to_string(), text: format!("There's no \x02{target_nick}\x02 here to vote on.") }]);
|
return None; // unknown target: treat as ordinary text so the bot can't be spammed into flooding, and the flood kicker still sees it
|
||||||
};
|
};
|
||||||
|
// Only a current member can be voted out — otherwise `!voteban <online nick
|
||||||
|
// from another channel>` would ban someone who never visited.
|
||||||
|
if !self.network.channel_members(channel).any(|u| u == target_uid.as_str()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let target_display = self.network.nick_of(&target_uid).unwrap_or(target_nick).to_string();
|
let target_display = self.network.nick_of(&target_uid).unwrap_or(target_nick).to_string();
|
||||||
// Never let the community vote out a service bot, an oper, or a user with
|
// Never let the community vote out a service bot, an oper, or a user with
|
||||||
// channel op-access — moderation authority isn't a valid vote target.
|
// channel op-access — moderation authority isn't a valid vote target.
|
||||||
|
|
@ -242,7 +248,11 @@ impl Engine {
|
||||||
|
|
||||||
let vs = self.votes.entry(key.clone()).or_insert(VoteState { ban, voters: std::collections::HashSet::new(), started: now });
|
let vs = self.votes.entry(key.clone()).or_insert(VoteState { ban, voters: std::collections::HashSet::new(), started: now });
|
||||||
vs.ban = ban;
|
vs.ban = ban;
|
||||||
vs.voters.insert(from.to_string());
|
if !vs.voters.insert(from.to_string()) {
|
||||||
|
// Already counted this voter: don't re-announce (which let one user spam
|
||||||
|
// the bot into flooding), and let the line fall through to the flood kicker.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let count = vs.voters.len() as u16;
|
let count = vs.voters.len() as u16;
|
||||||
|
|
||||||
if count < threshold {
|
if count < threshold {
|
||||||
|
|
|
||||||
|
|
@ -2898,6 +2898,7 @@
|
||||||
bs(&mut e, "SET #c VOTEKICK 2");
|
bs(&mut e, "SET #c VOTEKICK 2");
|
||||||
e.handle(NetEvent::UserConnect { uid: "000AAAAAV".into(), nick: "victim".into(), host: "h".into() , ip: "0.0.0.0".into() });
|
e.handle(NetEvent::UserConnect { uid: "000AAAAAV".into(), nick: "victim".into(), host: "h".into() , ip: "0.0.0.0".into() });
|
||||||
e.handle(NetEvent::UserConnect { uid: "000AAAAAC".into(), nick: "carol".into(), host: "h".into() , ip: "0.0.0.0".into() });
|
e.handle(NetEvent::UserConnect { uid: "000AAAAAC".into(), nick: "carol".into(), host: "h".into() , ip: "0.0.0.0".into() });
|
||||||
|
e.handle(NetEvent::Join { uid: "000AAAAAV".into(), channel: "#c".into(), op: false }); // the target must be in the channel
|
||||||
let kicked = |out: &[NetAction]| out.iter().any(|a| matches!(a, NetAction::Kick { from, uid, .. } if from.starts_with("42SB") && uid == "000AAAAAV"));
|
let kicked = |out: &[NetAction]| out.iter().any(|a| matches!(a, NetAction::Kick { from, uid, .. } if from.starts_with("42SB") && uid == "000AAAAAV"));
|
||||||
|
|
||||||
// First voter, then the same voter again (deduped) — no kick yet.
|
// First voter, then the same voter again (deduped) — no kick yet.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue