PRD: Hotwire IRC Client (from Ibis / mIRC reference scripts)

Version: 1.2 Status: Ready for implementation Source analysis: reference/stuff Β· 39 script files Output path: docs/pdf.md Stack: Ruby on Rails 8, Hotwire (Turbo + Stimulus), ActionCable, SQLite/PostgreSQL


1. Executive summary

Build a fully featured web IRC client that recreates the ergonomics of the legacy Ibis mIRC script suite (reference/stuff, formerly reference/newmirc) using modern Rails tooling.

Real-time chat flows through ActionCable WebSockets; UI updates use Turbo Streams and Stimulus controllers for nick lists, topic bars, and context menus. The goal is desktop-class IRC in the browser without requiring mIRC.

Analysis scanned 104 event handlers, 241 popup menu items, and 20 dialog definitions from the reference scripts.

The bundled mirc.pdf scripting reference is available under reference/mirc.pdf for alias/syntax parity during implementation.

Phased delivery

Phase Goal Outcome
Phase 1 Core client Connect, join/part channels, styled message log, nick list, PMs
Phase 2 Power user Context menus, CTCP, modes, notify/ignore, logging, dialogs
Phase 3 Advanced DCC/XDCC bridge, services auth, auto-voice/op, flood limits

2. Problem statement

The Ibis script collection (style.ibis, popups_*.ibis, dialogs.ibis, events.ibis, etc.) implements a rich IRC workflow:

No browser client in the Rabbot ecosystem reproduces this surface area. A Rails Hotwire app provides a maintainable path to parity while keeping server-side IRC connection logic testable.


3. Goals and non-goals

Goals

Non-goals (v1)


4. Architecture overview

app/
  channels/irc_channel.rb          # ActionCable β€” stream log lines to browser
  controllers/irc/
    sessions_controller.rb         # Connect/disconnect IRC
    channels_controller.rb         # Join/part, topic, modes
    messages_controller.rb         # Send channel/PM text
    context_menus_controller.rb    # Popup menu actions (Turbo responses)
  javascript/controllers/
    irc_log_controller.js            # Append Turbo Stream fragments
    context_menu_controller.js       # popups_* parity
    dialog_controller.js             # settings/flood/lock dialogs
  models/
    irc_connection.rb                # Server-side IRC client (Cinch / ircinch)
    irc_message.rb                   # Parsed PRIVMSG/NOTICE/JOIN/PART
    irc_companion_profile.rb         # user ↔ fandom URL ↔ character YAML path
    irc_event.rb                     # persisted IRC events for replay
  services/irc/
    event_formatter.rb               # style.ibis coloured output
    ctcp_handler.rb                  # ping/time/version
    dcc_coordinator.rb               # Phase 3 β€” WebRTC or relay fallback
    network_profile.rb               # per-network services + startup config
    capability_probe.rb            # parse 004/005/MOTD into supported features
    connection_notes.rb              # @stuff parity β€” structured connect log
    companion_bot_provisioner.rb     # spawn/stop Rabbot from profile
    event_store.rb                   # append + cursor query
    sync_catchup.rb                  # build Turbo Stream replay payload
  jobs/
    companion_bot_lifecycle_job.rb   # enable/disable, channel mirror
lib/irc_format.rb                    # Shared with Rabbot β€” mIRC colours
lib/character_profile.rb             # existing β€” persona source
lib/character_profile_generator.rb   # existing β€” URL β†’ YAML

Real-time flow

  1. Browser subscribes to IrcChannel with session token

  2. Rails IRC worker receives PRIVMSG β†’ parses β†’ IrcMessage record (optional) β†’ broadcasts Turbo Stream

  3. Stimulus updates scroll position, nick highlights, and unread badges


5. Feature mapping (reference β†’ web)

Detected from mirc.pdf, style.ibis, popups_chat.ibis, popups_channel.ibis, popups_nicklist.ibis, popups_status.ibis, popups_menubar.ibis:

Feature Reference Web implementation
auto_op events/aliases Auto-op trusted users on join
auto_voice events/aliases Auto-voice trusted users on join
away events/aliases Away/AFK state with nick suffix and mass-message suppression
channel_modes events/aliases Channel mode toggles (+m, +s, +i, voice/op batching)
context_menus popups_channel.ibis, popups_chat.ibis, popups_menubar.ibis, popups_nicklist.ibis, popups_status.ibis Right-click menus for chat, channel, nick list, status bar, and menubar
ctcp events/aliases CTCP ping, time, version, and finger requests/replies
dcc_chat events/aliases Direct Client-to-Client private chat sessions
dcc_send events/aliases Direct file transfer offers and progress tracking
dialogs asdf, chan, diuqil, draw, first, ircspy, lock, mp3, pc, settings, setup_autojoin, setup_ban, setup_flood, startup, tdccchangesettings, topic, who, whois, whoischan, xdccleech Modal setup dialogs (settings, flood protection, lock screen)
dns events/aliases Reverse DNS lookup tooling
flood_protection events/aliases Rate limits for CTCP and outbound messages
ignore events/aliases Per-user ignore list management
logging events/aliases Per-channel and per-query log files with open-in-editor actions
notify events/aliases Online/offline notify list with status window feed
server_management events/aliases Auto-connect servers and auto-join channel lists
services events/aliases NickServ/ChanServ/MemoServ integration (per-network; see Β§12)
style_reference style.ibis Centralised colour and echo-indent styling from style.ibis
styled_events events/aliases Custom join/part/kick/topic/notice rendering with mIRC colour codes
whois events/aliases WHOIS lookup from nick context menus
xdcc events/aliases XDCC pack lists, queues, and leech helpers
companion_bot settings.ibis (new) Opt-in Fandom persona bot; cursor-based missed-content sync on reconnect

Detailed script inventory, alias notes, and popup mapping: docs/ibis-script-reference.md.

Context menus (popups_*.ibis)

Implement Stimulus-driven menus mirroring:

Dialogs

Priority matrix: see docs/irc-client-foundations.md Β§20.


6. UI / UX requirements


7. Technical requirements


8. Phase 1 deliverables (MVP)

9. Phase 2 deliverables

10. Phase 3 deliverables


11. Success metrics


12. Legacy reference caveats

The Ibis mIRC scripts in reference/stuff were written for 2000s-era DALnet/Undernet networks. NickServ, ChanServ, and MemoServ menu items, startup aliases, and raw numeric handlers are historical defaults β€” not a portable spec for a modern web client.

12.1 Outdated services assumptions

Do not port these patterns literally:

Modern networks differ: SASL PLAIN, CertFP, different service nicks (e.g. nickserv@services.dal.net on DALnet via pns in aliases1.ibis), or no services at all (EFnet). Auth and recovery commands must be configured per network, never assumed globally.

12.2 Per-network connection profiles

Mirror mIRC’s servers.ini groups with a saved IrcNetworkProfile per network:

Field Purpose
services_available Whether services exist on this network
nickserv_target Nick or nick@host (e.g. legacy nickserv@services.dal.net)
auth_strategy :none, :msg_identify, :sasl_plain, etc.
supported_commands Subset of identify / ghost / recover / register actually used
chanserv_join_recovery Whether to attempt invite/ban recovery on 473/474
startup_sequence Ordered hooks (ghost β†’ nick β†’ identify β†’ mode +i β†’ autojoin delay)

Encrypted credentials stay per profile, not one global %ibisword.

12.3 Connection notes β€” capability discovery

The legacy @stuff status window (events.ibis opens it on start; raw.ibis feeds it) is the model for the web client’s connection log. On each connect, parse and surface startup IRC numerics, then gate services commands on what the server actually supports.

Startup IRC numerics (from reference/stuff/raw.ibis):

Numeric Legacy use Web client use
001–003 Connection banner Status bar + connection notes
004–005 %stUserModes, %stChanModes, PREFIX, CHANTYPES Mode UI + dynamic menus (setDynamicMenu)
251–255, 265–266 echo_startup_stats / startup dialog Optional stats panel (parity blah.ibis startup dialog)
375–377 @MOTD window + MOTD footer MOTD pane; scan for register/identify instructions
378 β€œconnecting” server messages Connection progress log
433 Auto-ghost Only if profile enables ghost
473/474/477 ChanServ recovery prompts Only if profile enables chanserv recovery

MOTD text often documents the network’s real registration flow β€” treat it as authoritative over hardcoded menu commands. Read connection notes (MOTD + startup numerics) to determine what is supported before enabling NickServ/ChanServ actions.

12.4 Implementation mapping

See Β§4 architecture: network_profile.rb, capability_probe.rb, and connection_notes.rb under app/services/irc/.


13. Fandom companion bot (offline sync)

Closing the browser tab drops the ActionCable subscription. Without a persistent agent, missed messages, nick-list changes, and topics are lost when the user returns.

Each web IRC user may enable a companion bot in settings. While enabled, a Rabbot instance (persona from a fandom.com wiki page) maintains channel presence and writes structured events to server storage. On reconnect, the client fetches and renders everything since its last cursor β€” no manual scroll-back or /names refresh.

13.1 Problem and solution

Problem: The web client is ephemeral; IRC traffic continues whether or not a tab is open.

Solution: An opt-in companion bot acts as a per-user bouncer (see Β§3 shared bouncer model). It logs IRC events server-side and replays them when the browser reconnects.

13.2 Settings (options dialog)

Add to the settings dialog (Β§5 dialogs / Phase 2 deliverables):

Setting Type Notes
companion_bot_enabled boolean Off by default; master toggle
companion_fandom_url URL Any *.fandom.com/wiki/... page (character, episode, item, location, etc.)
companion_visibility enum :visible (separate nick in channel) or :invisible (logger-only; no channel nick)
companion_stay_in_channels boolean When tab closed, bot remains in channels user had open (default: true)

Activation flow:

  1. User pastes fandom URL β†’ server validates via CharacterTranscripts.fandom_url?

  2. Server generates or loads CharacterProfile (reuse CharacterProfileGenerator for first-time URLs; cache YAML per user)

  3. Preview shows character name + summary before save

  4. On enable, provision companion bot config (nick derived from profile name, IRC-sanitized)

  5. On disable, graceful quit; optional event retention policy (keep N days for re-enable)

13.3 Fandom persona source

Any fandom wiki page is valid β€” not limited to characters:

Reuse existing Rabbot libs (lib/character_profile.rb, lib/character_profile_generator.rb, lib/character_transcripts.rb); no new fandom parser required for v1.

Bot behaviour (non-sync): Companion is a lightweight Rabbot instance β€” CharacterResponder + minimal plugins only (no AI spam). Persona is flavour; sync is the primary job.

13.4 Sync and catch-up

Event store (new service; do not reuse AiChannelLog, which caps at 10 entries):

Cursor model:

13.5 Visibility modes

User selects mode via companion_visibility in settings (see Β§13.2).

13.6 Architecture

See Β§4 architecture for irc_companion_profile.rb, irc_event.rb, companion_bot_provisioner.rb, event_store.rb, sync_catchup.rb, and companion_bot_lifecycle_job.rb.

Real-time catch-up flow:

  1. Companion bot (or bouncer worker) appends IRC events to IrcEventStore

  2. Browser tracks last_event_id per channel

  3. On reconnect, sync_catchup.rb builds Turbo Stream payload for events since cursor

  4. Stimulus appends missed lines and refreshes nick list / topic from snapshot events

13.7 Phased deliverables

Phase 2 (core companion bot):

Phase 3 (hardening):

13.8 Success metrics

See Β§11 success metrics for companion-bot-specific targets.

13.9 Non-goals


14. Foundations supplement

Build-from-scratch decisions (gem packaging, optional auth, schema, workers, embed API, style.ibis mapping, dialog triage, deployment, tests) are documented in docs/irc-client-foundations.md.

Key decisions:


End of PRD. Generated by !directchat prd from Rabbot directchat plugin. Foundations supplement: docs/irc-client-foundations.md (Β§14–§24). Script reference: docs/ibis-script-reference.md.