ChanServ: add SET URL channel homepage

Founders can attach a homepage URL to a channel; it shows in INFO and
clears when set empty, mirroring SET DESC. Local-scope (channel metadata),
not gossiped, consistent with the entrymsg/description events.
This commit is contained in:
Jean Chevronnet 2026-07-16 02:58:44 +00:00
parent 053f8cf832
commit 7eb1adce5e
No known key found for this signature in database
10 changed files with 52 additions and 5 deletions

View file

@ -11,7 +11,7 @@ impl Db {
self.log
.append(Event::ChannelRegistered { name: name.to_string(), founder: founder.to_string(), ts })
.map_err(|_| ChanError::Internal)?;
self.channels.insert(k, ChannelInfo { name: name.to_string(), founder: founder.to_string(), ts, lock_on: String::new(), lock_off: String::new(), access: Vec::new(), akick: Vec::new(), successor: None, desc: String::new(), entrymsg: String::new(), settings: ChanSettings::default(), topic: String::new(), suspension: None, assigned_bot: None , kickers: KickerSettings::default() , badwords: Vec::new(), badwords_rev: 0 , triggers: Vec::new(), triggers_rev: 0, last_used: ts, noexpire: false, expiry_warned: false, oper_note: None });
self.channels.insert(k, ChannelInfo { name: name.to_string(), founder: founder.to_string(), ts, lock_on: String::new(), lock_off: String::new(), access: Vec::new(), akick: Vec::new(), successor: None, desc: String::new(), entrymsg: String::new(), url: String::new(), settings: ChanSettings::default(), topic: String::new(), suspension: None, assigned_bot: None , kickers: KickerSettings::default() , badwords: Vec::new(), badwords_rev: 0 , triggers: Vec::new(), triggers_rev: 0, last_used: ts, noexpire: false, expiry_warned: false, oper_note: None });
Ok(())
}
@ -187,6 +187,18 @@ impl Db {
Ok(())
}
pub fn set_url(&mut self, channel: &str, url: &str) -> Result<(), ChanError> {
let k = key(channel);
if !self.channels.contains_key(&k) {
return Err(ChanError::NoChannel);
}
self.log
.append(Event::ChannelUrlSet { channel: channel.to_string(), url: url.to_string() })
.map_err(|_| ChanError::Internal)?;
self.channels.get_mut(&k).unwrap().url = url.to_string();
Ok(())
}
/// Turn one ChanServ SET option on or off for a channel.
pub fn set_channel_setting(&mut self, channel: &str, setting: ChanSetting, on: bool) -> Result<(), ChanError> {
let k = key(channel);

View file

@ -48,6 +48,7 @@ pub enum Event {
ChannelSuccessorSet { channel: String, successor: Option<String> },
ChannelDescSet { channel: String, desc: String },
ChannelEntryMsgSet { channel: String, msg: String },
ChannelUrlSet { channel: String, url: String },
ChannelSettingsSet { channel: String, settings: ChanSettings },
ChannelKickerSet { channel: String, kickers: KickerSettings },
ChannelBadwordsSet { channel: String, badwords: Vec<String> },
@ -208,6 +209,7 @@ impl Event {
| Event::ChannelSuccessorSet { .. }
| Event::ChannelDescSet { .. }
| Event::ChannelEntryMsgSet { .. }
| Event::ChannelUrlSet { .. }
| Event::ChannelSettingsSet { .. }
| Event::ChannelKickerSet { .. }
| Event::ChannelBadwordsSet { .. }
@ -393,7 +395,7 @@ pub(crate) fn apply(accounts: &mut HashMap<String, Account>, channels: &mut Hash
grouped.remove(&key(&nick));
}
Event::ChannelRegistered { name, founder, ts } => {
channels.insert(key(&name), ChannelInfo { name, founder, ts, lock_on: String::new(), lock_off: String::new(), access: Vec::new(), akick: Vec::new(), successor: None, desc: String::new(), entrymsg: String::new(), settings: ChanSettings::default(), topic: String::new(), suspension: None, assigned_bot: None , kickers: KickerSettings::default() , badwords: Vec::new(), badwords_rev: 0 , triggers: Vec::new(), triggers_rev: 0, last_used: ts, noexpire: false, expiry_warned: false, oper_note: None });
channels.insert(key(&name), ChannelInfo { name, founder, ts, lock_on: String::new(), lock_off: String::new(), access: Vec::new(), akick: Vec::new(), successor: None, desc: String::new(), entrymsg: String::new(), url: String::new(), settings: ChanSettings::default(), topic: String::new(), suspension: None, assigned_bot: None , kickers: KickerSettings::default() , badwords: Vec::new(), badwords_rev: 0 , triggers: Vec::new(), triggers_rev: 0, last_used: ts, noexpire: false, expiry_warned: false, oper_note: None });
}
Event::ChannelDropped { name } => {
channels.remove(&key(&name));
@ -441,6 +443,11 @@ pub(crate) fn apply(accounts: &mut HashMap<String, Account>, channels: &mut Hash
c.desc = desc;
}
}
Event::ChannelUrlSet { channel, url } => {
if let Some(c) = channels.get_mut(&key(&channel)) {
c.url = url;
}
}
Event::ChannelSettingsSet { channel, settings } => {
if let Some(c) = channels.get_mut(&key(&channel)) {
c.settings = settings;

View file

@ -419,6 +419,9 @@ pub struct ChannelInfo {
// Message noticed to users as they join.
#[serde(default)]
pub entrymsg: String,
// Channel homepage URL, shown in INFO (empty = none).
#[serde(default)]
pub url: String,
// On/off options set via ChanServ SET.
#[serde(default)]
pub settings: ChanSettings,

View file

@ -374,6 +374,9 @@ impl Store for Db {
fn set_desc(&mut self, channel: &str, desc: &str) -> Result<(), ChanError> {
Db::set_desc(self, channel, desc)
}
fn set_url(&mut self, channel: &str, url: &str) -> Result<(), ChanError> {
Db::set_url(self, channel, url)
}
fn set_channel_setting(&mut self, channel: &str, setting: ChanSetting, on: bool) -> Result<(), ChanError> {
Db::set_channel_setting(self, channel, setting, on)
}
@ -555,6 +558,7 @@ fn channel_view(c: &ChannelInfo) -> ChannelView {
akick: c.akick.iter().map(|k| ChanAkickView { mask: k.mask.clone(), reason: k.reason.clone() }).collect(),
desc: c.desc.clone(),
entrymsg: c.entrymsg.clone(),
url: c.url.clone(),
signkick: c.settings.signkick,
private: c.settings.private,
peace: c.settings.peace,