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 20:25:52 +00:00
const NodeCache = require ( 'node-cache' ) ;
2025-05-07 08:49:05 +00:00
const mime = require ( 'mime-types' ) ;
2025-05-07 09:03:28 +00:00
const fetch = require ( 'node-fetch' ) ; // en haut si pas encore ajouté
2025-05-07 08:49:05 +00:00
2025-05-06 19:25:43 +00:00
const {
2025-05-06 20:21:45 +00:00
default : makeWASocket ,
2025-05-06 19:25:43 +00:00
useMultiFileAuthState ,
DisconnectReason ,
fetchLatestBaileysVersion ,
2025-05-06 20:25:52 +00:00
proto ,
2025-05-07 08:51:45 +00:00
generateWAMessageFromContent ,
2025-05-07 09:29:19 +00:00
prepareWAMessageMedia ,
generateWAMessageContent
2025-05-06 20:25:52 +00:00
} = require ( '@fizzxydev/baileys-pro' ) ;
2025-05-07 09:05:17 +00:00
const app = express ( ) ;
2025-05-07 09:03:28 +00:00
const http = require ( 'http' ) ;
const WebSocket = require ( 'ws' ) ;
const server = http . createServer ( app ) ;
const wss = new WebSocket . Server ( { server } ) ;
2025-05-06 10:12:55 +00:00
2025-05-07 09:03:28 +00:00
function broadcastToClients ( data ) {
const message = JSON . stringify ( data ) ;
wss . clients . forEach ( client => {
if ( client . readyState === WebSocket . OPEN ) {
client . send ( message ) ;
}
} ) ;
}
2025-05-06 14:04:11 +00:00
app . use ( express . json ( ) ) ;
app . use ( express . static ( 'public' ) ) ;
2025-05-06 08:51:13 +00:00
2025-05-06 14:04:11 +00:00
let sock ;
let qrData = null ;
let isConnected = false ;
2025-05-06 08:51:13 +00:00
2025-05-06 20:25:52 +00:00
// 💾 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" } ;
}
2025-05-07 09:03:28 +00:00
app . post ( '/webhook/status' , ( req , res ) => {
console . log ( '📩 Webhook reçu :' , req . body ) ;
res . sendStatus ( 200 ) ;
} ) ;
2025-05-06 20:25:52 +00:00
2025-05-06 10:12:55 +00:00
const initBaileys = async ( ) => {
2025-05-06 14:04:11 +00:00
const { version } = await fetchLatestBaileysVersion ( ) ;
const { state , saveCreds } = await useMultiFileAuthState ( 'auth' ) ;
2025-05-06 08:51:13 +00:00
2025-05-06 10:12:55 +00:00
sock = makeWASocket ( {
version ,
auth : state ,
2025-05-06 20:25:52 +00:00
markOnlineOnConnect : false ,
getMessage : async ( key ) => await getMessageFromStore ( key ) ,
cachedGroupMetadata : async ( jid ) => groupCache . get ( jid )
2025-05-06 14:04:11 +00:00
} ) ;
2025-05-06 08:51:13 +00:00
2025-05-06 19:25:43 +00:00
sock . ev . on ( 'connection.update' , async ( { connection , lastDisconnect , qr } ) => {
2025-05-06 10:12:55 +00:00
if ( qr ) {
2025-05-06 14:04:11 +00:00
qrData = await qrcode . toDataURL ( qr ) ;
isConnected = false ;
2025-05-06 10:12:55 +00:00
}
2025-05-06 19:25:43 +00:00
2025-05-06 10:12:55 +00:00
if ( connection === 'close' ) {
2025-05-06 14:04:11 +00:00
const shouldReconnect = lastDisconnect ? . error ? . output ? . statusCode !== DisconnectReason . loggedOut ;
2025-05-06 20:25:52 +00:00
console . log ( shouldReconnect ? '🔁 Reconnexion...' : '❌ Déconnecté.' ) ;
if ( shouldReconnect ) initBaileys ( ) ;
2025-05-06 10:12:55 +00:00
} else if ( connection === 'open' ) {
2025-05-06 14:04:11 +00:00
console . log ( '✅ Connecté à WhatsApp' ) ;
isConnected = true ;
2025-05-06 10:12:55 +00:00
}
2025-05-06 14:04:11 +00:00
} ) ;
2025-05-06 13:54:09 +00:00
2025-05-06 14:04:11 +00:00
sock . ev . on ( 'creds.update' , saveCreds ) ;
2025-05-06 19:25:43 +00:00
2025-05-06 20:25:52 +00:00
// 📌 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 ] ;
2025-05-07 05:19:18 +00:00
2025-05-06 20:25:52 +00:00
if ( ! msg . key . fromMe && msg . message ? . conversation ) {
console . log ( '💬 Message reçu de' , msg . key . remoteJid , ':' , msg . message . conversation ) ;
}
2025-05-07 05:19:18 +00:00
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 : {
2025-05-07 05:34:09 +00:00
url : 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf' ,
2025-05-07 05:19:18 +00:00
} ,
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 : {
2025-05-07 05:34:09 +00:00
url : 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf' ,
2025-05-07 05:19:18 +00:00
} ,
mimetype : 'application/pdf' ,
fileName : 'Document_2.pdf' ,
caption : 'Voici votre *Document 2* 📄' ,
footer : '© Fizzxy Dev' ,
} ) ;
}
2025-05-07 05:16:23 +00:00
}
2025-05-06 20:25:52 +00:00
} ) ;
2025-05-07 09:07:37 +00:00
sock . ev . on ( 'messages.update' , async ( updates ) => {
broadcastToClients ( {
messageId ,
status ,
jid
} ) ;
} ) ;
2025-05-06 14:04:11 +00:00
} ;
2025-05-07 09:06:47 +00:00
2025-05-07 15:17:07 +00:00
2025-05-07 09:06:47 +00:00
initBaileys ( ) ;
2025-05-07 09:07:37 +00:00
2025-05-06 20:25:52 +00:00
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 ;
if ( ! sock || ! isConnected ) return res . status ( 400 ) . json ( { error : 'Non connecté' } ) ;
2025-05-06 09:38:23 +00:00
try {
2025-05-06 14:04:11 +00:00
await sock . sendMessage ( ` ${ phone } @s.whatsapp.net ` , { text : message } ) ;
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 15:34:25 +00:00
2025-05-07 12:30:01 +00:00
2025-05-07 15:15:56 +00:00
//// ***** bonne route ****** ///
/ * e x e m p l e d e j s o n
* {
"phone" : "33617452235" ,
"imageUrl" : "https://wa.canguidev.fr/static/logo-merlo-cs-FR.jpg" ,
"caption" : "🟢 Offre spéciale !\n📅 Merlo2025\n\nBonjour M. Jérôme VANDEMEULEBROUCK,\n\nSuite à nos échanges, je vous transmets l'offre que je vous propose.\nVous trouverez ci-joint votre offre au format PDF.\n\n📄 Proposition : https://pvmel.fr/V5Pi\n🛠️ Descriptif machine : \n\n—\nCanguilième Julien\n📎 Votre offre en PDF ci-joint."
}
*
* /
2025-05-07 12:30:01 +00:00
app . post ( '/sendImageWithBaileysPro' , async ( req , res ) => {
if ( ! sock || ! isConnected ) {
return res . status ( 400 ) . json ( { error : 'Non connecté à WhatsApp' } ) ;
}
const {
2025-05-07 12:50:11 +00:00
phone ,
imageUrl ,
caption ,
quoted ,
2025-05-07 12:30:01 +00:00
} = req . body ;
if ( ! phone || ! imageUrl ) {
return res . status ( 400 ) . json ( { error : 'Paramètres requis manquants (phone, imageUrl)' } ) ;
}
const jid = ` ${ phone } @s.whatsapp.net ` ;
try {
await sock . sendMessage (
jid ,
{
image : { url : imageUrl } ,
2025-05-07 13:11:58 +00:00
caption : caption || '' ,
2025-05-07 13:03:14 +00:00
2025-05-07 12:30:01 +00:00
} ,
quoted ? { quoted } : { }
) ;
res . json ( { success : true , to : jid } ) ;
} catch ( e ) {
console . error ( '❌ Erreur /sendImageWithBaileysPro :' , e ) ;
res . status ( 500 ) . json ( { error : e . message } ) ;
}
} ) ;
2025-05-06 15:35:50 +00:00
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 ) => {
console . error ( 'Middleware d’ erreur :' , err ) ;
res . status ( 500 ) . json ( { error : err . message } ) ;
} ) ;
2025-05-06 14:04:11 +00:00
app . listen ( 3001 , ( ) => console . log ( '🚀 Serveur Baileys démarré sur http://localhost:3001' ) ) ;