diff --git a/backend/package.json b/backend/package.json index 9041ddb..eef37b0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,7 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-async-errors": "^3.1.1", + "fs-extra": "^10.1.0", "http-graceful-shutdown": "^2.3.2", "jsonwebtoken": "^8.5.1", "multer": "^1.4.2", diff --git a/backend/src/controllers/WhatsAppSessionController.ts b/backend/src/controllers/WhatsAppSessionController.ts index 0bcbade..aa9d9b7 100644 --- a/backend/src/controllers/WhatsAppSessionController.ts +++ b/backend/src/controllers/WhatsAppSessionController.ts @@ -5,6 +5,8 @@ import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; +import { restartWhatsSession } from "../helpers/RestartWhatsSession"; + import path from 'path'; // import { WWebJsw } from "../../WWebJS/session-bd_40" @@ -31,6 +33,24 @@ const update = async (req: Request, res: Response): Promise => { return res.status(200).json({ message: "Starting session." }); }; + + + +const restart = async (req: Request, res: Response): Promise => { + + const { whatsappId } = req.params; + + console.log('FROM REQUEST WHATSAPP ID: ', whatsappId) + + const whatsapp = await ShowWhatsAppService(whatsappId); + + await restartWhatsSession(whatsapp) + + return res.status(200).json({ message: "Starting session." }); +}; + + + const remove = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; const whatsapp = await ShowWhatsAppService(whatsappId); @@ -44,9 +64,9 @@ const remove = async (req: Request, res: Response): Promise => { //removeDir(path.join(process.cwd(),'.wwebjs_auth', `session-bd_${whatsapp.id}`)) - wbot.logout(); + await wbot.logout(); return res.status(200).json({ message: "Session disconnected." }); }; -export default { store, remove, update }; +export default { store, remove, update, restart }; diff --git a/backend/src/helpers/CopyFolder.ts b/backend/src/helpers/CopyFolder.ts new file mode 100644 index 0000000..6813737 --- /dev/null +++ b/backend/src/helpers/CopyFolder.ts @@ -0,0 +1,15 @@ +const fsPromises = require("fs/promises"); +const fs = require('fs-extra') + +// Delete a directory and its children +export const copyFolder = (sourcePath: string, destPath: string) => { + + fs.copySync(sourcePath, destPath, { overwrite: true }, (err: any) => { + if (err) { + console.error(err); + } else { + console.log("Copy dir success!"); + } + }); + +} \ No newline at end of file diff --git a/backend/src/helpers/DeleteDirectory.ts b/backend/src/helpers/DeleteDirectory.ts index b577a16..be11efb 100644 --- a/backend/src/helpers/DeleteDirectory.ts +++ b/backend/src/helpers/DeleteDirectory.ts @@ -7,7 +7,7 @@ export const removeDir = async (dirPath:string) => { if (fs.existsSync(dirPath)){ try { - await fsPromises.rm(dirPath, { recursive: true }); + await fsPromises.rm(dirPath, { recursive: true, force: true}); console.log("Directory removed!"); } catch (err) { diff --git a/backend/src/helpers/GetDefaultWhatsApp.ts b/backend/src/helpers/GetDefaultWhatsApp.ts index 1724927..4470654 100644 --- a/backend/src/helpers/GetDefaultWhatsApp.ts +++ b/backend/src/helpers/GetDefaultWhatsApp.ts @@ -8,35 +8,47 @@ import { Op, where } from "sequelize"; import wbotByUserQueue from '../helpers/GetWbotByUserQueue' -const GetDefaultWhatsApp = async (userId?: string | number ): Promise => { - +// import WhatsQueueIndex from "./WhatsQueueIndex"; + +import { WhatsIndex } from "./LoadBalanceWhatsSameQueue"; + +const GetDefaultWhatsApp = async (userId?: string | number): Promise => { + // test del let defaultWhatsapp = await Whatsapp.findOne({ where: { isDefault: true } }); - if (!defaultWhatsapp) { + if (!defaultWhatsapp) { - if(userId){ - - let whatsapps = await wbotByUserQueue(userId) - if(whatsapps.length > 0){ - - defaultWhatsapp = whatsapps[0] + if (userId) { + + let whatsapps = await wbotByUserQueue(userId) + + if (whatsapps.length > 0) { + + if (whatsapps.length > 1) { + + defaultWhatsapp = whatsapps[+WhatsIndex(whatsapps)] + + } + else { + defaultWhatsapp = whatsapps[0] + } }// Quando o usuário não está em nenhuma fila - else{ - defaultWhatsapp = await Whatsapp.findOne({ where: { status: 'CONNECTED' } }); + else { + defaultWhatsapp = await Whatsapp.findOne({ where: { status: 'CONNECTED' } }); } - - - } - else{ - - defaultWhatsapp = await Whatsapp.findOne({ where: { status: 'CONNECTED' } }); - } + + } + else { + + defaultWhatsapp = await Whatsapp.findOne({ where: { status: 'CONNECTED' } }); + + } } @@ -49,7 +61,7 @@ const GetDefaultWhatsApp = async (userId?: string | number ): Promise - // const defaultWhatsapp = await Whatsapp.findOne({ + // const defaultWhatsapp = await Whatsapp.findOne({ // where: { isDefault: true } // }); diff --git a/backend/src/helpers/IsPositiveInteger.ts b/backend/src/helpers/IsPositiveInteger.ts new file mode 100644 index 0000000..6d255f9 --- /dev/null +++ b/backend/src/helpers/IsPositiveInteger.ts @@ -0,0 +1,16 @@ + +export const isPositiveInteger = (index: string ) => { + + if (typeof index !== 'string') { + return false; + } + + const num = Number(index); + + if (Number.isInteger(num) && num >= 0) { + return true; + } + + return false; + +} \ No newline at end of file diff --git a/backend/src/helpers/LoadBalanceWhatsSameQueue.ts b/backend/src/helpers/LoadBalanceWhatsSameQueue.ts new file mode 100644 index 0000000..c551c25 --- /dev/null +++ b/backend/src/helpers/LoadBalanceWhatsSameQueue.ts @@ -0,0 +1,25 @@ +const fsPromises = require("fs/promises"); +const fs = require('fs') + +import WhatsQueueIndex from "./WhatsQueueIndex"; + +// Delete a directory and its children +export const WhatsIndex = (whatsapps: Object[]) => { + + let index: number = 0 + + if (WhatsQueueIndex.getIndex() >= whatsapps.length) { + WhatsQueueIndex.setIndex(0) + } + + // console.log('WhatsQueueIndex.getIndex(): ', WhatsQueueIndex.getIndex()) + + index = +WhatsQueueIndex.getIndex() + + WhatsQueueIndex.setIndex(+WhatsQueueIndex.getIndex() + 1) + + console.log('WhatsQueue Index: ', index) + + return index + +} \ No newline at end of file diff --git a/backend/src/helpers/RestartWhatsSession.ts b/backend/src/helpers/RestartWhatsSession.ts new file mode 100644 index 0000000..bc6fb36 --- /dev/null +++ b/backend/src/helpers/RestartWhatsSession.ts @@ -0,0 +1,39 @@ +import path from "path"; +import { number } from "yup"; +import { removeWbot } from "../libs/wbot"; +import Whatsapp from "../models/Whatsapp"; +import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; +import { copyFolder } from "./CopyFolder"; +import { removeDir } from "./DeleteDirectory"; + +const fsPromises = require("fs/promises"); +const fs = require('fs') + +// Restart session +export const restartWhatsSession = async (whatsapp: Whatsapp) => { + + console.log('RESTARTING THE whatsapp.id: ', whatsapp.id) + + const sourcePath = path.join(__dirname, `../../.wwebjs_auth/sessions/`, `session-bd_${whatsapp.id}`) + const destPath = path.join(__dirname, `../../.wwebjs_auth/`, `session-bd_${whatsapp.id}`) + + console.log('================sourcePath: ', sourcePath) + console.log('================destPath: ', destPath) + + removeWbot(whatsapp.id) + + await removeDir(destPath) + + if (fs.existsSync(sourcePath)) { + // copy the good session for restars the new session + copyFolder(sourcePath, destPath) + } + else { + console.log('Directory not found to copy: ', sourcePath) + } + + console.log('RESTARTING SESSION...') + + await StartWhatsAppSession(whatsapp); + +} \ No newline at end of file diff --git a/backend/src/helpers/WhatsQueueIndex.ts b/backend/src/helpers/WhatsQueueIndex.ts new file mode 100644 index 0000000..0dd12d3 --- /dev/null +++ b/backend/src/helpers/WhatsQueueIndex.ts @@ -0,0 +1,16 @@ + + +class WhatsQueueIndex { + + static staticIndex:Number = 0; + + static setIndex(index:Number){ + this.staticIndex = index + } + + static getIndex(){ + return this.staticIndex + } + } + + export default WhatsQueueIndex; \ No newline at end of file diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts index f92dff4..ffe26fc 100644 --- a/backend/src/libs/wbot.ts +++ b/backend/src/libs/wbot.ts @@ -1,5 +1,5 @@ import qrCode from "qrcode-terminal"; -import { Client, LocalAuth } from "whatsapp-web.js"; +import { Client, LocalAuth } from "whatsapp-web.js"; import { getIO } from "./socket"; import Whatsapp from "../models/Whatsapp"; import AppError from "../errors/AppError"; @@ -7,12 +7,21 @@ import { logger } from "../utils/logger"; import { handleMessage } from "../services/WbotServices/wbotMessageListener"; const fs = require('fs') +import { copyFolder } from "../helpers/CopyFolder"; +import path from "path"; +import { number } from "yup"; + interface Session extends Client { id?: number; } const sessions: Session[] = []; +let backupSession: any[] = [] + + + + const syncUnreadMessages = async (wbot: Session) => { const chats = await wbot.getChats(); @@ -38,7 +47,7 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { return new Promise((resolve, reject) => { try { - + const io = getIO(); const sessionName = whatsapp.name; let sessionCfg; @@ -47,9 +56,10 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { sessionCfg = JSON.parse(whatsapp.session); } - + //NOVA OPÇÃO MD - const wbot: Session = new Client({session: sessionCfg, authStrategy: new LocalAuth({clientId: 'bd_'+whatsapp.id}), + const wbot: Session = new Client({ + session: sessionCfg, authStrategy: new LocalAuth({ clientId: 'bd_' + whatsapp.id }), puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || undefined }, }); @@ -75,7 +85,12 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { wbot.on("qr", async qr => { - console.log('************** whatsapp.id: ',whatsapp.id) + + if (!backupSession.includes(whatsapp.id)) { + backupSession.push(whatsapp.id) + } + + console.log('************** QRCODE whatsapp.id: ', whatsapp.id, ' | backupSession: ', backupSession) logger.info("Session:", sessionName); qrCode.generate(qr, { small: true }); @@ -95,9 +110,7 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { wbot.on("authenticated", async session => { logger.info(`Session: ${sessionName} AUTHENTICATED`); - await whatsapp.update({ - session: JSON.stringify(session) - }); + }); wbot.on("auth_failure", async msg => { @@ -141,6 +154,7 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); if (sessionIndex === -1) { + console.log('WBOT ADD ID: ', whatsapp.id) wbot.id = whatsapp.id; sessions.push(wbot); } @@ -149,6 +163,44 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { await syncUnreadMessages(wbot); resolve(wbot); + + + + + console.log(`>>>>>>>>>>>>>>>>>>>>>>>>>.. BACKUP SESSION whatsapp.id ${whatsapp.id} | backupSession: ${backupSession}`) + + const whatsIndex = backupSession.findIndex((id:number) => id === +whatsapp.id); + + console.log(' whatsIndex: ', whatsIndex) + + if (whatsIndex !== -1) { + + backupSession.splice(whatsIndex, 1); + + setTimeout(async () => { + + const sourcePath = path.join(__dirname, `../../.wwebjs_auth/`, `session-bd_${whatsapp.id}`) + const destPath = path.join(__dirname, `../../.wwebjs_auth/sessions`, `session-bd_${whatsapp.id}`) + + if (fs.existsSync(path.join(__dirname, `../../.wwebjs_auth/sessions`))) { + // copy the good session for backup dir + copyFolder(sourcePath, destPath) + } + else { + console.log('Directory not found to copy: ', destPath) + } + + console.log(` COPIOU backup whatsapp.id ---------------------------------->${whatsapp.id}`) + + + }, 30000); + + console.log(' PASSOU NO TIMEOUT!') + + + } + + }); } catch (err) { logger.error(`${err}`); @@ -169,6 +221,7 @@ export const removeWbot = (whatsappId: number): void => { try { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex !== -1) { + console.log('WBOT REMOVED ID: ', whatsappId) sessions[sessionIndex].destroy(); sessions.splice(sessionIndex, 1); } diff --git a/backend/src/routes/whatsappSessionRoutes.ts b/backend/src/routes/whatsappSessionRoutes.ts index ce449fc..8a325f2 100644 --- a/backend/src/routes/whatsappSessionRoutes.ts +++ b/backend/src/routes/whatsappSessionRoutes.ts @@ -25,4 +25,10 @@ whatsappSessionRoutes.delete( WhatsAppSessionController.remove ); +whatsappSessionRoutes.post( + "/restartwhatsappsession/:whatsappId", + isAuth, + WhatsAppSessionController.restart +); + export default whatsappSessionRoutes; diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index d4fc1a6..91bca39 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -70,15 +70,15 @@ const ListTicketsService = async ({ status }; - if (unlimited) { - whereCondition = { - ...whereCondition, - createdAt: { - [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', - [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' - } - } - } + // if (unlimited) { + // whereCondition = { + // ...whereCondition, + // createdAt: { + // [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', + // [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' + // } + // } + // } } diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts index c56696e..50394e1 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -8,8 +8,9 @@ import Ticket from "../../models/Ticket"; import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; import wbotByUserQueue from '../../helpers/GetWbotByUserQueue' - - + +import { WhatsIndex } from "../../helpers/LoadBalanceWhatsSameQueue"; + interface Request { body: string; @@ -28,25 +29,34 @@ const SendWhatsAppMessage = async ({ quotedMsgSerializedId = SerializeWbotMsgId(ticket, quotedMsg); } - + const whatsapp = await ShowWhatsAppService(ticket.whatsappId); - if(whatsapp.status!='CONNECTED'){ + if (whatsapp.status != 'CONNECTED') { - let whatsapps = await wbotByUserQueue(ticket.userId) + let whatsapps = await wbotByUserQueue(ticket.userId) - if(whatsapps.length > 0){ + if (whatsapps.length > 0) { - await ticket.update({ whatsappId: whatsapps[0].id }); + if (whatsapps.length > 1) { + + await ticket.update({ whatsappId: whatsapps[+WhatsIndex(whatsapps)].id }); + + } + else { + + await ticket.update({ whatsappId: whatsapps[0].id }); + + } } - } - + } + const wbot = await GetTicketWbot(ticket); - + try { const sentMessage = await wbot.sendMessage( diff --git a/backend/src/services/WbotServices/StartWhatsAppSession.ts b/backend/src/services/WbotServices/StartWhatsAppSession.ts index 3b7370b..af0c6ae 100644 --- a/backend/src/services/WbotServices/StartWhatsAppSession.ts +++ b/backend/src/services/WbotServices/StartWhatsAppSession.ts @@ -5,10 +5,8 @@ import { getIO } from "../../libs/socket"; import wbotMonitor from "./wbotMonitor"; import { logger } from "../../utils/logger"; -export const StartWhatsAppSession = async ( - whatsapp: Whatsapp -): Promise => { - await whatsapp.update({ status: "OPENING" }); +export const StartWhatsAppSession = async (whatsapp: Whatsapp): Promise => { + await whatsapp.update({ status: "OPENING" }); const io = getIO(); io.emit("whatsappSession", { @@ -20,9 +18,9 @@ export const StartWhatsAppSession = async ( const wbot = await initWbot(whatsapp); wbotMessageListener(wbot); wbotMonitor(wbot, whatsapp); - } catch (err) { - + } catch (err) { + logger.error(`${err}`); - + } }; diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 3daacfb..730af1e 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -3,6 +3,10 @@ import { promisify } from "util"; import { writeFile } from "fs"; import * as Sentry from "@sentry/node"; +import { copyFolder } from "../../helpers/CopyFolder"; +import { removeDir } from "../../helpers/DeleteDirectory"; +import path from 'path'; + import { Contact as WbotContact, Message as WbotMessage, @@ -29,7 +33,11 @@ import ShowTicketMessage from "../TicketServices/ShowTicketMessage" import BotIsOnQueue from "../../helpers/BotIsOnQueue" import Queue from "../../models/Queue"; -import fs from 'fs'; +import fs from 'fs'; + +import { StartWhatsAppSession } from "../../services/WbotServices/StartWhatsAppSession"; +import { removeWbot } from '../../libs/wbot' +import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; // test del import data_ura from './ura' @@ -732,10 +740,33 @@ const handleMessage = async ( } // + + + // test del + // console.log('WBOT.id: ',wbot.id) + + // const sourcePath = path.join(__dirname,`../../../.wwebjs_auth/sessions/`, `session-bd_${wbot.id}`) + // const destPath = path.join(__dirname,`../../../.wwebjs_auth/`, `session-bd_${wbot.id}`) + + // console.log('================sourcePath: ', sourcePath) + // console.log('================sourcePath: ', destPath) + + // removeWbot(33) + + // await removeDir(destPath) + + // copyFolder(sourcePath, destPath) + + // await StartWhatsAppSession(whatsapp); + + // console.log('RESTARTING SESSION...') + // + } catch (err) { Sentry.captureException(err); logger.error(`Error handling whatsapp message: Err: ${err}`); + } }; diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 98c47f4..3cfb6bc 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -112,6 +112,8 @@ const Connections = () => { const [confirmModalOpen, setConfirmModalOpen] = useState(false); + + const confirmationModalInitialState = { action: "", title: "", @@ -131,6 +133,16 @@ const Connections = () => { } }; + + const handleRestartWhatsAppSession = async whatsAppId => { + try { + await api.post(`/restartwhatsappsession/${whatsAppId}`); + } catch (err) { + toastError(err); + } + }; + + const handleRequestNewQrCode = async whatsAppId => { try { await api.put(`/whatsappsession/${whatsAppId}`); @@ -319,6 +331,7 @@ const Connections = () => { perform="connections-view:show" yes={() => ( + { > {confirmModalInfo.message} + + + + {i18n.t("connections.title")} - - { )} /> - - - + + + {i18n.t("connections.table.name")} + {i18n.t("connections.table.status")} - { + + ( + + Restart + + )} + /> + + + + {i18n.t("connections.table.lastUpdate")} @@ -403,6 +432,7 @@ const Connections = () => { whatsApps.map(whatsApp => ( {whatsApp.name} + {renderStatusToolTips(whatsApp)} @@ -419,9 +449,38 @@ const Connections = () => { + + + + ( + + + + + + )} + /> + + + + + {format(parseISO(whatsApp.updatedAt), "dd/MM/yy HH:mm")} + + {whatsApp.isDefault && (
@@ -429,6 +488,8 @@ const Connections = () => {
)}
+ + { )} - /> + />