Forum teuk.org

🦉 Mediabot v3: Fourteen New Wards Across the Castle — MB342 to MB355

in Mediabot · started by TeuK · yesterday

TeuK · yesterday

After several long evenings in the Restricted Section, Mediabot v3 has received one of its broadest reliability passes in quite some time.

This work began with Claude’s MB342–MB351 review and continued with the MB352–MB355 refinements. The result is not a flashy rewrite or a database migration. It is something more useful for a long-running IRC bot: tighter permissions, safer Partyline authentication, cleaner conversational data, more accurate reports, configurable operational policies, and calendar scheduling that remains correct when daylight-saving time decides to behave like a mischievous Time-Turner.

The guiding rule remained unchanged throughout the entire series:

Improve the bot without breaking existing installations and without changing the database schema.


🛡️ The Shield Charm: permissions and Partyline protection

MB342 — Quote deletion now respects real ownership and authority

The q del command no longer relies on an overly broad permission path.

A quote may now be deleted only by:

  • its original author;
  • a user with channel level 100 or higher;
  • an Administrator or higher-level operator.

This keeps normal moderation possible while preventing ordinary users from removing quotes they do not own.

MB343 — Partyline brute-force protection now follows the source IP

Partyline already had protection at connection level, but repeated reconnects could reset that local state.

MB343 added a persistent failure counter by source IP:

15 failures within 600 seconds

The per-connection guard remains active, while the IP-level guard survives reconnect attempts.

MB352 — The anti-brute-force map is now genuinely bounded

The first implementation removed expired IP entries when the map grew too large, but more than 1,024 still-active IP addresses could still push it past the advertised limit.

The corrected logic now:

  1. records the current failure;
  2. removes expired or malformed entries;
  3. evicts the oldest active entries when required;
  4. preserves the IP currently being processed;
  5. stops at the configured hard limit.

In other words, the protective ward now has an actual wall instead of a sign saying “please do not grow beyond here.”

MB344 — IRC ACTION output is protected against CR/LF injection

The three main IRC output paths are now aligned:

botPrivmsg
botNotice
botAction

They strip embedded carriage returns and line feeds and keep UTF-8 splitting byte-safe.

This prevents a crafted payload from turning one intended IRC action into several injected protocol lines.


🪞 The Pensieve: cleaner AI context, logs and achievements

MB345 — Claude history always starts with a user message

When Anthropic conversation history is truncated, Mediabot now makes sure an orphaned assistant message cannot remain at the beginning of the retained context.

The resulting history remains structurally valid and starts from a real user turn.

MB347 — Conversation achievements now count actual conversation

Join, part, mode, kick, topic and notice events no longer inflate achievements intended to represent human conversation.

Only genuine public messages and actions are considered.

MB348 — Statistics and AI context use the correct event filter

Several older queries used:

publictext IS NOT NULL

That condition was too broad because non-conversational events may also carry text.

The affected statistics and AI-context paths now use:

event_type IN ('public', 'action')

The bot therefore learns from, summarizes and counts actual conversation rather than channel mechanics.

MB349 — .logs is now a conversation view

The Partyline .logs command now focuses on public messages and actions instead of mixing them with joins, parts, modes, kicks and other channel events.

This is an intentional functional choice: .logs is now useful when an operator wants to read what was said, not reconstruct every IRC state transition.


📜 The Arithmancy classroom: reports that tell the truth

MB346 — Daily and weekly reports use period-accurate data

The report engine now:

  • counts only public and action events;
  • reads karma activity from KARMA_LOG for the requested period;
  • no longer presents a user’s historical lifetime karma as though it had been earned during the current day or week.

This matters because a polished report that uses the wrong period is still wrong. The new calculations describe the selected period rather than quietly borrowing totals from the past.


⌛ The Clock Tower: one clock, real calendar time

MB350 — Temporary ban expiry uses the MariaDB clock consistently

New temporary bans now calculate expiry with:

NOW() + INTERVAL ? SECOND

Creation and expiry comparison therefore use the same MariaDB clock.

This removes avoidable disagreement between the Perl process clock and the SQL server clock.

MB351 — The first scheduled report targets the next wall-clock boundary

The initial scheduler pass added first_interval so daily and weekly reports could target the next midnight or Monday midnight after startup.

That fixed the first execution, but later runs still relied on fixed durations such as 86,400 or 604,800 seconds.

A civil day is not always 86,400 seconds.

MB353 — Reports now re-arm from the calendar after every execution

The Scheduler gained a calendar mode based on:

next_run_cb => sub { ... return the next absolute epoch ... }

Calendar tasks use one-shot timers and calculate the next wall-clock occurrence again after every run.

This also added:

  • weak references to avoid reference cycles;
  • protection against obsolete callbacks after a restart;
  • the exact next epoch in task_info();
  • full date output in .timers and .schedule status;
  • tests covering 23-hour and 25-hour DST days.

Daily reports therefore continue to run at the configured local time instead of drifting after a DST transition.

MB355 — Calendar arithmetic now advances civil dates, not piles of seconds

A subtle autumn DST case remained.

The helper previously selected a future date using logic equivalent to:

localtime($now + ($days_ahead * 86400))

During the return to standard time, a Sunday can last 25 hours. Adding exactly 86,400 seconds from Sunday 00:30 may still land on Sunday 23:30.

For a weekly report targeting Monday midnight, that could select the wrong date and delay the report by almost a full week.

The corrected code advances the civil day through POSIX::mktime() and an increased tm_mday. This correctly normalizes:

  • month boundaries;
  • year boundaries;
  • leap years;
  • 23-hour days;
  • 25-hour days.

No report date is now selected through N × 86400 arithmetic.

One regression case explicitly checks:

Sunday 25 October 2026 00:30 CEST
→ Monday 26 October 2026 00:00 CET

The Time-Turner has finally been placed back in its cabinet.


🗝️ The Room of Requirement: configurable operational policy

MB354 — Security limits and report schedules are configurable

A central integer reader was added:

Mediabot::Conf->get_int()

It provides defaults and clamps values to safe ranges.

The sample configuration now documents:

PARTYLINE_LOGIN_MAX_FAILURES=5
PARTYLINE_LOGIN_IP_MAX_FAILURES=15
PARTYLINE_LOGIN_IP_WINDOW_SECONDS=600
PARTYLINE_LOGIN_IP_MAX_ENTRIES=1024

QUOTE_DELETE_CHANNEL_LEVEL=100

REPORT_DAILY_HOUR=0
REPORT_DAILY_MINUTE=0
REPORT_WEEKLY_WDAY=1
REPORT_WEEKLY_HOUR=0
REPORT_WEEKLY_MINUTE=0

The defaults reproduce the previous intended behavior, so existing installations do not need to change their live configuration.

Only mediabot.sample.conf was extended. Real configuration files remain private and untouched.

Mediabot also logs the effective report schedule and process timezone at startup, making operational diagnosis much easier.


🏰 The castle gates are now open to contributors

While the engine room was being hardened, the public GitHub repository also received a full community pass.

The project now provides:

  • guided Bug report and Feature request templates;
  • GitHub Discussions for questions and early ideas;
  • SUPPORT.md;
  • CONTRIBUTING.md;
  • a Pull Request template;
  • private vulnerability reporting and .github/SECURITY.md;
  • a Contributor Covenant Code of Conduct;
  • clear community links near the top of the README;
  • a public support channel on #i/o at EpiKnet;
  • a clarified GPL-3.0-or-later licensing statement;
  • a safer public Git identity using Teuk and a GitHub noreply address.

Live community contact:

Network:    EpiKnet
Server:     irc.epiknet.org
Port:       6697
Encryption: SSL/TLS
Channel:    #i/o

IRC is available for live discussion and quick troubleshooting. Confirmed bugs should still be recorded in GitHub Issues so they can be tracked and fixed.


⚗️ Validation cauldrons

This series was not accepted on appearance alone.

Documented validation included:

Claude MB342–MB351 independent validation : 132/132
MB352 targeted validation                 : 41/41
MB353 calendar re-arm validation          : 81/81
MB354 installer validation                : 202/202
MB354 wider development pass              : 317/317
MB355 DST calendar validation             : 111/111

The MB353 and MB354 application paths were also run twice on fresh copies to verify repeatability.

These numbers belong to overlapping targeted suites and should not be added together as one artificial grand total. Each result validates its own round and regression perimeter.


🧱 Database impact

None.

0 new tables
0 altered columns
0 migrations
0 schema changes

That was a deliberate constraint from the beginning of the work.


🔮 The next chapter

MB356 has already been prepared and validated separately, but it is not part of this MB342–MB355 commit.

Its purpose is to make Scheduler lifecycle changes transactional and prevent orphan timers when a calendar callback restarts, removes or re-adds its own task. It also makes .schedule start, .schedule stop and .schedule restart report the Scheduler’s real result instead of always announcing success.

After MB356, the current roadmap continues with:

  • MB357: isolate report failures by channel and section, and make ranking ties deterministic;
  • MB358: emit useful, non-spamming diagnostics when integer configuration values fall back or are clamped.

🕯️ Closing the book — for now

This batch is not a collection of cosmetic changes.

It closes permission gaps, hardens Partyline authentication, prevents IRC line injection, improves AI history integrity, removes non-conversation noise from statistics, makes reports period-accurate, aligns temporary bans to one clock, exposes operational policy through configuration, and teaches the Scheduler to respect real civil time.

Mediabot v3 remains the same long-running IRC bot at its core.

It is simply wearing stronger robes now.

Teuk

You must be logged in to reply.