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> textformat, 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
+band kicked. Off by default (sync_bans) - Smart replies - XEP-0461 replies from XMPP become IRCv3
+replyfrom nick puppets (no quote prefix when the msgid is known), ornick:/ 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/unreacttags are bridged natively to XMPP reactions - Message edits - XEP-0308 corrections are relayed to IRC as
* corrected text, with IRCv3+replypointing 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; setcomponent = truefor XEP-0114 component mode[[irc]]- one entry per IRC network; setrelaymsg = trueto enable RELAYMSG, orpuppet_mode = "nicks"for connected nick puppets[[bridge]]- one entry per MUC↔channel pair, referencing thenamefields 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.
- XMPP: j2i@room.telepath.im
- IRC: #j2i on irc.telepath.im
The MUC and channel are bridged together with j2i.
License
This project is released into the public domain under the Unlicense.