PK!A7��nconst contactGroup = require("../models/contactGroup"); const contact = require("../models/contact"); const campaign = require("../models/campaign"); const callerIdModel = require("../models/callerId"); const accountCreditsModel = require("../models/accountCredits"); const campaignReport = require("../models/campaignReport"); const campignvoicereport = require("../models/campignvoicereport"); const reports = require("../models/reports"); const reqLib = require("../library/request"); const messageIdGen = require("../library/messageIdGenerate"); const http = require("http"); const https = require("https"); const dotenv = require("dotenv"); const jwt = require("jsonwebtoken"); const readXlsxFile = require("read-excel-file/node"); const excelJS = require("exceljs"); const fsPromises = require("fs").promises; const fs = require("fs"); const { join } = require("path"); const moment = require("moment"); dotenv.config(); const config = process.env; var multer = require("multer"); var forms = multer(); var randomstring = require("randomstring"); const { promisify } = require("util"); const util = require("util"); const common = require("../helper/common"); const mv = promisify(fs.rename); const formidable = require("formidable"); const { exec } = require("child_process"); const awaitExec = require('await-exec') const opts = { errorEventName: "error", logDirectory: "logs", fileNamePattern: "log-.log", dateFormat: "YYYY-MM-DD", }; const log = require("simple-node-logger").createRollingFileLogger(opts); async function runcampaign(req, res) { try { let message = null; let audioFileName = ""; var storage = multer.diskStorage({ destination: function (req, file, callback) { callback(null, "./users/upload/voice/"); // callback(null, "/var/www/html/TxtConnect/audio"); }, filename: function (req, file, callback) { var originalname = file.originalname; var splitName = originalname.split("."); var fileType = splitName[splitName.length - 1]; var fileName = randomstring.generate() + file.fieldname + "." + fileType; fileName = "Voice_File_" + fileName; log.info(`Filename: ${fileName}`); message = `${config.FILEPATH_URL}/upload/voice/${fileName}`; audioFileName = fileName; callback(null, fileName); }, }); var upload = multer({ storage: storage }); const nupload = util.promisify(upload.any()); await nupload(req, res); let userId = req.headers["id"]; let { name, type, groupId, isScheduled, scheduledAt, callerId } = req.body; log.info( `[Campaign Voice], [runcampaign], User Id [${userId}], Name [${name}], Type [${type}], Group Id [${groupId}], Calller Id [${callerId}], Is Scheduled [${isScheduled}]` ); if (!name || !type || !groupId || !callerId) { log.info(`[Campaign Voice], [runcampaign], [Missing parameters]`); return res.status(400).json({ msg: "Missing parameters" }); } const isExists = await campaign.isCampaignNameExistsByUserId(name, userId); if (isExists) { log.info( `[Campaign Voice], [runcampaign], [Campaign Name is already exists]` ); return res.status(400).json({ msg: "Campaign Name is already exists" }); } let groupContacts = await contact.getCorrectContactList(groupId); // if (groupContacts) { // groupContacts = groupContacts?.filter( // (contact) => contact.mobile.length >= 9 // ); // } let contactLength = groupContacts?.length; let balanceConsumed = groupContacts?.length; //checking account balance const accountBal = await accountCreditsModel.checkAccountBal(userId); const isSufficientBal = common.checkingBalance( "Campaign Voice", "runcampaign", accountBal, balanceConsumed, type ); if (!isSufficientBal) { return res .status(400) .json({ msg: "Insufficient balance to run this campaign" }); } if (!isScheduled || isScheduled == "false") { isScheduled = 2; } else { isScheduled = 1; if (!scheduledAt) { log.info( `[Campaign Voice], [runcampaign], [Missing parameters scheduledAt]` ); return res.status(400).json({ msg: "Missing parameters" }); } } if (contactLength > 0) { const callerIdDetails = await callerIdModel.getCallerIdById(callerId); if (false == callerIdDetails) { log.info( `[Campaign Voice], [runcampaign], Group Id [${groupId}], Caller Id not found` ); return res.status(200).json({ msg: "No caller Id found" }); } const date = moment().format("YYYY-MM-DD"); const time = moment().format("hh:mm:ss"); const newname = name + "_" + date + "_" + time; if (2 == isScheduled) { const campaignId = await campaign.addCampaign( userId, groupId, newname, message, type, callerId, contactLength, balanceConsumed ); let uplodeFilePath = `${process.env.UPLOAD_PATH}/voice/${audioFileName}`; let ProcessFilePath = `${process.env.UPLOAD_PATH}/voice/${campaignId}/${campaignId}.wav`; let sipServerFilePath = `${process.env.AUDIO_COPY_PATH}/${campaignId}/${campaignId}.wav`; let audioPath = `${config.FILEPATH_URL}/upload/voice/${campaignId}/${campaignId}.wav`; await campaign.updateCampaignAudioPath(audioPath, campaignId); let newAudioFileName = `${campaignId}.wav`; await awaitExec(`mkdir -p ${process.env.AUDIO_COPY_PATH}/${campaignId}`); await awaitExec(`mkdir -p ${process.env.UPLOAD_PATH}/voice/${campaignId}`); await awaitExec(`/usr/bin/php ${process.env.CONVERSION_SCRIPT} 'NORMAL' '${campaignId}' '${uplodeFilePath}' '${ProcessFilePath}'`); await awaitExec(`cp '${ProcessFilePath}' '${sipServerFilePath}'`); await awaitExec(`rm -rf ${uplodeFilePath}`); log.info( `[Campaign Voice], [runcampaign], Audio File Copied Successfully ` ); // if (!fs.existsSync(`${process.env.AUDIO_COPY_PATH}/${campaignId}`)) { // fs.mkdirSync(`${process.env.AUDIO_COPY_PATH}/${campaignId}`); // await fs.promises.copyFile( // `./users/upload/voice/${audioFileName}`, // `${process.env.AUDIO_COPY_PATH}/${campaignId}/${audioFileName}` // ); // log.info( // `[Campaign Voice], [runcampaign], Audio File Copied Successfully ` // ); // } await accountCreditsModel.deductBalance( userId, accountBal?.voice - balanceConsumed, "voice" ); log.info( `[Campaign Voice], [runcampaign], [Campaign started], Campaign Id [${campaignId}], Balance Consumed [${balanceConsumed}], Campaign Type [VOICE], Group Id [${groupId}]` ); processCampaignPush( groupId, callerId, campaignId, newname, type, audioPath, newAudioFileName, userId ); const NewAccountBal = await accountCreditsModel.checkAccountBal(userId); return res.status(200).json({ msg: "Campaign has been started", userCredit: NewAccountBal, }); } else { const campaignId = await campaign.addScheduledCampaign( userId, groupId, newname, message, type, callerId, scheduledAt, isScheduled, contactLength, balanceConsumed ); log.info( `[Campaign Voice], [runcampaign], [Campaign Scheduled], Campaign Id [${campaignId}], Group Id [${groupId}], Scheduled At [${scheduledAt}]` ); // let uplodeFilePath = `./users/upload/voice/${audioFileName}`; // let convertFilePath = `${process.env.AUDIO_COPY_PATH}/${campaignId}/${campaignId}.wav`; // let audioPath = `${config.FILEPATH_URL}/upload/quickVoice/${campaignId}/${campaignId}.wav`; // await campaign.updateCampaignAudioPath(audioPath, campaignId); // let newAudioFileName = `${campaignId}.wav`; let uplodeFilePath = `${process.env.UPLOAD_PATH}/voice/${audioFileName}`; let ProcessFilePath = `${process.env.UPLOAD_PATH}/voice/${campaignId}/${campaignId}.wav`; let sipServerFilePath = `${process.env.AUDIO_COPY_PATH}/${campaignId}/${campaignId}.wav`; let audioPath = `${config.FILEPATH_URL}/upload/voice/${campaignId}/${campaignId}.wav`; await campaign.updateCampaignAudioPath(audioPath, campaignId); let newAudioFileName = `${campaignId}.wav`; await awaitExec(`mkdir -p ${process.env.AUDIO_COPY_PATH}/${campaignId}`); await awaitExec(`mkdir -p ${process.env.UPLOAD_PATH}/voice/${campaignId}`); await awaitExec(`/usr/bin/php ${process.env.CONVERSION_SCRIPT} 'NORMAL' '${campaignId}' '${uplodeFilePath}' '${ProcessFilePath}'`); await awaitExec(`cp '${ProcessFilePath}' '${sipServerFilePath}'`); await awaitExec(`rm -rf ${uplodeFilePath}`); log.info( `[Campaign Voice], [runcampaign], Audio File Copied Successfully for scheduled` ); // if (!fs.existsSync(`${process.env.AUDIO_COPY_PATH}/${campaignId}`)) { // fs.mkdirSync(`${process.env.AUDIO_COPY_PATH}/${campaignId}`); // await fs.promises.copyFile( // `./users/upload/voice/${audioFileName}`, // `${process.env.AUDIO_COPY_PATH}/${campaignId}/${audioFileName}` // ); // log.info( // `[Campaign Voice], [runcampaign], Audio File Copied Successfully ` // ); // } await accountCreditsModel.deductBalance( userId, accountBal?.voice - balanceConsumed, "voice" ); log.info( `[Campaign Voice], [runcampaign], [Campaign Scheduled], Campaign Id [${campaignId}], Balance Consumed [${balanceConsumed}], Campaign Type [VOICE], Group Id [${groupId}]` ); const NewAccountBal = await accountCreditsModel.checkAccountBal(userId); return res.status(200).json({ msg: "Campaign has been scheduled", isScheduled, userCredit: NewAccountBal, }); } } else { log.info( `[Campaign Voice], [runcampaign], Group Id [${groupId}], No contacts found in the group` ); return res.status(200).json({ msg: "Please add contacts to the group." }); } } catch (e) { log.info(`[Campaign Voice], [runcampaign], Error [${e}]`); return res.status(500).json(e); } } async function processCampaignPush( groupId, callerId, campaignId, newname, type, message, audioFileName, userId ) { return new Promise(async (resolve, reject) => { let groupContacts = await contact.getCorrectContactList(groupId); // if (groupContacts) { // groupContacts = groupContacts?.filter( // (contact) => contact.mobile.length >= 9 // ); // } const callerIdDetails = await callerIdModel.getCallerIdById(callerId); log.info( `[Campaign Voice], [processCampaignPush], Campaign Id [${campaignId}], Campaign Name [${newname}], Campaign Started` ); const path = `./users/upload/campaign/${campaignId}`; let folderAction = await fs.promises.mkdir(path, { recursive: true }); const workbook = new excelJS.Workbook(); const worksheet = workbook.addWorksheet("Contacts"); worksheet.columns = [ { header: "First Name", key: "firstName", width: 20 }, { header: "Last Name", key: "lastName", width: 15 }, { header: "Email", key: "email", width: 20 }, { header: "Phone Number", key: "mobile", width: 20 }, { header: "Caller Id", key: "callerId", width: 20 }, { header: "Campaign Name", key: "campaignName", width: 40 }, { header: "Status", key: "status", width: 20 }, { header: "File Name", key: "filename", width: 160 }, ]; worksheet.getRow(1).eachCell((cell) => { cell.font = { bold: true }; }); var xlsxFileName = `${campaignId}.xlsx`; for (let index = 0; index < groupContacts?.length; index++) { const contact = groupContacts[index]; let mobile = contact["mobile"]; mobile = mobile.toString(); mobile = "260" + mobile.slice(mobile.length - 9, mobile.length); log.info( `[Campaign Voice], [processCampaignPush], Campaign Name [${newname}], Caller ID [${callerIdDetails["callerid"]}], Campaign Id [${campaignId}], Group Id [${groupId}], Mobile [${mobile}], First Name [${contact["first_name"]}], Last Name [${contact["last_name"]}], Type [VOICE], FilePath [${message}], FileName [${audioFileName}]` ); var rowdata = { firstName: contact["first_name"], lastName: contact["last_name"], email: contact["email"], mobile: mobile, callerId: callerIdDetails["callerid"], campaignName: newname, status: "Sent", filename: audioFileName, }; worksheet.addRow(rowdata); campignvoicereport.createCampaignVoiceReport( userId, contact["first_name"], contact["last_name"], contact["email"], campaignId, contact["name"], process.env.VOICE, callerIdDetails["callerid"], mobile, message, 1 ); //------ CREATING CALL -------- const uploadPath = "./users/upload/calls"; const command = `find ${process.env.ASTERIK_PATH}/ -maxdepth 1 -type f -name "*.call" | wc -l`; exec(command, async (err, stdout, stderr) => { try { if (err || stderr) throw err || stderr; let currentNumOfFiles = stdout || 0; currentNumOfFiles = currentNumOfFiles.trim(); log.info( `[Campaign Voice], [processCampaignPush], Current File Count [${currentNumOfFiles}], Max Limit [${process.env.CALL_LIMIT}]` ); while (Number(currentNumOfFiles) > Number(process.env.CALL_LIMIT)) { log.info( `--------------------THE LOOP IS PAUSED---------------------------` ); await pauser(process.env.CALL_PAUSE_TIME); exec(command, async (err, stdout, stderr) => { try { if (err || stderr) throw err || stderr; currentNumOfFiles = stdout || 0; currentNumOfFiles = currentNumOfFiles.trim(); log.info( `[Campaign Voice], [processCampaignPush], Rechecked Current File Count [${currentNumOfFiles}]` ); } catch (err) { log.info( `[Campaign Voice], [processCampaignPush], Call File, Error [${err}] ` ); } }); } let audioFileNameWithoutExt = audioFileName.split(".wav"); audioFileNameWithoutExt = audioFileNameWithoutExt[0]; const nameFrom = `+${callerIdDetails["callerid"]} +${mobile} C${campaignId} ${userId}`; const obdString = `Channel: sip/+${mobile}@${process.env.MSC_IP}:${process.env.MSC_PORT}\nCallerID: ${nameFrom}<+${callerIdDetails["callerid"]}>\nApplication: Playback\nMaxRetries: 0\nRetryTime: 0\nWaitTime: ${process.env.MSC_WAIT_TIME}\nData: ${process.env.ASTERIK_AUDIO_PATH}/${campaignId}/${audioFileNameWithoutExt}`; const path = `${uploadPath}/${mobile}.call`; await fs.promises.writeFile(path, obdString); log.info( `[Campaign Voice], [processCampaignPush], [Call File Created Successfully], File Name [${mobile}.call] ` ); const newPath = `${process.env.ASTERIK_PATH}/${mobile}.call`; const moveCommand = `mv ${path} ${newPath}`; exec(moveCommand, async (err, stdout, stderr) => { try { if (err || stderr) throw err || stderr; log.info( `[Campaign Voice], [processCampaignPush], [Call File Moved Successfully], File Name [${mobile}.call] ` ); } catch (err) { log.info( `[Campaign Voice], [processCampaignPush], [Failed to Move Call File], Error [${err}] ` ); } }); // await fs.promises.rename(path, newPath); } catch (err) { log.info( `[Campaign Voice], [processCampaignPush], Call File, Error [${err}] ` ); } }); //------ END OF CREATING CALL -------- // let msgId = messageIdGen.generateMessageId(userId, campaignId); // reports.addMessageReport( // userId, // campaignId, // callerIdDetails["callerid"], // message, // mobile, // msgId // ); } await campaign.setCampaignStatus(campaignId, 2); log.info( `[Campaign Voice], [processCampaignPush], Campaign Id [${campaignId}], Campaign Name [${newname}], Campaign Ended` ); await workbook.xlsx.writeFile(`${path}/` + xlsxFileName); var fileUrl = `${config.FILEPATH_URL}/upload/campaign/${campaignId}/${xlsxFileName}`; await campaign.updateCampaignReport(fileUrl, campaignId); return resolve(true); }); } const pauser = (time) => { return new Promise((resolve) => { setTimeout(() => { resolve(true); log.info( `--------------------THE LOOP STARTS AGAIN---------------------------` ); }, time); }); }; var self = (module.exports = { runcampaign, processCampaignPush, });