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 q del command no longer relies on an overly broad permission path.
A quote may now be deleted only by:
This keeps normal moderation possible while preventing ordinary users from removing quotes they do not own.
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.
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:
In other words, the protective ward now has an actual wall instead of a sign saying “please do not grow beyond here.”
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.
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.
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.
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.
.logs is now a conversation viewThe 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 report engine now:
public and action events;KARMA_LOG for the requested period;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.
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.
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.
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:
task_info();.timers and .schedule status;Daily reports therefore continue to run at the configured local time instead of drifting after a DST transition.
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:
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.
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.
While the engine room was being hardened, the public GitHub repository also received a full community pass.
The project now provides:
SUPPORT.md;CONTRIBUTING.md;.github/SECURITY.md;#i/o at EpiKnet;GPL-3.0-or-later licensing statement;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.
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.
None.
0 new tables
0 altered columns
0 migrations
0 schema changes
That was a deliberate constraint from the beginning of the work.
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:
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.