whatsapp/index.js
2025-05-06 15:54:09 +02:00

266 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// index.js
const {
default: makeWASocket,
useMultiFileAuthState,
DisconnectReason,
fetchLatestBaileysVersion,
proto,
generateWAMessageFromContent
} = 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 axios = require('axios')
const app = express()
app.use(express.json())
app.use(express.static('public'))
let sock
let qrData = null
let isConnected = false
// Dictionnaire des URLs PDF
const PDF_LINKS = {
prop: 'https://merlo-ch.com/uploads/proposition/f_p_250505_0000136_00008_EB00001909.pdf',
spec: 'https://merlo-ch.com/uploads/proposition/d_p_250505_0000136_00008_EB00001909.pdf'
}
const initBaileys = async () => {
const { version } = await fetchLatestBaileysVersion()
const { state, saveCreds } = await useMultiFileAuthState('auth')
sock = makeWASocket({
version,
auth: state,
printQRInTerminal: false
})
sock.ev.on('connection.update', update => {
const { connection, lastDisconnect, qr } = update
if (qr) {
qrData = qrcode.toDataURL(qr)
isConnected = false
}
if (connection === 'close') {
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut
console.log(shouldReconnect ? '🔁 Reconnexion...' : '❌ Déconnecté.')
if (shouldReconnect) initBaileys()
} else if (connection === 'open') {
console.log('✅ Connecté à WhatsApp')
isConnected = true
}
})
sock.ev.on('creds.update', saveCreds)
// Gestion du clic sur les boutons pour envoyer le PDF
sock.ev.on('messages.upsert', async ({ messages }) => {
const msg = messages[0]
if (!msg.key.fromMe && msg.message?.buttonsResponseMessage) {
const btnId = msg.message.buttonsResponseMessage.selectedButtonId
const pdfUrl = PDF_LINKS[btnId]
if (!pdfUrl) return
try {
const resp = await axios.get(pdfUrl, { responseType: 'arraybuffer' })
const pdfBuffer = Buffer.from(resp.data)
await sock.sendMessage(msg.key.remoteJid, {
document: pdfBuffer,
fileName: btnId === 'prop' ? 'Proposition.pdf' : 'Spec_machine.pdf',
mimetype: 'application/pdf'
})
} catch (e) {
console.error('❌ Erreur envoi PDF :', e)
}
}
})
}
initBaileys().catch(console.error)
// Affichage du QR code pour login
app.get('/login', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'login.html'))
})
app.get('/api/qrcode', (req, res) => {
res.json({ qr: qrData, connected: isConnected })
})
// Envoi d'un simple texte
app.post('/sendText', async (req, res) => {
const { phone, message } = req.body
if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' })
try {
await sock.sendMessage(`${phone}@s.whatsapp.net`, { text: message })
res.json({ success: true })
} catch (e) {
res.status(500).json({ error: e.message })
}
})
// Envoi de boutons sans image
app.post('/sendButtons', async (req, res) => {
const { phone } = req.body
if (!sock || !isConnected) return res.status(400).json({ error: 'Non connecté' })
try {
const content = {
message: {
interactiveMessage: proto.Message.InteractiveMessage.create({
header: proto.Message.InteractiveMessage.Header.create({
title: "Menu principal",
hasMediaAttachment: false
}),
body: proto.Message.InteractiveMessage.Body.create({
text: "Bienvenue sur notre service !"
}),
footer: proto.Message.InteractiveMessage.Footer.create({
text: "Choisis une action ci-dessous"
}),
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
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"
})
}
]
})
})
}
}
const msg = generateWAMessageFromContent(
`${phone}@s.whatsapp.net`,
content,
{}
)
await sock.relayMessage(
msg.key.remoteJid,
msg.message,
{ messageId: msg.key.id }
)
res.json({ success: true })
} catch (e) {
console.error('❌ Erreur sendButtons :', e)
res.status(500).json({ error: e.message })
}
})
// Envoi de l'image interactive avec CTA URL
app.post('/sendInteractiveImage', async (req, res) => {
const {
phone,
caption = 'Description par défaut',
title = 'Titre par défaut',
footer = 'Pied de page',
propositionUrl,
specUrl
} = req.body
if (!sock || !isConnected) {
return res.status(400).json({ error: 'Non connecté' })
}
try {
const jid = `${phone}@s.whatsapp.net`
const imagePath = path.join(__dirname, 'public', 'logo-merlo-cs-FR.jpg')
const imgBuffer = fs.readFileSync(imagePath)
// Construction du native interactive message
const interactiveContent = proto.Message.InteractiveMessage.create({
header: proto.Message.InteractiveMessage.Header.create({
title,
hasMediaAttachment: true
}),
body: proto.Message.InteractiveMessage.Body.create({
text: caption
}),
footer: proto.Message.InteractiveMessage.Footer.create({
text: footer
}),
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
buttons: [
{
name: 'prop',
buttonParamsJson: JSON.stringify({
display_text: 'Proposition',
url: propositionUrl,
merchant_url: propositionUrl
})
},
{
name: 'spec',
buttonParamsJson: JSON.stringify({
display_text: 'Spec machine',
url: specUrl,
merchant_url: specUrl
})
}
]
})
})
// On emballe limage en tant que miniature (viewOnce)
const messageContent = {
viewOnceMessage: {
message: {
messageContextInfo: {
deviceListMetadata: {},
deviceListMetadataVersion: 2
},
viewOnceMessage: {
message: {
imageMessage: {
mimetype: 'image/jpeg',
jpegThumbnail: imgBuffer
}
}
},
interactiveMessage: interactiveContent
}
}
}
const waMsg = generateWAMessageFromContent(jid, messageContent, {})
await sock.relayMessage(
waMsg.key.remoteJid,
waMsg.message,
{ messageId: waMsg.key.id }
)
res.json({ success: true })
} catch (err) {
console.error('❌ Erreur sendInteractiveImage:', err)
res.status(500).json({ error: err.message })
}
})
const PORT = process.env.PORT || 3001
app.listen(PORT, () => {
console.log(`🚀 Serveur Baileys démarré sur http://localhost:${PORT}`)
})