Telegram-WhatsApp Bridge
Table of Contents
I almost lost a friend… π’
He lives in the US and only uses Telegram, while I use Signal and WhatsApp. Our communication β and our friendship β had been in decline due to our user habits…

To save our friendship, I implemented a bidirectional message forwarding system between Telegram and WhatsApp, running entirely on my hardware.
No cloud services, no third parties, just a Raspberry Pi acting as a message router.
This post explores the technical architecture, security considerations, and the technologies that make this possible.
GΓD’S GATE
π If you like this project, then fellow enthusiast, thereβs a statistically significant chance youβll enjoy my sci-fi novel GΓD’S GATE π, check it out! πΎπ
Run it yourself?
If you want to run it yourself, clone this GitHub repo and follow the setup.
Donations?
You can also toss me a coin: Paypal
The Problem
Telegram and WhatsApp are isolated ecosystems. If a friend messages you on Telegram while you’re primarily using WhatsApp, you need to constantly switch apps (end user multihoming).
Traditional solutions:
- Manually copying messages (tedious)
- Third-party services (privacy concerns)
- IFTTT/Zapier (limited, requires cloud intermediaries)
Solution: A self-hosted bridge running on a Raspberry Pi that automatically forwards messages in both directions.
The downside is that you still need an account in both WhatsApp and Telegram. But if you are already multihoming, then there’s none. Another downside is that if you have dozens friends that ise Telegram, then it could become a bit messy in your personal chat with all the tasgs from each friend. This is perfect for when you have a few friends in Telegram, that’s manageable on your chat.
Architecture Overview
High-Level Design
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Raspberry Pi β
β β
β ββββββββββββββββββ ββββββββββββββββββ β
β β Telegram ββββββββββΊβ Node.js β β
β β Client β β Bridge β β
β β (gramjs) β β (index.js) β β
β ββββββββββββββββββ ββββββββββββββββββ β
β β β β
β β βΌ β
β β ββββββββββββββββββββββββββ β
β β β WhatsApp Web Client β β
β β β (Puppeteer/Chromium) β β
β β ββββββββββββββββββββββββββ β
β β β β
βββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββββ
β β
βΌ βΌ
Telegram Servers WhatsApp Servers
(MTProto Protocol) (WhatsApp Web Protocol)
Data Flow
Telegram β WhatsApp:
- Friend sends message on Telegram
- Telegram client (logged in as you) receives event via MTProto
- Bridge script captures message, validates sender ID
- Formats message with prefix:
π¨ TG | Friend: message - WhatsApp client sends to your WhatsApp number
- You see it on your phone
WhatsApp β Telegram:
- You send message to yourself on WhatsApp:
tg:friend Hello! - WhatsApp client detects message via Puppeteer
- Bridge parses prefix and tag
- Removes prefix, keeps:
Hello! - Telegram client sends to specified friend
- Confirmation sent back to WhatsApp
Technology Stack
1. Node.js - The Runtime
Why Node.js?
- Event-driven architecture: Perfect for real-time messaging (asynchronous I/O)
- Single-threaded event loop: Efficiently handles multiple connections
- Rich ecosystem: Both Telegram and WhatsApp have Node.js libraries
- Low resource usage: Runs smoothly on Raspberry Pi’s limited RAM
How it’s used:
- Main bridge runs as a long-lived process
- Listens for events from both platforms simultaneously
- Non-blocking I/O prevents one platform from blocking the other
2. gramjs (Telegram Client Library)
What is gramjs?
- Pure JavaScript implementation of Telegram’s MTProto protocol
- Fork of GramJS, specifically designed for Node.js
- Supports userbot mode (logs in as a user, not a bot)
MTProto Protocol:
- Telegram’s custom protocol (not HTTP/WebSocket)
- Persistent connection over TCP
- Binary serialization (TL schema)
Why userbot instead of bot?
| Feature | Bot API | Userbot (gramjs) |
|---|---|---|
| Read private messages | β | β |
| Access to user’s chats | β | β |
| Send as yourself | β | β |
| Two-factor auth support | N/A | β |
| Rate limits | Stricter | More lenient |
Session persistence:
- Session saved as encrypted string
- Contains auth key, server salt, sequence numbers
- No password stored, only cryptographic session tokens
- Expires after ~30 days of inactivity
3. whatsapp-web.js (WhatsApp Client Library)
What is whatsapp-web.js?
- Reverse-engineered WhatsApp Web client
- Uses Puppeteer to control headless Chrome
- Intercepts WebSocket messages from WhatsApp servers
- Not official - based on observing web.whatsapp.com behavior
How it works:
- Launches headless Chrome browser
- Navigates to
web.whatsapp.com - Displays QR code for authentication
- Saves session in
.wwebjs_auth/(includes encryption keys) - Maintains WebSocket connection
- Exposes JavaScript API for sending/receiving messages
Puppeteer under the hood:
- Controls Chrome via DevTools Protocol
- Runs in “headless” mode (no visible window)
- Requires Chromium binary (~150MB)
- Memory-intensive (~200-300MB RAM)
Limitations:
- Unofficial API (could break if WhatsApp changes web client)
- Phone must stay online and connected to internet
- Sessions expire if phone is offline for extended periods
- Can’t bypass WhatsApp’s rate limits
Security Layers
1. Authentication & Authorization
Telegram:
- 2FA supported (optional but recommended)
- Session-based auth (no password stored)
- API credentials tied to your account
- Friend ID whitelist (only configured IDs forwarded)
WhatsApp:
- QR code authentication (proves phone ownership)
- Session encrypted by WhatsApp Web protocol
- Only processes messages from YOUR number
- Prefix requirement prevents accidental forwarding
2. Data Privacy
What’s stored:
.envfile (credentials) - never committed to Gittelegram-session.txt(session token) - gitignored.wwebjs_auth/(WhatsApp session) - gitignored- Logs (message previews) - stored only in RAM, cleared on restart
What’s NOT stored:
- Full message history (messages processed in real-time, then discarded)
- Media files (text-only forwarding)
- Passwords (only session tokens)
Encryption at rest:
- Session files encrypted by libraries (not plain text)
.envfile in plain text (protected by file permissions)- Docker volumes owned by
nodeuser (UID 1000)
Encryption in transit:
- Telegram: MTProto
- WhatsApp: Signal Protocol (end-to-end encrypted)
- Docker Hub: TLS (HTTPS)
3. Network Security
Firewall considerations:
- Bridge doesn’t open any ports (outbound connections only)
- Telegram: Connects to
149.154.167.xon port 443 (HTTPS) - WhatsApp: WebSocket to
web.whatsapp.comon port 443 - No incoming connections needed
4. Code Security
Input validation:
// Prevent BigInt conversion errors
friendTelegramIds.forEach(id => {
if (!/^\d+$/.test(id)) {
throw new Error('FRIEND_TELEGRAM_IDS must be numeric');
}
});
// Sanitize tag input
const tag = tagPart.replace(/[^a-z0-9]/gi, '').toLowerCase();
Rate limiting:
- Telegram: Built into gramjs (respects server limits)
- WhatsApp: Inherits web.whatsapp.com limits
- Consider adding client-side rate limiting for production
Error handling:
- Try-catch blocks prevent crashes
- Graceful degradation (skip invalid messages)
- Detailed logging for debugging
- No sensitive data in logs
5. Deployment Security
Docker security:
- Non-root user (
USER node) - Read-only filesystem where possible
- Resource limits (prevents DoS via memory exhaustion)
- No privileged mode
Secrets management:
# .env file permissions
chmod 600 .env # Owner read/write only
# Session file permissions
chmod 600 telegram-session.txt
chmod -R 700 .wwebjs_auth/
Git security:
# Never commit these
.env
.env.*
*.session
telegram-session.txt
.wwebjs_auth/
4. Docker - Containerization
Key considerations:
- Alpine Linux (minimal, 5MB base image)
- Chromium package (WhatsApp needs a browser)
- Build tools (node-gyp compiles native modules)
- Security (run as
nodeuser, not root)
5. Docker Compose - Orchestration
Deployment flow:
Local Mac β Build β Push to Docker Hub β Watchtower detects β Auto-deploy on Pi
Why this matters:
- No SSH needed for updates
- Zero-downtime deployments
- Rollback by pushing older tag
- Consistent deployment process
Security Architecture
Threat Model
What we’re protecting against:
- β Unauthorized access to your messages
- β Leaking credentials to third parties
- β Accidentally forwarding wrong messages
- β Remote attacks on the Pi
What we’re NOT protecting against:
- β Physical access to the Pi (assumes trusted hardware)
- β Compromised Telegram/WhatsApp servers (assumes trust in the platforms)
- β Nation-state actors (out of scope)
Security Layers
1. Authentication & Authorization
Telegram:
- 2FA supported (optional but recommended)
- Session-based auth (no password stored)
- API credentials tied to your account
- Friend ID whitelist (only configured IDs forwarded)
WhatsApp:
- QR code authentication (proves phone ownership)
- Session encrypted by WhatsApp Web protocol
- Only processes messages from YOUR number
- Prefix requirement prevents accidental forwarding
2. Data Privacy
What’s stored:
.envfile (credentials) - never committed to Gittelegram-session.txt(session token) - gitignored.wwebjs_auth/(WhatsApp session) - gitignored- Logs (message previews) - stored only in RAM, cleared on restart
What’s NOT stored:
- Full message history (messages processed in real-time, then discarded)
- Media files (text-only forwarding)
- Passwords (only session tokens)
Encryption at rest:
- Session files encrypted by libraries (not plain text)
.envfile in plain text (protected by file permissions)- Docker volumes owned by
nodeuser (UID 1000)
Encryption in transit:
- Telegram: MTProto
- WhatsApp: Signal Protocol (end-to-end encrypted)
- Docker Hub: TLS (HTTPS)
3. Network Security
Firewall considerations:
- Bridge doesn’t open any ports (outbound connections only)
- Telegram: Connects to
149.154.167.xon port 443 (HTTPS) - WhatsApp: WebSocket to
web.whatsapp.comon port 443 - No incoming connections needed
4. Code Security
Input validation:
// Prevent BigInt conversion errors
friendTelegramIds.forEach(id => {
if (!/^\d+$/.test(id)) {
throw new Error('FRIEND_TELEGRAM_IDS must be numeric');
}
});
// Sanitize tag input
const tag = tagPart.replace(/[^a-z0-9]/gi, '').toLowerCase();
Rate limiting:
- Telegram: Built into gramjs (respects server limits)
- WhatsApp: Inherits web.whatsapp.com limits
- Consider adding client-side rate limiting for production
Error handling:
- Try-catch blocks prevent crashes
- Graceful degradation (skip invalid messages)
- Detailed logging for debugging
- No sensitive data in logs
5. Deployment Security
Docker security:
- Non-root user (
USER node) - Read-only filesystem where possible
- Resource limits (prevents DoS via memory exhaustion)
- No privileged mode
Secrets management:
# .env file permissions
chmod 600 .env # Owner read/write only
# Session file permissions
chmod 600 telegram-session.txt
chmod -R 700 .wwebjs_auth/
Git security:
# Never commit these
.env
.env.*
*.session
telegram-session.txt
.wwebjs_auth/
Scalability & Performance
Resource Usage
Typical metrics (on Raspberry Pi 4):
- RAM: 250-400MB (mostly Chromium)
- CPU: <5% idle, 10-20% when processing messages
- Network: ~1KB/message (text only)
- Disk: ~500MB (Node.js + dependencies + Chromium)
Bottlenecks:
- Puppeteer/Chromium (memory-heavy)
- WhatsApp Web (WebSocket latency)
- Media forwarding (if implemented) - disk I/O
Limitations
Throughput:
- ~10 messages/minute (WhatsApp rate limit)
- Telegram more lenient (~30 messages/minute)
- No batching (messages processed one-by-one)
Latency:
- Telegram β WhatsApp: 1-3 seconds
- WhatsApp β Telegram: 2-5 seconds
- Depends on network conditions
Scalability:
- Current design: Single-instance only (no horizontal scaling)
- Multiple friends: Works (tested with 3, should handle 10+)
- Multiple Pis: Possible but requires separate accounts
Reliability & Error Handling
Failure Modes
| Failure | Impact | Mitigation |
|---|---|---|
| Telegram API down | No receiving from Telegram | Auto-reconnect logic (5 retries) |
| WhatsApp session expired | No sending to Telegram | Manual QR re-scan required |
| Network outage | Complete failure | Docker restart on recovery |
| Pi power loss | Complete failure | restart: unless-stopped |
| RAM exhaustion | Container killed by OOM | mem_limit: 512M prevents cascade |
Monitoring
What to monitor:
# Container health
docker ps
docker stats telegram-whatsapp-bridge
# Logs (real-time)
docker-compose logs -f --tail=50
# Resource usage
free -h
df -h
Alert on:
- Container restart loops (indicates bug)
- High memory usage (>450MB sustained)
- No log activity for >10 minutes (indicates hang)
Conclusion
This project demonstrates how modern JavaScript libraries enable powerful automation even on resource-constrained hardware like Raspberry Pi. By leveraging userbot capabilities, reverse-engineered APIs, and containerization, I built a robust message bridge that respects privacy while providing seamless cross-platform communication.
Key takeaways:
- MTProto (Telegram) is a custom protocol optimized for mobile
- Puppeteer enables browser automation for platforms without official APIs
- Docker makes deployment reproducible across architectures
- Event-driven architecture is perfect for real-time messaging
- Security layers (whitelisting, validation, encryption) prevent common attack vectors
Lexicon
General Terms
Bridge: Software that connects two different systems, translating messages between them.
Userbot: A bot that operates using a regular user account (not a dedicated bot account). Has full access to user’s messages and capabilities.
Session: An authenticated connection that persists across restarts. Contains cryptographic tokens to prove identity without re-entering passwords.
Headless Browser: A web browser without a graphical interface, controlled programmatically (e.g., Puppeteer controlling Chrome).
Container: Isolated environment that packages an application with its dependencies. Like a lightweight virtual machine.
Orchestration: Managing multiple containers, their configuration, networking, and lifecycle.
Telegram-Specific
MTProto: Telegram’s custom network protocol. Binary, encrypted, optimized for mobile networks. Not HTTP-based.
gramjs: JavaScript library that implements MTProto protocol. Allows Node.js apps to act as Telegram clients.
StringSession: A serialized representation of a Telegram session (auth key, server info, etc.). Can be saved as text and reloaded.
api_id / api_hash: Credentials that identify your “application” to Telegram servers. Required to use MTProto API.
Entity: Telegram’s term for Users, Groups, Channels, or Bots. Each has a unique numeric ID.
NewMessage Event: An event fired when a new message is received. Used to trigger forwarding logic.
WhatsApp-Specific
WhatsApp Web: Desktop version of WhatsApp that runs in a browser (web.whatsapp.com). Syncs with phone via QR code.
whatsapp-web.js: Unofficial library that reverse-engineers WhatsApp Web. Uses Puppeteer to control Chrome.
QR Code Authentication: WhatsApp’s method to link devices. Encodes encryption keys that phone scans to establish secure connection.
LocalAuth: Authentication strategy in whatsapp-web.js that saves session locally in .wwebjs_auth/ folder.
@c.us: WhatsApp’s internal suffix for personal contacts (e.g., [email protected]).
@g.us: WhatsApp’s internal suffix for group chats.
Node.js & JavaScript
Event Loop: JavaScript’s mechanism for handling asynchronous operations. Processes events (messages, network responses) one at a time.
Event Handler: A function that runs when a specific event occurs (e.g., new message received).
async/await: Modern JavaScript syntax for handling asynchronous operations. Makes async code look synchronous.
Buffer: Raw binary data in Node.js. Used for handling files, images, network data.
BigInt: JavaScript data type for integers larger than 2^53. Telegram IDs exceed JavaScript’s Number limit.
require() / import: Ways to load modules (libraries) in Node.js.
process.env: Environment variables accessible to Node.js (from .env file via dotenv package).
Docker
Image: A snapshot of a filesystem with an application and its dependencies. Immutable, versioned.
Container: A running instance of an image. Isolated process with its own filesystem, network, and resources.
Volume: Persistent storage that survives container restarts/recreations. Used for sessions, configs.
Docker Compose: Tool to define and run multi-container applications using a YAML file.
Dockerfile: Recipe for building a Docker image. Specifies base image, dependencies, commands.
Alpine Linux: Minimal Linux distribution (~5MB). Common base for Docker images due to small size.
buildx: Docker tool for building multi-platform images (e.g., ARM64 for Pi, AMD64 for PC).
Registry: Storage for Docker images. Docker Hub is the most popular public registry.
Watchtower: Tool that automatically updates running containers when new images are pushed to registry.
Puppeteer
Puppeteer: Node.js library to control Chrome/Chromium browsers programmatically. Developed by Google Chrome team.
Headless Mode: Running a browser without a visible window. Saves resources, enables automation.
DevTools Protocol: API that allows external programs to control Chrome. Puppeteer uses this under the hood.
Chromium: Open-source browser that Chrome is based on. Puppeteer downloads its own copy (~150MB).
Security
End-to-End Encryption (E2EE): Messages encrypted on sender’s device, only decrypted on recipient’s device. Servers can’t read content.
Session Token: Cryptographic proof of authentication. Allows reconnection without password.
Whitelist: List of allowed entities (friend IDs). Messages from non-whitelisted sources are ignored.
Rate Limiting: Restricting how many operations can be performed in a time window. Prevents spam and abuse.
OOM (Out Of Memory): When a process uses more RAM than allowed. Docker kills the container to protect the system.
TLS/HTTPS: Encryption protocol for web traffic. Ensures data isn’t intercepted during transmission.
Signal Protocol: WhatsApp’s encryption protocol. Also used by Signal messenger. Provides E2EE.
Raspberry Pi
ARM64: CPU architecture used by Raspberry Pi 4. Different from x86/AMD64 used by Intel/AMD PCs.
SSH: Secure Shell - protocol for remote command-line access to the Pi.
Protocols
TCP/IP: Fundamental internet protocol. Reliable, ordered data transmission. Used by Telegram (MTProto over TCP).
WebSocket: Protocol for real-time bidirectional communication over HTTP. Used by WhatsApp Web.
HTTP/HTTPS: Web protocol for request/response communication. WhatsApp Web used this initially, then upgraded to WebSocket.
TL (Type Language): Telegram’s schema language. Defines structure of MTProto messages in a compact binary format.