Re-check protection in the enforce sweep and key the kicker cache by registration
All checks were successful
CI / check (push) Successful in 5m19s

This commit is contained in:
Jean Chevronnet 2026-07-19 19:19:20 +00:00
parent 118782a4bf
commit 071380efae
No known key found for this signature in database
2 changed files with 18 additions and 11 deletions

View file

@ -30,12 +30,13 @@ impl Engine {
// list's revision changes).
let mut reason: Option<&'static str> = k.violation(text);
if reason.is_none() && k.badwords && !c.badwords.is_empty() {
let rev = c.badwords_rev;
if self.badword_cache.get(channel).map(|cb| cb.rev) != Some(rev) {
let (rev, ts) = (c.badwords_rev, c.ts);
let key = channel.to_ascii_lowercase();
if self.badword_cache.get(&key).map(|cb| (cb.rev, cb.ts)) != Some((rev, ts)) {
let set = db::build_badword_set(&c.badwords);
self.badword_cache.insert(channel.to_string(), CachedBadwords { rev, set });
self.badword_cache.insert(key.clone(), CachedBadwords { rev, ts, set });
}
if self.badword_cache.get(channel).unwrap().set.is_match(text) {
if self.badword_cache.get(&key).unwrap().set.is_match(text) {
reason = Some("Watch your language!");
}
}
@ -127,8 +128,9 @@ impl Engine {
}
let bot = c.assigned_bot.as_deref()?;
let botuid = self.network.uid_by_nick(bot)?.to_string();
let rev = c.triggers_rev;
if self.trigger_cache.get(channel).map(|ct| ct.rev) != Some(rev) {
let (rev, ts) = (c.triggers_rev, c.ts);
let key = channel.to_ascii_lowercase();
if self.trigger_cache.get(&key).map(|ct| (ct.rev, ct.ts)) != Some((rev, ts)) {
let entries = c
.triggers
.iter()
@ -141,14 +143,14 @@ impl Engine {
.map(|re| TriggerRt { re, response: t.response.clone(), cooldown: t.cooldown, last_fired: 0 })
})
.collect();
self.trigger_cache.insert(channel.to_string(), CachedTriggers { rev, entries });
self.trigger_cache.insert(key.clone(), CachedTriggers { rev, ts, entries });
}
let now = self.now_secs();
let nick = self.network.nick_of(from).unwrap_or(from).to_string();
// First matching trigger wins; $1..$9 fill from capture groups, $nick from
// the speaker. A trigger still cooling down suppresses the response.
let cached = self.trigger_cache.get_mut(channel).unwrap();
let cached = self.trigger_cache.get_mut(&key).unwrap();
let mut response = None;
for e in cached.entries.iter_mut() {
if let Some(caps) = e.re.captures(text) {

View file

@ -171,11 +171,13 @@ const ENFORCE_GRACE: u64 = 60;
struct CachedBadwords {
rev: u64,
ts: u64, // channel registration ts, so a drop+re-register (rev resets to 0) can't alias
set: regex::RegexSet,
}
struct CachedTriggers {
rev: u64,
ts: u64, // see CachedBadwords.ts
entries: Vec<TriggerRt>,
}
@ -996,7 +998,11 @@ impl Engine {
let p = self.pending_enforce.remove(i);
let still_there = self.network.uid_by_nick(&p.nick) == Some(p.uid.as_str());
let identified = self.network.account_of(&p.uid) == self.db.resolve_account(&p.nick);
if still_there && !identified {
// Re-check protection at fire time: the owner may have turned KILL
// off during the grace window, in which case don't rename.
let account = self.db.resolve_account(&p.nick).map(str::to_string);
let wants = account.as_deref().is_some_and(|a| self.db.account_wants_protect(a));
if still_there && !identified && wants {
fire.push(p.uid);
}
} else {
@ -1004,8 +1010,7 @@ impl Engine {
}
}
for uid in fire {
let guest = format!("{}{}", self.guest_nick, self.enforce_seq);
self.enforce_seq = self.enforce_seq.wrapping_add(1);
let guest = echo_api::next_guest_nick(&self.guest_nick, &mut self.enforce_seq, &self.network, &self.db);
if let Some(ns) = self.nick_service.clone() {
self.emit_irc(NetAction::Notice { from: ns, to: uid.clone(), text: "You didn't identify in time; you've been renamed.".to_string() });
}