Forum teuk.org

🧭📚🚪 Mediabot v3: The Karma Ledger Finds the Right Hall, Notes Keep Their Numbers, and False YouTube Doors Stay Shut — MB457 to MB464

in Mediabot · started by TeuK · 1mo ago

TeuK · 1mo ago

The previous chapter ended with Mediabot checking its own installation at startup, preserving accented text across more of the IRC pipeline, and gaining disposable lifecycle and games smoke tests for the road to 3.3.

The next pass was smaller, but it followed an important rule:

Once a bug pattern is found, do not merely patch the first occurrence. Find the other doors using the same broken key, centralise the rule, and leave a test behind.

That principle guided MB457 through MB464.

The work touched karma views, personal-note usability, YouTube request parsing, help pagination and test coverage for duration parsing. The final independent audit then found two remaining edge cases: one command was still mixing karma from different channels, and the supposedly safer YouTube matcher still trusted service-shaped text on foreign hosts.

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

As always for this chapter:

No database table, column or migration was added or altered.


📖 MB457–MB459 — The karma ledger stops trusting whichever shelf opens first

karmawatch list and karmadiff both wanted to show a nick’s “current” karma score from the in-memory history.

The old implementation walked channel keys from a Perl hash and stopped at the first channel containing the nick:

hash order
→ first matching channel
→ stop

Hash order is not a contract. A nick active in several channels could therefore show a different or stale score depending on which key happened to be visited first.

MB457 — karmawatch list reads the freshest entry

MB457 changed the watch list to compare timestamps across the stored channel histories and choose the most recent score.

The result no longer depended on which hash bucket Perl returned first.

MB458 — The same ghost is removed from karmadiff

The audit immediately searched for the same pattern elsewhere and found it in karmadiff.

The second copy received the same max-timestamp correction, and a lint was added to stop the old “reverse one channel, then last” idiom from returning.

MB459 — One reader for both ledgers

Two identical fixes are already one duplication too many.

MB459 introduced a shared helper:

_karma_current_score(...)

Both commands now delegate to one source of truth. The tests also enforce that the max-timestamp selection exists only once in the module.


🏰 MB464 — “Current” also means the right hall

The final pre-commit audit found that the centralised rule was deterministic in normal cases, but not yet completely correct.

Karma is stored per channel. When karmadiff runs in #alpha, its delta is calculated from #alpha — but the displayed score was still allowed to come from a newer event in #beta.

That could produce a response combining two different ledgers:

change shown for #alpha
current score borrowed from #beta

MB464 gives the shared helper an optional channel scope. karmadiff now passes the channel whose delta it is reporting. Private/global use keeps the historical all-channel view.

The audit also closed one last source of nondeterminism. Karma events use second-resolution timestamps, so two votes can legitimately share the same ts. A strict > comparison still left equal timestamps at the mercy of hash order.

The selection now has an explicit and stable order:

newest timestamp
then canonical channel key
then latest position inside that channel’s ring buffer

The ledger now gives the same answer every time, and it answers for the hall the question was actually asked in.


📝 MB460 — Search results use the same note numbers as deletion

Personal notes are displayed and deleted by their position in the complete note list:

notes del 4

note search, however, used to renumber only its matches from one:

full list matches: note 2 and note 4
search displayed : [1] and [2]

The displayed number therefore did not identify the note that notes del would remove. Acting on a search result could delete the wrong entry.

MB460 keeps each match’s original list index and displays that value:

search displays: [2] and [4]
notes del 4 removes the displayed note 4

The search output is now actionable rather than merely decorative.


🎵 MB461 and MB464 — A YouTube ID must come through a YouTube door

The radio cache recognises a direct YouTube URL or bare video ID so it can look up an existing MP3 by id_youtube before doing a fuzzy title search.

The historical matcher looked for fragments such as:

v=
youtu.be/
shorts/

anywhere in the request.

MB461 — adv= is no longer mistaken for v=

A parameter ending in the letter v, such as adv=, contained the substring v= and could supply a false eleven-character ID.

MB461 added a word boundary before v= and closed that immediate case.

MB464 — The host itself must now be genuine

A boundary was not enough. These foreign URLs still looked valid to the helper:

https://example.org/?v=dQw4w9WgXcQ
https://example.org/shorts/dQw4w9WgXcQ
https://notyoutu.be/dQw4w9WgXcQ

MB464 replaced the substring matcher with explicit supported forms:

youtube.com/watch?...&v=<id>
youtu.be/<id>
youtube.com/shorts/<id>
a bare eleven-character ID

The v value must be a complete query parameter and the hostname must be an actual YouTube hostname.

A sign reading “YouTube” in a foreign corridor no longer opens the radio vault.


📜 MB462 — Help no longer sends an empty heading before a long item

The internal help formatter builds comma-separated command lists under a prefix.

When one item exceeded the line budget by itself, the old code first pushed the current line even though it contained only the prefix, then started the item on the next line.

The user could receive a useless line such as:

Commands:

with no command after it.

MB462 pushes a line only after it contains a real item. Normal command lists are unchanged, and future reuse with a long item no longer emits an empty heading.


⏳ MB463 — The hourglass readers finally run under test

Several pure duration helpers were correct but effectively invisible to the test suite:

ChannelBan::parse_duration
Spotify duration from milliseconds
Spotify duration from ISO-8601

MB463 adds execution coverage for permanent bans, minute/hour/day/week multipliers, invalid durations, Spotify minute/second formatting and hour-long tracks.

The test extracts and executes the real function bodies rather than testing a hand-written imitation. A future change to those functions therefore changes what the test actually runs.

This round deliberately changes no production code. It converts an important blind spot into a guarded contract.


🔍 MB464 — The final pre-commit audit

The supplied snapshot was not committed blindly.

The audit found and corrected:

1. karmadiff could combine a channel-local delta with another channel’s score;
2. equal-second karma events still had no deterministic tie-break;
3. the YouTube ID helper still accepted YouTube-shaped paths on foreign hosts.

The existing MB458, MB459 and MB461 tests were strengthened, and the new test 677_mb464_precommit_audit_contracts.t executes the real helper bodies for both karma selection and YouTube extraction.


đź§Ş Validation ledger

The focused MB457–MB464 selection reports:

119/119

A broader karma, note, media, help and duration selection in the independent container reports:

860 passed out of 864

The four execution failures are existing runtime-dependent groups requiring JSON::MaybeXS in the full Perl environment. They are not assertion failures from this delta.

The MB464 installer was also checked for:

fresh application : OK
second application: idempotent
forced failure    : rollback restored every original file

The full suite must still be run on teuk.org; its actual harness total remains the only source of truth.


đź§± Database and release impact

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

Mediabot remains:

3.2dev-*

✨ What this chapter leaves behind

Mediabot now:

  • chooses karma history by an explicit, stable order;
  • reports a channel’s score from the same channel as its delta;
  • keeps one shared implementation for current-karma selection;
  • displays note-search indexes that can safely be passed to deletion;
  • extracts radio cache IDs only from genuine YouTube URL forms;
  • avoids empty prefix-only lines in chunked help output;
  • executes previously uncovered duration parsers under regression tests;
  • protects the final audit contracts with real-function execution.

The castle did not need another grand tower.

It needed the librarian to open the correct channel ledger, the note index to point at the page it promised, and the radio gatekeeper to inspect the crest on the door instead of trusting a familiar word painted nearby.

Those details are exactly why the code is beginning to feel less like a pile of features and more like a system that knows its own rules.

You must be logged in to reply.