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é' }); } 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 = { 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: { 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(jid, content, {}); await sock.relayMessage(jid, 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 }); } }); // 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('/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); 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`; await sock.sendMessage( jid, { product: { productImage: { url: productImageUrl }, productImageCount, title: productTitle, description: productDescription, priceAmount1000, currencyCode, retailerId, url: productUrl, }, businessOwnerJid, caption, title: messageTitle, footer, media: true, interactiveButtons }, quoted ? { quoted } : {} ); return res.json({ success: true }); } catch (e) { console.error('❌ Erreur /sendProductMessage :', e); return res.status(500).json({ error: e.message }); } }); app.post('/testViewOnce', 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' // Ton image publique }, viewOnce: true, caption: 'Voici une image à affichage unique 📷' }); res.json({ success: true }); } catch (e) { console.error('❌ Erreur testViewOnce :', e); res.status(500).json({ error: e.message }); } }); app.post('/testHeaderImage', 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 { // 1. Charger l'image locale const imagePath = path.join(__dirname, 'public/logo-merlo-cs-FR.jpg'); const imageBuffer = fs.readFileSync(imagePath); const generate = async (type, url) => { const generated = await generateWAMessageContent({ [type]: { url } }, { upload: sock.waUploadToServer }) return generated[`${type}Message`] } // 3. Créer le message interactif avec image dans le header const msg = generateWAMessageFromContent(jid.remoteJid, { viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }, interactiveMessage: proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.create({ text: "body text (optional)" }), footer: proto.Message.InteractiveMessage.Footer.create({ text: "footer text (optional)" }), header: proto.Message.InteractiveMessage.Header.create({ title: "some title", hasMediaAttachment: false, // false if you don't want to send media with it //imageMessage: generate("image", "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/TEIDE.JPG/500px-TEIDE.JPG"), //videoMessage: generate("video", "url/path to video"), // if it's an video }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: [{ name: "quick_reply", buttonParamsJson: JSON.stringify({ display_text: "button 1", // <-- displayed text id: ".menu" // <-- this is the id or you may call it command 🤷‍♂️ }) // REMEMBER TO USE "JSON.stringify()" BECAUSE "buttonParamsJson" ONLY ACCEPTING STIRNG JSON, NOT AN OBJECT },{ name: "cta_url", buttonParamsJson: JSON.stringify({ display_text: "subscribe my Youtube!", url: "https://youtube.com/@fannmods", merchant_url: "https://youtube.com" }) }] }) }) } } }, {}) console.log(msg.message) // 4. Envoyer le message await sock.relayMessage(jid, msg.message, { messageId: msg.key.id }); res.json({ success: true }); } catch (e) { console.error('❌ Erreur testHeaderImage :', e); res.status(500).json({ error: e.message }); } });