Forum teuk.org

πŸ—ΊοΈπŸšͺ🧠 Mediabot v3: Every Hall Gets One Name, Every Door Checks Its Host, and Summaries Speak Aloud β€” MB397 to MB416

in Mediabot Β· started by TeuK Β· 1mo ago

TeuK Β· 1mo ago

The previous chapter ended with Trivia finally returning questions instead of disappearing into a silent worker timeout.

The next twenty rounds were quieter on IRC, but no less important. They dealt with the kind of details that decide whether a long-lived bot remains understandable and efficient, or slowly accumulates contradictory rules, hidden database traffic and edge cases that only appear at the worst possible hour.

This pass covered configuration completeness, YouTube durations, birthdays around daylight-saving time, achievement-file integrity, Trivia hints, startup ordering, URL parsing, channel-name normalisation, database query reduction and a more useful Claude summary command.

A final independent audit then found three gaps before the commit and closed them as MB416.

The project remains on the 3.2dev line. This is not the 3.3 release.

As always:

No database schema was changed.


πŸ“š MB397 β€” Every configuration key has a place in the handbook

Mediabot read four settings that were absent from mediabot.sample.conf:

anthropic.TEMPERATURE
main.ACHIEVEMENTS_PATH
main.BOT_NICKS
radio.ENABLED

The runtime defaults worked, but a fresh installer could not discover or document those choices.

MB397 added the missing entries and, more importantly, introduced a coverage test that compares literal configuration reads in the code with the keys present in the sample file.

A future developer can no longer add a hidden configuration key without the test suite noticing.


⏳ MB398 β€” YouTube videos are allowed to last more than one day

YouTube uses ISO-8601 durations such as:

P1DT2H3M4S

The old parser understood hours, minutes and seconds, but ignored the day component. Long streams and archives could therefore lose exactly 86,400 seconds per day.

MB398 introduced one shared duration parser used by both display formatting and numeric duration calculations.

Examples now behave correctly:

P1DT2H3M4S β†’ 1d 2h 3mn 4s
P3D         β†’ 3d

It also keeps the ISO-8601 distinction between months before T and minutes after T.


πŸŽ‚ MB399 β€” Birthdays follow the local calendar, including DST

The automatic birthday announcement used local time, while birthday next calculated from UTC.

Around midnight in Europe/Paris, the same birthday could therefore be announced as β€œtoday” and displayed by the command as β€œin 1d”.

MB399 moved the calculation to the local civil calendar and uses rounded day differences. This matters on the 23-hour and 25-hour days created by daylight-saving transitions.

Leap-day birthdays remain supported and point to the next valid 29 February.


πŸ† MB400 β€” A damaged achievement file is no longer silently replaced

Achievement data is stored in a JSON file. Two failure paths could turn a recoverable incident into data loss:

  • an unreadable or invalid existing file could later be overwritten by a fresh empty state;
  • a failed write or close could still proceed towards the final rename.

MB400 preserves a corrupt source for investigation and requires the complete temporary-file write to succeed before replacing the live file.

The castle now refuses to burn the old ledger merely because it cannot read one page.


🧹 MB401 β€” A dead ban-expiry helper leaves the building

The old expires_sql_from_seconds() helper had no caller after the ban-expiry path moved to MariaDB’s clock.

MB401 removed the dead code and its unused strftime import. A regression test prevents the obsolete helper from quietly returning.

This round changed no behaviour. It removed a misleading alternative implementation that no longer belonged to the runtime.


🧩 MB402 β€” Trivia hints preserve the shape of the answer

The previous hint revealed the first character and replaced everything else with underscores, including spaces:

emile zola β†’ e_________

That hid useful structure and made multi-word answers unnecessarily opaque.

MB402 masks only answer characters while keeping separators visible:

emile zola       β†’ e____ ____
jean-paul sartre β†’ j___-____ ______
rock 'n' roll    β†’ r___ '_' ____

The hint still gives no extra letters, but it now looks like the answer it describes.


πŸ“Š MB403–MB404 β€” Startup order becomes explicit and testable

Two metrics assignments lived before the Metrics object was created. They were unreachable code: harmless at runtime, but misleading to anyone reading the startup sequence.

MB403 removed those dead writes and kept the real initial gauges after the Metrics server starts.

MB404 then documented and mechanically locked the required startup order:

DB
ChannelBan
dbCheckTables
Auth
logout stale users
populate channels
IO::Async loop
Metrics
Metrics HTTP
Partyline
IRC login
loop run

A careless future reordering will now fail a test instead of failing during a real boot.


πŸ”— MB405–MB406 β€” URLs are parsed by their structure, not by convenient coincidence

Balanced closing characters

The URL extractor used to strip a final parenthesis unconditionally. A valid Wikipedia URL such as:

https://fr.wikipedia.org/wiki/Talos_(mythologie)

could become a different, broken URL.

MB405 keeps balanced parentheses and brackets while still removing punctuation belonging to the surrounding IRC sentence.

Handler routing by real host

Several specialised URL handlers matched service names anywhere inside the URL. An unrelated page containing instagram.com in its query string could launch the Instagram path, and a nested YouTube URL could be mistaken for the site actually being visited.

MB406 anchored Instagram, Spotify, Apple Music and YouTube routing to the beginning and host of the real URL.

The final audit later discovered that Facebook and X/Twitter still used their older unanchored forms. MB416 completed the rule for every specialised dispatcher.

A door is now selected by the name above the entrance, not by a service name scribbled on a note in the corridor.


πŸ—οΈ MB407 β€” One canonical key for every IRC channel

The in-memory channel registry had two conventions:

boot population β†’ database spelling
live chanadd     β†’ lowercase

Because IRC channel names are case-insensitive, the same channel could occupy different keys depending on whether it existed before or after a restart.

MB407 makes lowercase the single canonical key and updates lookups across the tree. The object still retains the display name.

The regression test scans the source tree and rejects future non-normalised channel lookups.


βœ‚οΈ MB408 β€” Long page titles finish at a word boundary

Generic URL titles longer than 300 characters were cut in the middle of a word without indicating that text had been removed.

MB408 trims at the last available space and adds an ellipsis. A title with no spaces still receives a safe hard cut and ellipsis.

The same round aligned the anti-repeat URL cache with the lowercase channel-key convention from MB407.


πŸ›‘οΈ MB409 β€” The anti-flood cache remembers an empty answer too

checkAntiFlood() caches channel parameters, but only when a CHANNEL_FLOOD row exists.

For the common case of a channel with no anti-flood configuration, every outgoing message triggered another SQL lookup:

10 messages β†’ 10 SELECTs

MB409 adds a negative cache entry with the same TTL:

10 messages β†’ 1 SELECT per TTL

A later database configuration still becomes visible after the normal refresh interval.


πŸ›οΈ MB410–MB414 β€” Channel IDs come from the keeper, not the archives

Many commands performed this query repeatedly:

SELECT id_channel FROM CHANNEL WHERE name = ?

The same information already exists in the in-memory channel registry.

The migration proceeded carefully over five rounds:

  • MB410 moved the two main karma paths to cache-first resolution;
  • MB411 introduced the shared channel_id_cached() helper and migrated three handlers;
  • MB412 migrated four Partyline commands;
  • MB413 migrated four more UserCommands paths, including Trivia score operations;
  • MB414 migrated the final nominal UserCommands sites and tightened the regression budget.

The SQL lookup remains only as a compatibility fallback when a channel is genuinely absent from memory.

The last hot-path query found by MB416

The pre-commit audit found one important path outside the migration count: logBotAction() still selected the channel ID before every channel-log insert.

That function handles public messages, actions, joins, parts and other frequent events. The leftover query therefore mattered more than several command-only sites combined.

MB416 routes it through channel_id_cached() and makes the helper ask the Mediabot::DB wrapper for the current handle after a reconnect instead of trusting a potentially stale compatibility handle.

The central helper now owns the sole fallback query in Helpers.pm.


πŸ—£οΈ MB415 β€” Claude summaries can speak in the channel

The summary command gained a richer but backward-compatible syntax:

ai summary [last|today|yesterday|week|<N>d] [<N>] [<N>l] [public] [nick]

New options include:

public / pub β†’ feedback and summary on the current channel
5l           β†’ request five output lines, clamped from 1 to 10
help         β†’ detailed usage, periods, options and examples

A plain numeric argument still means the number of messages analysed, and the historical command without options still returns notices with the old default length.

The help for m help ai was updated as well.

One final channel-prefix correction

MB415 initially considered only channels beginning with # eligible for public output.

Mediabot already recognises the standard IRC channel prefixes:

#  &  !  +

MB416 replaced the local ^# check with the shared channel-target predicate. Public summaries now follow the same routing rules as the rest of the bot.


πŸ” MB416 β€” The final independent pre-commit audit

The fresh snapshot was not committed blindly.

The audit found and fixed three concrete gaps:

1. Facebook and X/Twitter routes were still not anchored to the real URL host.
2. ai summary public accepted only # channels instead of all standard prefixes.
3. logBotAction still performed a channel-id SELECT on a very hot event path.

It also hardened the shared channel-ID fallback against a stale database handle after reconnection.

The new test 631_mb416_precommit_audit_contracts.t protects all three contracts, while the earlier MB406, MB411 and MB415 tests were strengthened to describe the completed behaviour.


πŸ§ͺ Validation ledger

Claude reported the following baseline before the independent audit:

full suite through MB415: 8646/8646

The MB416 installer was tested on a fresh copy of the supplied snapshot and is checksum-guarded, idempotent and rollback-safe.

The independent focused selection covering every round in this article reports:

MB397–MB416 focused tests: 210/210
Affected-area regression:    915/915
Helpers.pm syntax:         OK
External/URL.pm syntax:    OK
External/Claude.pm syntax: OK

The installer repeats those syntax checks with the real Perl runtime on teuk.org before preserving any change. It restores the original files automatically on failure.

The focused, affected-area and full-suite totals overlap and must not be added together.


🧱 Database and release impact

0 new tables
0 altered columns
0 migrations
0 schema changes
0 release to 3.3

The work changes runtime code, tests and sample documentation only.

Mediabot remains on:

3.2dev-*

✨ What this chapter leaves behind

Mediabot now:

  • documents every literal configuration key it reads;
  • understands YouTube durations containing days;
  • calculates birthdays with the local civil calendar across DST;
  • protects achievement history from corrupt reads and incomplete writes;
  • shows useful Trivia hint structure;
  • keeps startup order explicit and regression-tested;
  • preserves balanced parentheses in real URLs;
  • routes specialised URL handlers only by the actual host;
  • uses one lowercase key for every IRC channel;
  • truncates titles cleanly and caches URL repeats consistently;
  • remembers the absence of anti-flood configuration;
  • resolves channel IDs from memory across commands, Partyline and channel logging;
  • refreshes the SQL fallback handle after a database reconnect;
  • lets Claude summaries speak publicly with a requested line count and proper help;
  • treats #, &, ! and + channels consistently.

No grand new tower was added in these rounds.

Instead, every door received the correct sign, every hall received one canonical name, the librarians stopped being summoned for facts already held by the keeper, and the public summary spell learned where a channel actually begins.

That is exactly the kind of work that makes a future stable release less dramatic β€” which, for production software, is the best kind of magic.

You must be logged in to reply.