diff --git a/index.js b/index.js index 44a86c7..072478d 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,16 @@ -const { default: makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, proto, generateWAMessageFromContent, generateWAMessageContent ,prepareWAMessageMedia} = 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 NodeCache = require('node-cache'); +const { + default: makeWASocket, + useMultiFileAuthState, + DisconnectReason, + fetchLatestBaileysVersion, + proto, + generateWAMessageFromContent +} = require('@fizzxydev/baileys-pro'); const app = express(); app.use(express.json()); @@ -13,6 +20,15 @@ let sock; let qrData = null; let isConnected = false; +// 💾 Group Metadata Cache (5 minutes) +const groupCache = new NodeCache({ stdTTL: 300, useClones: false }); + +// Simule une récupération de message depuis un store pour getMessage +async function getMessageFromStore(key) { + // À adapter selon ta logique réelle + return { conversation: "Message temporaire pour retry" }; +} + const initBaileys = async () => { const { version } = await fetchLatestBaileysVersion(); const { state, saveCreds } = await useMultiFileAuthState('auth'); @@ -20,23 +36,21 @@ const initBaileys = async () => { sock = makeWASocket({ version, auth: state, - printQRInTerminal: false + markOnlineOnConnect: false, + getMessage: async (key) => await getMessageFromStore(key), + cachedGroupMetadata: async (jid) => groupCache.get(jid) }); - sock.ev.on('connection.update', async (update) => { - const { connection, lastDisconnect, qr } = update; + sock.ev.on('connection.update', async ({ connection, lastDisconnect, qr }) => { 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é.'); - } + console.log(shouldReconnect ? '🔁 Reconnexion...' : '❌ Déconnecté.'); + if (shouldReconnect) initBaileys(); } else if (connection === 'open') { console.log('✅ Connecté à WhatsApp'); isConnected = true; @@ -44,9 +58,56 @@ const initBaileys = async () => { }); sock.ev.on('creds.update', saveCreds); + + // 📌 Caching des groupes + sock.ev.on('groups.update', async ([event]) => { + const metadata = await sock.groupMetadata(event.id); + groupCache.set(event.id, metadata); + }); + + sock.ev.on('group-participants.update', async (event) => { + const metadata = await sock.groupMetadata(event.id); + groupCache.set(event.id, metadata); + }); + + // (Facultatif) Gestion des messages reçus + sock.ev.on('messages.upsert', async ({ messages }) => { + const msg = messages[0]; + + if (!msg.key.fromMe && msg.message?.conversation) { + console.log('💬 Message reçu de', msg.key.remoteJid, ':', msg.message.conversation); + } + if (!msg.message || !msg.key.fromMe) { + const jid = msg.key.remoteJid; + const buttonId = msg.message.buttonsResponseMessage?.selectedButtonId; + + if (buttonId === 'doc_1') { + await sock.sendMessage(jid, { + document: { + url: 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf', + }, + mimetype: 'application/pdf', + fileName: 'Document_1.pdf', + caption: 'Voici votre *Document 1* 📄', + footer: '© Fizzxy Dev', + }); + } else if (buttonId === 'doc_2') { + await sock.sendMessage(jid, { + document: { + url: 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf', + }, + mimetype: 'application/pdf', + fileName: 'Document_2.pdf', + caption: 'Voici votre *Document 2* 📄', + footer: '© Fizzxy Dev', + }); + } + } + }); }; initBaileys(); + app.use('/static', express.static(path.join(__dirname, 'public'))); app.get('/login', (req, res) => { @@ -67,53 +128,145 @@ app.post('/sendText', async (req, res) => { res.status(500).json({ error: e.message }); } }); +app.post('/testButtons', async (req, res) => { + const { phone } = req.body; + + if (!sock || !isConnected) { + return res.status(400).json({ error: 'Non connecté' }); + } + + const jid = `${phone}@s.whatsapp.net`; + + try { + await sock.sendMessage(jid, { + image: { url : "https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg" }, // Can buffer + text: "Hello Wolrd !;", + caption: "Description Of Messages", //Additional information + footer: "© Fizzxy Dev", + media:true, + image: { + url: 'https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg' + }, + buttons: [ + { + buttonId: '.tes', + buttonText: { + displayText: 'TESTING BOT' + }, + type: 1 + }, + { + buttonId: ' ', + buttonText: { + displayText: 'PRIVATE SCRIPT' + }, + type: 1 + }, + { + buttonId: 'action', + buttonText: { + displayText: 'ini pesan interactiveMeta' + }, + type: 4, + nativeFlowInfo: { + name: 'single_select', + paramsJson: JSON.stringify({ + title: 'message', + sections: [ + { + title: 'FizzxyDev - 2025', + highlight_label: '😜', + rows: [ + { + header: 'HEADER', + title: 'TITLE', + description: 'DESCRIPTION', + id: 'YOUR ID 1' + }, + { + header: 'HEADER', + title: 'TITLE', + description: 'DESCRIPTION', + id: 'YOUR ID 2' + } + ] + } + ] + }) + } + } + ], + headerType: 1, + viewOnce: true + }, { + quoted: null // ou tu peux injecter un msg pour reply ici + }); + + res.json({ success: true }); + } catch (e) { + console.error('❌ Erreur testButtons :', 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é' }); + + if (!sock || !isConnected) { + return res.status(400).json({ error: 'Non connecté' }); + } + + const BASE_URL = process.env.BASE_URL || 'https://wa.canguidev.fr'; + const imageUrl = `${BASE_URL}/static/logo-merlo-cs-FR.jpg`; + const jid = `${phone}@s.whatsapp.net`; try { const content = { - message: { - interactiveMessage: proto.Message.InteractiveMessage.create({ - body: { text: "Bienvenue sur notre service !" }, - footer: { text: "Choisis une action ci-dessous" }, - header: { - title: "Menu principal", - hasMediaAttachment: false + viewOnceMessage: { + message: { + messageContextInfo: { + deviceListMetadata: {}, + deviceListMetadataVersion: 2 }, - nativeFlowMessage: { - buttons: [ - { - name: "cta_reply", - buttonParamsJson: JSON.stringify({ - display_text: "📩 Contacter support", - id: "support_action" - }) - }, - { - name: "cta_url", - buttonParamsJson: JSON.stringify({ - display_text: "🌐 Voir notre site", - url: "https://canguidev.fr", - merchant_url: "https://canguidev.fr" - }) - }, - { - name: "cta_call", - buttonParamsJson: JSON.stringify({ - display_text: "📞 Appeler le support", - id: "+33612345678" - }) - } - ] - } - }) + interactiveMessage: proto.Message.InteractiveMessage.create({ + body: { text: "Bienvenue sur notre service !" }, + footer: { text: "Choisis une action ci-dessous" }, + header: { + hasMediaAttachment: true, + imageMessage: { url: imageUrl } + }, + nativeFlowMessage: { + buttons: [ + { + name: "cta_reply", + buttonParamsJson: JSON.stringify({ + display_text: "📩 Contacter support", + id: "support_action" + }) + }, + { + name: "cta_url", + buttonParamsJson: JSON.stringify({ + display_text: "🌐 Voir notre site", + url: "https://canguidev.fr" + }) + }, + { + name: "cta_call", + buttonParamsJson: JSON.stringify({ + display_text: "📞 Appeler le support", + id: "+33612345678" + }) + } + ] + } + }) + } } }; - const msg = generateWAMessageFromContent(`${phone}@s.whatsapp.net`, content, {}); - await sock.relayMessage(`${phone}@s.whatsapp.net`, msg.message, { messageId: msg.key.id }); + const msg = generateWAMessageFromContent(jid, content, {}); + await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); res.json({ success: true }); } catch (e) { @@ -121,6 +274,8 @@ app.post('/sendButtons', async (req, res) => { res.status(500).json({ error: e.message }); } }); + + // Votre route POST app.post('/sendInteractiveImage', async (req, res) => { const { phone, caption, title, subtitle, footer } = req.body; @@ -133,17 +288,13 @@ app.post('/sendInteractiveImage', async (req, res) => { const imageUrl = `${BASE_URL}/static/logo-merlo-cs-FR.jpg`; // Construire le Header correct - const header = proto.Message.InteractiveMessage.Header.create({ - title, // 'Igna' ou envoyé par le client - subtitle, // 'test' ou envoyé par le client - hasMediaAttachment: true, // ← impératif pour afficher un media - imageMessage: { - url: imageUrl, // URL publique de l'image - mimetype: 'image/jpeg' // type MIME - }, - media: 'imageMessage' // ← sélectionne le bon union field - }); - + const header = proto.Message.InteractiveMessage.create({ + // ici on injecte l'image dans le header sans besoin de type explicite + header: proto.Message.InteractiveMessage.Header.create({ + title: 'Igna', + subtitle: 'test' + }), + }) // Body et Footer const body = proto.Message.InteractiveMessage.Body.create({ text: caption || 'Description par défaut' @@ -205,44 +356,44 @@ app.post('/sendInteractiveImage', async (req, res) => { } }); -// app.post('/sendInteractiveImage', async (req, res) => { -// const { phone, caption, title, subtitle, footer } = req.body; -// if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); +app.post('/sendInteractiveImage2', async (req, res) => { + const { phone, caption, title, subtitle, footer } = req.body; + if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); -// try { -// const imagePath = path.join(__dirname, 'public', 'logo-merlo-cs-FR.jpg'); -// const resizedBuffer = fs.readFileSync(imagePath); + try { + const imagePath = path.join(__dirname, 'public', 'logo-merlo-cs-FR.jpg'); + const resizedBuffer = fs.readFileSync(imagePath); -// await sock.sendMessage(`${phone}@s.whatsapp.net`, { -// image: resizedBuffer, -// caption: caption || 'Description par défaut', -// title: title || 'Titre par défaut', -// subtitle: subtitle || 'Sous-titre', -// footer: footer || 'Pied de page', -// media: true, -// interactiveButtons: [ -// { -// name: 'cta_url', -// buttonParamsJson: JSON.stringify({ -// display_text: 'Proposition', -// url: 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf' -// }) -// }, -// { -// name: 'cta_url', -// buttonParamsJson: JSON.stringify({ -// display_text: 'Spec machine', -// url: 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf' -// }) -// } -// ] -// }); -// res.json({ success: true }); -// } catch (e) { -// console.error('❌ Erreur interactive image :', e); -// res.status(500).json({ error: e.message }); -// } -// }); + await sock.sendMessage(`${phone}@s.whatsapp.net`, { + image: resizedBuffer, + caption: caption || 'Description par défaut', + title: title || 'Titre par défaut', + subtitle: subtitle || 'Sous-titre', + footer: footer || 'Pied de page', + media: true, + interactiveButtons: [ + { + name: 'cta_url', + buttonParamsJson: JSON.stringify({ + display_text: 'Proposition', + url: 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf' + }) + }, + { + name: 'cta_url', + buttonParamsJson: JSON.stringify({ + display_text: 'Spec machine', + url: 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf' + }) + } + ] + }); + res.json({ success: true }); + } catch (e) { + console.error('❌ Erreur interactive image :', e); + res.status(500).json({ error: e.message }); + } +}); app.post('/sendProductMessage', async (req, res) => { // On vérifie sock et isConnected, pas client if (!sock || !isConnected) { @@ -315,7 +466,117 @@ app.post('/sendProductMessage', async (req, res) => { return res.status(500).json({ error: e.message }); } }); +<<<<<<< HEAD app.post('/testViewOnce', async (req, res) => { +======= +app.post('/testProductMessage', async (req, res) => { + const { phone } = req.body; + + if (!sock || !isConnected) { + return res.status(400).json({ error: 'Non connecté à WhatsApp' }); + } + + const jid = `${phone}@s.whatsapp.net`; + + try { + await sock.sendMessage( + jid, + { + product: { + productImage: { url: "https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg" }, // image du produit + productImageCount: 1, + title: "Tracteur Merlo TF38.10", + description: "Charge maximale 3.8T, hauteur 10m", + priceAmount1000: 49500 * 1000, // 49 500 € + currencyCode: "EUR", + retailerId: "MERLO-FR-001", + url: "https://example.com/product/tf38-10" + }, + businessOwnerJid: "1234@s.whatsapp.net", + caption: "🛒 Découvrez notre nouveau modèle Merlo TF38.10", + title: "Offre Spéciale Merlo", + footer: "MERLO France • Offre valable jusqu'au 30/06", + media: true, + interactiveButtons: [ + { + name: "quick_reply", + buttonParamsJson: JSON.stringify({ + display_text: "📩 Demander un devis", + id: "request_quote" + }) + }, + { + name: "cta_url", + buttonParamsJson: JSON.stringify({ + display_text: "🌐 Voir la fiche produit", + url: "https://example.com/product/tf38-10" + }) + } + ] + }, + { + quoted: null // ou remplace par un message existant si tu veux répondre à un msg + } + ); + + res.json({ success: true }); + } catch (e) { + console.error('❌ Erreur envoi produit :', e); + res.status(500).json({ error: e.message }); + } +}); + +app.post('/testInteractiveImage', async (req, res) => { + const { phone } = req.body; + + if (!sock || !isConnected) { + return res.status(400).json({ error: 'Non connecté à WhatsApp' }); + } + + const content = { + viewOnceMessage: { + message: { + messageContextInfo: { + deviceListMetadata: {}, + deviceListMetadataVersion: 2 + }, + interactiveMessage: proto.Message.InteractiveMessage.create({ + header: { + hasMediaAttachment: true, + imageMessage: { url: "https://canguidev.fr/static/logo-merlo-cs-FR.jpg" } + }, + body: { text: "Bienvenue chez Merlo France 🇫🇷\nChoisissez une action ci-dessous." }, + footer: { text: "MERLO - Support & Documentation" }, + nativeFlowMessage: { + buttons: [ + { + name: "cta_reply", + buttonParamsJson: JSON.stringify({ + display_text: "📩 Contacter support", + id: "support_action" + }) + }, + { + name: "cta_url", + buttonParamsJson: JSON.stringify({ + display_text: "🌐 Voir la fiche produit", + url: "https://example.com/product" + }) + } + ] + } + }) + } + } +}; + +const jid = `${phone}@s.whatsapp.net`; +const msg = generateWAMessageFromContent(jid, content, {}); +await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); + +}); +app.post('/testButtons2', async (req, res) => { +>>>>>>> 15e5852440e045e5e8529d63206d79288ac62dab const { phone } = req.body; if (!sock || !isConnected) { @@ -326,6 +587,7 @@ app.post('/testViewOnce', async (req, res) => { try { await sock.sendMessage(jid, { +<<<<<<< HEAD image: { url: 'https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg' // Ton image publique }, @@ -336,6 +598,112 @@ app.post('/testViewOnce', async (req, res) => { res.json({ success: true }); } catch (e) { console.error('❌ Erreur testViewOnce :', e); +======= + text: '📚 *Veuillez choisir le document à télécharger :*', + footer: '© Fizzxy Dev', + buttons: [ + { + buttonId: 'doc_1', + buttonText: { displayText: 'Télécharger Document 1' }, + type: 1, + }, + { + buttonId: 'doc_2', + buttonText: { displayText: 'Télécharger Document 2' }, + type: 1, + } + ], + headerType: 1, // Texte seulement + }); + + res.json({ success: true }); + + } catch (e) { + console.error('❌ Erreur testButtons :', e); + res.status(500).json({ error: e.message }); + } +}); +app.post('/testButtons3', async (req, res) => { + const { phone } = req.body; + + if (!sock || !isConnected) { + return res.status(400).json({ error: 'Non connecté' }); + } + + const jid = `${phone}@s.whatsapp.net`; + let msg = generateWAMessageFromContent(m.chat, { + viewOnceMessage: { + message: { + "messageContextInfo": { + "deviceListMetadata": {}, + "deviceListMetadataVersion": 2 + }, + interactiveMessage: proto.Message.InteractiveMessage.create({ + body: proto.Message.InteractiveMessage.Body.create({ + text: "Fizzxy Dev" + }), + footer: proto.Message.InteractiveMessage.Footer.create({ + text: "Bot" + }), + header: proto.Message.InteractiveMessage.Header.create({ + title: "Igna", + subtitle: "test", + hasMediaAttachment: false + }), + nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ + 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": "" + } + ], + }) + }) + } + } + }, {}) + + try { + await sock.relayMessage(msg.key.remoteJid, msg.message, { messageId: msg.key.id }) + + + res.json({ success: true }); + + } catch (e) { + console.error('❌ Erreur testButtons :', e); +>>>>>>> 15e5852440e045e5e8529d63206d79288ac62dab res.status(500).json({ error: e.message }); } }); diff --git a/package.json b/package.json index 9a7c66b..e9d8771 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "axios": "^1.4.0", "express": "^4.18.4", "qrcode": "^1.5.1", - "sharp": "^0.33.0" + "sharp": "^0.33.0", + "node-cache": "^5.1.2" + } } diff --git a/public/login.html b/public/login.html index 44fad60..50ccbf2 100644 --- a/public/login.html +++ b/public/login.html @@ -2,116 +2,66 @@
-Scannez le QR Code avec votre application WhatsApp
-