Forum teuk.org

🧹 Safer APIs, Cleaner Contexts, and Better YouTube Results

in Mediabot · started by TeuK · 1w ago

TeuK · 1w ago

🧹 Safer APIs, Cleaner Contexts, and Better YouTube Results

Article

This update is another maintenance pass focused on reliability, defensive parsing, and cleaner command behavior.

The work is not about adding big flashy features. It is about making the bot harder to break when APIs return strange data, command contexts are malformed, or IRC clients truncate long output.

YouTube search: three visible results

The yt command now fetches up to three YouTube results instead of only one.

The first attempt displayed all three results on one IRC line, but that was too long in real clients: the line could be truncated before the second or third result became visible.

The output now sends one visible IRC line per result:

[YouTube] 1/3 title - duration - views - by channel - URL
[YouTube] 2/3 title - duration - views - by channel - URL
[YouTube] 3/3 title - duration - views - by channel - URL

The formatting keeps the same visual identity as the automatic YouTube URL preview:

  • [YouTube] label preserved;
  • title in the same style as direct link previews;
  • orange separators;
  • grey metadata and URL;
  • visible 1/3, 2/3, 3/3 markers.

This makes yt much more useful on IRC, because users can actually see the first three results instead of losing them to client-side line truncation.

YouTube metadata hardening

YouTube API handling was made more defensive in several places.

For direct YouTube URL previews, displayYoutubeDetails() now checks that:

items
items[0]
statistics
snippet
snippet.localized
contentDetails

have the expected structure before dereferencing them.

It also falls back from:

snippet.localized.title

to:

snippet.title

when the localized title is missing.

The oEmbed fallback now also requires decoded JSON to be a HASH before reading:

title
author_name

This avoids treating valid but unexpected JSON structures as safe objects.

Legacy YouTube helper fix

The older getYoutubeDetails() helper had a simple but nasty bug: it read the title into:

$sTitleItem

but later tested and displayed:

$sTitle

without assigning it.

The fix was intentionally minimal:

$sTitle = $sTitleItem // "";

Nothing else in that sensitive legacy path was changed.

Fortnite API hardening

Fortnite stats parsing was made safer and more useful.

The decoded API response must now be a HASH before the code accesses:

$data->{status}
$data->{data}

The nested payload is also guarded before reading:

account
battlePass
stats
stats.all
stats.all.overall

This prevents crashes when the API returns valid JSON with an unexpected shape.

There is also a better fallback for stats selection:

  1. prefer stats.all.overall;
  2. if missing, try the first available mode among solo, duo, trio, and squad.

So if the API has no global overall block but does provide mode-specific stats, the bot can still display something useful instead of empty values.

Context argument cleanup

The command wrapper cleanup was pushed further.

Mediabot::Context->args() no longer wraps scalar arguments into a valid one-item argument list. Non-array arguments now become an empty argument list.

This matches the defensive wrapper pattern used across the codebase:

my @args = (ref($ctx->args) eq 'ARRAY') ? @{ $ctx->args } : ();

The goal is simple: command wrappers should not accidentally treat malformed context data as valid user input.

Several remaining wrappers and style inconsistencies were cleaned up, especially in:

Mediabot/Hailo.pm
Mediabot/Helpers.pm
Mediabot/DBCommands.pm
Mediabot/Context.pm

This brings the code closer to one clear rule:

ctx->args must be an ARRAY, otherwise it means no args.

DBCommands wrapper consistency

DBCommands.pm now uses the same explicit ctx->args style as the rest of the command wrappers.

The older forms:

ref $ctx->args eq 'ARRAY'

and:

my @args = ();
@args = @{ $ctx->args } if ref($ctx->args) eq 'ARRAY';

were normalized to:

my @args = (ref($ctx->args) eq 'ARRAY') ? @{ $ctx->args } : ();

This affects wrappers such as:

addResponder_ctx
delResponder_ctx
lastCom_ctx
Yomomma_ctx

No command behavior was intentionally changed. The code is just clearer and more consistent.

Regression tests added or updated

This pass added or refreshed tests for:

  • YouTube yt showing three visible result lines;
  • YouTube metadata structure guards;
  • YouTube oEmbed requiring a hash response;
  • legacy getYoutubeDetails() title assignment;
  • Fortnite decoded response structure;
  • Fortnite nested stats guards;
  • Fortnite mode fallback;
  • no scalar wrapping in Context->args();
  • no scalar fallback in remaining wrappers;
  • DBCommands context argument style consistency.

Why this matters

This update keeps pushing the bot toward a more robust Perl codebase.

The visible improvement is the YouTube search output: three results, readable on IRC, with the same styling as link previews.

The deeper improvement is defensive behavior: API responses and command contexts are now treated as untrusted input. That means fewer crashes, fewer weird edge cases, and less surprising behavior.

Small fixes, but exactly the kind of work that makes the bot feel solid over time.

Suggested commit message

🧹 Scourgify Edgecases: reveal YouTube triples and scrub unsafe API/context paths

Suggested validation command

cd /home/mediabot/mediabot_v3 || exit 1

find Mediabot -name '*.pm' -print0 | xargs -0 -n1 runuser -u mediabot -- perl -I. -c

runuser -u mediabot -- perl -c mediabot.pl

runuser -u mediabot -- perl t/test_commands.pl --filter '169|170|171|172|173|174|175|176|177|178'

Suggested IRC smoke tests

m yt billie jean
m resolve teuk.org
m whereis Te[u]K
m fortnite <known-user>

Expected behavior:

  • yt shows up to three results as separate visible lines;
  • YouTube direct link previews still work;
  • context-based commands still behave normally;
  • malformed API structures should no longer crash the relevant commands.

You must be logged in to reply.