Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Teleport

A chat bridge that mirrors messages and medias between platforms (Currently it's Discord, Slack and Telegram) and is built so you can bolt on other platforms such as WhatsApp, Messenger and more later by writing a single connector class.

Every platform is essentially a peer, and each one runs when its credentials are configured, and a single bridge can span all of them at once.

Remote users show up with their own name and avatar (IF within platforms limitation) not as one generic bot account.

slack-icon discord-icon telegram-icon

Features

  • Per-user identity. Remote users are posted under their own name + avatar where the platform supports it (Discord webhooks, Slack message customization).
  • Pairing-code setup. No hardcoded channel IDs, run /setup on one side, type the code into /setup <code> on the other.
  • Multiple bridges. Link as many channel ↔ chat pairs as you like.
  • All media types. Photos, videos, GIFs/animations, audio, voice notes, video notes, stickers, and documents at both directions. (The old bot silently dropped Telegram photos, videos, and stickers.)
  • Replies, edits, and deletes follow the message across platforms (deletes propagate from Discord and Slack; Telegram's API never reports them, so a Telegram deletion can't be mirrored).
  • Reply-pings. Replying to a bridged message pings the original author on the other platform.
  • Cross-platform @mentions (optional). @-mention a Discord member from Telegram (and vice versa). /link connects accounts with different names, /who finds someone's handle.
  • Persistent SQLite storage with automatic pruning of old message links.
  • Safe by default. @everyone/@here from a remote platform can't ping, and bot tokens are never leaked into message text.
Untitled

Setup

  1. Install dependencies (Python 3.10+):

    pip install -r requirements.txt
  2. Create the bots and get their tokens. You need at least two platforms (each starts only if its credentials are set). Step-by-step instructions for every token, scope, and setting are in CREDENTIALS.md. In short:

    • Discord - bot token + Message Content Intent; invite with Manage Webhooks, Send Messages, Read Message History.
    • Telegram - bot token from @BotFather; disable Group Privacy and re-add the bot to the group.
    • Slack - enable Socket Mode (app token), add bot scopes + install (bot token), subscribe to message.channels, create the slash commands, and /invite the bot to the channel.
  3. Configure:

    cp .env.example .env
    # then edit .env and paste in the tokens for the platforms you're using
  4. Run:

    python main.py

Linking a channel to a chat

  1. In the Discord channel, run /setup. The bot replies (only to you) with a pairing code like K7QMP4.
  2. In the Telegram group, send /setup K7QMP4 within 15 minutes.
  3. Done, messages now flow both ways. Either side can start the process; the other side just needs the code.

Other commands: /status (show what a channel is linked to) and /unlink (disconnect it). In Telegram groups these are admin-only; on Discord they need Manage Server. On Slack, because it reserves some names, /status is /bridgestatus and /who is /whois, everything else is the same.

Mentions & pings

Reply-pings - always on. Reply to someone's bridged message and the original author gets pinged: a real @ mention on Discord, a @user mention on Telegram. No configuration needed.

Cross-platform @mentions - optional. Set ENABLE_CROSS_MENTIONS=true to let people @mention users on the other platform. When someone types @handle, it's rewritten into a real ping on the destination platform. Resolution has two layers:

  1. Native members - no setup. @username is matched against the real member directory of the destination platform:

    • From Telegram → any Discord member, by username / display name / nickname.
    • From Discord → any Telegram user who has posted in the group.

    An exact username match wins over a fuzzy one, so @onion_rings reaches onion_rings even if onion_rings. also exists. Anything ambiguous stays plain text, nobody is mis-pinged.

  2. Linked accounts - /link. Discord and Telegram are separate identity systems, so there's no safe way to guess that "Alice" on one is "Alice" on the other. /link lets a person bond their two accounts into one identity, so either of their handles pings them on either side, useful when their names differ across platforms.

    1. Run /link on one platform → get a code.
    2. Run /link <code> on the other within 15 minutes → connected.

    /unlinkme disconnects. (Telegram users need a Telegram @username to be mentionable by handle.)

Finding a handle - /who <name>. Only see someone's display name and don't know their username? /who onion searches the other side of the bridge and returns the exact @handle to type.

Notes & limits:

  • A mention is a single token - multi-word display names (e.g. "Do I love Onion Rings?") can't be a mention target; use the username (@onion_rings). This is true on Discord and Telegram natively too.
  • The rewrite happens on the destination platform, not the one you type on. Typing @onion_rings on Telegram stays plain there but lands as a ping on Discord.
  • @everyone / @here and role mentions from a remote platform are always blocked.

Adding a new platform

The core (teleport/router.py) only knows the Connector interface in teleport/connectors/base.py. To add e.g. WhatsApp:

  1. Subclass Connector, set a unique platform string, and set the capability flags (supports_impersonation, supports_native_reply, reports_deletions).
  2. On each inbound message, build a BridgeMessage and call await self.router.on_message(...) (plus on_edit / on_delete if supported).
  3. Implement send_message / edit_message / delete_message.
  4. Register it in main.py.

No routing, storage, or other-connector code needs to change, and a bridge can span more than two platforms at once.

/setup, /link, and @mentions come almost for free. The pairing and identity-linking flows live on the Router, not in any connector. To support them on a new platform you only write thin adapters that (a) pull the platform's native user id / username and (b) reply, the actual logic is shared:

# a new platform's /link handler, in full:
if not self.router.enable_cross_mentions:
    return reply("Cross-platform mentions are off.")
if code:
    other = await self.router.complete_identity_link(code, self.platform, uid, username, display)
    reply("Linked!" if other else "Invalid or expired code.")
else:
    code = await self.router.begin_identity_link(self.platform, uid, username, display)
    reply(f"Your link code: {code}")

# and to rewrite @mentions in outgoing text:
await self.router.resolve_mention(handle, self.platform)  # -> linked account or None

Optionally implement search_directory(channel_id, query) (returns [(display_name, handle)]) and your platform is searchable from /who; skip it and it just returns nothing.

Finally, add the platform's env vars to config.py + .env.example, start the connector in main.py when they're present, and document how to obtain its tokens in CREDENTIALS.md (there's a copy-paste template at the bottom of that file).

Project layout

main.py                     entrypoint / wiring
teleport/
  config.py                 env loading
  models.py                 BridgeMessage, Attachment, ReplyContext, Endpoint
  db.py                     async SQLite storage
  router.py                 fans messages out to linked endpoints
  util.py                   shared helpers (codes, chunking, sizes)
  connectors/
    base.py                 the Connector interface
    discord_connector.py
    telegram_connector.py
    slack_connector.py

About

An advanced chat bridge that mirrors messages between Discord, Slack, and Telegram (built so you can implement other platforms) with persistent formatting and reply/edit/delete tracking.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages