This pass continued the defensive cleanup of Mediabot v3, with a focus on the paths that run continuously or during important runtime events:
The goal was not to change user-facing behavior dramatically. The goal was to make the bot safer when the database misbehaves, avoid runtime/DB divergence, and prevent a few subtle correctness bugs from coming back later.
🧙 Protego Temporis: harden auth, channel spells, and timer consistency
Alternative shorter version:
🧙 Protego Temporis: harden runtime DB paths and timers
Mediabot::Auth::verify_credentials() still had DB calls wrapped in eval with unchecked prepare() / execute() calls.
That meant failures were caught, but not handled as cleanly as the rest of the codebase now expects.
The function now:
prepare();execute();Files changed:
Mediabot/Auth.pm
t/cases/64_auth_credentials_update_db_safety.t
maybe_autologin() also had an unchecked update path:
UPDATE USER SET auth=1, last_login=NOW() WHERE id_user=?
The update is now handled explicitly:
prepare() failure returns a clear reason;execute() failure returns a clear reason;This avoids misleading runtime auth state if the database update fails.
Files changed:
Mediabot/Auth.pm
t/cases/64_auth_credentials_update_db_safety.t
Mediabot::Channel object DB safetyMediabot::Channel is becoming a stable base for the ongoing channel refactoring, so it needed the same DB safety treatment as the helpers.
Several methods previously called:
my $sth = $self->{dbh}->prepare(...);
$sth->execute(...);
without checking whether prepare() succeeded.
A shared helper was introduced:
_execute_update()
It handles:
The following methods now use safer DB handling:
get_user_level()
get_user_info()
set_topic()
set_tmdb_lang()
set_key()
set_description()
set_chanmode()
set_auto_join()
exists_in_db()
create_in_db()
The setters now return a clear boolean result.
create_in_db() also retrieves last_insert_id from the DB handle after finishing the statement.
Files changed:
Mediabot/Channel.pm
t/cases/65_channel_object_db_safety.t
popcmd exact handle lookuppopcmd <nickhandle> was still using a LIKE pattern against the user handle.
That was unnecessary because popcmd is not a wildcard search command. It targets one exact handle.
Before:
WHERE U.nickname LIKE ? ESCAPE '!'
After:
WHERE U.nickname = ?
The function was also hardened:
prepare() checked;execute() checked;Files changed:
Mediabot/DBCommands.pm
t/cases/66_popcmd_exact_lookup_db_safety.t
.timers count bug fixedA real correctness bug was found in mbTimers_ctx().
The timer counter was incremented twice per DB timer:
$count++;
...
$count++;
So one timer could be counted as two, three timers as six, and so on.
The new version derives the count from collected timer lines:
my $count = scalar(@timer_lines);
No more manual counter drift.
Files changed:
Mediabot/DBCommands.pm
t/cases/67_timers_count_pagination.t
.timers output paginationThe .timers command was also improved to produce cleaner output.
It now sends:
DB timers: X result(s)
timer[01]: ...
timer[02]: ...
And if the runtime scheduler is available:
Scheduler tasks: X result(s)
schedule[01]: ...
schedule[02]: ...
This makes the command readable even when there are several DB timers and several scheduler tasks.
It also now checks:
prepare();execute();Files changed:
Mediabot/DBCommands.pm
t/cases/67_timers_count_pagination.t
addtimer DB/runtime consistencyaddtimer previously used dbh->do() inside eval.
It now uses explicit prepared statements and a safer order of operations.
Important behavior changes:
That last point matters: if the database insert fails, Mediabot no longer creates a memory-only timer that would disappear at restart.
Files changed:
Mediabot/DBCommands.pm
t/cases/68_timer_add_remove_db_safety.t
remtimer DB/runtime consistencyremtimer now also avoids dbh->do() inside eval.
It deletes from the database first, then removes the runtime timer.
This prevents the opposite inconsistency: a timer being removed from memory while still present in the database and therefore coming back after a restart.
It now:
prepare();execute();Files changed:
Mediabot/DBCommands.pm
t/cases/68_timer_add_remove_db_safety.t
This pass added tests covering the new behavior:
t/cases/64_auth_credentials_update_db_safety.t
t/cases/65_channel_object_db_safety.t
t/cases/66_popcmd_exact_lookup_db_safety.t
t/cases/67_timers_count_pagination.t
t/cases/68_timer_add_remove_db_safety.t
These tests check for:
prepare() guards;execute() guards;eval/dbh->do() timer writes;LIKE in popcmd;Syntax checks:
perl -I. -c Mediabot/Auth.pm
perl -I. -c Mediabot/Channel.pm
perl -I. -c Mediabot/DBCommands.pm
perl -I. -c mediabot.pl
Full regression suite:
env LANG=C.UTF-8 LC_ALL=C.UTF-8 perl t/test_commands.pl --verbose
Useful live checks:
m login teuk <password>
m whoami
m popcmd teuk
m popcmd %
m timers
m addtimer testtimer 60 PRIVMSG #teuk timer-test
m timers
m remtimer testtimer
m timers
Expected behavior:
popcmd % no longer behaves like a wildcard lookup;.timers no longer doubles the timer count;cd /home/mediabot/mediabot_v3
git status --short
git diff --stat
Stage the intended files:
git add Mediabot/Auth.pm Mediabot/Channel.pm Mediabot/DBCommands.pm t/cases/64_auth_credentials_update_db_safety.t t/cases/65_channel_object_db_safety.t t/cases/66_popcmd_exact_lookup_db_safety.t t/cases/67_timers_count_pagination.t t/cases/68_timer_add_remove_db_safety.t
Check staged content:
git diff --cached --stat
git diff --cached --name-only
Commit:
git commit -m "🧙 Protego Temporis: harden auth, channel spells, and timer consistency"
Push:
git push
This was another defensive pass, but an important one.
Authentication, channel state, and timers are the kind of code paths that must stay boring and predictable. With this work, Mediabot is less likely to drift between memory and database state, less likely to trip over a database hiccup, and better protected against old fragile patterns returning later.
A few more protective charms, and the bot keeps getting sturdier.
You must be logged in to reply.