kruno 8cfaea473b Implement XEP-0513 explicit mentions for IRC nick puppets
This update enhances the handling of mentions in the bridge between XMPP and IRC. Key changes include:

- IRC nick mentions of j2i puppets are now rewritten to their XMPP-visible nick and sent as explicit mentions using XEP-0513.
- Occupant IDs (XEP-0421) are utilized when available, ensuring accurate identification of users in MUCs.
- The documentation has been updated to reflect these changes, improving clarity on mention handling.

These improvements enhance the user experience by ensuring that mentions are accurately represented across platforms, fostering better communication.
2026-09-11 21:08:37 +02:00
2026-07-02 21:11:20 +02:00
2026-04-29 16:53:53 +02:00
2026-03-27 21:27:03 +01:00
2026-03-27 21:27:03 +01:00
2026-07-02 21:11:20 +02:00
2026-03-27 21:27:03 +01:00
2026-09-08 20:14:28 +02:00
2026-09-08 20:14:28 +02:00

j2i

A bridge between XMPP MUCs and IRC channels. Supports both basic plumbing (bot relays messages as text) and puppeteering mode (messages appear from the actual sender's nick).

Features

  • Basic plumbing mode - bridge bot relays messages in <nick> text format, works with any XMPP and IRC server
  • Puppeteering - XMPP users appear on IRC with their real nick via RELAYMSG or opt-in nick puppets (extra IRC connections); IRC users appear in XMPP MUCs as puppet JIDs via XEP-0114 component
  • Ban syncing - optional: when a component puppet is banned in a MUC, the corresponding IRC nick is given +b and kicked. Off by default (sync_bans)
  • Smart replies - XEP-0461 replies from XMPP become IRCv3 +reply from nick puppets (no quote prefix when the msgid is known), or nick: / quoted text for RELAYMSG and bot-relay; IRCv3 reply tags are preserved
  • Mentions - IRC nick mentions of j2i puppets (and of other IRC users) are rewritten to the XMPP-visible nick and sent as XEP-0513 explicit mentions, using XEP-0421 occupant ids when the MUC supports them (occupant JID otherwise)
  • Reactions - XMPP reactions (XEP-0444) are relayed to IRC as attributed text; IRC +draft/react/+draft/unreact tags are bridged natively to XMPP reactions
  • Message edits - XEP-0308 corrections are relayed to IRC as * corrected text, with IRCv3 +reply pointing at the original when its msgid is known
  • Pastebin - messages exceeding a configurable line limit are uploaded to a pastebin and linked instead of flooding
  • Typing indicators - XEP-0085 (XMPP) ↔ IRCv3 typing tag
  • Multiline messages - IRCv3 draft/multiline batches are joined into a single XMPP message; multi-line XMPP messages are sent to IRC as one batch when supported, with per-line fallback otherwise
  • Anti-ping - zero-width space inserted into relayed nicks to avoid unwanted highlights
  • Multiple networks - bridge as many XMPP/IRC connections and channel pairs as you want, each configured independently

Requirements

  • Python 3.11+
  • slixmpp

Installation

pip/pipx (PyPI)

You can install j2i from PyPI with pip:

pip install j2i

Or with pipx for an isolated environment:

pipx install j2i

pip/pipx (FSKY Foundry)

To download the package from FSKY Foundry instead of PyPI:

pip install j2i --pip-args="--index-url https://foundry.fsky.io/api/packages/fsky/pypi/simple --extra-index-url https://pypi.org/simple"

Or with pipx:

pipx install j2i --pip-args="--index-url https://foundry.fsky.io/api/packages/fsky/pypi/simple --extra-index-url https://pypi.org/simple"

From wheel

Download the wheel from the releases page and install with pip:

pip install j2i-*.whl

Running

Installed package

After installing, simply run:

j2i -c config.toml

Local development

Requires uv:

git clone https://foundry.fsky.io/fsky/j2i.git
cd j2i
cp config.example.toml config.toml
$EDITOR config.toml
uv run j2i -c config.toml

Docker/Podman

The image expects the config file at /config/config.toml.

docker run -v ./config.toml:/config/config.toml foundry.fsky.io/fsky/j2i:latest

Podman quadlet (systemd)

A quadlet unit file is provided in contrib/quadlet/j2i.container. It runs the container as a systemd user service with auto-update enabled and a read-only filesystem.

To install, place the unit file into .config/containers/systemd/ or /etc/containers/systemd/ and run:

systemctl --user daemon-reload
systemctl --user start j2i.service

Configuration

Copy config.example.toml and edit it. The example file has comments explaining every option.

The config has four sections:

  • [[xmpp]] - one entry per XMPP account or component; set component = true for XEP-0114 component mode
  • [[irc]] - one entry per IRC network; set relaymsg = true to enable RELAYMSG, or puppet_mode = "nicks" for connected nick puppets
  • [[bridge]] - one entry per MUC↔channel pair, referencing the name fields above
  • [settings] - global defaults (anti_ping, max_lines, pastebin, etc.); can be overridden per [[bridge]]

Basic plumbing mode (simple setup)

Set component = false in [[xmpp]] and relaymsg = false in [[irc]]. The bridge connects as a regular XMPP user and IRC bot and relays messages as <nick> text. No special server configuration needed.

Puppeteering mode (full setup)

IRC side (RELAYMSG): Set relaymsg = true in [[irc]]. The IRC bot must have operator status (+o) in the channel. The bridge detects RELAYMSG support on connect and falls back to prefixed bot messages (<nick> text) if unavailable.

IRC side (nick puppets): Set puppet_mode = "nicks" (or "auto") to connect a separate IRC nick per XMPP occupant. This is how you get native IRCv3 reactions and typing as that nick; RELAYMSG cannot send TAGMSG. Nicks look like alice|xmpp by default (| is legal in a real nick; / is not). Pool size and idle QUIT are per-network (max_puppets, puppet_idle_seconds; 0 means unlimited / never). Public networks often cap connections per IP — this is intended for small rooms or an ircd you control.

puppet_presence (default lazy): JOIN on first speak/react, idle-QUIT while the occupant is still lurking in the MUC, and PART immediately when they leave. A MUC kick or ban QUITs the IRC nick (or PARTs that channel with the kick/ban reason if the puppet is in other channels). Set eager to JOIN on MUC presence so /names matches the room; idle timeout is ignored, and a full pool never evicts someone still present (extra occupants overflow to prefixed bot text). Eager is for an ircd you control (max_puppets = 0, or a cap at least as large as the room).

Nick puppets register with the original MUC nick as GECOS (/whois realname) so sanitized IRC nicks remain attributable. MUC nick changes also SETNAME when the network supports it.

Mentioning a puppet nick on IRC (hello alice|xmpp) is rewritten on XMPP to the real MUC nick (hello alice) with a XEP-0513 mention. Mentioning a real IRC nick is forwarded as a mention of that user's XMPP puppet. Rooms that advertise occupant ids (XEP-0421) get occupantid; other rooms fall back to the occupant JID. Clients that do not support XEP-0513 still see the regular nick in the body.

puppet_mode values: relaymsg (default, current behaviour), nicks (never RELAYMSG), auto (RELAYMSG then nicks), prefix (always <nick> text). Existing configs that omit these keys are unchanged.

XMPP side: Set component = true in [[xmpp]] and configure your XMPP server with a component subdomain. Each IRC user will appear in the MUC as a puppet JID under that domain (e.g. johndoe.libera@irc.example.org). RELAYMSG nicks on IRC get a /xmpp suffix (e.g. alice/xmpp) to distinguish them from real IRC users.

Optional sync_bans (global, or per [[xmpp]] / [[irc]] / [[bridge]]) forwards a live MUC ban of a puppet to IRC as MODE +b nick!*@* plus KICK. The IRC bot needs channel operator status; if it does not have it, the failure is logged and ignored. Kicks-without-ban, unbans, and IRC→XMPP bans are not synced.

Support chatroom

If you want to ask anything or need assistance with j2i, we have a public chatroom on XMPP and IRC.

The MUC and channel are bridged together with j2i.

License

This project is released into the public domain under the Unlicense.

j2i v1.1.0
Latest
2026-04-29 14:46:30 +00:00
Languages
Python 99.7%
Dockerfile 0.3%