Skip to content

Mediabot v3

Mediabot 3.5: IRC events flow through per-channel policy, MariaDB memory, Hailo or opt-in Gemini, then return as bounded replies with mbweb and Prometheus visibility.

Stable release 3.5 CI Debian 13 fresh-install gate Tested with Perl 5.40 GPL-3.0-or-later GitHub Discussions

Mediabot 3.5 helps IRC communities run, remember, and understand their channels. It combines an event-driven IRC core, channel administration, persistent community history, analytics, media integrations, observability, and upgrade tooling in one tested application.

Operators get one configuration model, one MariaDB-backed data model, ordered migrations, security and startup-integrity checks, systemd deployment guidance, and a reproducible release path.

Download Mediabot 3.5 · Install in production · Explore commands · Ask a question


What ships in 3.5

Area What Mediabot actually provides
IRC runtime A Net::Async::IRC event loop, reconnect handling, channel administration, antiflood controls, URL/media enrichment, radio tooling, and a TCP/DCC Partyline
Community memory MariaDB-backed seen, onthisday, memory, awards, yearbook, achievements, quotes, factoids, karma, notes, reminders, milestones, and channel history
Conversation Per-channel Hailo brains and a separate Gemini capability that remains disabled until an operator explicitly enables it
Operator surfaces Prometheus metrics, Grafana resources, structured logs, a read-only-by-default mbweb console, Doctor diagnostics, and systemd deployment tooling
Release engineering Ordered migrations, schema/type/index drift checks, 37 fail-closed security invariants, and reproducible archives exercised through fresh-install and upgrade/rollback paths on Debian 13

Mediabot is a strong fit for communities that want a bot to become part of their long-term infrastructure rather than remain a small disposable script. If you only need a tiny classic IRC bot with minimal dependencies, a lighter platform may be the better choice.


How Mediabot behaves

flowchart TB
    IRC["IRC networks<br/>events and commands"] --> Core["Mediabot core<br/>Net::Async::IRC event loop"]
    Core --> Policy["Per-channel policy<br/>authentication, roles and capabilities"]
    Policy --> Features["Community services<br/>commands, memory, Hailo and opt-in Gemini"]
    Features --> Reply["Bounded replies<br/>and moderation actions"]
    Core <--> DB["MariaDB<br/>state and history"]
    DB --> Web["mbweb console<br/>read-only by default"]
    Core --> Metrics["Prometheus metrics<br/>and structured logs"]
Loading

The diagram is architectural, not a mock interface: optional features stay behind their own channel switches, durable state lives in MariaDB, and external work is bounded so it does not stall the IRC event loop.

Release evidence

Gate Mediabot 3.5 result
Complete local suite 927 files · 18,760 assertions passed
Cross-cutting security audit 37/37 fail-closed security invariants across 16 axes
Debian acceptance Candidate archive · fresh install · stable 3.3 upgrade · exact rollback · deterministic reapplication
Published source Tagged commit a55d030 · reproducible .tar.gz and .tar.xz · SHA-256 and SHA-512 manifests

These are release-gate results for the tagged 3.5 source, not rolling coverage claims. See the 3.5 release notes and the release process for the exact boundaries.


See what it can do

A typical channel can use commands such as:

<prefix>seen Alice
<prefix>mood
<prefix>onthisday
<prefix>memory
<prefix>awards 7d
<prefix>yearbook 2025
<prefix>achievements progress
<prefix>topquote
<prefix>milestone
?coffee
<prefix>tell Bob remember the meeting

It can also enrich supported links, expose Prometheus metrics, integrate with radio workflows, and provide a dedicated TCP/DCC administration interface through Partyline.

Instagram previews are non-blocking and type-aware: public posts, reels, profiles, stories, and highlights use available metadata for compact rich previews, while private, removed, expired, or otherwise unavailable content falls back cleanly without stalling the IRC event loop.

The configured prefix is instance-specific and may be !, m, ., or another value chosen by the operator.


Start here

Goal Recommended path
Install the stable release Download Mediabot 3.5, verify the checksums, then follow the Installation guide
Understand the feature set Browse Public commands, Private/admin commands, and Partyline
Upgrade an existing instance Read the Release and upgrade notes before applying migrations
Operate and monitor it See Configuration, Testing, and Monitoring
Report or discuss something Use Issues for reproducible work and Discussions for questions and ideas

The wiki is the operational reference. This README keeps the essential installation and validation path available in the repository.


Release status

Mediabot uses odd minor versions for stable releases and even minor versions for development lines:

3.5      current stable release
3.6dev   current development line

Release resources:


Community

Contributions of all sizes are welcome: code, tests, documentation, plugins, translations, installation feedback, and validation on additional IRC networks.

Report a bug · Request a feature · Ask a question · Open the wiki

Live IRC support:

Network:    EpiKnet
Server:     irc.epiknet.org
Port:       6697
Encryption: SSL/TLS
Channel:    #i/o

Development: Wit and Quip

The 3.6dev line adds optional +Quip: sharper contextual humor through the existing automatic AI provider selection. +Wit +Quip shares one request and reply budget; enabling both does not create a second stream of messages. See Wit/Quip configuration and live evaluation. The new capability is registered without enabling it on any channel.

Quick install on Debian

The full install guide is here:

This section is only the essential path.

1. Install bootstrap packages

As root:

apt update
apt install -y \
  sudo \
  git \
  curl \
  wget \
  jq \
  unzip \
  zip \
  ca-certificates \
  perl \
  build-essential \
  make \
  gcc \
  pkg-config \
  mariadb-server \
  mariadb-client \
  libmariadb-dev

systemctl enable --now mariadb

libmariadb-dev provides the MariaDB Connector/C headers and mariadb_config needed to compile the CPAN driver. It is a native build dependency, not a Perl module package.

Do not install libdbi-perl, libdbd-mariadb-perl or libdbd-mysql-perl for the supported installation path. ./configure installs and verifies DBI, DBD::MariaDB and the remaining Perl modules through CPAN.

Debian 13 is also guarded by the dedicated debian13.yml CI workflow. The gate runs in the official debian:13-slim container. It first builds the exact non-publishable rehearsal archive from the tested commit, verifies its SHA-256 and SHA-512 manifests and both compressed formats, then performs every current installation action from the extracted archive rather than from the Git checkout. The gate checks the Debian system Perl 5.40 baseline, installs the documented MariaDB/bootstrap packages, builds the runtime Perl dependency set against that system Perl, verifies it through install/cpan_install.sh --verify-only, and exercises a fresh non-root configuration generation with ./configure --sync-only --skip-db --skip-cpan --yes. It then starts the Debian 13 MariaDB server, executes the real install/db_install.sh -c ... fresh-database path with safe defaults, verifies that the private config remains owned by mediabot with mode 0600, and runs tools/check_schema_drift.pl --strict --types --indexes through the generated application credentials. The same Debian 13 job also exports the real stable 3.3 database schema from the Git tag, proves that it is detectably behind the current reference schema, applies only migration files added after 3.3 in the authoritative order from install/migrations/README.md, and requires the final strict type/index drift check to return clean. Released migration files shared with 3.3 are checksum-compared and must remain immutable. The same job also runs a private deterministic pre-upgrade dump. Its rollback restores that dump byte for byte after the first successful migration, requires the old drift to return, and proves that a second ordered reapplication produces the same final database. It also runs the supported systemd installation helper against an isolated filesystem root, proves idempotent/fail-closed replacement behavior, and parses the installed published template with Debian 13 systemd-analyze verify. The CI-only cpanm local library is an acceleration/isolation mechanism; the supported operator installation path remains the CPAN flow described above. Live systemd deployment and IRC connectivity remain MB722 operational checks, not container-CI claims. The archive-derived disposable gate is the MB725 final technical install/upgrade proof.

Optional but useful:

apt install -y screen tmux htop lsof net-tools iproute2 dnsutils rsync chromium

2. Create the dedicated user

Mediabot must not run as root.

adduser mediabot
su - mediabot

Expected:

whoami
pwd
mediabot
/home/mediabot

3. Get Mediabot

For the development tree:

cd /home/mediabot || exit 1
git clone https://github.com/teuk/mediabot_v3.git
cd /home/mediabot/mediabot_v3 || exit 1

For the stable 3.5 release, use one of the published source archives:

mediabot_v3-3.5.tar.gz
mediabot_v3-3.5.tar.xz

Verify the download against mediabot_v3-3.5-SHA256SUMS or mediabot_v3-3.5-SHA512SUMS before extracting it. The GitHub release uses the plain 3.5 tag, matching the established project tag convention. See docs/RELEASING.md for the complete artifact and verification workflow.

4. Run ./configure

./configure is the supported fresh-install entry point.

Do not replace it with a manual cp mediabot.sample.conf mediabot.conf workflow.

cd /home/mediabot/mediabot_v3 || exit 1
./configure

mediabot.sample.conf is a reference file. The installer now generates a complete mediabot.conf directly from it, including all active safe defaults. It never enables Partyline eval.

On a fresh installation it creates the database, installs dependencies, configures IRC/network data and validates schema drift.

On an existing installation it creates a timestamped backup, preserves current and custom values, adds missing defaults, normalizes duplicate INI keys and offers the database drift/migration workflow without automatically applying generated SQL.

Useful maintenance modes:

./configure --config mediabot.conf --sync-only
./configure --config mediabot.conf --drift-only

See docs/CONFIGURE.md for the complete fresh/existing workflow and safety rules.

5. Review mediabot.conf

After configure:

chmod 600 mediabot.conf
vi mediabot.conf

Review at least:

[main]
[mysql]
[connection]
[undernet] or [libera]
[metrics]
[antiflood]
[openai]
[anthropic]
[chromium]
[radio]

Never commit the real mediabot.conf.


Database validation

Fresh installs use the current reference schema through the installer. Validate the newly created database with strict type checking:

cd /home/mediabot/mediabot_v3 || exit 1

perl tools/check_schema_drift.pl --conf=mediabot.conf --strict --types --indexes

For an existing instance, first generate a reviewable migration plan against the configuration that actually points to the target database:

perl tools/check_schema_drift.pl --conf=mediabot.conf --generate-migration --types --indexes

For example, on the Undernet instance:

perl tools/check_schema_drift.pl --conf=mbundernet.conf --generate-migration --types --indexes

Review the output, back up the database, and apply only the required ordered migrations from install/migrations/README.md. With --indexes, the drift checker also compares every index required by install/mediabot.sql and can generate non-destructive ADD INDEX statements for missing non-primary indexes. Extra live-only indexes are intentionally ignored. Keep the explicit index checks in the release checklist as an independent verification step.

After the migration work, run:

perl tools/check_schema_drift.pl --conf=mediabot.conf --strict --types --indexes

Do not blindly apply historical migrations to a fresh install.

Durable IRC identity is intentionally hidden behind the read-only Mediabot::Achievements API. Runtime consumers should use resolve_registered_user(channel, nick) and known_aliases(channel, nick) instead of querying ACHIEVEMENT_PROFILE or ACHIEVEMENT_IDENTITY directly. This keeps ambiguity handling and registered-user authority in one place.

Social and channel-history commands are implemented in Mediabot::SocialHistory. The historical Mediabot::UserCommands::* symbols remain available for compatibility, but new work on profil, dashboard, mood, leaderboard, chronos, recap, onthisday, memory, milestone, awards, and yearbook should live in the dedicated module instead of growing UserCommands.pm again.

See:


Built-in updater observability

The built-in updater separates remote availability checks from local history:

update / update check
    remote version diagnostic

update now
    apply an eligible update through the existing deployment workflow

update status
    read the durable result of the last updater run locally

update status is deliberately local-only: it does not contact GitHub, rerun eligibility checks or start an update. On a deployment rooted at /home/mediabot/mediabot_v3, its durable status file lives beside the rotating release tree:

/home/mediabot/.mediabot_v3.update-status.json

The record can describe running, success, failed or rolled_back and keeps the observed old -> target -> installed version trail. It contains operational metadata only and is written atomically.

Mediabot Doctor consumes the same durable record through its updater domain, also without fetching from the network:

perl tools/mediabot_doctor.pl --conf=mediabot.conf --domain updater

A missing history is informational until the first updater run creates the record. A failed/rolled-back/stale run or a version inconsistency is surfaced as a warning for operator review.


Mediabot Doctor

tools/mediabot_doctor.pl is the read-only operational diagnostic for a real Mediabot instance. It inspects runtime, configuration, filesystem, systemd / deployment state, Git state when applicable, the durable result of the last built-in updater run, database state and migrations without repairing, restarting or modifying the instance.

Run it against the configuration actually used by the instance:

perl tools/mediabot_doctor.pl --conf=mediabot.conf

For example, for an Undernet-style instance:

perl tools/mediabot_doctor.pl --conf=mbundernet.conf

Useful modes include:

perl tools/mediabot_doctor.pl --conf=mediabot.conf --strict
perl tools/mediabot_doctor.pl --conf=mediabot.conf --json
perl tools/mediabot_doctor.pl --conf=mediabot.conf --domain database
perl tools/mediabot_doctor.pl --conf=mediabot.conf --domain updater

The final verdict is intentionally operational:

READY     no blocking problem detected
DEGRADED  warning or unknown state requires review
UNSAFE    at least one failing condition makes operation/update unsafe

--strict also returns failure for warnings or unknown states. The Doctor is a diagnostic tool only: it does not apply migrations, edit configuration, restart services or repair deployment trees.


Syntax checks

From the project root:

cd /home/mediabot/mediabot_v3 || exit 1

perl -c mediabot.pl
find Mediabot -name '*.pm' -print -exec perl -I. -c {} \;
perl -c tools/check_schema_drift.pl
perl -c t/test_commands.pl
perl -c t/test_live.pl

All files should report syntax OK.


Tests

Run the full static suite:

perl t/test_commands.pl --verbose

Profile the suite without changing its execution order:

perl t/test_commands.pl --profile

By default the 20 slowest test files are reported. Choose another limit with:

perl t/test_commands.pl --profile-top 30

Profiling is observational only: it does not parallelise, reorder or skip test files.

Inspect the conservative MB660 test classification without running the suite:

perl t/test_commands.pl --class-summary

List or select a capability family:

perl t/test_commands.pl --class PROCESS --list-selected
perl t/test_commands.pl --class DB --filter 'achievement|schema'
perl t/test_commands.pl --exclude-class NETWORK --filter 'external|version'

The available tags are PURE, FILESYSTEM, PROCESS, DB and NETWORK. Tags are conservative source-touchpoint metadata: a mocked SQL/HTTP test may still carry DB/NETWORK, and a file can carry multiple tags. The primary class is only a reporting convenience. Classification does not certify that a test is safe to run in parallel. MB660 intentionally adds no parallel executor and changes no default full-suite behaviour.

Run the MB661 fast development-validation lane with:

perl t/test_commands.pl --fast

For long interactive runs, MB679 adds an opt-in single-line progress display:

perl t/test_commands.pl --fast --progress
perl t/test_commands.pl --progress

Progress is based on selected test files while the counter shows the actual number of completed assertions. Passing per-file chatter is hidden until the final summary, while failures are still reported. --progress is intentionally incompatible with --verbose; neither default runner behaviour nor test selection changes when progress mode is absent.

The fast lane is deterministic: it starts from tests whose MB660 primary class is PURE, removes a small explicit manifest of profiler-confirmed slow cases, then adds a fail-closed set of cross-cutting sentinels for runner isolation, dispatch, startup integrity, profiling, classification and module structure. Sentinels always win over the slow manifest. If a named sentinel disappears, --fast refuses to run rather than silently reducing coverage.

PURE means dependency-light, not necessarily quick. The slow manifest exists because the first real MB661 profile showed that a handful of PURE timing-heavy tests dominated the lane. Those cases remain covered by targeted regressions and by the default full suite.

Inspect the exact lane without executing it:

perl t/test_commands.pl --fast --class-summary
perl t/test_commands.pl --fast --list-selected

--filter, --class and --exclude-class can further narrow the already selected fast lane when debugging. They do not add tests outside it.

--fast is not equivalent to the full suite. Combine targeted regression tests with --fast during normal development. Keep the default full suite as the global checkpoint for cross-cutting changes and release validation.

MB662 adds an opt-in parallel pilot without changing either default runner mode:

perl t/fast_parallel.pl --jobs 2

The pilot asks the committed MB661 lane for its exact selection, overlaps only non-sentinel files whose selected primary class is PURE, and runs all 11 cross-cutting sentinels afterwards in a separate serial stage. It refuses more than four jobs and can show the deterministic plan without running tests:

perl t/fast_parallel.pl --jobs 2 --plan-only

For equivalence checks, --expect-assertions <n> can require the aggregate passing assertion count to match a known serial --fast reference. The pilot is deliberately separate from t/test_commands.pl: MB662 does not make --fast parallel by default and does not parallelise the full suite. It is a measured experiment that must prove both coverage and stability before any parallel mode is promoted.

Run live tests when a local IRC test server is available:

perl t/test_live.pl --server localhost --channel '#testchan' --verbose

If t/full_test.sh is present, use it for a full validation with logs:

./t/full_test.sh -d /tmp/mediabot_tests

Expected final result:

===== Final verdict =====
OK: static tests passed
OK: live tests passed
OK: logs written successfully

See:


First start

Start in foreground first:

cd /home/mediabot/mediabot_v3 || exit 1

perl mediabot.pl --conf=mediabot.conf

For production, use the systemd template unit (recommended):

sudo systemctl start mediabot@<instance>

See tools/systemd/README.md for the systemd setup.

Do not switch to systemd until foreground startup is clean.

Watch for:

  • missing Perl modules;
  • database errors;
  • IRC connection errors;
  • charset warnings;
  • missing config keys.

First registration and login

When the bot is connected to IRC, register and login by private message to the bot.

Example with a bot named mediabot:

/msg mediabot register <user> <password>
/msg mediabot login <user> <password>

Then verify with the configured command prefix:

<prefix>whoami

Examples:

m whoami
!whoami
.whoami

Do not use the public channel for the password.


Partyline

Partyline is the Mediabot admin interface.

Connect locally with telnet:

telnet localhost 23456

Partyline can also be reached through DCC CHAT or CTCP CHAT depending on your IRC client and bot configuration.

A local TCP Partyline session prompts interactively:

Mediabot Partyline

Please enter your nickname.
<user>

Enter your password.

Connected to Mediabot Partyline.

Once authenticated, Partyline commands start with a dot:

.help
.stat
.console 3
.floodstatus
.netsplit
.quit

The Partyline implementation is intentionally split by responsibility. The historical Mediabot::Partyline package remains the public facade/core, while physical implementations live in focused modules:

Mediabot::Partyline
    construction, port access and runtime-status publication

Mediabot::Partyline::Transport
    TCP/DCC listeners, streams and transport boundaries

Mediabot::Partyline::SessionAuth
    session lifecycle and authentication

Mediabot::Partyline::Dispatcher
    line and command routing

Mediabot::Partyline::Commands
    ordinary operator/diagnostic commands

Mediabot::Partyline::Privileged
    privileged .eval / .die controls

The parent contains no physical _cmd_* implementations. Historical method names remain available through the facade so callers do not need to know which module owns the implementation.

See:


Configuration notes

The generated mediabot.conf is local runtime configuration.

Important rules:

  • do not commit mediabot.conf;
  • do not commit real API keys;
  • do not commit real database passwords;
  • do not commit IRC passwords;
  • keep PARTYLINE_STATUS_JSON unique per bot instance;
  • keep METRICS_PORT unique when multiple bots run on the same host;
  • review CHARSET_MODE carefully on legacy databases.

For fresh installs, CHARSET_MODE=utf8mb4 is recommended.

For old production databases, especially historical IRC instances, review charset behavior before changing it.

See:


Metrics

If metrics are enabled:

[metrics]
METRICS_ENABLED=1
METRICS_BIND=127.0.0.1
METRICS_PORT=9108

Validate:

curl -s http://127.0.0.1:9108/metrics | head

Use one metrics port per bot instance.


Security notes

Do not run Mediabot as root.

Do not leave temporary passwordless sudo on the mediabot user after installation.

If you granted temporary sudo access for installation, remove it before normal IRC use:

sudo rm -f /etc/sudoers.d/mediabot
sudo -k

Then verify:

sudo -n true && echo "ERROR: sudo still active" || echo "OK: no passwordless sudo"

A bot connected to IRC must not have passwordless root access.

Recent versions also avoid logging some runtime secrets such as DCC passive tokens and channel JOIN keys.

For security vulnerabilities, do not open a public Issue or disclose the problem in a public IRC channel.

Use GitHub's private vulnerability reporting feature and read:


Troubleshooting

Start with:

cd /home/mediabot/mediabot_v3 || exit 1

perl -c mediabot.pl
find Mediabot -name '*.pm' -print -exec perl -I. -c {} \;
perl tools/check_schema_drift.pl --conf=mediabot.conf --strict
./t/full_test.sh -d /tmp/mediabot_tests

Then check:

tail -n 100 mediabot.log

or instance-specific log paths.

Common issues are documented here:


Useful links

Documentation

Community

Live IRC support

  • Network: EpiKnet
  • Server: irc.epiknet.org
  • Port: 6697
  • Encryption: SSL/TLS
  • Channel: #i/o

License

Mediabot v3 is free software licensed under the GNU General Public License version 3 or later.

SPDX license identifier: GPL-3.0-or-later

See LICENSE.md for the complete GNU GPL version 3 license text.

About

Production-oriented IRC bot written in Perl, with MariaDB, plugins, Partyline administration, URL/media helpers, Prometheus metrics and Debian tooling.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages