2025-05-06 17:58:53 +00:00
|
|
|
|
const venom = require('venom-bot');
|
2025-05-06 14:04:11 +00:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const qrcode = require('qrcode');
|
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
const fs = require('fs');
|
2025-05-06 10:12:55 +00:00
|
|
|
|
|
2025-05-06 14:04:11 +00:00
|
|
|
|
const app = express();
|
|
|
|
|
|
app.use(express.json());
|
|
|
|
|
|
app.use(express.static('public'));
|
2025-05-06 08:51:13 +00:00
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
let client;
|
2025-05-06 14:04:11 +00:00
|
|
|
|
let qrData = null;
|
|
|
|
|
|
let isConnected = false;
|
2025-05-06 08:51:13 +00:00
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
// Initialisation Venom-bot
|
|
|
|
|
|
const initVenom = async () => {
|
|
|
|
|
|
client = await venom.create({
|
|
|
|
|
|
session: 'whatsapp-session',
|
|
|
|
|
|
disableSpins: true,
|
|
|
|
|
|
logQR: false,
|
|
|
|
|
|
catchQR: (base64Qrimg) => {
|
|
|
|
|
|
qrcode.toDataURL(base64Qrimg, (err, url) => {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
console.error('Erreur QR code:', err);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
qrData = url;
|
|
|
|
|
|
isConnected = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-05-06 14:04:11 +00:00
|
|
|
|
});
|
2025-05-06 08:51:13 +00:00
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
client.onStateChange((state) => {
|
|
|
|
|
|
console.log('État de connexion:', state);
|
|
|
|
|
|
isConnected = state === 'CONNECTED';
|
|
|
|
|
|
|
|
|
|
|
|
if (state === 'DISCONNECTED') {
|
|
|
|
|
|
console.log('🔁 Tentative de reconnexion...');
|
|
|
|
|
|
setTimeout(initVenom, 5000);
|
2025-05-06 10:12:55 +00:00
|
|
|
|
}
|
2025-05-06 14:04:11 +00:00
|
|
|
|
});
|
|
|
|
|
|
};
|
2025-05-06 10:12:55 +00:00
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
initVenom();
|
|
|
|
|
|
|
|
|
|
|
|
// Routes Express
|
2025-05-06 14:31:16 +00:00
|
|
|
|
app.use('/static', express.static(path.join(__dirname, 'public')));
|
2025-05-06 08:51:13 +00:00
|
|
|
|
|
2025-05-06 09:54:23 +00:00
|
|
|
|
app.get('/login', (req, res) => {
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.sendFile(path.join(__dirname, 'public', 'login.html'));
|
|
|
|
|
|
});
|
2025-05-06 10:12:55 +00:00
|
|
|
|
|
2025-05-06 09:54:23 +00:00
|
|
|
|
app.get('/api/qrcode', (req, res) => {
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.json({ qr: qrData, connected: isConnected });
|
|
|
|
|
|
});
|
2025-05-06 08:51:13 +00:00
|
|
|
|
|
2025-05-06 09:38:23 +00:00
|
|
|
|
app.post('/sendText', async (req, res) => {
|
2025-05-06 14:04:11 +00:00
|
|
|
|
const { phone, message } = req.body;
|
2025-05-06 17:58:53 +00:00
|
|
|
|
if (!client || !isConnected) return res.status(400).json({ error: 'Non connecté' });
|
|
|
|
|
|
|
2025-05-06 09:38:23 +00:00
|
|
|
|
try {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
await client.sendText(`${phone}@c.us`, message);
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.json({ success: true });
|
2025-05-06 09:54:23 +00:00
|
|
|
|
} catch (e) {
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.status(500).json({ error: e.message });
|
2025-05-06 09:38:23 +00:00
|
|
|
|
}
|
2025-05-06 14:04:11 +00:00
|
|
|
|
});
|
2025-05-06 09:43:48 +00:00
|
|
|
|
|
2025-05-06 09:54:23 +00:00
|
|
|
|
app.post('/sendButtons', async (req, res) => {
|
2025-05-06 14:04:11 +00:00
|
|
|
|
const { phone } = req.body;
|
2025-05-06 17:58:53 +00:00
|
|
|
|
if (!client || !isConnected) return res.status(400).json({ error: 'Non connecté' });
|
2025-05-06 12:00:21 +00:00
|
|
|
|
|
2025-05-06 11:53:28 +00:00
|
|
|
|
try {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
const buttons = [
|
|
|
|
|
|
{ body: "📩 Contacter support" },
|
|
|
|
|
|
{ body: "🌐 Voir notre site" },
|
|
|
|
|
|
{ body: "📞 Appeler le support" }
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
await client.sendButtons(
|
|
|
|
|
|
`${phone}@c.us`,
|
|
|
|
|
|
"Bienvenue sur notre service !",
|
|
|
|
|
|
buttons,
|
|
|
|
|
|
"Choisis une action ci-dessous"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.json({ success: true });
|
2025-05-06 11:53:28 +00:00
|
|
|
|
} catch (e) {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
console.error('❌ Erreur boutons:', e);
|
2025-05-06 14:04:11 +00:00
|
|
|
|
res.status(500).json({ error: e.message });
|
2025-05-06 11:53:28 +00:00
|
|
|
|
}
|
2025-05-06 14:04:11 +00:00
|
|
|
|
});
|
2025-05-06 17:58:53 +00:00
|
|
|
|
|
2025-05-06 12:17:18 +00:00
|
|
|
|
app.post('/sendInteractiveImage', async (req, res) => {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
const { phone, caption, footer } = req.body;
|
|
|
|
|
|
if (!client || !isConnected) return res.status(400).json({ error: 'Non connecté' });
|
2025-05-06 12:17:18 +00:00
|
|
|
|
|
|
|
|
|
|
try {
|
2025-05-06 17:49:13 +00:00
|
|
|
|
const imagePath = path.join(__dirname, 'public', 'logo-merlo-cs-FR.jpg');
|
|
|
|
|
|
const imageBuffer = fs.readFileSync(imagePath);
|
|
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
await client.sendImage(
|
|
|
|
|
|
`${phone}@c.us`,
|
|
|
|
|
|
imageBuffer,
|
|
|
|
|
|
'logo.jpg',
|
|
|
|
|
|
caption || 'Description par défaut',
|
|
|
|
|
|
{
|
|
|
|
|
|
footer: footer || 'Pied de page',
|
|
|
|
|
|
buttons: [
|
|
|
|
|
|
{ buttonId: 'id1', buttonText: { displayText: '📄 Voir proposition' }, type: 1 },
|
|
|
|
|
|
{ buttonId: 'id2', buttonText: { displayText: '🔧 Spécifications' }, type: 1 }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2025-05-06 17:49:13 +00:00
|
|
|
|
);
|
2025-05-06 17:58:53 +00:00
|
|
|
|
|
|
|
|
|
|
res.json({ success: true });
|
2025-05-06 14:04:11 +00:00
|
|
|
|
} catch (e) {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
console.error('❌ Erreur image interactive:', e);
|
|
|
|
|
|
res.status(500).json({ error: e.message });
|
2025-05-06 12:17:18 +00:00
|
|
|
|
}
|
2025-05-06 14:04:11 +00:00
|
|
|
|
});
|
2025-05-06 14:52:28 +00:00
|
|
|
|
|
2025-05-06 15:28:38 +00:00
|
|
|
|
app.post('/sendProductMessage', async (req, res) => {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
if (!client || !isConnected) return res.status(400).json({ error: 'Non connecté' });
|
2025-05-06 15:28:38 +00:00
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
|
phone,
|
|
|
|
|
|
productImageUrl,
|
|
|
|
|
|
productTitle,
|
|
|
|
|
|
productDescription,
|
2025-05-06 17:58:53 +00:00
|
|
|
|
price,
|
|
|
|
|
|
productUrl
|
2025-05-06 15:28:38 +00:00
|
|
|
|
} = req.body;
|
2025-05-06 12:17:18 +00:00
|
|
|
|
|
2025-05-06 15:28:38 +00:00
|
|
|
|
try {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
// Venom-bot n'a pas de méthode native pour les produits
|
|
|
|
|
|
// On simule avec une image et des boutons
|
|
|
|
|
|
const buttons = [
|
|
|
|
|
|
{ buttonId: 'buy', buttonText: { displayText: '🛒 Acheter' }, type: 1 },
|
|
|
|
|
|
{ buttonId: 'details', buttonText: { displayText: 'ℹ️ Détails' }, type: 1 }
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const caption = `*${productTitle}*\n\n${productDescription}\n\nPrix: ${price}`;
|
|
|
|
|
|
|
|
|
|
|
|
await client.sendImage(
|
|
|
|
|
|
`${phone}@c.us`,
|
|
|
|
|
|
productImageUrl,
|
|
|
|
|
|
'product.jpg',
|
|
|
|
|
|
caption,
|
|
|
|
|
|
{ buttons }
|
2025-05-06 15:28:38 +00:00
|
|
|
|
);
|
2025-05-06 17:58:53 +00:00
|
|
|
|
|
|
|
|
|
|
res.json({ success: true });
|
2025-05-06 15:28:38 +00:00
|
|
|
|
} catch (e) {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
console.error('❌ Erreur produit:', e);
|
|
|
|
|
|
res.status(500).json({ error: e.message });
|
2025-05-06 15:28:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-05-06 15:34:25 +00:00
|
|
|
|
|
2025-05-06 17:58:53 +00:00
|
|
|
|
// Gestion des erreurs
|
2025-05-06 15:34:25 +00:00
|
|
|
|
app.use((req, res) => res.status(404).json({ error: 'Ressource introuvable' }));
|
|
|
|
|
|
app.use((err, req, res, next) => {
|
2025-05-06 17:58:53 +00:00
|
|
|
|
console.error('Erreur:', err);
|
2025-05-06 15:34:25 +00:00
|
|
|
|
res.status(500).json({ error: err.message });
|
|
|
|
|
|
});
|
2025-05-06 17:58:53 +00:00
|
|
|
|
|
|
|
|
|
|
app.listen(3001, () => console.log('🚀 Serveur Venom démarré sur http://localhost:3001'));
|