Forum teuk.org

Async eval, botNotice hardening, help filtering (May 2026)

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

TeuK Β· 1w ago

Mediabot v3 β€” Async eval, botNotice hardening, help filtering (May 2026)

Context

Audit of snapshot 20260503. Six modules touched. The main story is the completion of the async .eval pipe read, a missing truncation in botNotice, a repeated prepare() in the ban list loop, two stray warn calls, and three new Partyline commands.


Bug fixes

B1 β€” .eval parent read pipe was blocking the event loop

The child process was correctly forked with alarm() inside it for the timeout. But the parent was reading the pipe with a synchronous while (my $line = <$pipe>) loop. For the entire duration of the child β€” up to $eval_timeout seconds β€” the parent event loop was frozen: no IRC messages were processed, pings were not answered, and a long enough eval could cause a server disconnect.

The blocking read is replaced by an IO::Async::Stream attached to the pipe file handle. Lines are processed in the on_read callback as they arrive. An IO::Async::Timer::Countdown watchdog on the parent side kills the child (TERM then KILL) if it overruns the deadline. The event loop remains fully responsive throughout.

B2 β€” botNotice had no length truncation

botPrivmsg was already capped at 400 characters (IRC protocol limit). botNotice was not β€” a long notice would be silently truncated mid-line by the server, potentially at a bad UTF-8 boundary. The same substr($text, 0, 397) . '...' guard is now applied in botNotice before encoding.

B3 β€” _cmd_bans_list called prepare() inside the ban loop

for my $ban (@bans) {
    my $sth_now = $self->{dbh}->prepare('SELECT TIMESTAMPDIFF(...)');
    ...
}

One prepare() per ban row on every .bans / !bans call. The statement is now prepared once before the loop and reused with execute() on each iteration.

B4 β€” External::get_tmdb_info had a misplaced $self reference

The function get_tmdb_info is a standalone function, not a method β€” its signature is ($api_key, $lang, $query) with no $self. Replacing the warn with $self->{logger}->log(...) caused a compilation failure under use strict. Reverted to warn for this function.

B5 β€” Conf::get wrote to STDERR under MEDIABOT_DEBUG_CONF

The warn in Conf->get() for missing keys was routing to STDERR β€” invisible in the bot log. The call now checks for an attached logger object (set_logger()) and routes through it at DEBUG4. The warn fallback is kept for the early-init period before the logger exists.


Improvements

A1 β€” .eval async pipe (see B1)

Beyond fixing the blocking issue, the async implementation brings a cleaner separation of concerns: output lines are streamed to the Partyline session as they arrive rather than being buffered and sent all at once after the child exits. The watchdog timer is a proper IO::Async citizen and does not interfere with other scheduled tasks.

A2 β€” botNotice truncation (see B2)

Uniform treatment: both botPrivmsg and botNotice now cap at 400 characters before IRC encoding.

A3 β€” _cmd_bans_list prepare outside loop (see B3)

On a channel with 50 active bans, this reduces the number of DB round trips from 51 (1 list query + 50 TIMESTAMPDIFF prepares) to 2 (1 list query + 1 TIMESTAMPDIFF prepare reused 50 times).

A4 β€” !help filtered by user level

mbHelp_ctx previously sent a single wiki link. It now sends a list of commands filtered by the caller’s level:

[public]  help, uptime, status, version, date, leet, echo, colors,
          whoami, seen, q, addquote, weather, resolve, ban, kickban,
          unban, bans
[master+] join, part, nick, mode, kick, topic, say, timers, log
[owner]   die, restart, jump, rehash, addtimer, remtimer, eval

The wiki link is still appended for full documentation.

A6 β€” .uptime from the Partyline

.uptime
Uptime: 2d 4h 17m 32s

Shows the bot uptime formatted in days, hours, minutes and seconds. Reads $bot->{metrics}->{started} set at bot startup.

A7 β€” .schedule start/stop <name> from the Partyline

.schedule stop channel_log_purge
Task 'channel_log_purge' stopped.

.schedule start channel_log_purge
Task 'channel_log_purge' started.

Allows starting and stopping individual Scheduler tasks at runtime without restarting the bot. Lists available task names if called without arguments. Owner level required via Partyline authentication.


Files changed

File Items
Mediabot/Partyline.pm B1 A1 A6 A7
Mediabot/Helpers.pm B2 A2
Mediabot/ChannelCommands.pm B3 A3
Mediabot/External.pm B4
Mediabot/Conf.pm B5
Mediabot/Mediabot.pm A4

You must be logged in to reply.