modules: draft/event-playback — record JOIN/PART/QUIT/NICK/MODE/TOPIC/KICK into CHATHISTORY, replayed only for cap clients (event_playback config, default on)
This commit is contained in:
parent
9dcc269f45
commit
6bb8ccd8ee
8 changed files with 179 additions and 19 deletions
61
src/modules/event_playback.rs
Normal file
61
src/modules/event_playback.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//! draft/event-playback — replay channel *events* (JOIN/PART/QUIT/NICK/MODE/TOPIC/
|
||||
//! KICK) in CHATHISTORY, interleaved with messages, for clients that negotiate the
|
||||
//! cap. Without it, history stays messages-only (the classic behaviour).
|
||||
//!
|
||||
//! Events are recorded into the same per-conversation ring as messages
|
||||
//! ([`crate::modules::chathistory::record_event`]) and replayed verbatim; the CHATHISTORY
|
||||
//! command and the `+H` join-backlog filter them out for clients lacking the cap.
|
||||
//! JOIN/PART/QUIT are captured here through the module lifecycle hooks; NICK/MODE/TOPIC/
|
||||
//! KICK have no hook, so they call `record_event` directly from their command paths.
|
||||
//! The whole feature is gated by `event_playback` (default on) inside `record_event`.
|
||||
|
||||
use crate::modules::chathistory::record_event;
|
||||
use crate::module::Module;
|
||||
use crate::server::Server;
|
||||
use crate::Uid;
|
||||
|
||||
pub struct EventPlayback;
|
||||
|
||||
impl Module for EventPlayback {
|
||||
fn name(&self) -> &'static str {
|
||||
"event-playback"
|
||||
}
|
||||
|
||||
fn on_join(&mut self, srv: &mut Server, uid: Uid, chan: &str) {
|
||||
let Some(prefix) = srv.users.get(&uid).map(|u| u.prefix()) else {
|
||||
return;
|
||||
};
|
||||
let key = chan.to_ascii_lowercase();
|
||||
record_event(srv, &key, &format!(":{prefix} JOIN {chan}"));
|
||||
}
|
||||
|
||||
fn on_part(&mut self, srv: &mut Server, uid: Uid, chan: &str, reason: &str) {
|
||||
let Some(prefix) = srv.users.get(&uid).map(|u| u.prefix()) else {
|
||||
return;
|
||||
};
|
||||
let key = chan.to_ascii_lowercase();
|
||||
let line = if reason.is_empty() {
|
||||
format!(":{prefix} PART {chan}")
|
||||
} else {
|
||||
format!(":{prefix} PART {chan} :{reason}")
|
||||
};
|
||||
record_event(srv, &key, &line);
|
||||
}
|
||||
|
||||
fn on_user_quit(&mut self, srv: &mut Server, uid: Uid, reason: &str) {
|
||||
// A QUIT isn't channel-scoped on the wire, so mirror it into every channel the
|
||||
// user still shares — that's where a scrolling client expects to see it.
|
||||
let Some((prefix, chans)) = srv.users.get(&uid).map(|u| {
|
||||
(
|
||||
u.prefix(),
|
||||
u.channels.iter().cloned().collect::<Vec<String>>(),
|
||||
)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
let line = format!(":{prefix} QUIT :{reason}");
|
||||
for key in chans {
|
||||
record_event(srv, &key, &line);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue