chanserv: support param mode locks (+f flood, +j joinflood)
Some checks failed
CI / check (push) Has been cancelled

Mode locks were char-only, so Anope FLOOD/JOINFLOOD locks were dropped on
migration. Carry a value per locked param-mode through the event log, MLOCK
parsing/display, drift enforcement, compaction, and the Anope importer, so a
migrated network keeps its flood protection verbatim.
This commit is contained in:
Jean Chevronnet 2026-07-16 15:23:09 +00:00
parent 3f97a66dbb
commit 9d94dd5eb0
No known key found for this signature in database
9 changed files with 168 additions and 26 deletions

View file

@ -408,6 +408,10 @@ pub struct ChannelInfo {
pub lock_on: String,
#[serde(default)]
pub lock_off: String,
// Params for locked +modes that take one (e.g. ('f', "mute:7:8s"), ('j', "10:6")).
// Each char here is also in `lock_on`; applied and re-asserted with its param.
#[serde(default)]
pub lock_params: Vec<(char, String)>,
#[serde(default)]
pub access: Vec<ChanAccess>,
#[serde(default)]
@ -613,6 +617,13 @@ impl ChannelInfo {
s.push('-');
s.push_str(&self.lock_off);
}
// Params for locked +modes that take one, in the order they appear in lock_on.
for ch in self.lock_on.chars() {
if let Some((_, p)) = self.lock_params.iter().find(|(c, _)| *c == ch) {
s.push(' ');
s.push_str(p);
}
}
s
}
@ -647,6 +658,13 @@ impl ChannelInfo {
s.push('-');
s.push_str(&reremove);
}
// A re-asserted param mode (e.g. +f) must carry its locked param.
for ch in readd.chars() {
if let Some((_, p)) = self.lock_params.iter().find(|(c, _)| *c == ch) {
s.push(' ');
s.push_str(p);
}
}
Some(s)
}
}
@ -1087,7 +1105,7 @@ impl Db {
for c in self.channels.values() {
snapshot.push(Event::ChannelRegistered { name: c.name.clone(), founder: c.founder.clone(), ts: c.ts });
if !c.lock_on.is_empty() || !c.lock_off.is_empty() {
snapshot.push(Event::ChannelMlock { name: c.name.clone(), on: c.lock_on.clone(), off: c.lock_off.clone() });
snapshot.push(Event::ChannelMlock { name: c.name.clone(), on: c.lock_on.clone(), off: c.lock_off.clone(), params: c.lock_params.clone() });
}
for a in &c.access {
snapshot.push(Event::ChannelAccessAdd { channel: c.name.clone(), account: a.account.clone(), level: a.level.clone() });