With respect to David Gerrold’s When HARLIE Was One.
Harlie is a multi-channel IRC bot written in Common Lisp. It maintains persistent user state in PostgreSQL, shortens and annotates URLs, learns from conversation using a Markov chainer, and serves a small web interface per channel showing the URL archive and bot source.
- Connects to multiple IRC servers and channels simultaneously, each with its own nick and web port
- Auto-reconnect with exponential backoff and keepalive PING
- NickServ authentication and registration via an event-driven state machine
- URL shortener with title fetching, UTM-parameter stripping, and a per-channel web archive
- Markov-chain text generation (
!babble,!haiku) with per-channel contexts - Memo system (
!tell,!memos) for leaving messages for offline users - METAR weather lookups (
!metar) - Wikipedia, dictionary, GitHub, and timezone lookups
(
!wiki,!define,!gh,!tz) - Channel quote database (
!addquote,!quote) - Per-user and per-channel persistent state in PostgreSQL
- Built-in web server (Hunchentoot) per context
- Slynk REPL server for live inspection (
slynky) - Cron-style scheduled tasks
| Requirement | Notes |
|---|---|
| SBCL | Tested on current releases |
| Quicklisp | For library loading |
| PostgreSQL | 14+ recommended; bot uses Unix socket auth |
| clatter-irc | glenneth1/clatter-irc — clone to a path |
| visible to ASDF’s source registry |
All other Lisp dependencies are fetched by Quicklisp.
createdb botdb
sed 's/<bot_runner_uid>/YOUR_UNIX_USER/g' \
database/bot-schema.sql.template > database/bot-schema.sql
psql botdb -f database/bot-schema.sqlCopy config-template.lisp to config.lisp and edit it:
(defparameter *bot-config*
(make-config
:db-credentials (list "botdb" (uiop:getenv "USER") nil :unix)
:web-server-name "localhost"
:connections
(list
(make-connection-spec
:server "irc.libera.chat" :ssl t
:nick "MyBot"
:channel "#mychannel"
:web-port 9000))))NickServ credentials should come from the environment, never from literals:
(make-connection-spec
...
:nickserv-password (uiop:getenv "HARLIE_NICKSERV_PASSWORD")
:nickserv-email (uiop:getenv "HARLIE_NICKSERV_EMAIL"))export HARLIE_NICKSERV_PASSWORD="your-nickserv-password"
export HARLIE_NICKSERV_EMAIL="your@email.com"(ql:quickload :harlie)
(in-package :harlie)
(run-bot)To also open a Slynk REPL for live inspection:
(slynky :port 4007)(kill-bot)A test database is required. Create it once:
createdb harlie-test
pg_dump botdb --schema-only | psql harlie-testThen from a Lisp image:
(asdf:test-system :harlie)This runs the complete test suite: NickServ flow integration tests, pure-function
unit tests, and database integration tests. All tests target harlie-test; the
production botdb database is never touched.
See the Operator’s Manual, §Testing, for a full description of the test suite architecture.
make-config accepts:
| Key | Type | Purpose |
|---|---|---|
:db-credentials | list | Postmodern connection spec (db user pw host) |
:web-server-name | string | Hostname for web server Location: headers |
:connections | list | One make-connection-spec per channel |
make-connection-spec accepts:
| Key | Required | Default | Purpose |
|---|---|---|---|
:server | yes | — | IRC server hostname |
:nick | yes | — | Bot nick |
:channel | yes | — | Channel to join |
:ssl | no | nil | Use TLS |
:web-port | no | nil | Hunchentoot port for this context |
:channel-key | no | nil | Channel password |
:nickserv-password | no | nil | NickServ IDENTIFY password |
:nickserv-email | no | nil | NickServ REGISTER email |
:extra-channels | no | nil | List of additional channels to join |
| Command | Effect |
|---|---|
!help | List available commands |
!help CMD | Show help for a specific command |
!babble | Generate text via Markov chain |
!haiku | Generate a haiku |
!metar ICAO | Fetch weather for airport ICAO code |
!status | Short status report |
!status full | Full status including chainer phrase count |
!tell USER MSG | Leave a message for USER, delivered on return |
!memos | Check your pending memos |
!wiki TOPIC | Wikipedia summary |
!define WORD | Dictionary definition |
!tz TIMEZONE | Current time in a timezone |
!gh OWNER/REPO | GitHub repository summary |
!addquote TEXT | Save a channel quote |
!quote | Recall a random quote |
!quote N | Recall quote number N |
!uptime | How long the bot has been running |
!seen USER | When a user was last seen speaking |
!remind TIME MSG | Set a reminder (e.g. !remind 30m check build) |
!rpn EXPR | RPN calculator |
See LICENSE.