autoop: +w <prefix>:<mask> channel list mode grants status on join

This commit is contained in:
Jean Chevronnet 2026-08-10 08:18:07 +00:00
parent 87e3d5436d
commit 9d84a66437
8 changed files with 70 additions and 3 deletions

47
src/modules/autoop.rs Normal file
View file

@ -0,0 +1,47 @@
//! autoop — the channel list mode `+w <prefix>:<hostmask>` grants a status prefix
//! to matching users the moment they join, e.g. `+w o:*!*@trusted.host` auto-ops
//! them, `+w v:*!*@*.friend` auto-voices. The list lives on the channel (like +b,
//! stored verbatim); this module applies it on join via the server-authority mode
//! path. Reference: InspIRCd's `m_autoop`. Original native Rust.
use crate::channels::glob_match;
use crate::module::Module;
use crate::server::Server;
use crate::Uid;
pub struct AutoOp;
impl Module for AutoOp {
fn name(&self) -> &'static str {
"autoop"
}
fn on_join(&mut self, s: &mut Server, uid: Uid, chan: &str) {
let key = chan.to_ascii_lowercase();
let Some(who) = s.users.get(&uid).map(|u| u.prefix()) else {
return;
};
let Some(nick) = s.users.get(&uid).map(|u| u.nick.clone()) else {
return;
};
// gather the prefix modes this user's mask earns (deduped)
let mut modes = String::new();
if let Some(c) = s.channels.get(&key) {
for e in &c.autoop {
let Some((pfx, mask)) = e.mask.split_once(':') else {
continue;
};
let Some(m) = pfx.chars().next() else { continue };
if "qaohv".contains(m) && !modes.contains(m) && glob_match(mask, &who) {
modes.push(m);
}
}
}
if modes.is_empty() {
return;
}
// grant them under server authority (the joiner can't op themselves)
let args: Vec<String> = modes.chars().map(|_| nick.clone()).collect();
crate::coremods::core_mode::svs_set_chan_modes(s, chan, &format!("+{modes}"), &args);
}
}

View file

@ -6,6 +6,7 @@
pub mod account_registration;
pub mod antimixedutf8;
pub mod antirandom;
pub mod autoop;
pub mod banredirect;
pub mod blockamsg;
pub mod channames;
@ -90,6 +91,7 @@ pub fn default_modules() -> Vec<Box<dyn Module>> {
Box::new(maphide::MapHide),
Box::new(dccallow::DccAllow),
Box::new(solvemsg::SolveMsg),
Box::new(autoop::AutoOp),
]
}