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
| 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.
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"]
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.
| 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.
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.
| 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.
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:
- Mediabot 3.5 GitHub release
- Mediabot 3.5 release notes
- Complete changelog
- Release and upgrade notes
- Release artifact workflow
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
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.
The full install guide is here:
This section is only the essential path.
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 mariadblibmariadb-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 chromiumMediabot must not run as root.
adduser mediabot
su - mediabotExpected:
whoami
pwdmediabot
/home/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 1For 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.
./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
./configuremediabot.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-onlySee docs/CONFIGURE.md for the complete fresh/existing
workflow and safety rules.
After configure:
chmod 600 mediabot.conf
vi mediabot.confReview at least:
[main]
[mysql]
[connection]
[undernet] or [libera]
[metrics]
[antiflood]
[openai]
[anthropic]
[chromium]
[radio]
Never commit the real mediabot.conf.
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 --indexesFor 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 --indexesFor example, on the Undernet instance:
perl tools/check_schema_drift.pl --conf=mbundernet.conf --generate-migration --types --indexesReview 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 --indexesDo 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:
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 updaterA 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.
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.confFor example, for an Undernet-style instance:
perl tools/mediabot_doctor.pl --conf=mbundernet.confUseful 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 updaterThe 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.
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.plAll files should report syntax OK.
Run the full static suite:
perl t/test_commands.pl --verboseProfile the suite without changing its execution order:
perl t/test_commands.pl --profileBy default the 20 slowest test files are reported. Choose another limit with:
perl t/test_commands.pl --profile-top 30Profiling 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-summaryList 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 --fastFor long interactive runs, MB679 adds an opt-in single-line progress display:
perl t/test_commands.pl --fast --progress
perl t/test_commands.pl --progressProgress 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 2The 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-onlyFor 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' --verboseIf t/full_test.sh is present, use it for a full validation with logs:
./t/full_test.sh -d /tmp/mediabot_testsExpected final result:
===== Final verdict =====
OK: static tests passed
OK: live tests passed
OK: logs written successfully
See:
Start in foreground first:
cd /home/mediabot/mediabot_v3 || exit 1
perl mediabot.pl --conf=mediabot.confFor 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.
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 is the Mediabot admin interface.
Connect locally with telnet:
telnet localhost 23456Partyline 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:
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_JSONunique per bot instance; - keep
METRICS_PORTunique when multiple bots run on the same host; - review
CHARSET_MODEcarefully 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:
If metrics are enabled:
[metrics]
METRICS_ENABLED=1
METRICS_BIND=127.0.0.1
METRICS_PORT=9108Validate:
curl -s http://127.0.0.1:9108/metrics | headUse one metrics port per bot instance.
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 -kThen 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:
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_testsThen check:
tail -n 100 mediabot.logor instance-specific log paths.
Common issues are documented here:
- Report a bug
- Request a feature
- GitHub Discussions
- Support guidelines
- Contribution guidelines
- Code of Conduct
- Security policy
- Network: EpiKnet
- Server:
irc.epiknet.org - Port:
6697 - Encryption: SSL/TLS
- Channel:
#i/o
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.