Merge branch 'main' of https://lab.canguidev.fr/cangui/whatsapp
This commit is contained in:
commit
7f0ba5a600
558
index.js
558
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 });
|
||||
}
|
||||
});
|
||||
|
||||
@ -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"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,116 +2,66 @@
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Connexion WhatsApp & Envoi Interactif</title>
|
||||
<title>Connexion WhatsApp</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 600px; margin: auto; padding: 20px; }
|
||||
h1, h2 { text-align: center; }
|
||||
#qrcode { text-align: center; margin-bottom: 20px; }
|
||||
form { display: flex; flex-direction: column; gap: 10px; }
|
||||
label { display: flex; flex-direction: column; font-weight: bold; }
|
||||
input { padding: 8px; font-size: 1rem; }
|
||||
button { padding: 10px; font-size: 1rem; cursor: pointer; }
|
||||
#result { margin-top: 15px; font-weight: bold; }
|
||||
hr { margin: 30px 0; }
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: center;
|
||||
background: #f2f2f2;
|
||||
padding: 40px;
|
||||
}
|
||||
#qr-container {
|
||||
margin-top: 40px;
|
||||
}
|
||||
#qr {
|
||||
max-width: 300px;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
#status {
|
||||
margin-top: 20px;
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Scanne le QR Code WhatsApp</h1>
|
||||
<div id="qrcode">Chargement...</div>
|
||||
<h1>Connexion WhatsApp</h1>
|
||||
<p>Scannez le QR Code avec votre application WhatsApp</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Envoyer du message interactif avec image</h2>
|
||||
<form id="interactiveForm">
|
||||
<label>
|
||||
Téléphone (sans +, ex: 33612345678):
|
||||
<input type="text" name="phone" required placeholder="33612345678">
|
||||
</label>
|
||||
<label>
|
||||
Caption:
|
||||
<input type="text" name="caption" placeholder="Description par défaut">
|
||||
</label>
|
||||
<label>
|
||||
Title:
|
||||
<input type="text" name="title" placeholder="Titre par défaut">
|
||||
</label>
|
||||
<label>
|
||||
Subtitle:
|
||||
<input type="text" name="subtitle" placeholder="Sous-titre">
|
||||
</label>
|
||||
<label>
|
||||
Footer:
|
||||
<input type="text" name="footer" placeholder="Pied de page">
|
||||
</label>
|
||||
<label>
|
||||
URL Proposition:
|
||||
<input type="url" name="propositionUrl" placeholder="https://exemple.com/prop.pdf" required>
|
||||
</label>
|
||||
<label>
|
||||
URL Spec Machine:
|
||||
<input type="url" name="specUrl" placeholder="https://exemple.com/spec.pdf" required>
|
||||
</label>
|
||||
<button type="submit">Envoyer le message</button>
|
||||
</form>
|
||||
<div id="result"></div>
|
||||
<div id="qr-container">
|
||||
<img id="qr" src="" alt="QR Code WhatsApp" />
|
||||
<div id="status">Chargement du QR code...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Vérification du QR code et statut de connexion
|
||||
async function checkQR() {
|
||||
try {
|
||||
const res = await fetch('/api/qrcode');
|
||||
const data = await res.json();
|
||||
console.log('Réponse /api/qrcode :', data);
|
||||
async function fetchQRCode() {
|
||||
try {
|
||||
const res = await fetch('/api/qrcode');
|
||||
const data = await res.json();
|
||||
|
||||
const qrContainer = document.getElementById('qrcode');
|
||||
if (data.connected) {
|
||||
qrContainer.innerHTML = '✅ Connecté à WhatsApp !';
|
||||
} else if (data.qr) {
|
||||
// Si data.qr est une chaîne (Data-URL)
|
||||
if (typeof data.qr === 'string') {
|
||||
qrContainer.innerHTML = `<img src="${data.qr}" alt="QR Code WhatsApp" width="200" height="200"/>`;
|
||||
}
|
||||
// Sinon, si c'est un objet { data: […], mime: 'image/png' }
|
||||
else if (data.qr.data) {
|
||||
const uint8Array = new Uint8Array(data.qr.data);
|
||||
const base64 = btoa(String.fromCharCode(...uint8Array));
|
||||
const src = `data:${data.qr.mime};base64,${base64}`;
|
||||
qrContainer.innerHTML = `<img src="${src}" alt="QR Code WhatsApp" width="200" height="200"/>`;
|
||||
const qrImg = document.getElementById('qr');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
if (data.connected) {
|
||||
qrImg.style.display = 'none';
|
||||
status.textContent = "✅ Connecté à WhatsApp";
|
||||
} else if (data.qr) {
|
||||
qrImg.src = data.qr;
|
||||
qrImg.style.display = 'block';
|
||||
status.textContent = "QR code prêt à être scanné";
|
||||
} else {
|
||||
qrContainer.textContent = 'QR reçu, mais format non pris en charge.';
|
||||
status.textContent = "⏳ En attente de génération du QR code...";
|
||||
}
|
||||
} else {
|
||||
qrContainer.textContent = 'Pas encore de QR disponible.';
|
||||
} catch (error) {
|
||||
console.error("Erreur de récupération QR:", error);
|
||||
document.getElementById('status').textContent = "❌ Impossible de charger le QR Code";
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur checkQR:', err);
|
||||
}
|
||||
setTimeout(checkQR, 3000);
|
||||
}
|
||||
checkQR();
|
||||
|
||||
// Envoi du formulaire interactif
|
||||
document.getElementById('interactiveForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const body = {};
|
||||
formData.forEach((value, key) => body[key] = value);
|
||||
|
||||
const res = await fetch('/sendInteractiveImage', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
const data = await res.json();
|
||||
const resultDiv = document.getElementById('result');
|
||||
if (data.success) {
|
||||
resultDiv.textContent = '✅ Message envoyé avec succès';
|
||||
resultDiv.style.color = 'green';
|
||||
} else {
|
||||
resultDiv.textContent = '❌ Erreur : ' + (data.error || 'Inconnue');
|
||||
resultDiv.style.color = 'red';
|
||||
}
|
||||
});
|
||||
// Rafraîchit toutes les 5 secondes
|
||||
fetchQRCode();
|
||||
setInterval(fetchQRCode, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user