customprefix: config-override channel-prefix sigils per tier (customprefix = <tier> <sigil>); PREFIX/NAMES/FJOIN consistent
This commit is contained in:
parent
b4186ae08d
commit
749a1c3b69
6 changed files with 93 additions and 29 deletions
|
|
@ -51,20 +51,22 @@ impl Member {
|
|||
}
|
||||
}
|
||||
|
||||
/// Highest prefix char for NAMES (`""` for a plain member).
|
||||
/// Highest prefix char for NAMES (`""` for a plain member). The sigil per tier is
|
||||
/// config-overridable via [`crate::modules::customprefix`].
|
||||
pub fn prefix_char(&self) -> &'static str {
|
||||
use crate::modules::customprefix::sigil;
|
||||
if self.oprefix {
|
||||
"!"
|
||||
sigil(0)
|
||||
} else if self.owner {
|
||||
"~"
|
||||
sigil(1)
|
||||
} else if self.admin {
|
||||
"&"
|
||||
sigil(2)
|
||||
} else if self.op {
|
||||
"@"
|
||||
sigil(3)
|
||||
} else if self.halfop {
|
||||
"%"
|
||||
sigil(4)
|
||||
} else if self.voice {
|
||||
"+"
|
||||
sigil(5)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
|
@ -85,17 +87,18 @@ impl Member {
|
|||
|
||||
/// Every prefix char this member holds, high→low (for the `multi-prefix` cap).
|
||||
pub fn all_prefixes(&self) -> String {
|
||||
use crate::modules::customprefix::sigil;
|
||||
let mut s = String::new();
|
||||
for (on, c) in [
|
||||
(self.oprefix, '!'),
|
||||
(self.owner, '~'),
|
||||
(self.admin, '&'),
|
||||
(self.op, '@'),
|
||||
(self.halfop, '%'),
|
||||
(self.voice, '+'),
|
||||
for (on, i) in [
|
||||
(self.oprefix, 0),
|
||||
(self.owner, 1),
|
||||
(self.admin, 2),
|
||||
(self.op, 3),
|
||||
(self.halfop, 4),
|
||||
(self.voice, 5),
|
||||
] {
|
||||
if on {
|
||||
s.push(c);
|
||||
s.push_str(sigil(i));
|
||||
}
|
||||
}
|
||||
s
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ impl Ircd {
|
|||
crate::modules::metadata::load(&mut server); // restore channel metadata
|
||||
crate::modules::reputation::load(&mut server); // restore per-IP reputation
|
||||
crate::modules::geoip::init(&mut server); // load the GeoIP database
|
||||
crate::modules::customprefix::init(&server); // load prefix-sigil overrides
|
||||
Ircd {
|
||||
server,
|
||||
commands: command_table(),
|
||||
|
|
|
|||
10
src/link.rs
10
src/link.rs
|
|
@ -1434,14 +1434,8 @@ fn split_member(tok: &str) -> (String, String) {
|
|||
|
||||
/// Map a prefix char to its mode letter (`@` → `o`, …).
|
||||
fn prefix_letter(c: char) -> char {
|
||||
match c {
|
||||
'~' => 'q',
|
||||
'&' => 'a',
|
||||
'@' => 'o',
|
||||
'%' => 'h',
|
||||
'+' => 'v',
|
||||
_ => ' ',
|
||||
}
|
||||
// config-overridable sigils (customprefix); a linked network shares this config
|
||||
crate::modules::customprefix::letter_for_sigil(c)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
67
src/modules/customprefix.rs
Normal file
67
src/modules/customprefix.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//! customprefix — override the *symbol* (sigil) of the channel prefix tiers from
|
||||
//! config, e.g. show `!` for op instead of `@`. One line per tier:
|
||||
//!
|
||||
//! ```text
|
||||
//! customprefix = op * # ops show as *nick, PREFIX advertises it too
|
||||
//! customprefix = voice -
|
||||
//! ```
|
||||
//!
|
||||
//! Tiers: `oper founder admin op halfop voice`. Only the displayed symbol changes —
|
||||
//! the mode letters (`yqaohv`) and ranks stay fixed, so NAMES/WHO, ISUPPORT PREFIX
|
||||
//! and the S2S FJOIN burst stay consistent (a linked network must share this config,
|
||||
//! as with InspIRCd). Loaded once at boot; unset tiers keep their default sigil.
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::server::Server;
|
||||
|
||||
const TIER_NAMES: [&str; 6] = ["oper", "founder", "admin", "op", "halfop", "voice"];
|
||||
const LETTERS: [char; 6] = ['y', 'q', 'a', 'o', 'h', 'v'];
|
||||
const DEFAULT_SIGILS: [&str; 6] = ["!", "~", "&", "@", "%", "+"];
|
||||
|
||||
/// The configured sigils, indexed by tier. Being a `static` its `String`s live for
|
||||
/// the program, so `sigil()` can hand out `&'static str` without leaking.
|
||||
static SIGILS: OnceLock<[String; 6]> = OnceLock::new();
|
||||
|
||||
/// Load the `customprefix` overrides once, at boot.
|
||||
pub fn init(s: &Server) {
|
||||
let mut a: [String; 6] = DEFAULT_SIGILS.map(String::from);
|
||||
for line in s.conf_all("customprefix") {
|
||||
let mut it = line.split_whitespace();
|
||||
if let (Some(name), Some(sym)) = (it.next(), it.next()) {
|
||||
if let Some(i) = TIER_NAMES.iter().position(|t| t.eq_ignore_ascii_case(name)) {
|
||||
if let Some(c) = sym.chars().next() {
|
||||
a[i] = c.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = SIGILS.set(a);
|
||||
}
|
||||
|
||||
/// The sigil for tier `i` (0 = oper … 5 = voice).
|
||||
pub fn sigil(i: usize) -> &'static str {
|
||||
SIGILS
|
||||
.get()
|
||||
.map(|a| a[i].as_str())
|
||||
.unwrap_or(DEFAULT_SIGILS[i])
|
||||
}
|
||||
|
||||
/// The prefix mode letter for a sigil char (used by the FJOIN decode); `' '` if the
|
||||
/// char isn't a prefix sigil.
|
||||
pub fn letter_for_sigil(c: char) -> char {
|
||||
let cs = c.to_string();
|
||||
(0..6)
|
||||
.find(|&i| sigil(i) == cs)
|
||||
.map(|i| LETTERS[i])
|
||||
.unwrap_or(' ')
|
||||
}
|
||||
|
||||
/// The ISUPPORT `PREFIX=(modes)symbols` token; `include_oper` adds the `y` tier
|
||||
/// (operprefix/ojoin).
|
||||
pub fn isupport(include_oper: bool) -> String {
|
||||
let start = if include_oper { 0 } else { 1 };
|
||||
let letters: String = LETTERS[start..].iter().collect();
|
||||
let sigils: String = (start..6).map(sigil).collect();
|
||||
format!("({letters}){sigils}")
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ pub mod conn_waitpong;
|
|||
pub mod connclass;
|
||||
pub mod connectban;
|
||||
pub mod connflood;
|
||||
pub mod customprefix;
|
||||
pub mod customtitle;
|
||||
pub mod dccallow;
|
||||
pub mod denychans;
|
||||
|
|
|
|||
|
|
@ -616,12 +616,10 @@ impl Server {
|
|||
let chathist = crate::modules::chathistory::limit(self);
|
||||
let maxnick = self.conf_num("maxnick", 30usize);
|
||||
let maxchan = self.conf_num("maxchannel", 50usize);
|
||||
// operprefix/ojoin add the server oper prefix `y` (sigil `!`) above owner
|
||||
let prefix = if self.conf_bool("operprefix", false) || self.conf_bool("ojoin", false) {
|
||||
"(yqaohv)!~&@%+"
|
||||
} else {
|
||||
"(qaohv)~&@%+"
|
||||
};
|
||||
// operprefix/ojoin add the server oper prefix `y` above owner; sigils are
|
||||
// config-overridable (see modules::customprefix)
|
||||
let include_oper = self.conf_bool("operprefix", false) || self.conf_bool("ojoin", false);
|
||||
let prefix = crate::modules::customprefix::isupport(include_oper);
|
||||
let mut lines = vec![format!(
|
||||
"CHANTYPES=# PREFIX={prefix} CHANMODES=beIgXw,k,lfjFLHBJdK,ACDGMNOPQRSTUcimnpstuz EXTBAN=,Gbcgjmnrsy WATCH={maxwatch} MONITOR={maxmon} SILENCE={maxsil} CALLERID=g WHOX CHATHISTORY={chathist} MSGREFTYPES=timestamp,msgid UTF8ONLY CASEMAPPING=ascii NICKLEN={maxnick} CHANNELLEN={maxchan} NETWORK={}",
|
||||
self.network
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue