diff --git a/src/engine/kicker.rs b/src/engine/kicker.rs index a2b3461..9992630 100644 --- a/src/engine/kicker.rs +++ b/src/engine/kicker.rs @@ -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) { diff --git a/src/engine/mod.rs b/src/engine/mod.rs index c2a7108..c1dc2c8 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -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, } @@ -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() }); }