BotServ: TRIGGER — regex auto-responses

TRIGGER <#channel> ADD <regex>|<response> | DEL <num> | LIST | CLEAR: when
a channel line matches <regex>, the assigned bot says <response> ($nick
becomes the speaker's nick). Only the first matching trigger fires, and a
line the bot just kicked for gets no response.

Same engine machinery as badwords: patterns compile into one RegexSet per
channel (linear-time, ReDoS-safe), cached and rebuilt only on a revision
change. Patterns validated + size-limited at add time.
This commit is contained in:
Jean Chevronnet 2026-07-13 18:03:46 +00:00
parent 47b1fd351e
commit 929a4cdd92
No known key found for this signature in database
6 changed files with 237 additions and 4 deletions

View file

@ -336,6 +336,12 @@ pub struct MemoView {
}
// A service bot registered with BotServ.
#[derive(Debug, Clone)]
pub struct TriggerView {
pub pattern: String,
pub response: String,
}
#[derive(Debug, Clone)]
pub struct BotView {
pub nick: String,
@ -578,6 +584,11 @@ pub trait Store {
fn kicker_test(&self, channel: &str, text: &str) -> Option<String>;
// Copy one channel's BotServ config (kickers/badwords/greet/nobot) to another.
fn copy_bot_config(&mut self, src: &str, dst: &str) -> Result<(), ChanError>;
// Auto-response triggers (regex -> response).
fn trigger_add(&mut self, channel: &str, pattern: &str, response: &str) -> Result<bool, ChanError>;
fn trigger_del(&mut self, channel: &str, index: usize) -> Result<bool, ChanError>;
fn trigger_clear(&mut self, channel: &str) -> Result<usize, ChanError>;
fn triggers(&self, channel: &str) -> Vec<TriggerView>;
fn set_channel_topic(&mut self, channel: &str, topic: &str) -> Result<(), ChanError>;
fn suspend_channel(&mut self, channel: &str, by: &str, reason: &str, expires: Option<u64>) -> Result<(), ChanError>;
fn unsuspend_channel(&mut self, channel: &str) -> Result<bool, ChanError>;