Hussien Haider commited on
Commit
094d07e
·
1 Parent(s): 799d9f1
Files changed (7) hide show
  1. AGENTS.md +13 -12
  2. Dockerfile +9 -1
  3. index.js +71 -91
  4. memory.md +7 -21
  5. package.json +6 -3
  6. pnpm-lock.yaml +0 -0
  7. pnpm-workspace.yaml +8 -0
AGENTS.md CHANGED
@@ -1,11 +1,11 @@
1
  # Project: WhatsApp-Telegram Bot
2
 
3
  ## Stack
4
- - **Runtime**: Node.js 22, pnpm v10, Docker (HF Spaces)
5
- - **WhatsApp**: `@whiskeysockets/baileys` (WebSocket, no browser)
6
- - **Telegram**: `@mtproto/core` (MTProto, **NOT** Bot API — HTTPS to `api.telegram.org` is blocked on HF Spaces)
7
- - **Server**: Express on port 7860 (HF Spaces health check)
8
- - **Storage**: `/data/` directory for sessions (must be enabled in HF Spaces settings)
9
 
10
  ## File Structure
11
  - `index.js` — main app
@@ -18,7 +18,7 @@
18
 
19
  ## Connection Architecture
20
  ```
21
- WhatsApp (user) → @whiskeysockets/baileys → index.js → @mtproto/core → Telegram (admin)
22
  Telegram (admin) → @mtproto/core → index.js → WhatsApp → WhatsApp (user)
23
  ```
24
 
@@ -36,11 +36,12 @@ The library (`@mtproto/core`) has schemas for: `replyKeyboardMarkup`, `keyboardB
36
  `parseMarkdown()` handles `*bold*`, `_italic_`, `` `code` `` — converts to message entities.
37
 
38
  ## WhatsApp Constraints
39
- - Uses `@whiskeysockets/baileys` — no browser needed
40
- - Auth stored in `/data/baileys_auth/` (multi-file JSON)
41
- - `useMultiFileAuthState()` for persistence
42
  - Auto-reconnect on disconnect (unless logged out)
43
- - Events: `connection.update`, `messages.upsert`, `creds.update`
 
44
 
45
  ## Telegram Constraints
46
  - `@mtproto/core` v6.3.0, layer 158
@@ -63,9 +64,9 @@ The library (`@mtproto/core`) has schemas for: `replyKeyboardMarkup`, `keyboardB
63
  - CORS headers allow all origins
64
 
65
  ## Important Notes
66
- - `web.whatsapp.com` HTTPS times out via Node.js but Chrome connects via Puppeteer
67
  - No emojis in code unless user asks
68
  - Comments should NOT be added unless asked
69
  - Keep responses short and direct
70
  - Always check syntax with `node -c index.js` before committing
71
- - Baileys ref: `sock.sendMessage(jid, { text, image, document })`, `downloadContentFromMessage()`, `DisconnectReason.loggedOut`
 
1
  # Project: WhatsApp-Telegram Bot
2
 
3
  ## Stack
4
+ - **Runtime**: Node.js 22, pnpm v10, Docker
5
+ - **WhatsApp**: `whatsapp-web.js` (Puppeteer + Chrome)
6
+ - **Telegram**: `@mtproto/core` (MTProto, **NOT** Bot API)
7
+ - **Server**: Express on port 7860 (health check)
8
+ - **Storage**: `/data/` directory for sessions
9
 
10
  ## File Structure
11
  - `index.js` — main app
 
18
 
19
  ## Connection Architecture
20
  ```
21
+ WhatsApp (user) → whatsapp-web.js (Puppeteer/Chrome) → index.js → @mtproto/core → Telegram (admin)
22
  Telegram (admin) → @mtproto/core → index.js → WhatsApp → WhatsApp (user)
23
  ```
24
 
 
36
  `parseMarkdown()` handles `*bold*`, `_italic_`, `` `code` `` — converts to message entities.
37
 
38
  ## WhatsApp Constraints
39
+ - Uses `whatsapp-web.js` — requires Chrome (Puppeteer)
40
+ - Auth stored in `/data/wwebjs_auth/` (LocalAuth)
41
+ - `LocalAuth` for session persistence
42
  - Auto-reconnect on disconnect (unless logged out)
43
+ - Events: `qr`, `ready`, `disconnected`, `message`
44
+ - Media: `msg.downloadMedia()` returns base64 data; use `Buffer.from(data, 'base64')`
45
 
46
  ## Telegram Constraints
47
  - `@mtproto/core` v6.3.0, layer 158
 
64
  - CORS headers allow all origins
65
 
66
  ## Important Notes
67
+ - web.whatsapp.com loads in Chrome (Puppeteer) no raw WebSocket needed
68
  - No emojis in code unless user asks
69
  - Comments should NOT be added unless asked
70
  - Keep responses short and direct
71
  - Always check syntax with `node -c index.js` before committing
72
+ - wwebjs ref: `client.sendMessage(jid, text)`, `msg.downloadMedia()`, `msg.getContact()`, `client.sendSeen(jid)`
Dockerfile CHANGED
@@ -2,6 +2,14 @@ FROM node:22-slim
2
 
3
  RUN corepack enable && corepack prepare pnpm@10 --activate
4
 
 
 
 
 
 
 
 
 
5
  WORKDIR /app
6
 
7
  COPY package.json pnpm-lock.yaml ./
@@ -11,4 +19,4 @@ COPY . .
11
 
12
  EXPOSE 7860
13
 
14
- CMD ["node", "index.js"]
 
2
 
3
  RUN corepack enable && corepack prepare pnpm@10 --activate
4
 
5
+ RUN apt-get update && apt-get install -y \
6
+ chromium \
7
+ libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
8
+ libdrm2 libdbus-1-3 libxkbcommon0 libxcomposite1 libxdamage1 \
9
+ libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 \
10
+ --no-install-recommends \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
  WORKDIR /app
14
 
15
  COPY package.json pnpm-lock.yaml ./
 
19
 
20
  EXPOSE 7860
21
 
22
+ CMD ["node", "index.js"]
index.js CHANGED
@@ -1,4 +1,4 @@
1
- const { makeWASocket, useMultiFileAuthState, DisconnectReason, downloadContentFromMessage, getContentFromMessage } = require('@whiskeysockets/baileys');
2
  const qrcode = require('qrcode');
3
  const qrcodeTerminal = require('qrcode-terminal');
4
  const MTProto = require('@mtproto/core');
@@ -14,8 +14,6 @@ const API_ID = parseInt(process.env.API_ID || '1344311', 10);
14
  const API_HASH = process.env.API_HASH || '95778042101cb549a4ca6235d7cf87e1';
15
  const BOT_TOKEN = process.env.BOT_TOKEN || '903567266:AAFS-juBV5QYL_kKNkc7ovqp-i6eVwTOX-U';
16
 
17
-
18
-
19
  const app = express();
20
  app.use((req, res, next) => {
21
  res.setHeader('Access-Control-Allow-Origin', '*');
@@ -362,107 +360,89 @@ pollUpdates();
362
 
363
  console.log('🚀 جاري تشغيل البوت...');
364
 
365
- let sock = null;
366
 
367
  async function startWhatsApp() {
368
- console.log('🔵 بدء تشغيل واتساب (Baileys)...');
369
- const { state, saveCreds } = await useMultiFileAuthState(`${DATA_DIR}/baileys_auth`);
370
-
371
- const sockConfig = {
372
- auth: state,
373
- browser: ['WhatsApp Bot', 'Chrome', '1.0'],
374
- syncFullHistory: false,
375
- markOnlineOnConnect: false,
376
- connectTimeoutMs: 120000,
377
- };
378
-
379
- const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.ALL_PROXY;
380
- if (proxyUrl) {
381
- const { HttpsProxyAgent } = require('https-proxy-agent');
382
- sockConfig.agent = new HttpsProxyAgent(proxyUrl);
383
- console.log('🔵 باستخدام بروكسي:', proxyUrl);
384
- }
385
-
386
- sock = makeWASocket(sockConfig);
387
 
388
- sock.ev.on('creds.update', saveCreds);
 
 
 
 
 
 
 
 
 
 
389
 
390
- sock.ev.on('connection.update', async (update) => {
391
- const { connection, lastDisconnect, qr } = update;
 
 
392
 
393
- if (qr) {
394
- qrcodeTerminal.generate(qr, { small: true });
395
- qrcode.toFile('whatsapp-qr.png', qr, { width: 400 }, (err) => {
396
- if (err) console.error('خطأ في حفظ QR:', err);
397
- else {
398
- console.log('📸 تم حفظ QR كصورة: whatsapp-qr.png');
399
- if (telegramChatId) tgSendPhoto(telegramChatId, 'whatsapp-qr.png', '📱 امسح رمز QR باستخدام واتساب');
400
- }
401
- });
402
- console.log('📱 امسح رمز QR باستخدام واتساب');
403
  }
 
404
 
405
- if (connection === 'open') {
406
- whatsappConnected = true;
407
- console.log('✅ واتساب جاهز');
408
- }
409
 
410
- if (connection === 'close') {
411
- const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
412
- const reason = lastDisconnect?.error?.message || 'غير معروف';
413
- console.log('❌ واتساب فصل:', reason);
414
- if (telegramChatId) tgSend(telegramChatId, `❌ واتساب فصل: ${reason}`);
415
- whatsappConnected = false;
416
- if (shouldReconnect) {
417
- console.log('🔄 إعادة الاتصال بعد 5 ثواني...');
418
- setTimeout(startWhatsApp, 5000);
419
- }
 
 
 
 
 
 
 
 
 
420
  }
421
- });
422
 
423
- sock.ev.on('messages.upsert', async ({ messages, type }) => {
424
- if (type !== 'notify') return;
425
- for (const msg of messages) {
426
- if (msg.key.fromMe || !msg.message) continue;
427
- if (!botEnabled) return;
428
-
429
- const jid = msg.key.remoteJid;
430
- const pushName = msg.pushName || jid.split('@')[0];
431
- const waNum = jid.split('@')[0];
432
-
433
- let msgText = '';
434
- let hasMedia = false;
435
- let mediaBuffer = null;
436
- let mediaMime = '';
437
- let mediaName = '';
438
-
439
- if (msg.message.conversation) msgText = msg.message.conversation;
440
- else if (msg.message.extendedTextMessage?.text) msgText = msg.message.extendedTextMessage.text;
441
- else if (msg.message.imageMessage) { hasMedia = true; mediaMime = 'image/jpeg'; mediaName = msg.message.imageMessage.caption || 'image'; }
442
- else if (msg.message.videoMessage) { hasMedia = true; mediaMime = 'video/mp4'; mediaName = msg.message.videoMessage.caption || 'video'; }
443
- else if (msg.message.documentMessage) { hasMedia = true; mediaMime = msg.message.documentMessage.mimetype || 'application/octet-stream'; mediaName = msg.message.documentMessage.fileName || 'file'; }
444
-
445
- await sock.readMessages([msg.key]);
446
- await sock.sendMessage(jid, { text: '✅ تم استلام رسالتك' });
447
-
448
- if (hasMedia && telegramChatId) {
449
- try {
450
- const stream = await downloadContentFromMessage(
451
- msg.message.imageMessage || msg.message.videoMessage || msg.message.documentMessage,
452
- msg.message.imageMessage ? 'image' : msg.message.videoMessage ? 'video' : 'document'
453
- );
454
- const chunks = [];
455
- for await (const chunk of stream) chunks.push(chunk);
456
- mediaBuffer = Buffer.concat(chunks);
457
- await tgSendMedia(telegramChatId, mediaBuffer, mediaName, mediaMime);
458
- } catch (e) { console.error('❌ خطأ بتحميل الميديا:', e.message); }
459
- }
460
 
461
- if (msgText && telegramChatId) {
462
- await tgSend(telegramChatId, `📩 من: ${pushName}\nرقم: ${waNum}\nالرسالة: ${msgText}`);
463
- }
 
464
  }
465
  });
 
 
466
  }
467
 
468
  startWhatsApp();
 
1
+ const { Client, LocalAuth } = require('whatsapp-web.js');
2
  const qrcode = require('qrcode');
3
  const qrcodeTerminal = require('qrcode-terminal');
4
  const MTProto = require('@mtproto/core');
 
14
  const API_HASH = process.env.API_HASH || '95778042101cb549a4ca6235d7cf87e1';
15
  const BOT_TOKEN = process.env.BOT_TOKEN || '903567266:AAFS-juBV5QYL_kKNkc7ovqp-i6eVwTOX-U';
16
 
 
 
17
  const app = express();
18
  app.use((req, res, next) => {
19
  res.setHeader('Access-Control-Allow-Origin', '*');
 
360
 
361
  console.log('🚀 جاري تشغيل البوت...');
362
 
363
+ let waClient = null;
364
 
365
  async function startWhatsApp() {
366
+ console.log('🔵 بدء تشغيل واتساب (whatsapp-web.js)...');
367
+ waClient = new Client({
368
+ authStrategy: new LocalAuth({ dataPath: `${DATA_DIR}/wwebjs_auth` }),
369
+ puppeteer: {
370
+ headless: true,
371
+ args: [
372
+ '--no-sandbox',
373
+ '--disable-setuid-sandbox',
374
+ '--disable-dev-shm-usage',
375
+ '--disable-accelerated-2d-canvas',
376
+ '--no-first-run',
377
+ '--no-zygote',
378
+ '--single-process',
379
+ '--disable-gpu',
380
+ ],
381
+ },
382
+ });
 
 
383
 
384
+ waClient.on('qr', (qr) => {
385
+ qrcodeTerminal.generate(qr, { small: true });
386
+ qrcode.toFile('whatsapp-qr.png', qr, { width: 400 }, (err) => {
387
+ if (err) console.error('خطأ في حفظ QR:', err);
388
+ else {
389
+ console.log('📸 تم حفظ QR كصورة: whatsapp-qr.png');
390
+ if (telegramChatId) tgSendPhoto(telegramChatId, 'whatsapp-qr.png', '📱 امسح رمز QR باستخدام واتساب');
391
+ }
392
+ });
393
+ console.log('📱 امسح رمز QR باستخدام واتساب');
394
+ });
395
 
396
+ waClient.on('ready', () => {
397
+ whatsappConnected = true;
398
+ console.log('✅ واتساب جاهز');
399
+ });
400
 
401
+ waClient.on('disconnected', (reason) => {
402
+ console.log('❌ واتساب فصل:', reason);
403
+ if (telegramChatId) tgSend(telegramChatId, `❌ واتساب فصل: ${reason}`);
404
+ whatsappConnected = false;
405
+ if (reason !== 'LOGGED_OUT') {
406
+ console.log('🔄 إعادة الاتصال بعد 5 ثواني...');
407
+ setTimeout(startWhatsApp, 5000);
 
 
 
408
  }
409
+ });
410
 
411
+ waClient.on('message', async (msg) => {
412
+ if (msg.fromMe) return;
413
+ if (!botEnabled) return;
 
414
 
415
+ const jid = msg.from;
416
+ const waNum = jid.split('@')[0];
417
+
418
+ let pushName = waNum;
419
+ try {
420
+ const contact = await msg.getContact();
421
+ pushName = contact.pushname || contact.name || waNum;
422
+ } catch (_) {}
423
+
424
+ let msgText = msg.body || '';
425
+
426
+ if (msg.hasMedia && telegramChatId) {
427
+ try {
428
+ const media = await msg.downloadMedia();
429
+ if (media && media.data) {
430
+ const buf = Buffer.from(media.data, 'base64');
431
+ await tgSendMedia(telegramChatId, buf, media.filename || 'file', media.mimetype);
432
+ }
433
+ } catch (e) { console.error('❌ خطأ بتحميل الميديا:', e.message); }
434
  }
 
435
 
436
+ await waClient.sendSeen(jid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
 
438
+ await waClient.sendMessage(jid, '✅ تم استلام رسالتك');
439
+
440
+ if (msgText && telegramChatId) {
441
+ await tgSend(telegramChatId, `📩 من: ${pushName}\nرقم: ${waNum}\nالرسالة: ${msgText}`);
442
  }
443
  });
444
+
445
+ waClient.initialize();
446
  }
447
 
448
  startWhatsApp();
memory.md CHANGED
@@ -2,7 +2,7 @@
2
 
3
  ## Current State
4
  - Telegram connected via MTProto (DC 1) — working
5
- - WhatsApp Connection — BLOCKED on HF Spaces (WebSocket handshake timeout)
6
  - Bot forwards WhatsApp messages to Telegram admin (when connected)
7
  - Bot responds to `/start`, `/toggle`, button presses from Telegram
8
 
@@ -13,18 +13,15 @@
13
 
14
  3. **Keyboard format**: `replyKeyboardMarkup` requires `{ _: 'keyboardButtonRow', buttons: [...] }` format. Using `one_time` instead of `single_use` causes errors.
15
 
16
- 4. **Migrated from `whatsapp-web.js` to `@whiskeysockets/baileys`**: Removed Chrome/Puppeteer dependency. Baileys connects via WebSocket directly no browser needed.
17
 
18
- 5. **Proxy support added**: `startWhatsApp` checks `HTTPS_PROXY` env var first; if unset, auto-fetches free proxies from `proxyscrape.com` on first failure and rotates through them on each reconnect.
19
 
20
- 6. **Markdown parsing**: Custom `parseMarkdown()` converts `*bold*`, `_italic_`, `` `code` `` to Telegram message entities.
21
-
22
- 7. **QR auto-send**: QR image sent to Telegram via `upload.saveFilePart` + `messages.sendMedia` when generated.
23
 
24
  ## Pending Issues
25
- - WhatsApp WebSocket handshake times out from HF Spaces (`web.whatsapp.com` blocked at network level)
26
- - Free proxies from proxyscrape may not support WebSocket CONNECT tunneling — rotation may fail
27
- - `/data/` storage must be enabled in HF Spaces for session persistence (user created `Whatsappbot-storage` bucket)
28
 
29
  ## API Credentials
30
  - API_ID: 1344311
@@ -38,15 +35,4 @@
38
  - QR code sent to Telegram automatically when generated
39
  - Keyboard buttons preferred over text commands
40
  - No emojis in code
41
- - No comments in code unless asked
42
-
43
- ## Hugging Face Space
44
- - URL: https://huggingface.co/spaces/hsoneabadi/Whatsappbot
45
- - Port: 7860 (health check)
46
- - Docker: node:22-slim (no Chrome)
47
-
48
- ## Proxy Options (if free proxies fail)
49
- 1. Set `HTTPS_PROXY` env var on HF Spaces with a working proxy
50
- 2. Deploy Cloudflare Worker as WebSocket proxy (free, requires CF account)
51
- 3. Run WhatsApp client locally on Windows, bridge to HF Spaces via WS
52
- 4. Move whole bot to local Windows machine
 
2
 
3
  ## Current State
4
  - Telegram connected via MTProto (DC 1) — working
5
+ - WhatsApp Connection — using whatsapp-web.js (Puppeteer + Chrome)
6
  - Bot forwards WhatsApp messages to Telegram admin (when connected)
7
  - Bot responds to `/start`, `/toggle`, button presses from Telegram
8
 
 
13
 
14
  3. **Keyboard format**: `replyKeyboardMarkup` requires `{ _: 'keyboardButtonRow', buttons: [...] }` format. Using `one_time` instead of `single_use` causes errors.
15
 
16
+ 4. **Migrated from `@whiskeysockets/baileys` to `whatsapp-web.js`**: Uses Puppeteer + Chrome instead of raw WebSocket. Requires Chromium installed in Docker.
17
 
18
+ 5. **Markdown parsing**: Custom `parseMarkdown()` converts `*bold*`, `_italic_`, `` `code` `` to Telegram message entities.
19
 
20
+ 6. **QR auto-send**: QR image sent to Telegram via `upload.saveFilePart` + `messages.sendMedia` when generated.
 
 
21
 
22
  ## Pending Issues
23
+ - wwebjs requires Chrome Chrome must be installed in Docker (added to Dockerfile)
24
+ - `/data/` storage for session persistence
 
25
 
26
  ## API Credentials
27
  - API_ID: 1344311
 
35
  - QR code sent to Telegram automatically when generated
36
  - Keyboard buttons preferred over text commands
37
  - No emojis in code
38
+ - No comments in code unless asked
 
 
 
 
 
 
 
 
 
 
 
package.json CHANGED
@@ -6,12 +6,15 @@
6
  "start": "node index.js"
7
  },
8
  "type": "commonjs",
 
 
 
9
  "dependencies": {
10
  "@mtproto/core": "^6.3.0",
11
- "@whiskeysockets/baileys": "^6.7.0",
12
  "express": "^5.2.1",
13
- "https-proxy-agent": "^7.0.6",
14
  "qrcode": "^1.5.4",
15
- "qrcode-terminal": "^0.12.0"
 
16
  }
17
  }
 
6
  "start": "node index.js"
7
  },
8
  "type": "commonjs",
9
+ "pnpm": {
10
+ "onlyBuiltDependencies": ["puppeteer"]
11
+ },
12
  "dependencies": {
13
  "@mtproto/core": "^6.3.0",
 
14
  "express": "^5.2.1",
15
+ "puppeteer": "^24.0.0",
16
  "qrcode": "^1.5.4",
17
+ "qrcode-terminal": "^0.12.0",
18
+ "whatsapp-web.js": "^1.26.0"
19
  }
20
  }
pnpm-lock.yaml CHANGED
The diff for this file is too large to render. See raw diff
 
pnpm-workspace.yaml CHANGED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ allowBuilds:
2
+ '[': true
3
+ ']': true
4
+ e: true
5
+ p: true
6
+ r: true
7
+ t: true
8
+ u: true