Skip to content

Bridge replies both ways using IRCv3 reply tags - #95

Open
RustyBower wants to merge 4 commits into
zachbr:masterfrom
RustyBower:irc-threaded-replies
Open

RustyBower wants to merge 4 commits into
zachbr:masterfrom
RustyBower:irc-threaded-replies

Conversation

@RustyBower

@RustyBower RustyBower commented Oct 18, 2026 •

Copy link
Copy Markdown

Replies are currently bridged as a Reply to "…" context line plus the message on IRC, and as plain messages on Discord. On IRC servers that support IRCv3 message tags, this makes them real replies in both directions:

  • Discord → IRC: replies are sent with the +draft/reply client tag, so clients such as IRCCloud, The Lounge and recent WeeChat show them threaded under the original.
  • IRC → Discord: IRC replies (+draft/reply, or +reply as some clients also send) to a bridged message are sent as Discord replies to its Discord copy.

How it works

  • The bridge requests echo-message (KICL already requests message-tags). When the server echoes back a line the bridge sent, the bridge records the msgid the server assigned it against the Discord message it came from.
  • For IRC messages bridged to Discord, the Discord message ID returned by the webhook or bot send is recorded against the IRC message's msgid.
  • The mappings are kept in both directions in an in-memory LRU cache (2048 entries). Nothing is persisted.
  • Discord webhooks can't send replies, so IRC replies that can be threaded are sent by the bot as <nick> text with a message reference. Everything else still goes through webhooks as before.

Fallback

Nothing changes unless the server supports it:

  • no message-tags, or draft/reply blocked by CLIENTTAGDENY → no tags are sent
  • no echo-message → msgids of the bridge's own messages aren't recorded

In those cases, and for replies to messages the bridge doesn't know (e.g. from before a restart), messages are bridged exactly as before, including the context line on IRC.

New options

irc {
    # Whether to send Discord replies as IRCv3 threaded replies (+draft/reply), if the server supports it.
    discord-reply-tags=true
    # Whether to also send the reply context line when a reply is sent threaded, for clients without
    # IRCv3 reply support. Unthreaded replies always get it.
    discord-reply-context-with-tags=false
}
discord-options {
    # Whether IRCv3 replies from IRC to bridged messages are sent as Discord replies.
    # Webhooks can't reply, so these are sent by the bot itself.
    thread-irc-replies=true
}

Bridge can now be created without the main Dis4IRC instance, so tests can use it on its own.

Testing

  • An in-process fake IRCv3 server (test only) runs the real IRC pier. The tests cover:
    • msgid tracking via echo-message
    • reply tags for Discord-origin and IRC-origin parents
    • long replies being tagged on every part
    • the context line with and without threading
    • falling back without message-tags, without echo-message, and with CLIENTTAGDENY
  • Unit tests for the ID cache, the reply-tag parsing and resolving IRC replies to Discord messages.
  • Run live between a Discord server (webhook channel) and UnrealIRCd 6.2.7, which advertises CLIENTTAGDENY=*,-draft/reply,…, viewed in IRCCloud:
    • Discord replies to IRC and to Discord messages were threaded in IRCCloud.
    • IRCCloud replies to Discord and to IRC messages arrived as Discord replies.

The fake-server tests add about a minute to ./gradlew test, mostly KICL's send rate limit.

When the IRC server supports message-tags and echo-message, Discord
replies are sent with a +draft/reply tag pointing at the IRC copy of the
message being replied to, so IRCv3 clients (IRCCloud, The Lounge, etc.)
show them threaded.

The bridge requests echo-message and records the msgid the server
assigns each bridged Discord message, and records which Discord message
each IRC message became when posted. Lookups use an in-memory LRU cache;
replies to messages it doesn't know fall back to the existing context
line. CLIENTTAGDENY is respected.

New irc options:
- discord-reply-tags (default true)
- discord-reply-context-with-tags (default true): keep the
  'Reply to' context line for clients without reply support
…nthreaded

IRC messages with an IRCv3 reply tag (+draft/reply or +reply) that point
at a bridged message are now sent to Discord as replies to its Discord
copy. Webhooks can't reply, so these are sent by the bot. This can be
turned off with discord-options.thread-irc-replies.

Message ids are now remembered in both directions to support this.

discord-reply-context-with-tags now defaults to false: threaded replies
are sent without the 'Reply to' context line, which threaded clients
already show. Replies that can't be threaded still get it.

Bridge can now be created without the main Dis4IRC instance, so it can
be used on its own in tests.
Adds a minimal in-process IRCv3 server so the IRC pier can be tested end
to end: echo-message msgid tracking, reply tags, line splitting, the
context line, and falling back when message-tags, echo-message or the
reply tag (CLIENTTAGDENY) aren't available.
@RustyBower RustyBower changed the title Send Discord replies to IRC as IRCv3 threaded replies Bridge replies both ways using IRCv3 reply tags Oct 18, 2026
@zachbr
zachbr self-requested a review November 2, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant