IRC bot to greet new IRC #lounge users
  • TypeScript 97.8%
  • Dockerfile 2.2%
Find a file
alex b021f0c05f
All checks were successful
Build and Push Container Image / build-and-push-1 (push) Successful in 1m31s
Build and Push Container Image / build-and-push (push) Successful in 0s
Build and Push Container Image / deploy-1 (push) Successful in 4s
Build and Push Container Image / deploy (push) Successful in 0s
chore(ci): update runner label, remove already installed package
Signed-off-by: alex <alex@noreply.forge.l3x.in>
2026-07-15 14:20:59 +00:00
.forgejo/workflows chore(ci): update runner label, remove already installed package 2026-07-15 14:20:59 +00:00
.zed Initial commit 2026-07-14 08:11:42 +03:00
scripts feat: add chatto integration 2026-07-14 10:49:39 +03:00
src feat: add chatto integration 2026-07-14 10:49:39 +03:00
.env.example feat: add chatto integration 2026-07-14 10:49:39 +03:00
.fallowrc.json feat: add chatto integration 2026-07-14 10:49:39 +03:00
.gitignore Initial commit 2026-07-14 08:11:42 +03:00
Containerfile Initial commit 2026-07-14 08:11:42 +03:00
deno.json feat: add chatto integration 2026-07-14 10:49:39 +03:00
deno.lock feat: add chatto integration 2026-07-14 10:49:39 +03:00
fallow.schema.json Initial commit 2026-07-14 08:11:42 +03:00
LICENSE Initial commit 2026-07-14 08:11:42 +03:00
README.md feat: add chatto integration 2026-07-14 10:49:39 +03:00
renovate.json Initial commit 2026-07-14 08:11:42 +03:00

irc-greeter-bot

A small Deno IRC bot that greets newcomers to a channel with a fixed message. The bot remembers who it has already greeted by persisting nicks to a JSON file on disk, so the same person is not re-greeted — even after a restart.

Requirements

  • Deno 2.9 or newer
  • Node.js 22+ (only for running Fallow locally; CI installs it automatically)

Configuration

Configuration is loaded from a .env file and the process environment. Process environment variables take precedence over .env values. Every variable below is required (there are no built-in defaults); a missing or empty value is a fatal error.

Copy .env.example to .env to get started:

cp .env.example .env  # then edit .env
Variable Description
IRC_SERVER IRC server hostname.
IRC_PORT IRC server port (165535).
IRC_TLS Connect with TLS (true/false).
IRC_NICK Nickname the bot registers as.
IRC_CHANNELS Comma-separated channels to join and greet in (≥1).
IRC_GREETING Greeting template; {nick} substituted.
IRC_STORE_PATH Path to the file that persists greeted nicks. Optional; defaults to greeted-nicks.json.
IRC_PASSWORD Account password for NickServ/SASL auth. Optional; unset = anonymous.
IRC_AUTH_METHOD Auth method when IRC_PASSWORD is set: NickServ (default), sasl, saslThenNickServ, or saslExternal.
CHATTO_BASE_URL Chatto server origin for notifications (e.g. https://chat.example.com). Optional; see below.
CHATTO_TOKEN Bearer token for the Chatto API. Optional.
CHATTO_ROOM_ID ID of the Chatto room to post newcomer notifications into. Optional.
CHATTO_MESSAGE Notification template with {nick} and {channel} placeholders. Optional; defaults to a sensible message.

NickServ / SASL registration

The bot supports authenticating against a registered account using the built-in mechanisms provided by @irc/client. Set IRC_PASSWORD and pick an IRC_AUTH_METHOD:

Method Description
NickServ Non-standard NickServ IDENTIFY after connect. Default. Requires IRC_PASSWORD.
sasl SASL PLAIN during registration; aborts if SASL fails. Requires IRC_PASSWORD.
saslThenNickServ SASL PLAIN, falling back to NickServ. Requires IRC_PASSWORD.
saslExternal SASL EXTERNAL via a TLS client certificate (no password). Requires IRC_TLS=true plus a client cert.

sasl / saslThenNickServ are recommended on networks that support them (e.g. Libera.Chat) — they authenticate before the bot is visible on the network, avoiding enforcer kills on a nick-protected account.

Example (SASL PLAIN):

IRC_PASSWORD=your-account-password
IRC_AUTH_METHOD=sasl

Leave IRC_PASSWORD unset (or empty) to connect anonymously — in that case IRC_AUTH_METHOD is ignored.

Note: saslExternal authenticates with a TLS client certificate rather than a password. Certificate file paths are not currently exposed via env vars; use plain sasl for password-based auth. Open an issue if you need saslExternal wired up.

Chatto newcomer notifications

When an untracked user is greeted in IRC, the bot can mirror a notification into a Chatto room via the ConnectRPC API. The feature is off by default — set all three of CHATTO_BASE_URL, CHATTO_TOKEN, and CHATTO_ROOM_ID to enable it. Setting only some of them is a fatal error (so a misconfiguration is caught at startup rather than failing silently per-join).

The notification is fire-and-forget: a Chatto failure is logged and never affects IRC greeting behavior.

Obtaining a CHATTO_TOKEN

Chatto has no "create API token" button. Bearer tokens (cht_AT…) are only minted through the OAuth 2.0 Authorization Code + PKCE flow (ADR-024). The included chatto:token task automates this once against a loopback callback (loopback origins are always trusted by Chatto, so no server-side allow-listing is needed):

deno task chatto:token --base-url https://chat.example.com
# → opens a browser; log in as the bot account and approve consent
# → prints: CHATTO_TOKEN=cht_AT...

The resulting token has a 90-day inactivity TTL with a sliding window — every successful API call resets it — so a token used regularly by the bot effectively never expires. It is revoked by a password change/reset on the account or by explicit revocation, at which point the bot logs a clear 401 message pointing back here.

Usage

Run the bot (it needs network, environment, and filesystem access):

cp .env.example .env  # then edit .env
deno task start

For development with automatic reload on file changes:

deno task dev

Compile a standalone binary:

deno task compile
./dist/bot

Development

This project follows standard Deno conventions. Common tasks (defined in deno.json):

Task Description
deno task dev Run the bot with --watch.
deno task start Run the bot.
deno task test Run the test suite.
deno task lint Lint the source.
deno task fmt Format the source.
deno task check Type-check the entry point.
deno task compile Build a standalone binary.
deno task chatto:token Obtain a Chatto bearer token via OAuth + PKCE (see below).
deno task fallow Run Fallow static analysis.

Project structure

src/
  main.ts         Entry point: connects to IRC and wires up the greeter.
  config.ts       Loads and validates configuration from .env + env vars.
  greet.ts        Pure greeting / who-to-greet logic (no I/O).
  chatto.ts       Posts newcomer notifications to a Chatto room (Connect JSON).
  storage.ts      Disk-backed store of already-greeted nicks.
  *_test.ts       Unit tests (Deno's built-in test runner); one per module.
scripts/
  chatto-token.ts Obtains a Chatto bearer token via the OAuth + PKCE flow.

Every module in src/ has a corresponding *_test.ts file. The greeting and join-handling logic live in src/greet.ts and the handleJoin export of src/main.ts, both kept free of network I/O so they can be tested with in-memory fakes.

Codebase analysis (Fallow)

This project is configured for Fallow, a TypeScript/JavaScript codebase-intelligence tool. It detects dead code, duplication, and complexity issues. Configuration lives in .fallowrc.json (with editor autocomplete via fallow.schema.json); test files are excluded from analysis.

# Dead-code check (also what CI runs)
deno task fallow

# Duplication and health reports
npx fallow dupes
npx fallow health

License

MIT