IRC network connector for the eevee framework.
connector-irc bridges the eevee framework to IRC networks. It manages one or more IRC connections defined in a YAML configuration file, translates incoming IRC events (messages, actions, notices) into NATS messages for the router, and relays outgoing NATS messages back to IRC channels and users.
The connector is designed as a standalone service that communicates entirely through NATS — no direct API calls between modules. Each IRC connection is identified by a unique name, and all NATS subjects include that name so downstream consumers can route messages to the correct instance.
Configuration is file-driven and hot-reloaded: when the YAML config file changes on disk, connector-irc disconnects all existing clients and reconnects using the new settings, with no restart required.
- Multiple connections — connect to any number of IRC networks simultaneously from a single process
- Hot config reload — watches the config file and reconnects automatically on changes
- Bidirectional messaging — publishes incoming IRC messages to NATS and subscribes to outgoing NATS subjects
- Message types — supports privmsg, action (
/me), and notice messages in both directions - Post-connect actions — auto-join channels after connecting
- Control channel — runtime join, part, kick, and user-list queries via NATS control messages
- Auto-reconnect & auto-rejoin — configurable retry behavior for network drops and channel kicks
- Prometheus metrics — message counters, connection gauges, channel gauges, error counters exposed via HTTP
- Uptime & stats — responds to
stats.uptimeandstats.emit.requestNATS subjects, including per-connection health details (connected/disconnected, reconnect counts, timestamps)
This module is part of the eevee ecosystem and is not published independently. Install from source:
cd connector-irc
npm install| Variable | Required | Description |
|---|---|---|
MODULE_CONFIG_PATH |
Yes | Absolute path to the YAML configuration file |
NATS_HOST |
Yes | Hostname of the NATS server |
NATS_TOKEN |
Yes | Authentication token for NATS |
HTTP_API_PORT |
No | Port for the Prometheus metrics HTTP server (default: 9000) |
The YAML file defines a list of connections, each with its own IRC server settings, identity, and post-connect actions:
connections:
- name: liberachat
irc:
host: irc.libera.chat
port: 6697
ssl: true
autoReconnect: true
autoReconnectMaxRetries: 10
autoReconnectWait: 5000
autoRejoin: true
autoRejoinMaxRetries: 5
autoRejoinWait: 5000
pingInterval: 30
pingTimeout: 120
ident:
nick: mybot
username: mybot
gecos: My eevee Bot
quitMsg: Goodbye!
version: 1.0.0
postConnect:
- action: join
join:
- channel: '#eevee'
- channel: '#ops'
key: secretkey
commands:
commonPrefixRegex: '^[!]'| Key | Type | Default | Description |
|---|---|---|---|
host |
string | localhost |
IRC server hostname |
port |
number/string | 6667 |
IRC server port |
ssl |
boolean | false |
Enable TLS |
autoReconnect |
boolean | true |
Reconnect on disconnect |
autoReconnectMaxRetries |
number | 10 |
Max reconnect attempts |
autoReconnectWait |
number | 5000 |
Milliseconds between retries |
autoRejoin |
boolean | true |
Rejoin channels after kick |
autoRejoinMaxRetries |
number | 5 |
Max rejoin attempts |
autoRejoinWait |
number | 5000 |
Milliseconds between rejoin retries |
pingInterval |
number | 30 |
Seconds between keepalive pings |
pingTimeout |
number | 120 |
Seconds before ping timeout |
| Key | Type | Default | Description |
|---|---|---|---|
nick |
string | eevee |
Bot nickname |
username |
string | eevee |
IRC username/ident |
gecos |
string | eevee.bot |
Real name (GECOS) field |
quitMsg |
string | — | Quit message (required) |
version |
string | connector version | CTCP VERSION response |
Each entry has an action field. Currently only join is supported:
postConnect:
- action: join
join:
- channel: '#general'
- channel: '#private'
key: channelkey| Key | Type | Description |
|---|---|---|
commonPrefixRegex |
string | Regex pattern for command prefixes, included in outgoing message metadata |
connector-irc uses structured NATS subjects to route messages. The {name} placeholder refers to the connection name from the config.
Incoming messages (IRC → NATS):
| Subject | Description |
|---|---|
chat.message.incoming.irc.{name}.{channel}.{nick} |
Incoming privmsg or action from IRC |
Outgoing messages (NATS → IRC):
| Subject | Payload | Description |
|---|---|---|
chat.message.outgoing.irc.{name}.> |
{ channel, text } |
Send a message to a channel/user |
chat.action.outgoing.irc.{name}.> |
{ channel, text } |
Send a /me action |
chat.notice.outgoing.irc.{name}.> |
{ channel, text } |
Send a notice to a channel |
chat.notice.outgoing.irc.{name} |
{ target, text } |
Send a private notice to a user |
Send JSON messages to control.chatConnectors.irc.{name} to control the bot at runtime:
Join a channel:
{
"action": "join",
"data": { "channel": "#newchannel", "key": "optionalkey" }
}Part a channel:
{
"action": "part",
"data": { "channel": "#oldchannel" }
}Kick a user:
{
"action": "kick",
"data": { "channel": "#channel", "nick": "troublemaker", "reason": "optional reason" }
}List users in a channel:
{
"action": "list-users-in-channel",
"data": { "channel": "#channel", "replyChannel": "ephemeral.reply.subject" }
}The user list response is published to the replyChannel:
{
"channel": "#channel",
"users": [
{ "nick": "alice", "ident": "alice", "hostname": "user/host", "modes": ["o"], "isChannelAdmin": true }
],
"count": 1
}Each user object includes an isChannelAdmin boolean — true if the user has channel mode +h (halfop), +o (op), +a (admin/protect), or +q (owner).
If the NAMES response times out (10 seconds), an error response is sent:
{
"channel": "#channel",
"error": "Timeout waiting for user list",
"users": []
}Get modes for a user:
{
"action": "get-modes-for-user",
"data": { "channel": "#channel", "nick": "someuser", "replyChannel": "ephemeral.reply.subject" }
}The modes response is published to the replyChannel:
{
"channel": "#channel",
"nick": "someuser",
"modes": ["o", "v"],
"isChannelAdmin": true
}If the user is not found in the channel:
{
"channel": "#channel",
"nick": "someuser",
"modes": [],
"isChannelAdmin": false,
"warning": "User not found in channel"
}If the WHO query times out (5 seconds):
{
"channel": "#channel",
"nick": "someuser",
"modes": [],
"isChannelAdmin": false,
"error": "Timeout waiting for user modes"
}This action sends a WHO query to the IRC server every time (no caching) to ensure fresh, authoritative mode data.
| Subject | Description |
|---|---|
stats.uptime |
Responds with module uptime via replyChannel |
stats.emit.request |
Responds with full stats (uptime, memory, Prometheus metrics, connector details) via replyChannel |
control.connectors.irc.core.> |
Logs core control messages |
The stats.emit.request response includes a connector array with per-connection health details:
{
"module": "connector-irc",
"stats": {
"version": "1.7.1",
"uptime_seconds": 86400,
"connector": [
{
"name": "liberachat",
"connected": true,
"host": "irc.libera.chat",
"nick": "mybot",
"channels": 3,
"reconnects": 0,
"lastConnect": "2026-05-16T00:00:00.000Z",
"lastDisconnect": null
}
]
}
}The admin health command uses this data to display connection status, flag disconnected connectors as degraded, and show reconnect history.
Messages published to NATS from incoming IRC events include:
{
"type": "chat.message.incoming",
"platform": "irc",
"instance": "liberachat",
"network": "irc.libera.chat",
"channel": "#eevee",
"nick": "someuser",
"user": "someident",
"userHost": "user@host",
"text": "!weather NYC",
"time": "2026-05-07T20:00:00.000Z",
"account": "someaccount",
"botNick": "mybot",
"commonPrefixRegex": "^[!]",
"action": false
}The action field is true for /me messages, omitted or false for regular privmsgs.
┌─────────────────────────────────────────────────────┐
│ connector-irc │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ IrcClient │ │ IrcClient │ ... │
│ │ (liberachat)│ │ (oftc) │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ privmsg/action privmsg/action │
│ events events │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ main.mts │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ chokidar │ │ express │ │ │
│ │ │ (config │ │ (metrics │ │ │
│ │ │ reload) │ │ HTTP) │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ └──────────────┬──────────────────┘ │
│ │ │
└─────────────────┼────────────────────────────────────┘
│ NATS
▼
┌──────────────┐
│ router │ → command modules
└──────────────┘
Key components:
main.mts— Entry point. Sets up NATS, reads config, createsIrcClientinstances, wires event handlers, subscribes to outgoing and control NATS subjects, watches the config file for changes.IrcClient— Wrapsirc-framework'sClientwith eevee-specific concerns: status tracking, channel management, prometheus metrics, and EventEmitter passthrough of all IRC events.metrics/— Re-exports Prometheus counters and gauges from@eeveebot/libeeveeplus theprom-clientregister for full metrics collection.
Message flow:
- IRC server sends a privmsg →
irc-frameworkemitsprivmsgevent IrcClientre-emits the event (passthrough)main.mtshandler constructs a structured message and publishes tochat.message.incoming.irc.{name}.{channel}.{nick}on NATS- The eevee router picks up the message and dispatches to command modules
- A command module publishes a response to
chat.message.outgoing.irc.{name}.> main.mtshandler callsclient.say(channel, text)to deliver the message on IRC
Config hot-reload:
chokidar watches the config file. On add (initial load) or change, the NATS client is drained (with a 3-second per-client timeout), all existing IRC clients quit, the config file is re-read, and new clients are created and connected. The NATS client is then recreated so subscriptions are fresh. This ensures no stale subscriptions or messages are lost during the transition.
# Clone the repo and navigate to the connector
cd connector-irc
# Install dependencies
npm install
# Lint
npm test
# Build (lint + TypeScript compile)
npm run build
# Run locally (build + start)
npm run dev
# Update libeevee to latest
npm run update-librariesThe module requires Node.js ≥ 24.0.0.
You need a running NATS server and a valid config file:
export NATS_HOST=nats.example.com
export NATS_TOKEN=your-token
export MODULE_CONFIG_PATH=/path/to/config.yaml
npm run devThis module is part of the eevee.bot project. See the repository for contribution guidelines.
CC BY-NC-SA 4.0 — see LICENSE for the full text.