An undemanding IRC chat logger with a JS-free web interface. A fresh take written from scratch - not an evolution of v2, whose code was a pile of horrors from back when I was learning to program and doing it desperately.
- Storage format is fully compatible with v1/v2: plain text files
data/<server>/<channel>/yyyy/MM/dd.txtwith[nick] messagelines. An existing production data folder is picked up as is, no conversion needed. - Configuration is plain JSON (
./ircabot --example config.jsonwrites a documented template). Top-level keys:data_path,log_local_time(day rotation timezone:false/default = UTC,true= server local time),log_cache_mb(RAM budget for the archive log cache, default 30,0disables),web{}(address, port, service name/emoji,realtime_disabled),voicegate{}(enabled,set_moderated,captcha_url,captcha_length,connect_delay_seconds,offline_ttl_hours,pm_interval_hours(0disables the automatic PM),private_message),defaults{}(nick/user/real_name/ password for all servers),triggers{}(request -> answer) andservers[](name, address, port, optionalssl, channels, per-server overrides). Keys starting with_are ignored and can be used as comments. - Per-channel language: a
channels[]entry is either"#name"or{"name": "#name", "lang": "ru"}. The tag (defaulten) becomes the<html lang>of that channel's log pages, so a browser offers to translate a chat known to be held in that language; the interface and the nicks are markedlang="en"and stay untouched. A channel object with a missingname, an unknown key or a malformed language tag stops the service at startup. - Moderation-only channels:
{"name": "#staff", "moderation_only": true}joins the channel for the voice gate alone. Nothing said there is logged - no directory underdata/, no line on disk, not even a console echo - and the channel is absent from the sidebar and from every log URL. It is named once, at the end of the channel list on the server description page, marked(moderation only). - URL scheme is compatible with v1/v2: old links to
/<server>/<channel>/yyyy/MM/dd(and.txt),/~realtime/...,/~images/...keep working. - No JavaScript anywhere in the interface. The only exception is the
real time reading page (
/~realtime/<server>/<channel>), which uses a small polling script. It can be turned off entirely withrealtime_disabled = true. - Multithreaded asynchronous web server: QHttpServer, heavy handlers run on
the global thread pool (
QFuture<QHttpServerResponse>), the IRC event loop is never blocked. Search is bounded by a time budget and a hit limit, so a greedy regexp cannot hang the service (the main v1 disease). Archive day logs (every day except today's, which keeps growing) are served from a shared, size-bounded LRU cache in RAM, so active reading and repeated searches barely touch the disk. - No worker threads for IRC, no blocking
waitFor*()calls: every connection is event-driven, reconnects are timer-based. - No X server or offscreen platform hacks: pure QCoreApplication console binary.
- Dark and light themes: the default follows the browser
(
prefers-color-scheme), the manual choice (sidebar switcher) is stored in a persistent cookie server-side - still no JavaScript; - Mobile friendly: collapsible menu and adaptive log layout, pure CSS;
- Unlimited servers and channels, current online and topic per channel;
- Connection status in real time (green/red dot);
- Scoped search, plain substring or regular expression: the grep box searches from wherever you are - the whole channel, a single year, month or day - so a large history stays reachable instead of the hit limit being spent on recent matches alone;
- Plain text day log: append
.txtto the day URL; - Messages starting with a dot are stored as "Blinded message";
- CTCP ACTION (
/me) is stored as*** text ***; - Customizable triggers: the bot answers when addressed
(
botnick, webui),%CHANNEL_FOR_URL%and%VERSION%are substituted automatically; - NickServ authorization, busy nickname fallback and automatic recovery;
- Voice gate (on by default): on moderated (
+m) channels where the bot is an operator, a newly joined user is givenconnect_delay_secondsto settle, then PMed a link to a JS-free captcha (/~captcha/<nick>). Solving it voices the user (+v) on every gated channel of that server. The grant is bound to nick + an md5 of the host and stored underdata/_voicegate/<server>/; it survives a reconnect (the bot re-voices automatically) and is dropped afteroffline_ttl_hoursoffline.pm_interval_hoursis the minimum delay between two captcha PMs to the same user;0turns the automatic PM off completely, leaving the rest of the gate (+m, voicing, the link sent in answer to a user's own PM) working. The captcha is stateless - the challenge lives in an AES-256 encrypted, signed nonce in the form, so no challenge database is kept. Every challenge handed out counts against a per-client limit (10 a minute, then a minute's pause), so fetching images until an easy one turns up costs exactly as much as answering them. The bot announces the mode with/me Voice gate mode activated; an operator who takes a voice back by hand (-v) overrules the gate: the grant is deleted instead of re-issued, the channel is told with/me Voice of <nick> revoked by a moderator of this channel, and the next captcha invitation waits out the usualpm_interval_hours; - Customizable pages:
data/_ircabot/web/main_page.txt(with%LOCAL_TIME%and%DAILY_REQUESTS%placeholders) and a per-server pagedata/<server>/about_server.txt- plain HTML, edited on disk. Custom images go todata/_ircabot/web/images/and are served via/~images/<name>. All of these (and the config) are read once and kept in RAM for the whole runtime, so the disk is never touched per request; applying an edit needs a restart.
sudo apt install qt6-base-dev qt6-httpserver-dev cmake g++
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)The version reported by --version, the startup banner and the web footer is
taken from the nearest v* git tag (git describe --tags), falling back to the
CMake project version when built outside a tagged checkout. Override explicitly
with -DIRCABOT_VERSION=x.y.z.
# Create a configuration file template:
./build/ircabot --example ./config.json
# Edit config.json, then:
./build/ircabot --config ./config.jsonDownload the package from the Releases
page (or build it locally with cd build && cpack -G DEB) and install:
sudo apt install ./ircabot_<version>_amd64.debThe package:
- installs the binary to
/usr/bin/ircabotand a systemd unit to/usr/lib/systemd/system/ircabot.service; - creates the
ircabotsystem user and the data directory/srv/ircabot/data; - writes a starter config to
/etc/ircabot/config.jsononly if it does not already exist - your configuration is never overwritten on upgrades; - starts the service on first install and restarts it on upgrade.
Edit /etc/ircabot/config.json, then sudo systemctl restart ircabot.
sudo cmake --install build --prefix /usr
ircabot --example /etc/ircabot/config.json # then edit it
sudo systemctl daemon-reload
sudo systemctl enable --now ircabot.serviceGPLv3 (c) acetone, 2021-2026. Source: https://github.com/freeacetone/ircabot