Mediabot 3.6dev · MB951–MB958 · Eight rounds of source organisation, contributor guidance and regression checks, taking UserCommands.pm from 7,885 to 2,288 lines in the prepared MB958 code.
The previous article, The Pensieve, the Remembrall and a Map Finally Folded, ended at MB950 and the closure of the development roadmap. This chapter begins with a practical ambition: make Mediabot a project that someone can clone, understand and contribute to.
An IRC bot can accumulate years of useful behaviour inside a small number of increasingly large files. Every command still works, but discovering where to make a change becomes a task of its own. A contributor looking for a dice formatter, a birthday calculation or a statistics query should have a clear starting point.
MB951–MB958 give those responsibilities dedicated source owners. The work also improves the route from the README to an offline test, preserves the existing public command API, and moves regression checks alongside the implementations they protect.
The guiding spell is Alohomora: open the door, label the rooms, and let the next contributor find their way around. 🪄
The scope here is Mediabot development. The code remains on the 3.6dev line, with no database schema change. Server application results are confirmed through MB956; MB957 and MB958 are described as prepared, locally qualified code, with their outstanding validation limits recorded below.
At the start of MB951, Mediabot/UserCommands.pm contained 7,885 lines. It combined command entry points, game workers, timers, statistics, user lookups, calendar helpers and presentation logic.
The refactoring keeps this file as a compatibility facade: callers can still use their historical command names, while implementations live in modules named for their responsibilities.
| Round | Source owners introduced or extended | UserCommands.pm after the round |
|---|---|---|
| Before MB951 | Original command facade | 7,885 lines |
| MB951 | Games/Trivia.pm, Games/QuoteGame.pm |
6,594 lines |
| MB952 | Stats/Activity.pm, Stats/History.pm |
5,408 lines |
| MB953 | Games/Chance.pm, Games/Duel.pm |
4,841 lines |
| MB954 | Users/Birthdays.pm, Users/Seen.pm |
4,104 lines |
| MB955 | Users/Levels.pm, Users/Profiles.pm |
3,730 lines |
| MB956 | Extend Stats/History.pm; add Stats/Affinity.pm |
3,332 lines |
| MB957 | Users/Horoscope.pm, Users/AchievementView.pm |
2,686 lines |
| MB958 | Stats/Radar.pm, Diagnostics/Commands.pm |
2,288 lines |
The final two rows refer to the locally prepared implementations. Across the complete prepared sequence, 5,597 lines leave the facade, a reduction of approximately 71%.
That figure measures the size of one central file. The implementation code moves into domain modules, and compatibility bridges add some code of their own. The practical gain is a smaller area to read when changing a particular feature.
Fifteen dedicated modules are introduced across these rounds. The developer guide records where to start, how a command reaches its implementation, and which test filenames cover the relevant boundaries.
The first extraction tackles two substantial games.
Mediabot/Games/Trivia.pm receives the Trivia command family: starting a round, checking an answer, reading scores and rankings, stopping or resetting the game, and adapting the asynchronous fetch worker.
Mediabot/Games/QuoteGame.pm receives quote guessing, answer checking and the timer helpers that belong to the game.
These are useful boundaries for a contributor. A question about Trivia fetching now starts in the Trivia module and its external source adapter. A question about the quote-game timeout starts in QuoteGame. Both remain connected to the existing command catalogue.
The extraction preserves the details that make these games reliable:
Eleven historical command/helper names remain available through UserCommands, and the eight public entry points remain available on the real Mediabot class. The existing hooks for output, progress and synchronous fetching continue to resolve through their established compatibility points.
The first game extraction normalises explicit internal qualifications while retaining the behaviour of the moved bodies. A comparative qualification reuses 162 native MB920 scenarios against the source and candidate, checking game state, output, UTF-8 bytes, SQL parameters, persistent scores, metrics and timer presence.
The tests that inspect source code also follow the games to their new locations. Their original assertions continue to protect the implementation.
MB951 also adds a direct Clone and contribute entry to the README and a substantial docs/DEVELOPING.md guide. CONTRIBUTING.md points to the same path.
The guide separates two activities that newcomers could otherwise confuse:
A contributor can work on commands, parsers and plugins without an IRC connection, a Mediabot MariaDB instance or provider keys. The tests still require their dependencies; some use private temporary files, SQLite databases and child processes.
The guide explains those requirements, describes a local Perl dependency tree, and adds /local/ to the ignored paths. It also uses the native test runner, which supports historical closure-style cases that a blanket prove -lr t invocation does not execute correctly.
The result is a clearer first contribution: find a feature, prepare its dependencies, run a useful slice, then make a focused change.
The next round removes another 1,186 lines from the facade and gives ten existing statistics commands clear owners.
Mediabot/Stats/Activity.pm owns the command implementations for statistics, rankings, activity, time-based views, comparisons, heatmaps and monthly statistics.
Mediabot/Stats/History.pm initially owns word counts, activity streaks and last-message history. MB956 later extends it with recurring sayings.
The distinction helps contributors follow the purpose of a query. Activity summaries and charts have one starting point; word and message-history views have another.
All ten extracted function bodies remain identical to the supplied source. Their SQL, parameter order, cache behaviour, calendar boundaries and achievement effects are preserved.
The history boundaries matter particularly here. Mediabot already has shared live/archive readers and established failure semantics. Moving a statistics handler must retain its channel scope, its archive policy and the distinction between an unavailable live read and an empty result.
Those responsibilities remain with the existing helpers and channel-log reader. The new domain modules organise command implementations around them.
Cross-cutting tests continue to inspect all the relevant owners: public/action filtering, archive completeness, cached channel identifiers, date predicates and live-query failure behaviour. Existing count thresholds and SQL checks remain in place.
A contributor investigating a dropped die, an 8ball translation or a duel cooldown should be able to open a small, relevant file.
MB953 creates two more source owners:
| Module | Built-in commands |
|---|---|
Games/Chance.pm |
8ball, slap, roll, flip and choose |
Games/Duel.pm |
Duel, including its existing records and achievement hooks |
The six function bodies are preserved. Dice draws, coin flips, choices, language selection, action rendering and bounded histories retain their existing behaviour.
Duel keeps its karma SQL, pair cooldowns, channel state and persistent achievement progress. Its progress hook remains available through the historical facade at call time.
This also clarifies an existing architectural distinction: the built-in games and the separately activated playful-v3 plugin have their own implementations and policies. Extracting the built-ins does not activate the plugin or change its permissions.
Fresh-interpreter checks exercise the real game paths, alias identities, public bot methods, output hooks and channel isolation. The facade falls to 4,841 lines, while the game behaviour remains familiar to channel users.
Birthdays and last-seen lookups share a user-facing context, but their difficult parts are different.
Mediabot/Users/Birthdays.pm receives the birthday command and its calendar helpers. Mediabot/Users/Seen.pm receives the seen command.
The birthday extraction preserves the existing authentication and administration checks, accepted storage formats, Gregorian validation, February 29 handling and local-calendar arithmetic. Its upcoming-birthday view retains the thirty-day window and ten-result cap.
Local time is a particularly important boundary. Counting calendar days across a daylight-saving transition is different from adding a fixed number of seconds. Existing calendar and DST tests continue to exercise the original implementation after it moves.
The horoscope also continues to use the historical birthday-validation helper name. Separating the source files preserves that dependency.
Seen retains its established lookup sequence: an online shortcut, persisted USER_SEEN information, and the existing archived-history fallback. It keeps the requested-channel scope, literal wildcard escaping and its original private/public reply routing.
That makes the new source map useful in two directions. Calendar defects lead to Birthdays; presence and history defects lead to Seen, with the shared history reader still owning its own rules.
Eight bodies move without a business-logic rewrite, and UserCommands.pm reaches 4,104 lines.
MB955 separates four account/channel level readers from three account presentation commands.
Mediabot/Users/Levels.pm owns:
getIdUserLevel;getLevelUser;getUserLevelDesc;getUserChannelLevel.Their exact-match SQL, bind order and established return values are preserved, including meaningful differences between zero and an undefined result.
Mediabot/Users/Profiles.pm owns userstats, userinfo and greet. These are account and profile views; the existing community profil implementation remains in SocialHistory.pm.
The extraction keeps the actual Context boundaries. User statistics still require Master, user information still requires Administrator, and greet retains its original requested-channel and output behaviour.
The account-information query continues to project password presence, never the password value. Hostmask reading retains its twenty-row limit and two-entry pagination. Existing UTF-8 truncation and output hooks remain available through the facade.
These are important properties to preserve during a structural change. Moving an account view should leave its access rules and data projection easy to audit in the new owner.
All seven function bodies and the remaining facade bodies stay identical to the base. Tests follow the exact lookup, password and pagination implementations, while fresh processes exercise real authentication and level-denial paths.
The facade now contains 3,730 lines.
Two commands complete the next part of the statistics split.
userTopSay_ctx moves into Stats/History.pm, alongside word counts, streaks and last-message views. mbCompat_ctx moves into the new Stats/Affinity.pm.
The sayings command keeps its authentication and Administrator requirements, explicit channel handling, literal nickname escaping, SQL limit and UTF-8 pagination. It sends at most a summary to the channel and keeps the paginated details in private notices.
Affinity keeps its four existing dimensions: activity hours, vocabulary, karma and adjacent-message presence. The scoring and weighting are unchanged.
Its history reads retain their live/archive boundaries and global sampling limits. Recent adjacent-message analysis continues to use the live table. Games policy, output, progress counters, achievements, metrics and logging retain their existing paths.
For contributors, this gives a concrete reading route: change a historical saying view in History; investigate compatibility scoring in Affinity; inspect shared history gathering in its existing helper owner.
The round updates the ten existing source-regression cases that follow those implementations. It also exercises denied access, literal SQL parameters, private pagination, Games refusal and a progress hook replaced after loading.
The two moved bodies remain identical, and the facade reaches 3,332 lines. This is the latest state in the supplied development snapshot, with a confirmed full application run of 31,158 / 31,158 assertions.
The next prepared round creates Users/Horoscope.pm and Users/AchievementView.pm, bringing the facade to 2,686 lines.
The horoscope module owns the command, four zodiac helpers and the canonical slug table. The existing external horoscope module keeps its provider, parsing and forecast-rendering responsibilities.
This separation preserves a surprisingly broad contract:
The shared zodiac table remains accessible through its historical facade alias. Calendar, output and progress hooks retain their existing compatibility points.
Users/AchievementView.pm owns the achievement command and its three presentation helpers: progress bars, compact numbers and goal lines. The achievement engine continues to own durable storage, identity and unlock policy.
Catalogue output remains private, detailed progress remains in notices, and channel versus cross-channel views keep their existing scope. Locked secret achievements retain their visibility rules.
The existing community profile can still obtain its goal formatter through the historical facade. A presentation extraction therefore keeps its connection to the other social views.
The nine moved bodies and thirty-two remaining facade bodies are identical to the source. 912 comparative scenarios produce identical results before and after the extraction, including outputs, SQL parameters, metrics, progress hooks, counters and random-sequence behaviour.
The latest prepared round gives three existing channel views their own source owners.
Mediabot/Stats/Radar.pm owns the activity radar and its bounded daily-history mode. It preserves the live-table queries, public/action filter, one-to-thirty-day window, activity-rate comparisons, quiet periods, newcomers, silent nicknames, bot exclusion, recent karma activity and top talkers.
Its history mode retains the existing data source. This extraction makes no new claim that radar history merges archives.
Mediabot/Diagnostics/Commands.pm owns features, observatory and their existing state, uptime and energy helpers.
The command aliases remain familiar:
!features
!capabilities
!caps
!observatory
!obs
!radar
!radar 7d
The capability summary retains its eight private notices, including the distinction between configured states, missing settings and legacy defaults. The observatory retains two public lines for the requested channel, its last-hour activity, energy labels, process uptime and channel-labelled metric.
The reporting commands keep their output destinations and existing fallbacks. They continue to report capabilities without changing which features are enabled.
All six extracted bodies and the twenty-six remaining facade functions are identical to the base. A further 1,687 comparative scenarios preserve return values and errors, output destinations, SQL and binds, chanset reads, metrics and helper boundaries.
The prepared facade is now 2,288 lines long.
The common mechanism across these rounds is deliberately straightforward.
The command catalogue still refers to the established handlers. UserCommands imports those handlers from their domain modules and preserves its public export list. The real Mediabot class therefore continues to expose the same methods.
Shared transport, logging, formatting or progress functions retain explicit forwarding where the old facade is the established interception point. They are resolved when called, so an integration that replaces a supported hook after loading can still intercept the real command path.
Loading order is tested too. The checks load each domain first, reverse the domain order, and load the facade first in fresh Perl interpreters. They verify that a domain does not eagerly pull in the facade and that the expected aliases and methods resolve to the same implementations.
This is more useful than a successful syntax check alone. Perl can compile a moved function while a missing helper or broken import only appears when someone invokes the command. The native path tests exercise those boundaries directly.
The modules remain parts of Mediabot’s existing application. Their smaller scope makes them easier to read and change within that application.
A mature test suite contains both behavioural scenarios and structural guards. Refactoring has to keep both useful.
A source check that searches the old file for a moved SQL query must follow its new owner. A broad regression guard must still cover all of its historical scope: live/archive reads, public/action filters, date predicates, password projection, pagination and wire encoding.
The rounds retain those criteria instead of lowering thresholds until the new layout passes. Native command tests complement the source checks by exercising actual Context gates, outputs, channel scopes and late hooks.
The confirmed application runs on the development host are:
| Round | Passing full-suite assertions | Test files |
|---|---|---|
| MB951 | 31,114 / 31,114 | 1,166 |
| MB952 | 31,124 / 31,124 | 1,166 |
| MB953 | 31,133 / 31,133 | 1,166 |
| MB954 | 31,143 / 31,143 | 1,166 |
| MB955 | 31,152 / 31,152 | 1,166 |
| MB956 | 31,158 / 31,158 | 1,166 |
Each reported round also passes the 37 security invariants and startup integrity. The assertion count grows while the file count stays stable because the earlier rounds extend existing cases.
The latest two rounds have a different evidence status:
| Prepared round | Local targeted result | Comparative scenarios | Local full-suite result |
|---|---|---|---|
| MB957 | 2,676 / 2,676 | 912 identical | 31,151 / 31,153 |
| MB958 | 1,863 / 1,863 | 1,687 identical | 31,165 / 31,166 |
These are separate selections and separate runs. Their assertion totals should not be added together as a count of unique behaviours or fixed bugs.
The complete local suites are not entirely green. The updater rehearsal fails in both rounds and is also reproduced on the corresponding base code. MB957 additionally records a process-identity observation failure in the full Doctor test; that test passes in an isolated selection and in the MB958 full run. Its earlier full-run divergence remains unexplained.
Those results stay visible. They are not converted into successful server acceptance, and no new server success or push is claimed for MB957–MB958 in this article.
The comparative checks establish equivalence for the scenarios they exercise. They do not prove every possible runtime condition, provider response or concurrent database event. The full application suite remains required for acceptance of the prepared changes on the development host.
The source map is now useful enough to support a small, concrete first task.
A dice-output change starts in Games/Chance.pm. A calendar issue starts in Users/Birthdays.pm. A word-count or sayings change starts in Stats/History.pm. An achievement display issue starts in Users/AchievementView.pm, with storage policy left to the achievement engine.
Start with an ordinary development checkout:
git clone https://github.com/teuk/mediabot_v3.git
cd mediabot_v3
git switch -c refactor/my-change
Follow docs/DEVELOPING.md to prepare the Perl dependencies and any fixture drivers needed by the selected tests. There is no need to run the instance-configuration wizard just to begin an offline contribution.
Once the dependencies are available, a first game slice uses the native runner:
perl -I. -It/lib -c Mediabot/Games/Trivia.pm
perl -I. -It/lib -c Mediabot/Games/QuoteGame.pm
perl t/test_commands.pl --filter 'trivia|quotegame|mb326|mb610' --progress
The guide also shows how to list a selected slice and run the existing fast lane. Those are useful development checks; final acceptance retains the full unfiltered suite and the project’s integrity checks.
A contributor can therefore begin with a bounded area of code, understand the existing policy and transport boundaries, and run the tests that describe them. Documentation, reproductions and focused regression tests are useful contributions alongside code changes.
Mediabot still has substantial legacy modules. This chapter introduces fifteen dedicated modules, keeps the existing command surface usable, and documents how to follow the next change through the repository.
The castle’s rooms now have names on their doors. The next contributor has somewhere concrete to begin. 🗝️
Explore the repository · Read the developer guide · Contribution guidelines · Changelog
Prepared on 25 October 2026 from the supplied Mediabot source, maintenance notes, qualification results and operator records. This article covers development work from MB951 through prepared MB958; confirmed server application results extend through MB956. Repository links follow master.
You must be logged in to reply.