diff --git a/index.js b/index.js index 134decf..fc39eda 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ + const express = require('express'); const qrcode = require('qrcode'); const path = require('path'); @@ -8,11 +9,10 @@ const { useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, - proto, - generateWAMessageFromContent + generateWAMessageFromContent, + proto } = require('@whiskeysockets/baileys'); - const app = express(); app.use(express.json()); app.use(express.static('public')); @@ -21,12 +21,9 @@ 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" }; } @@ -60,18 +57,6 @@ 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) { @@ -82,8 +67,6 @@ const initBaileys = async () => { initBaileys(); -app.use('/static', express.static(path.join(__dirname, 'public'))); - app.get('/login', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'login.html')); }); @@ -102,408 +85,12 @@ 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é à WhatsApp' }); - } - - const BASE_URL = process.env.BASE_URL || 'https://wa.canguidev.fr'; - const imageUrl = `${BASE_URL}/static/logo-merlo-cs-FR.jpg`; + if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' }); const jid = `${phone}@s.whatsapp.net`; - - try { - const content = { - viewOnceMessage: { - message: { - messageContextInfo: { - deviceListMetadata: {}, - deviceListMetadataVersion: 2 - }, - interactiveMessage: proto.Message.InteractiveMessage.create({ - header: { - hasMediaAttachment: true, - imageMessage: { url: imageUrl } - }, - body: { text: "Bienvenue sur notre service !" }, - footer: { text: "Choisis une action ci-dessous" }, - 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(jid, content, {}); - await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); - - res.json({ success: true }); - } catch (e) { - console.error('❌ Erreur /sendButtons :', e); - res.status(500).json({ error: e.message }); - } -}); - - - -// Votre route POST -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é à WhatsApp' }); - } - - try { - const BASE_URL = process.env.BASE_URL || 'https://wa.canguidev.fr'; - const imageUrl = `${BASE_URL}/static/logo-merlo-cs-FR.jpg`; - - // Construire le Header correct - 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' - }); - const foot = proto.Message.InteractiveMessage.Footer.create({ - text: footer || 'Pied de page' - }); - - // Vos boutons URL - const nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.create({ - buttons: [ - { - name: 'cta_url', - buttonParamsJson: JSON.stringify({ - display_text: '📄 Voir proposition', - url: 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf' - }) - }, - { - name: 'cta_url', - buttonParamsJson: JSON.stringify({ - display_text: '🔧 Spécifications', - url: 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf' - }) - } - ] - }); - - // Construire l'InteractiveMessage complet - const interactiveMsg = proto.Message.InteractiveMessage.create({ - header, - body, - footer: foot, - nativeFlowMessage: nativeFlow - }); - - // Envelopper (ici dans viewOnceMessage, comme dans votre exemple) - const raw = { - viewOnceMessage: { - message: { - messageContextInfo: { - deviceListMetadata: {}, - deviceListMetadataVersion: 2 - }, - interactiveMessage: interactiveMsg - } - } - }; - - // Générer et relayer - const jid = `${phone}@s.whatsapp.net`; - const msg = generateWAMessageFromContent(jid, raw, {}); - await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); - - return res.json({ success: true }); - } catch (e) { - console.error('❌ Erreur /sendInteractiveImage :', e); - return res.status(500).json({ error: e.message }); - } -}); - -// 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é' }); - -// 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 }); -// } -// }); -app.post('/sendProductMessage', async (req, res) => { - // On vérifie sock et isConnected, pas client - if (!sock || !isConnected) { - return res.status(400).json({ error: 'Non connecté à WhatsApp' }); - } - - const { - phone, - productImageUrl, - productImageCount, - productTitle, - productDescription, - priceAmount1000, - currencyCode, - retailerId, - productUrl, - businessOwnerJid, - caption, - messageTitle, - footer, - interactiveButtons = [], - quoted - } = req.body; - - try { - const jid = `${phone}@s.whatsapp.net`; - - // Build product payload exactly comme avant - const productPayload = { - productImage: { url: productImageUrl }, - productImageCount, - title: productTitle, // ici productTitle - description: productDescription, - priceAmount1000: priceAmount1000 * 1000, // ajustez si besoin - currencyCode, - retailerId, - url: productUrl - }; - - // Transformer les boutons - const buttons = interactiveButtons.map(btn => { - const params = {}; - if (btn.id) params.id = btn.id; - if (btn.url) params.url = btn.url; - if (btn.display_text) params.display_text = btn.display_text; - return { - name: btn.name, - buttonParamsJson: JSON.stringify(params) - }; - }); - - // **On utilise sock.sendMessage** et non client.sendMessage - await sock.sendMessage( - jid, - { - product: productPayload, - businessOwnerJid, - caption, - title: messageTitle, - footer, - media: true, - interactiveButtons: buttons - }, - quoted ? { quoted } : {} - ); - - return res.json({ success: true }); - } catch (e) { - console.error('❌ Erreur /sendProductMessage :', e); - return res.status(500).json({ error: e.message }); - } -}); -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 imageUrl = 'https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg'; const content = { viewOnceMessage: { @@ -515,25 +102,19 @@ app.post('/testInteractiveImage', async (req, res) => { interactiveMessage: proto.Message.InteractiveMessage.create({ header: { hasMediaAttachment: true, - imageMessage: { url: "https://canguidev.fr/static/logo-merlo-cs-FR.jpg" } + imageMessage: { url: imageUrl } }, - body: { text: "Bienvenue chez Merlo France 🇫🇷\nChoisissez une action ci-dessous." }, - footer: { text: "MERLO - Support & Documentation" }, + body: { text: 'Bienvenue !' }, + footer: { text: 'Choisissez une action :' }, nativeFlowMessage: { buttons: [ { - name: "cta_reply", - buttonParamsJson: JSON.stringify({ - display_text: "📩 Contacter support", - id: "support_action" - }) + name: 'cta_reply', + buttonParamsJson: JSON.stringify({ display_text: 'Répondre', id: 'reply_id' }) }, { - name: "cta_url", - buttonParamsJson: JSON.stringify({ - display_text: "🌐 Voir la fiche produit", - url: "https://example.com/product" - }) + name: 'cta_url', + buttonParamsJson: JSON.stringify({ display_text: 'Site Web', url: 'https://canguidev.fr' }) } ] } @@ -541,17 +122,65 @@ app.post('/testInteractiveImage', async (req, res) => { } } }; - - const jid = `${phone}@s.whatsapp.net`; - const msg = generateWAMessageFromContent(jid, content, {}); - await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); - + + try { + const msg = generateWAMessageFromContent(jid, content, {}); + await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); + res.json({ success: true }); + } catch (e) { + console.error('❌ Erreur sendButtons :', 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é à WhatsApp' }); + + const jid = `${phone}@s.whatsapp.net`; + const imageUrl = 'https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg'; + + const content = { + viewOnceMessage: { + message: { + messageContextInfo: { + deviceListMetadata: {}, + deviceListMetadataVersion: 2 + }, + interactiveMessage: proto.Message.InteractiveMessage.create({ + header: { + hasMediaAttachment: true, + imageMessage: { url: imageUrl } + }, + body: { text: '📄 Document interactif' }, + footer: { text: 'Sélectionne une option :' }, + nativeFlowMessage: { + buttons: [ + { + name: 'cta_url', + buttonParamsJson: JSON.stringify({ display_text: 'Voir PDF', url: 'https://example.com/doc.pdf' }) + } + ] + } + }) + } + } + }; + + try { + const msg = generateWAMessageFromContent(jid, content, {}); + await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); + res.json({ success: true }); + } catch (e) { + console.error('❌ Erreur interactive image :', e); + res.status(500).json({ error: e.message }); + } }); -// 5) 404 et gestion des erreurs app.use((req, res) => res.status(404).json({ error: 'Ressource introuvable' })); app.use((err, req, res, next) => { - console.error('Middleware d’erreur :', err); + console.error('❌ Middleware erreur :', err); res.status(500).json({ error: err.message }); }); -app.listen(3001, () => console.log('🚀 Serveur Baileys démarré sur http://localhost:3001')); + +app.listen(3001, () => console.log('🚀 Serveur Baileys opérationnel sur http://localhost:3001'));