|
|
|
const fs = require('fs'); |
|
const csv = require('csv-parser'); |
|
const axios = require('axios'); |
|
const { promisify } = require('util'); |
|
const sleep = promisify(setTimeout); |
|
require('dotenv').config(); |
|
|
|
const token = process.env.WHATSAPP_TOKEN; |
|
const phoneNumberId = process.env.PHONE_NUMBER_ID; |
|
const inputCsv = process.env.INPUT_CSV || 'contacts.csv'; |
|
const minDelay = parseInt(process.env.MIN_DELAY_SECONDS || '1') * 1000; |
|
const maxDelay = parseInt(process.env.MAX_DELAY_SECONDS || '3') * 1000; |
|
|
|
const now = new Date(); |
|
const timestamp = `${now.getMinutes()}-${now.getHours()}-${now.getDate()}-${now.getMonth() + 1}-${now.getFullYear()}`; |
|
let counter = 1; |
|
|
|
const logsDir = 'logs'; |
|
const failedDir = 'failed'; |
|
if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir); |
|
if (!fs.existsSync(failedDir)) fs.mkdirSync(failedDir); |
|
|
|
const logFile = `${logsDir}/send-${timestamp}-${counter}.log`; |
|
const failedCsv = `${failedDir}/failed-${timestamp}-${counter}.csv`; |
|
const logStream = fs.createWriteStream(logFile, { flags: 'a' }); |
|
const failedStream = fs.createWriteStream(failedCsv); |
|
failedStream.write('phone,message,error\n'); |
|
|
|
const sendMessage = async (to, message) => { |
|
try { |
|
const res = await axios.post( |
|
`https://graph.facebook.com/v19.0/${phoneNumberId}/messages`, |
|
{ |
|
messaging_product: 'whatsapp', |
|
to, |
|
type: 'text', |
|
text: { body: message }, |
|
}, |
|
{ |
|
headers: { |
|
Authorization: `Bearer ${token}`, |
|
'Content-Type': 'application/json', |
|
}, |
|
} |
|
); |
|
logStream.write(`[SUCCESS] ${to}: ${message}\n`); |
|
console.log(`β
Sent to ${to}`); |
|
} catch (err) { |
|
const errorMsg = err.response?.data?.error?.message || err.message; |
|
logStream.write(`[FAILURE] ${to}: ${errorMsg}\n`); |
|
failedStream.write(`"${to}","${message}","${errorMsg}"\n`); |
|
console.error(`β Failed for ${to}: ${errorMsg}`); |
|
} |
|
}; |
|
|
|
const processCsv = async () => { |
|
const rows = []; |
|
fs.createReadStream(inputCsv) |
|
.pipe(csv()) |
|
.on('data', (row) => { |
|
if (row.phone && row.message) rows.push(row); |
|
}) |
|
.on('end', async () => { |
|
console.log(`π ${rows.length} messages to process...`); |
|
for (const row of rows) { |
|
await sendMessage(row.phone, row.message); |
|
const delay = Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay; |
|
await sleep(delay); |
|
counter++; |
|
} |
|
logStream.end(); |
|
failedStream.end(); |
|
console.log('β
Processing completed.'); |
|
}); |
|
}; |
|
|
|
processCsv(); |
|
|
|
|