feat: follow upstream twtxt post rules: reject control characters, allow U+2028, make the length cap configurable
This commit is contained in:
parent
dd6865782a
commit
801c66d3b0
12 changed files with 63 additions and 58 deletions
|
|
@ -94,6 +94,7 @@ pub struct PendingRecord {
|
|||
/// Failures that draw an error reply (PLAN.md, Management mail).
|
||||
#[derive(Debug)]
|
||||
pub enum ApplyError {
|
||||
TooLong,
|
||||
OverQuota(bool),
|
||||
Collision(String),
|
||||
FeedFull,
|
||||
|
|
@ -102,7 +103,7 @@ pub enum ApplyError {
|
|||
ReplyToChanged,
|
||||
}
|
||||
|
||||
/// Applies a published record to the feed: quota, uniqueness, retention
|
||||
/// Applies a published record to the feed: length, quota, uniqueness, retention
|
||||
/// (pruning in Fifo mode, rejecting in Off mode), then the append.
|
||||
/// Returns the rendered size after appending, for the 90% warning.
|
||||
pub fn apply_record(
|
||||
|
|
@ -112,6 +113,9 @@ pub fn apply_record(
|
|||
record: PendingRecord,
|
||||
now_unix: i64,
|
||||
) -> Result<(), ApplyError> {
|
||||
if record.text.chars().count() > cfg.max_post_chars {
|
||||
return Err(ApplyError::TooLong);
|
||||
}
|
||||
State::roll_counters(fs, now_unix);
|
||||
let (counter, limit) = if record.repost {
|
||||
(&mut fs.reposts, cfg.reposts_per_day)
|
||||
|
|
@ -929,6 +933,7 @@ impl Daemon {
|
|||
}
|
||||
Err(e) => {
|
||||
let reason = match e {
|
||||
ApplyError::TooLong => Reason::TooLong { max: self.cfg.max_post_chars },
|
||||
ApplyError::OverQuota(repost) => Reason::OverQuota { repost },
|
||||
ApplyError::Collision(timestamp) => Reason::Collision { timestamp },
|
||||
ApplyError::FeedFull => Reason::FeedFull,
|
||||
|
|
@ -1068,6 +1073,18 @@ mod tests {
|
|||
assert_eq!(feed.entries().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_cap_is_configurable() {
|
||||
let mut feed = smolboid_core::Feed::new();
|
||||
let mut fs = feed_state();
|
||||
let cfg = Config { max_post_chars: 5, ..config(1 << 20) };
|
||||
let five = record("2026-10-01T12:47:00Z", "🙂🙂🙂🙂🙂");
|
||||
apply_record(&mut feed, &mut fs, &cfg, five, 0).unwrap();
|
||||
let six = record("2026-10-01T12:47:01Z", "abcdef");
|
||||
assert!(matches!(apply_record(&mut feed, &mut fs, &cfg, six, 0), Err(ApplyError::TooLong)));
|
||||
assert_eq!(fs.posts, 1, "a refused post spends no quota");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reposts_publish_as_repost_records() {
|
||||
let mut feed = smolboid_core::Feed::new();
|
||||
|
|
@ -1136,7 +1153,7 @@ mod tests {
|
|||
fn warning_flag_flips_at_ninety_percent() {
|
||||
let mut feed = smolboid_core::Feed::new();
|
||||
let mut fs = feed_state();
|
||||
let cfg = config(1300);
|
||||
let cfg = Config { max_post_chars: 700, ..config(1300) };
|
||||
apply_record(&mut feed, &mut fs, &cfg, record("2026-10-01T12:47:00Z", "x"), 0).unwrap();
|
||||
assert!(!fs.warned);
|
||||
apply_record(&mut feed, &mut fs, &cfg, record("2026-10-01T12:47:01Z", &"y".repeat(700)), 0).unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue