const { default: makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, proto, generateWAMessageFromContent, generateWAMessageContent } = require('@fizzxydev/baileys-pro'); const express = require('express'); const { Boom } = require('@hapi/boom'); const qrcode = require('qrcode'); const path = require('path'); const fs = require('fs'); const app = express(); app.use(express.json()); app.use(express.static('public')); let sock; let qrData = null; let isConnected = false; const initBaileys = async () => { const { version } = await fetchLatestBaileysVersion(); const { state, saveCreds } = await useMultiFileAuthState('auth'); sock = makeWASocket({ version, auth: state, printQRInTerminal: false }); sock.ev.on('connection.update', async (update) => { const { connection, lastDisconnect, qr } = update; if (qr) { qrData = await qrcode.toDataURL(qr); isConnected = false; } if (connection === 'close') { const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut; if (shouldReconnect) { console.log('🔁 Reconnexion...'); initBaileys(); } else { console.log('❌ Déconnecté.'); } } else if (connection === 'open') { console.log('✅ Connecté à WhatsApp'); isConnected = true; } }); sock.ev.on('creds.update', saveCreds); }; async function generateMediaMessage(sock, type, url) { const generated = await generateWAMessageContent( { [type]: { url } }, { upload: sock.waUploadToServer } ); return generated[`${type}Message`]; } async function sendInteractive(sock, jid, imageUrl) { const imageMsg = await generateMediaMessage(sock, 'image', imageUrl); const content = { viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2, }, interactiveMessage: proto.Message.InteractiveMessage.create({ body: { text: 'Quel produit vous intéresse ?' }, footer: { text: 'Répondez via un bouton' }, header: { title: 'Menu produits', hasMediaAttachment: true, imageMessage: imageMsg, }, nativeFlowMessage: { buttons: [ { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: '📦 Voir produits', id: '.produits', }), }, { name: 'ctl_url', buttonParamsJson: JSON.stringify({ display_text: '🛒 Visiter boutique', url: 'https://canguidev.fr', merchant_url: 'https://canguidev.fr', }), }, ], }, }), }, }, }; const msg = generateWAMessageFromContent(jid, content, {}); await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); } initBaileys(); app.get('/login', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'login.html')); }); app.get('/api/qrcode', (req, res) => { res.json({ qr: qrData, connected: isConnected }); }); app.post('/sendText', async (req, res) => { const { phone, message } = req.body; if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); try { await sock.sendMessage(`${phone}@s.whatsapp.net`, { text: message }); res.json({ success: true }); } catch (e) { res.status(500).json({ error: e.message }); } }); app.post('/sendButtons', async (req, res) => { const { phone } = req.body; if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); try { const content = { viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }, interactiveMessage: proto.Message.InteractiveMessage.create({ body: { text: "Bienvenue sur notre service !" }, footer: { text: "Choisis une action ci-dessous" }, header: { title: "Menu principal", hasMediaAttachment: false }, nativeFlowMessage: { buttons: [ { "name": "single_select", "buttonParamsJson": "{\"title\":\"title\",\"sections\":[{\".menu\":\".play dj webito\",\"highlight_label\":\"label\",\"rows\":[{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"},{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"}]}]}" }, { "name": "cta_reply", "buttonParamsJson": "{\"display_text\":\"quick_reply\",\"id\":\"message\"}" }, { "name": "cta_url", "buttonParamsJson": "{\"display_text\":\"url\",\"url\":\"https://www.google.com\",\"merchant_url\":\"https://www.google.com\"}" }, { "name": "cta_call", "buttonParamsJson": "{\"display_text\":\"call\",\"id\":\"message\"}" }, { "name": "cta_copy", "buttonParamsJson": "{\"display_text\":\"copy\",\"id\":\"123456789\",\"copy_code\":\"message\"}" }, { "name": "cta_reminder", "buttonParamsJson": "{\"display_text\":\"Recordatorio\",\"id\":\"message\"}" }, { "name": "cta_cancel_reminder", "buttonParamsJson": "{\"display_text\":\"cta_cancel_reminder\",\"id\":\"message\"}" }, { "name": "address_message", "buttonParamsJson": "{\"display_text\":\"address_message\",\"id\":\"message\"}" }, { "name": "send_location", "buttonParamsJson": "" } ], } }) } } }; const msg = generateWAMessageFromContent(`${phone}@s.whatsapp.net`, content, {}); await sock.relayMessage(`${phone}@s.whatsapp.net`, msg.message, { messageId: msg.key.id }); res.json({ success: true }); } catch (e) { console.error('❌ Erreur bouton actif :', e); res.status(500).json({ error: e.message }); } }); app.post('/sendInteractiveImage', async (req, res) => { const { phone } = req.body; if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); try { await sock.sendMessage(`${phone}@s.whatsapp.net`, { image: { url: 'https://merlo-ch.com/assets/images/site/logo-fond-blanc.svg' }, caption: 'Description Of Messages', title: 'Title Of Messages', subtitle: 'Subtile Message', footer: 'Footer Messages', media: true, interactiveButtons: [ { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Display Button', id: 'ID' }) }, { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Display Button', url: 'https://www.google.com' }) } ] }); res.json({ success: true }); } catch (e) { console.error('❌ Erreur interactive image :', e); res.status(500).json({ error: e.message }); } }); app.listen(3001, () => console.log('🚀 Serveur Baileys démarré sur http://localhost:3001'));