- Migrate all HTTP command handlers (PRIVMSG, JOIN, PART, NICK, TOPIC,
KICK, QUIT, AWAY, OPER, MODE) to use hdlr.svc.* service methods
instead of direct database calls. Both HTTP and IRC transports now
share the same business logic path.
- Fix BroadcastQuit bug: was inserting N separate message rows (one per
recipient); now uses FanOut pattern with 1 InsertMessage + N
EnqueueToSession calls.
- Fix README: IRC listener is enabled by default on :6667, not
disabled. Remove redundant -e IRC_LISTEN_ADDR from Docker example.
- Add EXPOSE 6667 to Dockerfile alongside existing HTTP port.
- Add service layer unit tests (JoinChannel, PartChannel,
SendChannelMessage, FanOut, BroadcastQuit, moderated channel).
- Update handler test setup to provide Service instance.
- Use constant-time comparison in Oper credential validation to
prevent timing attacks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up service.Service in HTTP handlers and delegate cleanupUser to
svc.BroadcastQuit for consistent quit/part logic across transports.
Default IRC_LISTEN_ADDR to :6667, remove unused import, fix all lint
issues (dogsled, funcorder, wrapcheck, varnamelen, nolintlint).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a backward-compatible IRC protocol listener (RFC 1459/2812) that
allows standard IRC clients (irssi, weechat, hexchat, etc.) to connect
directly via TCP.
Key features:
- TCP listener on configurable port (IRC_LISTEN_ADDR env var, e.g. :6667)
- Full IRC wire protocol parsing and formatting
- Connection registration (NICK + USER + optional PASS)
- Channel operations: JOIN, PART, MODE, TOPIC, NAMES, LIST, KICK, INVITE
- Messaging: PRIVMSG, NOTICE (channel and direct)
- Info commands: WHO, WHOIS, LUSERS, MOTD, AWAY
- Operator support: OPER (with configured credentials)
- PING/PONG keepalive
- CAP negotiation (for modern client compatibility)
- Full bridge to HTTP/JSON API (shared DB, broker, sessions)
- Real-time message relay via broker notifications
- Comprehensive test suite (parser + integration tests)
The IRC listener is an optional component — disabled when IRC_LISTEN_ADDR
is empty (the default). The Broker is now an Fx-provided dependency shared
between HTTP handlers and the IRC server.
closes#89