105 lines
3.8 KiB
Markdown
105 lines
3.8 KiB
Markdown
# Writing modes
|
|
|
|
Modes are stateless `&'static` handler objects. The `MODE` parser dispatches each
|
|
letter to its handler, so adding a mode is a new handler plus one line in a table —
|
|
you never touch the parser. See the [mode reference](../modes.md) for the existing
|
|
letters (don't collide).
|
|
|
|
## User modes — the `UserMode` trait
|
|
|
|
```rust
|
|
pub trait UserMode: Sync {
|
|
fn letter(&self) -> char;
|
|
/// Apply +/- to the user; return true if it took effect (so the change is echoed).
|
|
fn apply(&self, s: &mut Server, uid: Uid, adding: bool) -> bool;
|
|
}
|
|
```
|
|
|
|
A user-mode handler is typically a zero-sized struct. Store the actual flag on the
|
|
user (existing flags live in `UserFlags`; module-specific state goes in
|
|
[`User.ext`](server.md#per-entity-state)).
|
|
|
|
```rust
|
|
struct BotMode;
|
|
impl UserMode for BotMode {
|
|
fn letter(&self) -> char { 'B' }
|
|
fn apply(&self, s: &mut Server, uid: Uid, adding: bool) -> bool {
|
|
match s.users.get_mut(&uid) {
|
|
Some(u) => { u.flags.bot = adding; true }
|
|
None => false,
|
|
}
|
|
}
|
|
}
|
|
static BOT: BotMode = BotMode;
|
|
```
|
|
|
|
Register it in `src/mode.rs` by adding `&BOT` to the `USER_MODES` slice.
|
|
|
|
## Channel modes — the `ChanMode` trait
|
|
|
|
```rust
|
|
pub trait ChanMode: Sync {
|
|
fn letter(&self) -> char;
|
|
/// Whether this sign consumes an argument (taken only if one remains).
|
|
fn wants_param(&self, adding: bool) -> bool;
|
|
/// A list mode (like +b): a no-arg query is just viewing, so it needn't
|
|
/// require operator rank. Default false.
|
|
fn is_list(&self) -> bool { false }
|
|
/// Apply +/- to channel `key` (display name `chan`) on behalf of `uid`.
|
|
fn apply(&self, s: &mut Server, chan: &str, key: &str, uid: Uid,
|
|
adding: bool, param: Option<&str>) -> Applied;
|
|
}
|
|
|
|
pub enum Applied {
|
|
No, // nothing to echo (no-op, rejected, or a list query)
|
|
Yes(Option<String>), // echo the change; Some(param) appends a parameter
|
|
}
|
|
```
|
|
|
|
Key points:
|
|
|
|
- `key` is the channel's lookup key (lower-cased name); `chan` is the display name.
|
|
Look the channel up with `s.channels.get_mut(key)`.
|
|
- `wants_param` controls whether the parser hands you a `param`. Return `true` for
|
|
a mode that takes an argument (a key, a limit, a mask); parameterless flags
|
|
return `false`.
|
|
- Return `Applied::Yes(None)` for a flag that flipped, `Applied::Yes(Some(p))` to
|
|
echo a parameter (e.g. the limit you set), or `Applied::No` if nothing changed
|
|
or you rejected it.
|
|
- **Enforce permissions yourself.** For an ordinary settable mode, check the
|
|
caller's rank (`s.rank(uid, key) >= RANK_OP`) before applying and reply/refuse
|
|
if they're not allowed.
|
|
|
|
```rust
|
|
struct NoCtcp;
|
|
impl ChanMode for NoCtcp {
|
|
fn letter(&self) -> char { 'C' }
|
|
fn wants_param(&self, _adding: bool) -> bool { false }
|
|
fn apply(&self, s: &mut Server, _chan: &str, key: &str, uid: Uid,
|
|
adding: bool, _param: Option<&str>) -> Applied {
|
|
if s.rank(uid, key) < crate::channels::RANK_OP { return Applied::No; }
|
|
match s.channels.get_mut(key) {
|
|
Some(c) if c.modes.no_ctcp != adding => { c.modes.no_ctcp = adding; Applied::Yes(None) }
|
|
_ => Applied::No,
|
|
}
|
|
}
|
|
}
|
|
static NO_CTCP: NoCtcp = NoCtcp;
|
|
```
|
|
|
|
Register it in `src/mode.rs` by adding `&NO_CTCP` to the `CHAN_MODES` slice, and
|
|
add its letter to the `CHANMODES=` group in the ISUPPORT string (`server.rs`) so
|
|
clients learn about it.
|
|
|
|
## List modes
|
|
|
|
Set `is_list() -> true` and `wants_param() -> true`. A no-argument use is a list
|
|
query (rank-free); an argument adds/removes an entry. The built-in list modes
|
|
(`+b`, `+e`, `+I`, …) share a common `ListMode` handler parameterised by kind —
|
|
follow that pattern for a new list.
|
|
|
|
## Named-mode access
|
|
|
|
Every channel mode is also reachable by long name through the `PROP` command
|
|
(`namedmodes`) without extra work on your part — the mapping is derived from the
|
|
registered handlers.
|