308 lines
8.9 KiB
Markdown
308 lines
8.9 KiB
Markdown
# WhatsApp Mirror — Full-Stack Real-Time Bridge
|
|
|
|
A production-grade, highly-responsive WhatsApp Mirror application. This system allows you to remotely view and manage a WhatsApp account on a dark-themed Flutter mobile app (iOS + Android) by bridging commands and real-time events over a standalone WebSockets Node.js bridge server.
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture
|
|
|
|
```
|
|
📱 Mobile Client (Flutter) ⚡ [WebSockets Protocol] ⚡ 🖥️ standalone Backend Server (Node.js) ⚙️ [Puppeteer] ⚙️ 🟩 WhatsApp Web (LocalAuth Session)
|
|
```
|
|
|
|
---
|
|
|
|
## 📂 Project Structure
|
|
|
|
```
|
|
whatsapp-app/
|
|
├── README.md ← Full system configuration & documentation
|
|
├── whatsapp_bridge/ ← standalone Node.js server
|
|
│ ├── package.json ← Package dependencies (whatsapp-web.js, ws, qrcode, express)
|
|
│ ├── server.js ← Bridge server entrypoint (Port 3025)
|
|
│ └── .wwebjs_auth/ ← Session persistence auth cache (git-ignored)
|
|
└── whatsapp_app/ ← Dark-themed Flutter Client
|
|
├── pubspec.yaml ← Flutter configuration and plugins
|
|
└── lib/
|
|
├── main.dart ← Flutter app entry point (GetX service initializer)
|
|
├── config/
|
|
│ └── app_config.dart ← Server host & port variables
|
|
├── theme/
|
|
│ └── app_theme.dart ← WhatsApp-style immersive Dark Theme
|
|
├── services/
|
|
│ └── whatsapp_service.dart ← Websocket request/response manager
|
|
├── models/
|
|
│ ├── conversation_model.dart
|
|
│ └── message_model.dart
|
|
├── controllers/
|
|
│ ├── conversations_controller.dart
|
|
│ └── chat_controller.dart
|
|
├── screens/
|
|
│ ├── conversations_screen.dart
|
|
│ ├── chat_screen.dart
|
|
│ └── qr_screen.dart
|
|
└── widgets/
|
|
├── conversation_tile.dart
|
|
└── message_bubble.dart
|
|
```
|
|
|
|
---
|
|
|
|
## ⚡ WebSockets Protocol Specification
|
|
|
|
To handle asynchronous requests and map them back to specific UI components or triggers, a strict **Request-Response ID matching mechanism** is implemented.
|
|
|
|
### 1. Client-to-Server Requests
|
|
Every request from the Flutter client MUST contain a unique `requestId`. The server replies with the exact same `requestId`.
|
|
|
|
#### A. Ping Check (`ping`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "ping", "requestId": "1" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{ "type": "pong", "ready": true, "requestId": "1" }
|
|
```
|
|
|
|
#### B. Get Conversation List (`get_conversations`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "get_conversations", "limit": 50, "offset": 0, "requestId": "2" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{
|
|
"type": "conversations",
|
|
"data": [
|
|
{
|
|
"id": "1234567890@c.us",
|
|
"name": "Jane Doe",
|
|
"isGroup": false,
|
|
"unreadCount": 2,
|
|
"avatar": "https://pps.whatsapp.net/v/...",
|
|
"lastMessage": {
|
|
"body": "Hello there!",
|
|
"timestamp": 1716035000,
|
|
"fromMe": false,
|
|
"hasMedia": false
|
|
},
|
|
"timestamp": 1716035000,
|
|
"pinned": false,
|
|
"isMuted": false
|
|
}
|
|
],
|
|
"total": 1,
|
|
"requestId": "2"
|
|
}
|
|
```
|
|
|
|
#### C. Get Message History (`get_messages`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "get_messages", "chatId": "1234567890@c.us", "limit": 50, "requestId": "3" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{
|
|
"type": "messages",
|
|
"chatId": "1234567890@c.us",
|
|
"data": [
|
|
{
|
|
"id": "true_1234567890@c.us_ABC123",
|
|
"body": "Hello there!",
|
|
"fromMe": false,
|
|
"timestamp": 1716035000,
|
|
"type": "chat",
|
|
"hasMedia": false,
|
|
"isForwarded": false,
|
|
"author": null,
|
|
"ack": 4
|
|
}
|
|
],
|
|
"requestId": "3"
|
|
}
|
|
```
|
|
|
|
#### D. Send Message (`send_message`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "send_message", "chatId": "1234567890@c.us", "text": "Hello!", "requestId": "4" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{
|
|
"type": "message_sent",
|
|
"chatId": "1234567890@c.us",
|
|
"data": {
|
|
"id": "true_1234567890@c.us_XYZ987",
|
|
"body": "Hello!",
|
|
"fromMe": true,
|
|
"timestamp": 1716035005,
|
|
"type": "chat",
|
|
"hasMedia": false,
|
|
"isForwarded": false,
|
|
"author": null,
|
|
"ack": 1
|
|
},
|
|
"requestId": "4"
|
|
}
|
|
```
|
|
|
|
#### E. Mark Chat as Read (`mark_read`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "mark_read", "chatId": "1234567890@c.us", "requestId": "5" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{ "type": "marked_read", "chatId": "1234567890@c.us", "requestId": "5" }
|
|
```
|
|
|
|
#### F. Search Conversations (`search_conversations`)
|
|
* **Request:**
|
|
```json
|
|
{ "type": "search_conversations", "query": "Jane", "requestId": "6" }
|
|
```
|
|
* **Response:**
|
|
```json
|
|
{
|
|
"type": "conversations",
|
|
"data": [...],
|
|
"search": true,
|
|
"requestId": "6"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Server-to-Client Push Events
|
|
The server broadcasts live events to all connected clients immediately.
|
|
|
|
* **QR Code Broadcast:**
|
|
```json
|
|
{ "type": "qr", "qr": "data:image/png;base64,iVBORw0KGgo..." }
|
|
```
|
|
* **Authenticated:**
|
|
```json
|
|
{ "type": "authenticated" }
|
|
```
|
|
* **Client Ready:**
|
|
```json
|
|
{ "type": "ready" }
|
|
```
|
|
* **Status Updates:**
|
|
```json
|
|
{ "type": "status", "ready": true }
|
|
```
|
|
* **Client Disconnected:**
|
|
```json
|
|
{ "type": "disconnected", "reason": "Session expired or logged out" }
|
|
```
|
|
* **New Incoming Message:**
|
|
```json
|
|
{
|
|
"type": "new_message",
|
|
"chatId": "1234567890@c.us",
|
|
"data": {
|
|
"id": "false_1234567890@c.us_DEF456",
|
|
"body": "Live incoming text!",
|
|
"fromMe": false,
|
|
"timestamp": 1716035100,
|
|
"type": "chat",
|
|
"hasMedia": false,
|
|
"isForwarded": false,
|
|
"author": null,
|
|
"ack": 2
|
|
}
|
|
}
|
|
```
|
|
* **Message Delivery / Read Receipt (`message_ack`):**
|
|
```json
|
|
{
|
|
"type": "message_ack",
|
|
"messageId": "true_1234567890@c.us_XYZ987",
|
|
"chatId": "1234567890@c.us",
|
|
"ack": 4
|
|
}
|
|
```
|
|
*(Ack codes: 0 = Error/None, 1 = Pending, 2 = Sent, 3 = Delivered, 4 = Read)*
|
|
|
|
---
|
|
|
|
## 🚀 Quick Setup & Installation
|
|
|
|
### 1. Server Setup (`whatsapp_bridge/`)
|
|
Navigate into the backend project, install dependencies, and start the standalone service:
|
|
|
|
```bash
|
|
cd whatsapp_bridge
|
|
npm install
|
|
node server.js
|
|
```
|
|
The server will boot up and bind to **Port 3025**. It will automatically print:
|
|
`[SERVER] Standalone WhatsApp Bridge running on port 3025`
|
|
|
|
### 2. Flutter App Setup (`whatsapp_app/`)
|
|
|
|
First, ensure that you have created the Flutter project structure using standard platform templates:
|
|
```bash
|
|
# Run this inside the workspace directory
|
|
flutter create whatsapp_app
|
|
```
|
|
Then, copy all files in `whatsapp_app/` into the newly created project folder. Open the folder and install Dart packages:
|
|
|
|
```bash
|
|
cd whatsapp_app
|
|
flutter pub get
|
|
```
|
|
|
|
#### Run on Simulator / Device
|
|
```bash
|
|
flutter run
|
|
```
|
|
|
|
---
|
|
|
|
## 🌐 Production Deployment (CloudPanel Server)
|
|
|
|
Since this backend server acts as a standalone daemon and does not conflict with existing apps, it operates exclusively on **Port 3025**.
|
|
|
|
### 1. Create Node.js Site in CloudPanel
|
|
1. Navigate to **CloudPanel Admin Portal** -> **Sites** -> **Add Site** -> **Create a Node.js Application**.
|
|
2. Set Domain Name: `mywhatsappapp.interlap.com` (or your subdomain).
|
|
3. Set Port: `3025`.
|
|
4. Set Entry Point: `server.js`.
|
|
5. Select Node.js Version: `18+ LTS` (or Node 20 LTS).
|
|
|
|
### 2. Bypass Nginx Reverse Proxy
|
|
By default, CloudPanel routes external traffic from Port 80/443 through an Nginx reverse proxy. For our standalone WebSockets configuration, we want direct access.
|
|
Make sure to open **Port 3025** on your firewall (e.g. AWS Security Group or UFW):
|
|
```bash
|
|
sudo ufw allow 3025/tcp
|
|
```
|
|
This isolates Node.js directly on port 3025.
|
|
|
|
### 3. Keep Server Alive with PM2
|
|
We highly recommend running your Node.js application inside PM2 to ensure it auto-reconnects, logs diagnostic crashes, and recovers seamlessly:
|
|
|
|
```bash
|
|
# Install PM2 globally
|
|
npm install -g pm2
|
|
|
|
# Start the bridge server
|
|
pm2 start server.js --name "whatsapp-bridge"
|
|
|
|
# Persist server launch on reboot
|
|
pm2 save
|
|
pm2 startup
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 Security & Performance Features
|
|
* **Session Persistence:** Configured using `whatsapp-web.js`'s built-in `LocalAuth` session strategies, meaning that once you scan the QR code once, you will not have to scan it again unless explicitly logged out.
|
|
* **Resilience & Crash Prevention:** The server wraps critical events in structured `try/catch` clauses and binds events to `process.on('uncaughtException')`, protecting the server from unexpected Puppeteer execution crashes.
|
|
* **Auto-Reconnection:** If WhatsApp Web loses connection or the user logs out, the server attempts re-initialization automatically after a 5-second backoff delay.
|
|
* **Dark Mode Design:** Beautiful custom-tailored dark theme matching official WhatsApp guidelines (`#111B21`, `#1F2C34`, `#00A884`) with full custom ripple alerts, status badges, and tick indicators.
|