Files
xmpp-server-prosody/references/slidge-telegram-bridge-nixg.md
2026-09-06 13:51:11 +00:00

5.6 KiB

XMPP↔Telegram bridge via Slidge/slidgram (nixg.ru) — implemented 2026-08-29

STATUS: WORKING. Component telegram.nixg.ru authenticates in Prosody, Slidge starts, Telegram MTProto reachable over SOCKS5. Remaining: XMPP-side registration of the Telegram account (message register to telegram.nixg.ru from an XMPP client as an admin user) + api_id/api_hash. Port 5347 stays internal to the docker network (NOT mapped to host — correct).

Architecture (this environment)

[XMPP client (Gajim/Dino/Converse)]
        │ c2s 5222
        ▼
[Prosody container icq-prosody]  ← component port 5347 (docker-internal)
        ▲  XEP-0114 external component, JID telegram.nixg.ru
[slidgram container icq-slidgram]  (image slidgram-proxy:latest, Pyrogram)
        │ MTProto → SOCKS5 172.27.0.1:1080   (docker bridge gateway = host loopback)
        ▼
[VPS01 SSH tunnel telegram-tunnel.service] → api.telegram.org / MTProto DCs (outside RF)

Key facts (correct the older planning notes)

  • Image: codeberg.org/slidge/slidgram (NOT ghcr.io/slidge/slidge-telegram; ghcr.io times out from RF). Installed inside: Pyrogram (2.3.69) + PySocks (import socks — PySocks-1.7.1 is ALREADY in the image; do not pip-install it). slidgram version 0.4.2.dev0.
  • Prosody config — secret goes INSIDE the Component stanza (0.11 behaviour):
    component_ports = { 5347 }              -- global section
    Component "telegram.nixg.ru"
        component_secret = "<secret>"       -- global component_secrets table is IGNORED in 0.11
        modules_enabled = { "disco" }
    
    Without this: Prosody log Component attempted to identify as telegram.nixg.ru, but component_secret is not set; slidgram sees Stream error: not-authorized and loops restarting until it authenticates.

SOCKS5 routing (required from RF — Telegram blocked; direct api.telegram.org times out http=000)

  • Host has telegram-tunnel.service: SSH -D 0.0.0.0:1080 → VPS01 (outside RF). Docker bridge gateway = host loopback, so a container in icq_default (172.27.0.0/16) reaches it at 172.27.0.1:1080.
  • slidgram does NOT expose a proxy option in CLI/help/config. The Pyrogram Client is constructed in slidgram/telegram.py and slidgram/gateway.py without proxy=. Fix: build a custom image and patch both files to read SLIDGRAM_PROXY=socks5://host:port env and pass proxy=dict(...) to the Client. Pyrogram accepts proxy=dict(scheme="socks5", hostname=..., port=...) (pysocks already installed). Custom image lives at /opt/icq/slidgram/ (Dockerfile + patch-telegram.py + patch-gateway.py).
  • Verify from inside the container: python socks socket to MTProto DC 149.154.167.51:443 via proxy → OK.

docker-compose service

  slidgram:
    image: slidgram-proxy:latest
    container_name: icq-slidgram
    restart: unless-stopped
    volumes:
      - ./slidgram/data:/var/lib/slidge
    environment:
      - SLIDGE_JID=telegram.nixg.ru
      - SLIDGE_SECRET=<secret>            # MUST match Component component_secret
      - SLIDGE_SERVER=icq-prosody
      - SLIDGE_PORT=5347
      - SLIDGE_HOME_DIR=/var/lib/slidge
      - SLIDGE_ADMINS=admin@nixg.ru
      - SLIDGRAM_PROXY=socks5://172.27.0.1:1080
    depends_on:
      - prosody

Do NOT set network_mode: bridge — it isolates the container from icq_default and it can no longer resolve icq-prosody. Leave it in the project network.

Pitfalls hit (all fixed)

  • Volume ownership: image runs as uid 10000 (slidge). A root-owned bind mount → SQLAlchemy unable to open database file crash loop. Fix: sudo chown -R 10000:10000 <hostdata>.
  • rm in Dockerfile RUN fails with Operation not permitted when the build runs as an unprivileged user (COPY writes root-owned files the USER can't delete from /tmp). Leave the patch scripts in place; don't rm them.
  • http avatar/logo download (https://web.telegram.org/img/logo_share.png) times out from RF — slidge's own aiohttp HTTP client does NOT go through the SOCKS5 proxy (only Pyrogram MTProto does). Non-blocking (avatar fetch), but means some https fetches to Telegram assets fail; not required for messaging.

Registration (user side, next step)

  • Docs: user/registration.rst — two ways: adhoc "Register" command (Gajim/ Movim/Cheogram) or just send register as a message to the gateway address.
  • Gateways' JID is the component domain telegram.nixg.ru (no @).
  • Needs Telegram api_id/api_hash; if not preset, the registration form asks for them.

References / docs saved in-project

  • Offline copy of slidge.im/docs/slidgram/main (18 pages + styles) + README at /opt/icq/docs/slidgram/. Prosody config examples are inline in main/admin/examples/index.html (#prosody-upload / #prosody-no-upload): they need mod_privilege + privileged_entities on the VirtualHost for roster-sync/legacy carbons, and upload as a Component (http_file_share).
  • STATUS.md (/opt/icq/STATUS.md) tracks bridge state; plan file /opt/hermes/.hermes/plans/2026-08-29_085200-telegram-bridge.md had the original (now partly outdated) plan.

Superseded planning notes (kept for history)

  • Earlier notes proposed Telethon / ghcr.io/slidge/slidge-telegram / global component_secrets table — all WRONG as implemented. The image ships Pyrogram, ghcr is blocked from RF, and 0.11 needs per-Component component_secret.
  • mautrix-telegram remains the WRONG tool for XMPP (Matrix-only); do not propose.
  • Ban risk: user accepted low risk for a single quiet personal account; a dedicated spare number is the hygiene recommendation.