diff --git a/.gitignore b/.gitignore index b2d5d94..e19e6cd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,16 @@ session-bd* */session-bd*/* **session-bd* +sessions* +/sessions* +*/sessions*/* +**sessions* + +log* +/log* +*/log*/* +**log* + WWebJS /WWebJS */WWebJS/* diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index 84c7cf3..f9641d6 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -70,8 +70,9 @@ export const store = async (req: Request, res: Response): Promise => { Parcial ticket info: ticket.id: ${ticket.id} ticket.status: ${ticket.status} - ticket.whatsapp.id: ${ticket.whatsappId} + ticket.whatsapp.id: ${ticket.whatsappId} ticket.contact.number: ${ticket.contact.number} + message: ${body} ticket.contact.name: ${ticket.contact.name} ticket.contact.profilePicUrl: ${ticket.contact.profilePicUrl} ticket.user.id: ${ticket.user.id} diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 253714d..504edfa 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -123,6 +123,9 @@ export const remove = async ( removeDir(path.join(process.cwd(), '.wwebjs_auth', `session-bd_${whatsappId}`)) + removeDir(path.join(process.cwd(), '.wwebjs_auth','sessions', `session-bd_${whatsappId}`)) + + removeWbot(+whatsappId); const io = getIO(); diff --git a/backend/src/controllers/WhatsAppSessionController.ts b/backend/src/controllers/WhatsAppSessionController.ts index 6cd2317..dcb698d 100644 --- a/backend/src/controllers/WhatsAppSessionController.ts +++ b/backend/src/controllers/WhatsAppSessionController.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { getWbot } from "../libs/wbot"; +import { getWbot, removeWbot } from "../libs/wbot"; import { removeDir } from "../helpers/DeleteDirectory"; import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; @@ -7,9 +7,16 @@ import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppSer import { restartWhatsSession } from "../helpers/RestartWhatsSession"; -import path from 'path'; +import path from 'path'; +import { getIO } from "../libs/socket"; +import { stat } from "fs"; -// import { WWebJsw } from "../../WWebJS/session-bd_40" +import { setRestoreControll, getRestoreControll, shifRestoreControll } from "../helpers/RestoreControll"; + +import autoRestore from "../helpers/AutoRestore"; + + +// let lstRestore: any = [] const store = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; @@ -36,16 +43,31 @@ const update = async (req: Request, res: Response): Promise => { + const restart = async (req: Request, res: Response): Promise => { - + const { whatsappId } = req.params; - console.log('FROM REQUEST WHATSAPP ID: ', whatsappId) + const io = getIO(); - const whatsapp = await ShowWhatsAppService(whatsappId); + if (Object.keys(req.body).length > 0) { - restartWhatsSession(whatsapp, true) - //restartWhatsSession(whatsapp) + let lstRestore: any = getRestoreControll() + + for (let i = 0; i < lstRestore.length; i++) { + + io.emit("whatsappSession", { + action: "update", + session: { 'id': +lstRestore[i].id, 'disabled': true } + }); + + } + + return res.status(200).json({}); + + } + + await autoRestore(whatsappId, 'human') return res.status(200).json({ message: "Starting session." }); }; @@ -56,10 +78,21 @@ const remove = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; const whatsapp = await ShowWhatsAppService(whatsappId); - const wbot = getWbot(whatsapp.id); + const wbot = getWbot(whatsapp.id); await wbot.logout(); + + // TEST DEL + // removeWbot(+whatsapp.id) + + // await removeDir(path.join(process.cwd(), '.wwebjs_auth', 'sessions', `session-bd_${whatsappId}`)) + + // console.log('REMOVEU!!!!!!!!!!!!!!!!!!!!!') + + // + + return res.status(200).json({ message: "Session disconnected." }); }; diff --git a/backend/src/database/migrations/20221112040251-add-number-column-to-whatsapp.ts b/backend/src/database/migrations/20221112040251-add-number-column-to-whatsapp.ts new file mode 100644 index 0000000..58cd82e --- /dev/null +++ b/backend/src/database/migrations/20221112040251-add-number-column-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "number", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "number"); + } +}; diff --git a/backend/src/helpers/AutoRestore.ts b/backend/src/helpers/AutoRestore.ts new file mode 100644 index 0000000..e56e3c2 --- /dev/null +++ b/backend/src/helpers/AutoRestore.ts @@ -0,0 +1,69 @@ +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; +import { restartWhatsSession } from "./RestartWhatsSession"; +import { getRestoreControll, setRestoreControll, shifRestoreControll } from "./RestoreControll"; + +const fsPromises = require("fs/promises"); +const fs = require('fs') + +import { getIO } from "../libs/socket"; +import path from "path"; +import { splitDateTime } from "./SplitDateTime"; +import { format } from "date-fns"; +import ptBR from 'date-fns/locale/pt-BR'; +import { number } from "yargs"; + +const autoRestore = async (whatsappId: string | number, started_action_by: string = '') => { + + const whatsapp = await ShowWhatsAppService(whatsappId); + + restartWhatsSession(whatsapp, true) + + + const sourcePath = path.join(__dirname, `../../.wwebjs_auth/sessions/log`) + + if (fs.existsSync(sourcePath)) { + + let log = new Date(new Date() + 'UTC'); + + const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + + let timestamp = Math.floor(Date.now() / 1000) + + let number: any = whatsapp.number + + if (!number) { + number = '' + } + + fs.writeFileSync(`${sourcePath}/${timestamp}_restore_${number}_triggered_by_${started_action_by}_id_${whatsappId}.txt`, `Whatsapp id: ${whatsapp.id} \nDate: ${dateToday.fullDate} ${dateToday.fullTime}`, (error: any) => { console.log(error) }); + + } + + + setRestoreControll({ 'id': +whatsappId, 'disabled': true }) + + let lstRestore: any = getRestoreControll() + + const io = getIO(); + + io.emit("whatsappSession", { + action: "update", + session: { 'id': +whatsappId, 'disabled': true } + }); + + setTimeout(() => { + + let whatsapp = shifRestoreControll(); + + whatsapp.disabled = false + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + + }, 25000); + +} + +export default autoRestore; \ No newline at end of file diff --git a/backend/src/helpers/ContactsCache.ts b/backend/src/helpers/ContactsCache.ts index 1cb7835..902a385 100644 --- a/backend/src/helpers/ContactsCache.ts +++ b/backend/src/helpers/ContactsCache.ts @@ -67,9 +67,7 @@ const updateContactCacheById = async (id: string | number, update_fields: object const contact_cache: any = await redis.hgetall(`contact:${id}`) try { - if (contact_cache && Object.keys(contact_cache).length > 0) { - - // await redis.del(`contact:${id}`) + if (contact_cache && Object.keys(contact_cache).length > 0) { update_fields.escaped_name = escapeCharCache(update_fields.name) diff --git a/backend/src/helpers/CreateSessionDir.ts b/backend/src/helpers/CreateSessionDir.ts new file mode 100644 index 0000000..6f20c80 --- /dev/null +++ b/backend/src/helpers/CreateSessionDir.ts @@ -0,0 +1,72 @@ +import os from 'os'; +import dir from 'path'; +import fs from 'fs'; +import path from 'path'; + +// Delete a directory and its children +export const createSessionDir = () => { + + console.log('process.cwd(): ', process.cwd()) + + const wwebjsAuthPath = dir.join(process.cwd(), '.wwebjs_auth'); + const sessionPath = dir.join(process.cwd(), '.wwebjs_auth', 'sessions'); + const sessionLogPath = dir.join(process.cwd(), '.wwebjs_auth', 'sessions','log'); + + fs.access(wwebjsAuthPath, (error) => { + // To check if the given directory + // already exists or not + if (error) { + // If current directory does not exist + // then create it + fs.mkdir(wwebjsAuthPath, (error) => { + if (error) { + console.log(error); + } else { + console.log(`wwebjs_auth Directory created successfully !!!`); + } + }); + } else { + console.log("wwebjs_auth Directory already exists !!"); + } + }); + + + fs.access(sessionPath, (error) => { + // To check if the given directory + // already exists or not + if (error) { + // If current directory does not exist + // then create it + fs.mkdir(sessionPath, (error) => { + if (error) { + console.log(error); + } else { + console.log(`sessions Directory created successfully !!!`); + } + }); + } else { + console.log("sessions Directory already exists !!"); + } + }); + + fs.access(sessionLogPath, (error) => { + + // To check if the given directory + // already exists or not + if (error) { + // If current directory does not exist + // then create it + fs.mkdir(sessionLogPath, (error) => { + if (error) { + console.log(error); + } else { + console.log(`log Directory created successfully !!!`); + } + }); + } else { + console.log("log Directory already exists !!"); + } + }); + + +} \ No newline at end of file diff --git a/backend/src/helpers/LastMessageIdByContactCache.ts b/backend/src/helpers/LastMessageIdByContactCache.ts new file mode 100644 index 0000000..15e26a4 --- /dev/null +++ b/backend/src/helpers/LastMessageIdByContactCache.ts @@ -0,0 +1,262 @@ + +import Redis from 'ioredis' +import { type } from 'os' +const unflatten = require('flat').unflatten +var flatten = require('flat') +import ListContactsServiceCache from "../services/ContactServices/ListContactsServiceCache" +import { redisConn } from './TicketCache' + + +const deleteContactsByIdCache = async (id: string | number) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + const contact_cache: any = await redis.hgetall(`contact:${id}`) + + try { + if (contact_cache && Object.keys(contact_cache).length > 0) { + + await redis.del(`contact:${id}`) + + console.log(`Contacts cache number ${contact_cache['number']} deleted!`) + } + else { + console.log('CONTACT CACHE NOT FOUND!') + } + } catch (error) { + console.log(`There was an error on deleteContactsByIdCache: ${error}`) + } + + redis.quit() +} + +const updateContactCache = async (hash: any, json_object: any) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + const pipeline = redis.pipeline() + + let entries = Object.entries(json_object) + + entries.forEach((e: any) => { + pipeline.hset(hash, e[0], e[1]) + }) + + await pipeline.exec(() => { console.log("Key/value inserted/updated") }); + + redis.quit() + +} + +const updateContactCacheById = async (id: string | number, update_fields: object | any) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + const contact_cache: any = await redis.hgetall(`contact:${id}`) + + try { + if (contact_cache && Object.keys(contact_cache).length > 0) { + + // await redis.del(`contact:${id}`) + + update_fields.escaped_name = escapeCharCache(update_fields.name) + + await updateContactCache(`contact:${id}`, update_fields) + + console.log(`CONTACT ${contact_cache['number']} CACHE WAS UPDATED!`) + } + else { + console.log('CONTACT CACHE NOT FOUND!') + } + } catch (error) { + console.log(`There was an error on updateContactCacheById: ${error}`) + } + + redis.quit() +} + +const createOrUpdateContactCache = async (hash: any, contact: any) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + if (contact.name) { + contact.escaped_name = escapeCharCache(contact.name) + } + + await redis.hmset(hash, contact); + + redis.quit() + +} + + +async function searchContactCache(search: string, offset: number, limit: number) { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return null + + search = escapeCharCache(search) + + const response: any = await redis.call('FT.SEARCH', 'idx_contact_message', `(@escaped_name:*${search}*)|(@number:*${search}*)`, 'LIMIT', offset, limit, 'SORTBY', 'escaped_name', 'ASC') + redis.quit() + + + if (response.length === 1) { + return [] + } + + const results: any = [] + + for (let n = 2; n < response.length; n += 2) { + const result: any = {} + const fieldNamesAndValues = response[n] + + for (let m = 0; m < fieldNamesAndValues.length; m += 2) { + const k = fieldNamesAndValues[m] + const v = fieldNamesAndValues[m + 1] + result[k] = v + } + + results.push(result) + } + + return results +} + + +const removeExtraSpace = (str: string) => { + + str = str.replace(/^\s+/g, '') + + return str.replace(/\s+/g, ' ') +} + +const escapeCharCache = (str: string) => { + + const pattern = /[\'|\"|\.|\,|\;|\<|\>|\{|\}|\[|\]|\"|\'|\=|\~|\*|\:|\#|\+|\^|\$|\@|\%|\!|\&|\)|\(|/|\-|\\)]/g; // no match, use replace function. + + let newStr = str.replace(pattern, (t1) => `\\${t1}`); + + newStr = removeExtraSpace(newStr) + + return newStr.trim() + +} + +const getLastId = async (hash: any) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + const contact_cache: any = await redis.hgetall(hash) + + return contact_cache + +} + +const insertMessageContactCache = async (hash: any, contact_message: any) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + await redis.hmset(hash, contact_message); + + console.log('CREATED/UPDATED CONTACT MESSAGE') + + redis.quit() + +} + +const loadContactsCache = async () => { + + await createContactMessageIndexCache('idx_contact_message') + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + let contacts = await ListContactsServiceCache() + + const pipeline = redis.pipeline() + + for (let i = 0; i < contacts.length; i++) { + + contacts[i].createdAt = new Date(contacts[i].createdAt).toISOString() + contacts[i].updatedAt = new Date(contacts[i].updatedAt).toISOString() + + contacts[i].escaped_name = escapeCharCache(contacts[i].name) + + pipeline.hmset(`contact:${contacts[i].id}`, contacts[i]); + } + + await pipeline.exec(() => { console.log(`${contacts.length} CONTACTS INSERTED IN CACHE!`) }); + + redis.quit() +} + +const createContactMessageIndexCache = async (hashIndex: string) => { + + const redis: any = await redisConn(); + + if (!redis) return + + if (redis.status !== 'connect') return + + try { + + const lst_index_redis: any = await redis.call('FT._LIST') + + if (lst_index_redis.includes(hashIndex)) { + console.log('entrou...') + await redis.call('FT.DROPINDEX', hashIndex) + } + + const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'contact_message:', 'SCHEMA', 'number', 'TEXT', 'SORTABLE') + + console.log('contact_message index created: ', response) + + } catch (error) { + console.log('There was an error on contact_message: ', error) + } + + redis.quit() +} + +export { + loadContactsCache, + searchContactCache, + deleteContactsByIdCache, + updateContactCacheById, + createOrUpdateContactCache, + escapeCharCache, + + insertMessageContactCache, + getLastId +} \ No newline at end of file diff --git a/backend/src/helpers/RestoreControll.ts b/backend/src/helpers/RestoreControll.ts new file mode 100644 index 0000000..22ae676 --- /dev/null +++ b/backend/src/helpers/RestoreControll.ts @@ -0,0 +1,135 @@ + +import os from 'os'; +import dir from 'path'; +import fs from 'fs'; + +export const setRestoreControll = (obj: object) => { + + const restoreInfoFile = dir.join(os.tmpdir(), `restoreInfo.json`); + + try { + + if (fs.existsSync(restoreInfoFile)) { + + if (Array.isArray(obj)) { + + fs.writeFileSync(restoreInfoFile, JSON.stringify(obj), "utf8"); + + } + else { + + const restoreInfo = fs.readFileSync(restoreInfoFile, { encoding: 'utf8', flag: 'r' }); + + let lstRestore: any = JSON.parse(restoreInfo) + + lstRestore.push(obj) + + fs.writeFileSync(restoreInfoFile, JSON.stringify(lstRestore), "utf8"); + } + + + } else { + + console.log('restoreInfo.json file not found! It will be created.'); + + if (Array.isArray(obj)) { + + fs.writeFileSync(restoreInfoFile, JSON.stringify(obj), "utf8"); + + } + else { + + fs.writeFileSync(restoreInfoFile, JSON.stringify([obj]), "utf8"); + + } + + + } + + } catch (error) { + console.log('There was an error on try to read the restoreInfo.json file: ', error) + } + +} + +export const shifRestoreControll = () => { + + const restoreInfoFile = dir.join(os.tmpdir(), `restoreInfo.json`); + + try { + + if (fs.existsSync(restoreInfoFile)) { + + const restoreInfo = fs.readFileSync(restoreInfoFile, { encoding: 'utf8', flag: 'r' }); + + let lstRestore: any = JSON.parse(restoreInfo) + + let whatsapp: any = lstRestore.shift() + + fs.writeFileSync(restoreInfoFile, JSON.stringify(lstRestore), "utf8"); + + return whatsapp + + } + + } catch (error) { + console.log('There was an error on try to read the restoreInfo.json file: ', error) + } + + return {} + +} + +export const delRestoreControllFile = () => { + + const restoreInfoFile = dir.join(os.tmpdir(), `restoreInfo.json`); + + try { + + if (fs.existsSync(restoreInfoFile)) { + + fs.unlinkSync(restoreInfoFile) + + } else { + + console.log('restoreInfo.json file not found!'); + + } + + } catch (error) { + console.log('There was an error on try delete the restoreInfo.json file: ', error) + } + +} + + + + +export const getRestoreControll = () => { + + const restoreInfoFile = dir.join(os.tmpdir(), `restoreInfo.json`); + + try { + + if (fs.existsSync(restoreInfoFile)) { + + const restoreInfo = fs.readFileSync(restoreInfoFile, { encoding: 'utf8', flag: 'r' }); + + let lstRestore: any = JSON.parse(restoreInfo) + + return lstRestore + + + } else { + + console.log('restoreInfo.json file not found!'); + + } + + } catch (error) { + console.log('There was an error on try to read the restoreInfo.json file: ', error) + } + + return [] + +} \ No newline at end of file diff --git a/backend/src/helpers/SetTicketMessagesAsRead.ts b/backend/src/helpers/SetTicketMessagesAsRead.ts index 326ca86..a14d97c 100644 --- a/backend/src/helpers/SetTicketMessagesAsRead.ts +++ b/backend/src/helpers/SetTicketMessagesAsRead.ts @@ -12,6 +12,7 @@ import { splitDateTime } from "./SplitDateTime"; import { format } from "date-fns"; import ptBR from 'date-fns/locale/pt-BR'; +import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { await Message.update( @@ -26,39 +27,37 @@ const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { await ticket.update({ unreadMessages: 0 }); - try { + try { - const wbot = await GetTicketWbot(ticket); + const wbot = await GetTicketWbot(ticket); // test del // throw new Error('Throw makes it go boom!') // - + await wbot.sendSeen(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`); } catch (err) { - logger.warn( - `Could not mark messages as read. Maybe whatsapp session disconnected? Err: ${err}` - ); - //Solução para contornar erro de sessão - if ((`${err}`).includes("Evaluation failed: r") && ticket.whatsappId) { + logger.warn(`Could not mark messages as read. Maybe whatsapp session disconnected? Err: ${err}`); + + //Solução para contornar erro de sessão + if ((`${err}`).includes("Evaluation failed: r") && ticket.whatsappId) { + + const sourcePath = path.join(__dirname, `../../.wwebjs_auth/sessions/log`) - const sourcePath = path.join(__dirname,`../../.wwebjs_auth/sessions`) - const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) - const whatsapp = await ShowWhatsAppService(ticket.whatsappId); + const whatsapp = await ShowWhatsAppService(ticket.whatsappId); - if (whatsapp && whatsapp.status == 'CONNECTED') { - + if (whatsapp && whatsapp.status == 'CONNECTED') { let timestamp = Math.floor(Date.now() / 1000) - - fs.writeFile(`${sourcePath}/${timestamp}_SetTicketMessagesAsRead.txt`, `Whatsapp id: ${whatsapp.id} \nDate: ${dateToday.fullDate} ${dateToday.fullTime} \nFile: SetTicketMessagesAsRead.ts \nError: ${err}`, (error)=>{}); - await restartWhatsSession(whatsapp) - + fs.writeFile(`${sourcePath}/${timestamp}_SetTicketMessagesAsRead.txt`, `Whatsapp id: ${whatsapp.id} \nDate: ${dateToday.fullDate} ${dateToday.fullTime} \nFile: SetTicketMessagesAsRead.ts \nError: ${err}`, (error) => { }); + + // await restartWhatsSession(whatsapp) + } } diff --git a/backend/src/helpers/TicketCache.ts b/backend/src/helpers/TicketCache.ts index dc4fe80..68e53b2 100644 --- a/backend/src/helpers/TicketCache.ts +++ b/backend/src/helpers/TicketCache.ts @@ -207,9 +207,7 @@ const createOrUpdateTicketCache = async (hash: any, ticket: any) => { if(!redis) return - if (redis.status !== 'connect') return - - if (redis.status !== 'connect') return + if (redis.status !== 'connect') return ticket.escaped_name = escapeCharCache(ticket['contact.name']) diff --git a/backend/src/helpers/TrySendMessageMultiSession.ts b/backend/src/helpers/TrySendMessageMultiSession.ts new file mode 100644 index 0000000..ecc0069 --- /dev/null +++ b/backend/src/helpers/TrySendMessageMultiSession.ts @@ -0,0 +1,49 @@ + +import Ticket from "../models/Ticket"; +import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; +import GetTicketWbot from "./GetTicketWbot"; + +const sendMessageMultiSession = async (ticket: Ticket, body?: any, quotedMsgSerializedId?: any, sendSeen?: boolean) => { + + let sentMessage: any = '' + + const listWhatsapp: any = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED') + + if (listWhatsapp.length > 0) { + + for (let w = 0; w < listWhatsapp.length; w++) { + + if(listWhatsapp[w].id == ticket.whatsappId) continue + + try { + + console.log('CHANGE THE WHATSAPP SESSION ID: ', listWhatsapp[w].id) + + await ticket.update({ whatsappId: listWhatsapp[w].id }) + + const wbot = await GetTicketWbot(ticket); + + if (sendSeen) { + + await wbot.sendSeen(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`); + + } + else if (body) { + + sentMessage = await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, body, { quotedMessageId: quotedMsgSerializedId, linkPreview: false }); + + } + + break + + } catch (error) { + console.log('Cannot send send the message using the whatsapp id: ', listWhatsapp[w].id, ' | error: ', error) + } + + } + } + + return sentMessage +}; + +export default sendMessageMultiSession; diff --git a/backend/src/helpers/WhatsCache.ts b/backend/src/helpers/WhatsCache.ts new file mode 100644 index 0000000..4a6559b --- /dev/null +++ b/backend/src/helpers/WhatsCache.ts @@ -0,0 +1,254 @@ + +import Redis from 'ioredis' +import { type } from 'os' +const unflatten = require('flat').unflatten +var flatten = require('flat') +import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber" +import { redisConn } from './TicketCache' +import Whatsapp from "../models/Whatsapp"; +import { response } from 'express' + +const deleteWhatsappCache = async (hash:any) => { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + const whatsapp_cache: any = await redis.hgetall(hash) + + try { + if (whatsapp_cache && Object.keys(whatsapp_cache).length > 0) { + + await redis.del(hash) + + console.log(`Whatsapp cache number ${whatsapp_cache['number']} deleted!`) + } + else { + console.log('WHATSAPP CACHE NOT FOUND!') + } + } catch (error) { + console.log(`There was an error on deleteWhatsappCache: ${error}`) + } + + redis.quit() +} + +const updateWhatsappCache = async (hash: any, json_object: any) => { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + const pipeline = redis.pipeline() + + let entries = Object.entries(json_object) + + entries.forEach((e: any) => { + pipeline.hset(hash, e[0], e[1]) + }) + + await pipeline.exec(() => { console.log("whatsapp Key/value inserted/updated") }); + + redis.quit() + +} + +const updateWhatsappCacheById = async (hash:any, update_fields: object | any) => { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + const whatsapp_cache: any = await redis.hgetall(hash) + + try { + if (whatsapp_cache && Object.keys(whatsapp_cache).length > 0) { + + // update_fields.escaped_name = escapeCharCache(update_fields.name) + + await updateWhatsappCache(hash, update_fields) + + console.log(`WHATSAPP ${whatsapp_cache['number']} CACHE WAS UPDATED!`) + } + else { + console.log('WHATSAPP CACHE NOT FOUND!') + } + } catch (error) { + console.log(`There was an error on updateWhatsappCacheById: ${error}`) + } + + redis.quit() +} + +// const createOrUpdateContactCache = async (hash: any, contact: any) => { + +// const redis: any = await redisConn(); + +// if(!redis) return + +// if (redis.status !== 'connect') return + +// if (contact.name) { +// contact.escaped_name = escapeCharCache(contact.name) +// } + +// await redis.hmset(hash, contact); + +// redis.quit() + +// } + + +async function searchWhatsappCache(id: string, status: string) { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return null + + + const number_cache: any = await redis.hgetall(`whatsapp:${id}`) + + if(Object.entries(number_cache).length == 0){ + return [] + } + + // console.log('NUMBER_CACHED: ', number_cache) + + // @x:foo @y:bar + + const response: any = await redis.call('FT.SEARCH', 'idx_whatsapp', `(@status:*${status}*) (@number:*${number_cache.number}*)`, 'SORTBY', 'status', 'ASC') + + + redis.quit() + + + if (response.length === 1) { + return [] + } + + const results: any = [] + + for (let n = 2; n < response.length; n += 2) { + const result: any = {} + const fieldNamesAndValues = response[n] + + for (let m = 0; m < fieldNamesAndValues.length; m += 2) { + const k = fieldNamesAndValues[m] + const v = fieldNamesAndValues[m + 1] + result[k] = v + } + + results.push(result) + } + + return results +} + + + +const insertOrUpeateWhatsCache = async (hash:any, whatsapp: any) => { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + if(Array.isArray(whatsapp)){ + + const pipeline = redis.pipeline() + + for (let i = 0; i < whatsapp.length; i++) { + + pipeline.hmset(hash, whatsapp[i]); + } + + await pipeline.exec(() => { console.log(`${whatsapp.length} WHATSAPP INSERTED IN CACHE!`) }); + } + else{ + + await redis.hmset(hash,JSON.parse(JSON.stringify(whatsapp))); + + console.log(`WHATSAPP ID ${whatsapp.id} INSERTED OR UPADTED IN CACHE!`) + + } + + + redis.quit() +} + + + + +const loadWhatsappCache = async () => { + + await createWhatsappIndexCache('idx_whatsapp') + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + let whatsapps:any = await Whatsapp.findAll({raw: true}) + + const pipeline = redis.pipeline() + + for (let i = 0; i < whatsapps.length; i++) { + + whatsapps[i].createdAt = new Date(whatsapps[i].createdAt).toISOString() + whatsapps[i].updatedAt = new Date(whatsapps[i].updatedAt).toISOString() + + // whatsapps[i].escaped_name = escapeCharCache(whatsapps[i].name) + + pipeline.hmset(`whatsapp:${whatsapps[i].id}`, whatsapps[i]); + } + + await pipeline.exec(() => { console.log(`${whatsapps.length} WHATSAPPS INSERTED IN CACHE!`) }); + + redis.quit() +} + +const createWhatsappIndexCache = async (hashIndex: string) => { + + const redis: any = await redisConn(); + + if(!redis) return + + if (redis.status !== 'connect') return + + try { + + const lst_index_redis: any = await redis.call('FT._LIST') + + if (lst_index_redis.includes(hashIndex)) { + console.log('entrou...') + await redis.call('FT.DROPINDEX', hashIndex) + } + + const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'whatsapp:', 'SCHEMA','id', 'NUMERIC', 'status', 'TEXT', 'SORTABLE', 'number', 'TEXT', 'SORTABLE') + + console.log('Whatsapp index created: ', response) + + } catch (error) { + console.log('There was an error on createWhatsappIndexCache: ', error) + } + + redis.quit() +} + +export { + loadWhatsappCache, + searchWhatsappCache, + updateWhatsappCacheById, + insertOrUpeateWhatsCache, + deleteWhatsappCache +} \ No newline at end of file diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts index 079022b..0401c0a 100644 --- a/backend/src/libs/wbot.ts +++ b/backend/src/libs/wbot.ts @@ -20,6 +20,9 @@ const sessions: Session[] = []; let backupSession: any[] = [] +import { insertOrUpeateWhatsCache } from "../helpers/WhatsCache"; +import { json } from "sequelize/types"; +import { restartWhatsSession } from "../helpers/RestartWhatsSession"; @@ -35,7 +38,9 @@ const syncUnreadMessages = async (wbot: Session) => { }); for (const msg of unreadMessages) { + // console.log('--BACKEND MSG: ', msg) + await handleMessage(msg, wbot); } @@ -85,6 +90,7 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean logger.info("Session:", sessionName); qrCode.generate(qr, { small: true }); await whatsapp.update({ qrcode: qr, status: "qrcode", retries: 0 }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { qrcode: qr, status: "qrcode", retries: 0 }) const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); if (sessionIndex === -1) { @@ -110,6 +116,7 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean if (whatsapp.retries > 1) { await whatsapp.update({ session: "", retries: 0 }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { session: "", retries: 0 }) } const retry = whatsapp.retries; @@ -117,6 +124,10 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean status: "DISCONNECTED", retries: retry + 1 }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { + status: "DISCONNECTED", + retries: retry + 1 + }) io.emit("whatsappSession", { action: "update", @@ -127,15 +138,35 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean }); wbot.on("ready", async () => { - logger.info(`Session: ${sessionName} READY`); + logger.info(`Session: ${sessionName} READY`); + + if(whatsapp.name.includes(wbot.info["wid"]["user"])){ + console.log('-----------------> THIS IS THE RIGHT NUMBER') + } + else{ + console.log('-----------------> THIS IS THE WRONG NUMBER') + let read_number = wbot.info["wid"]["user"] + + await wbot.logout() + + io.emit("whatsappSession", { + action: "error", + msg: `Numero lido: ${read_number} \nEssa sessão de whatsapp foi desconectada porque o numero que esta descrito no nome dessa sessão não corresponde ao numero lido!` + }); + + // restartWhatsSession(whatsapp) + + return + } - // console.log('>>>>>>>>>>>>>> ready wbot.ts MOBILE NUMBER: ', wbot.info["wid"]["user"]) await whatsapp.update({ status: "CONNECTED", qrcode: "", - retries: 0 - }); + retries: 0, + number: wbot.info["wid"]["user"] + }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`,whatsapp) io.emit("whatsappSession", { action: "update", @@ -157,7 +188,7 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean - console.log(`>>>>>>>>>>>>>>>>>>>>>>>>>.. BACKUP SESSION whatsapp.id ${whatsapp.id} | backupSession: ${backupSession}`) + console.log(`>>>>>>>>>>>>>>>>>>>>>>>>> BACKUP SESSION whatsapp.id ${whatsapp.id} | backupSession: ${backupSession}`) const whatsIndex = backupSession.findIndex((id: number) => id === +whatsapp.id); @@ -189,7 +220,7 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean console.log(` COPIOU backup whatsapp.id ---------------------------------->${whatsapp.id}`) - }, 55000); + }, 90000); console.log(' PASSOU NO TIMEOUT whatsapp.id: ',whatsapp.id) @@ -207,6 +238,9 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean export const getWbot = (whatsappId: number): Session => { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + // console.log('----------> sessionIndex: ', sessionIndex, ' | whatasappId: ', whatsappId) + // console.log('----------> sessions: ',sessions.map(s => s.id)) + if (sessionIndex === -1) { throw new AppError("ERR_WAPP_NOT_INITIALIZED"); } diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts index 8442faa..1a3a6fc 100644 --- a/backend/src/models/Whatsapp.ts +++ b/backend/src/models/Whatsapp.ts @@ -53,6 +53,9 @@ class Whatsapp extends Model { @Column(DataType.TEXT) farewellMessage: string; + @Column + number: string; + @Default(false) @AllowNull @Column diff --git a/backend/src/routes/whatsappSessionRoutes.ts b/backend/src/routes/whatsappSessionRoutes.ts index 8a325f2..90e551e 100644 --- a/backend/src/routes/whatsappSessionRoutes.ts +++ b/backend/src/routes/whatsappSessionRoutes.ts @@ -12,6 +12,7 @@ whatsappSessionRoutes.post( isAuth, WhatsAppSessionController.store ); + whatsappSessionRoutes.put( "/whatsappsession/:whatsappId", diff --git a/backend/src/server.ts b/backend/src/server.ts index 2f239f1..1988caf 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -11,6 +11,9 @@ import { loadTicketsCache, flushCache, cacheSize } from './helpers/TicketCache' import { loadContactsCache } from './helpers/ContactsCache' import "./helpers/CloseBotTickets"; +import { loadWhatsappCache } from './helpers/WhatsCache' +import { delRestoreControllFile } from "./helpers/RestoreControll"; +import { createSessionDir } from "./helpers/CreateSessionDir"; const server = app.listen(process.env.PORT, () => { logger.info(`Server started on port: ${process.env.PORT}`); @@ -33,10 +36,13 @@ gracefulShutdown(server); await flushCache() await loadContactsCache() await loadTicketsCache() + await loadWhatsappCache() } + })() - +createSessionDir() +delRestoreControllFile() startSchedulingMonitor(5000) startWhoIsOnlineMonitor(3000) diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts index 7ad1f50..636c0e5 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -5,13 +5,26 @@ import GetWbotMessage from "../../helpers/GetWbotMessage"; import SerializeWbotMsgId from "../../helpers/SerializeWbotMsgId"; import Message from "../../models/Message"; import Ticket from "../../models/Ticket"; +import Whatsapp from "../../models/Whatsapp"; import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; import wbotByUserQueue from '../../helpers/GetWbotByUserQueue' import { WhatsIndex } from "../../helpers/LoadBalanceWhatsSameQueue"; -import { updateTicketCacheByTicketId } from '../../helpers/TicketCache' +import { deleteTicketsByContactsCache, updateTicketCacheByTicketId } from '../../helpers/TicketCache' + +import ListWhatsAppsNumber from "../WhatsappService/ListWhatsAppsNumber"; +import { getWbot } from "../../libs/wbot"; +import { json } from "sequelize/types"; + +import sendMessageMultiSession from "../../helpers/TrySendMessageMultiSession"; +import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; +import { insertOrUpeateWhatsCache, searchWhatsappCache } from "../../helpers/WhatsCache"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import autoRestore from "../../helpers/AutoRestore"; + + interface Request { body: string; @@ -24,6 +37,13 @@ const SendWhatsAppMessage = async ({ ticket, quotedMsg }: Request): Promise => { + + var timetaken = "####### Time taken to send message"; + + + console.time(timetaken) + + let quotedMsgSerializedId: string | undefined; if (quotedMsg) { await GetWbotMessage(ticket, quotedMsg.id); @@ -31,11 +51,40 @@ const SendWhatsAppMessage = async ({ } - const whatsapp = await ShowWhatsAppService(ticket.whatsappId); + let whatsapps: any - if (whatsapp.status != 'CONNECTED') { + //TEST DEL - let whatsapps = await wbotByUserQueue(ticket.userId) + // const defaultWhatsapp = await GetDefaultWhatsApp(); + // console.log('DEFAULT WHATSAPP: ', JSON.parse(JSON.stringify(defaultWhatsapp))) + + let listWhatsapp = null + + listWhatsapp = await searchWhatsappCache(`${ticket.whatsappId}`, 'CONNECTED') + + if (!listWhatsapp) { + listWhatsapp = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED') + } + + // console.log('---') + // console.log('listWhatsapp search: ', listWhatsapp) + // console.log('---') + + if (listWhatsapp.length > 1) { + + console.log('entrou --------------------->') + + const _whatsapp = listWhatsapp[Math.floor(Math.random() * listWhatsapp.length)]; + + await ticket.update({ whatsappId: +_whatsapp.id }); + + } + + console.log('1 --------> ticket.whatsappId: ', ticket.whatsappId) + + if (listWhatsapp.length == 0) { + + whatsapps = await wbotByUserQueue(ticket.userId) if (whatsapps.length > 0) { @@ -55,21 +104,104 @@ const SendWhatsAppMessage = async ({ } + // + + + + + + // const listWhatsapp = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED') + + // if (listWhatsapp.length > 1) { + + // const _whatsapp = listWhatsapp[Math.floor(Math.random() * listWhatsapp.length)]; + + // await ticket.update({ whatsappId: _whatsapp.id }); + + // } + // else { + + // whatsapps = await Whatsapp.findOne({ + // where: { id: ticket.whatsappId }, + // attributes: ['status'] + // }) + + // } + + + // console.log('1 --------> ticket.whatsappId: ', ticket.whatsappId) + + + + // if (listWhatsapp.length == 0 || (whatsapps && whatsapps.status != 'CONNECTED')) { + + // whatsapps = await wbotByUserQueue(ticket.userId) + + // if (whatsapps.length > 0) { + + // 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); + console.log('2 --------> send from whatsapp ticket.whatsappId: ', ticket.whatsappId) try { + console.time + const sentMessage = await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, body, { quotedMessageId: quotedMsgSerializedId, linkPreview: false }); + await ticket.update({ lastMessage: body }); - - // TEST DEL await updateTicketCacheByTicketId(ticket.id, { lastMessage: body, updatedAt: new Date(ticket.updatedAt).toISOString() }) - // + + console.timeEnd(timetaken) return sentMessage; } catch (err) { + + const whatsapp = await ShowWhatsAppService(ticket.whatsappId); + + if (whatsapp.status != 'RESTORING') { + + console.log('THE WHATSAAP ID: ', whatsapp.id, ' WILL BE RESTORED SOON!') + + await whatsapp.update({ + status: "RESTORING", + }); + + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { + status: "RESTORING", + }) + + // setTimeout(() => restartWhatsSession(whatsapp, true), 90000); + setTimeout(async () => await autoRestore(whatsapp.id, 'auto_send_message'), 95000); + } + + const sentMessage = await sendMessageMultiSession(ticket, body, quotedMsgSerializedId) + + if (sentMessage.length > 0) { + + await ticket.update({ lastMessage: body }); + await updateTicketCacheByTicketId(ticket.id, { lastMessage: body, updatedAt: new Date(ticket.updatedAt).toISOString() }) + return sentMessage; + + } + + throw new AppError("ERR_SENDING_WAPP_MSG"); } }; diff --git a/backend/src/services/WbotServices/StartWhatsAppSession.ts b/backend/src/services/WbotServices/StartWhatsAppSession.ts index 3b02cd2..05cd425 100644 --- a/backend/src/services/WbotServices/StartWhatsAppSession.ts +++ b/backend/src/services/WbotServices/StartWhatsAppSession.ts @@ -4,9 +4,97 @@ import { wbotMessageListener } from "./wbotMessageListener"; import { getIO } from "../../libs/socket"; import wbotMonitor from "./wbotMonitor"; import { logger } from "../../utils/logger"; +import { insertOrUpeateWhatsCache } from "../../helpers/WhatsCache"; + +import { getRestoreControll, setRestoreControll, shifRestoreControll } from "../../helpers/RestoreControll"; +import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; +import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; + +import autoRestore from "../../helpers/AutoRestore"; + +let lstAutoRestore: any = [] export const StartWhatsAppSession = async (whatsapp: Whatsapp, backupSession: boolean = false): Promise => { - await whatsapp.update({ status: "OPENING" }); + await whatsapp.update({ status: "OPENING" }); + + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { + status: "OPENING", + }) + + + + try { + + let lstRestore: any = getRestoreControll() + + if (Object.keys(lstRestore.filter((e: any) => +e.id == +whatsapp.id)).length) { + + console.log('Restore executed by human whatasappId: ', whatsapp.id) + + } + else { + + lstAutoRestore.push({ 'whatsappId': +whatsapp.id }) + + setTimeout(async () => { + + lstRestore = getRestoreControll() + + let count = lstAutoRestore.length + + for (let i = 0; i < count; i++) { + + let autoR = lstAutoRestore.shift() + + console.log('----------------> autoR: ', autoR) + + if (autoR && autoR.whatsappId) { + + if (Object.keys(lstRestore.filter((e: any) => +e.id == +autoR.whatsappId)).length) { + + console.log(' ACONTECENDO RESTORING autoR: ', autoR) + + continue + } + + const _whatsapp = await Whatsapp.findOne({ where: { id: autoR.whatsappId } }); + + let whatsappStatus = ["CONFLICT", + "DEPRECATED_VERSION ", + "OPENING ", + "PROXYBLOCK ", + "SMB_TOS_BLOCK ", + "TIMEOUT ", + "TOS_BLOCK ", + "UNLAUNCHED ", + "UNPAIRED ", + "UNPAIRED_IDLE"] + + if (_whatsapp?.status) { + + if (whatsappStatus.includes(_whatsapp.status)) { + + await autoRestore(autoR.whatsappId, 'auto_monit') + + } + + } + + } + + } + + + }, 20000); + + } + + } catch (error) { + console.log('There was an error on try execute AUTO-RESTORE: ', error) + } + + + const io = getIO(); io.emit("whatsappSession", { diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 76f5464..db17d66 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -64,9 +64,14 @@ import { updateTicketCacheByTicketId } from '../../helpers/TicketCache' import endPointQuery from "../../helpers/EndpointQuery"; import { Console } from "console"; import ShowContactCustomFieldService from "../ContactServices/ShowContactCustomFieldsService"; +import { insertMessageContactCache, getLastId } from '../../helpers/LastMessageIdByContactCache' +let lst: any[] = [] +let clear_lst: any + + interface Session extends Client { id?: number; } @@ -814,16 +819,107 @@ const botSendMessage = (ticket: Ticket, contact: Contact, wbot: Session, msg: st } +const _clear_lst = () => { + + console.log('WHATSAPP MESSAGE ID MULTI SESSION: ', lst.length) + + if (lst.length <= 200 ) return + + console.log('BEFORE lst SLICE: ', lst) + + console.log('lst whatsapp message id sliced! | lst.length: ', lst.length) + + const chunk: any = Math.floor((lst.length / 2)) + + lst = lst.slice(chunk, chunk + lst.length); + + console.log('AFTER lst SLICE: ', lst) + +} + + +const clearMultiSessionWhatsappMessageId = () => { + + + try { + clearInterval(clear_lst); + + _clear_lst() + + } catch (error) { + console.log('error on clear lst whatsapp id message: ', error) + } + finally { + clear_lst = setInterval(_clear_lst, 10000); + } + +} + +clear_lst = setInterval(clearMultiSessionWhatsappMessageId, 10000); + + const handleMessage = async ( msg: WbotMessage, wbot: Session ): Promise => { + + // TEST DEL MULTI SESSION + + let index = lst.findIndex((x: any) => x.id == msg.id.id) + + console.log('INDEX: ', index) + + if (index == -1) { + + lst.push({ id: msg.id.id }) + + } + else { + console.log('IGNORED ID: ', msg.id.id) + + return + } + + // console.log('LIST OF ID MESSAGE lst: ', lst) + + console.log('PASSOU.................................FROM: ', msg.from.split("@")[0], ' | ID: ', msg.id.id) + + // const contact_message = await getLastId(`contact_message:5517988310949`) + + // if (contact_message && contact_message.id == msg.id.id) { + // console.log('IGNORED MESSAGE SAME ID FROM CLIENT: ', contact_message.id) + // return + // } + + // await insertMessageContactCache(`contact_message:5517988310949`, + // { + // from: msg.from.split("@")[0], + // to: msg.to.split("@")[0], + // id: msg.id.id + // }) + + // console.log('PASSOU............................................... contact_message.id: ',contact_message.id) + + + // if (testLastId == msg.id.id) { + // console.log('IGNORED MESSAGE SAME ID!') + // return + // } + // testLastId = msg.id.id + + // console.log('PASSOU............................................... msg.id.id: ',msg.id.id) + + + + + + + + if (!isValidMsg(msg)) { return; } - - try { let msgContact: WbotContact; let groupContact: Contact | undefined; @@ -842,7 +938,7 @@ const handleMessage = async ( msgContact = await msg.getContact(); - // + // console.log(`\n <<<<<<<<<< RECEIVING MESSAGE: Parcial msg and msgContact info: @@ -994,7 +1090,7 @@ const handleMessage = async ( //Solução para contornar erro de sessão if ((`${err}`).includes("Evaluation failed: r")) { - const sourcePath = path.join(__dirname, `../../../.wwebjs_auth/sessions`) + const sourcePath = path.join(__dirname, `../../../.wwebjs_auth/sessions/log`) let log = new Date(new Date() + 'UTC'); const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) @@ -1009,9 +1105,7 @@ const handleMessage = async ( fs.writeFile(`${sourcePath}/${timestamp}_wbotMessageListener.txt`, `Whatsapp id: ${whatsapp.id} \nDate: ${dateToday.fullDate} ${dateToday.fullTime} \nFile: wbotMessageListener.ts \nError: ${err}`, (error) => { }); - - await restartWhatsSession(whatsapp) - + // await restartWhatsSession(whatsapp) } diff --git a/backend/src/services/WbotServices/wbotMonitor.ts b/backend/src/services/WbotServices/wbotMonitor.ts index 54b4c2e..d2b1e46 100644 --- a/backend/src/services/WbotServices/wbotMonitor.ts +++ b/backend/src/services/WbotServices/wbotMonitor.ts @@ -1,5 +1,6 @@ import * as Sentry from "@sentry/node"; import { Client } from "whatsapp-web.js"; +import { insertOrUpeateWhatsCache } from "../../helpers/WhatsCache"; import { getIO } from "../../libs/socket"; import Whatsapp from "../../models/Whatsapp"; @@ -25,6 +26,7 @@ const wbotMonitor = async ( try { await whatsapp.update({ status: newState }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { status: newState }) } catch (err) { Sentry.captureException(err); logger.error(err); @@ -58,7 +60,9 @@ const wbotMonitor = async ( wbot.on("disconnected", async reason => { logger.info(`Disconnected session: ${sessionName}, reason: ${reason}`); try { + await whatsapp.update({ status: "OPENING", session: "" }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { status: "OPENING", session: "" }) } catch (err) { Sentry.captureException(err); logger.error(err); diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts index b9f8d37..e2d896f 100644 --- a/backend/src/services/WhatsappService/CreateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -62,6 +62,7 @@ const CreateWhatsAppService = async ({ }); if (oldDefaultWhatsapp) { await oldDefaultWhatsapp.update({ isDefault: false }); + } } diff --git a/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts b/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts new file mode 100644 index 0000000..b9c921f --- /dev/null +++ b/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts @@ -0,0 +1,28 @@ + +import Whatsapp from "../../models/Whatsapp"; + +const ListWhatsAppsNumber = async (whatsappId: string | number, status: string): Promise => { + + const whatsapp = await Whatsapp.findOne({ + raw: true, + where: { id: whatsappId } + }) + + if (whatsapp) { + + const whatsapps = await Whatsapp.findAll({ + raw: true, + where: { number: whatsapp.number, status: status }, + attributes: ['id', 'number', 'status', 'isDefault'] + }); + + return whatsapps; + + } + + return [] + + +}; + +export default ListWhatsAppsNumber; diff --git a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts index 5c6fde7..6b4d3c6 100644 --- a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts @@ -5,6 +5,11 @@ import AppError from "../../errors/AppError"; import Whatsapp from "../../models/Whatsapp"; import ShowWhatsAppService from "./ShowWhatsAppService"; import AssociateWhatsappQueue from "./AssociateWhatsappQueue"; +import { insertOrUpeateWhatsCache } from "../../helpers/WhatsCache"; +import { getWbot } from "../../libs/wbot"; +import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; + + interface WhatsappData { name?: string; @@ -46,6 +51,8 @@ const UpdateWhatsAppService = async ({ queueIds = [] } = whatsappData; + + try { await schema.validate({ name, status, isDefault }); } catch (err) { @@ -67,8 +74,16 @@ const UpdateWhatsAppService = async ({ } } - const whatsapp = await ShowWhatsAppService(whatsappId); + const whatsapp = await ShowWhatsAppService(whatsappId); + // console.log('############## whatsapp: ', JSON.parse(JSON.stringify(whatsapp))) + + if(name && !name.includes(whatsapp.number) && whatsapp.status === 'CONNECTED'){ + + throw new AppError("ERR_WAPP_WRONG_SESSION_NAME"); + + } + await whatsapp.update({ name, status, @@ -78,6 +93,15 @@ const UpdateWhatsAppService = async ({ isDefault }); + await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { + name, + status, + session, + greetingMessage, + farewellMessage, + isDefault + }) + await AssociateWhatsappQueue(whatsapp, queueIds); return { whatsapp, oldDefaultWhatsapp }; diff --git a/frontend/src/components/QrcodeModal/index.js b/frontend/src/components/QrcodeModal/index.js index 78b75b6..dcca527 100644 --- a/frontend/src/components/QrcodeModal/index.js +++ b/frontend/src/components/QrcodeModal/index.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useContext } from "react"; import QRCode from "qrcode.react"; import openSocket from "socket.io-client"; import toastError from "../../errors/toastError"; @@ -7,7 +7,12 @@ import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core"; import { i18n } from "../../translate/i18n"; import api from "../../services/api"; +import { AuthContext } from "../../context/Auth/AuthContext"; + const QrcodeModal = ({ open, onClose, whatsAppId }) => { + + const { user } = useContext(AuthContext); + const [qrCode, setQrCode] = useState(""); useEffect(() => { @@ -36,12 +41,24 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => { if (data.action === "update" && data.session.qrcode === "") { onClose(); } + + if (data.action === "error") { + + console.log('user.profile: ', user.profile) + + if(user.profile === 'master'){ + + alert(data.msg) + + } + + } }); return () => { socket.disconnect(); }; - }, [whatsAppId, onClose]); + }, [whatsAppId, onClose, user.profile]); return ( diff --git a/frontend/src/hooks/useWhatsApps/index.js b/frontend/src/hooks/useWhatsApps/index.js index a0b51fd..1360ac1 100644 --- a/frontend/src/hooks/useWhatsApps/index.js +++ b/frontend/src/hooks/useWhatsApps/index.js @@ -2,6 +2,7 @@ import { useState, useEffect, useReducer } from "react"; import openSocket from "socket.io-client"; import toastError from "../../errors/toastError"; + import api from "../../services/api"; const reducer = (state, action) => { @@ -28,16 +29,39 @@ const reducer = (state, action) => { const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); if (whatsAppIndex !== -1) { - state[whatsAppIndex].status = whatsApp.status; - state[whatsAppIndex].updatedAt = whatsApp.updatedAt; - state[whatsAppIndex].qrcode = whatsApp.qrcode; - state[whatsAppIndex].retries = whatsApp.retries; + + if ('disabled' in whatsApp) { + state[whatsAppIndex].disabled = whatsApp.disabled + } + else { + state[whatsAppIndex].status = whatsApp.status; + state[whatsAppIndex].updatedAt = whatsApp.updatedAt; + state[whatsAppIndex].qrcode = whatsApp.qrcode; + state[whatsAppIndex].retries = whatsApp.retries; + } + + return [...state]; } else { return [...state]; } } + // if(action.type === "UPDATE_SESSION_RESTORE"){ + + // const whatsApp = action.payload; + + // console.log('------> whatsApp: ', whatsApp) + + // const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); + + // if (whatsAppIndex !== -1) { + // console.log('kkkkkkkkkkkkkkkkkkkkkkkkkk: ',whatsAppIndex) + + // return [...state]; + // } + // } + // if (action.type === "UPDATE_DISK_SPACE_MONIT") { // const whatsApp = action.payload; @@ -56,7 +80,7 @@ const reducer = (state, action) => { const whatsApp = action.payload; const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); - if (whatsAppIndex !== -1) { + if (whatsAppIndex !== -1) { state[whatsAppIndex].sessionSize = whatsApp.sessionSize; return [...state]; } else { @@ -84,6 +108,8 @@ const useWhatsApps = () => { const [whatsApps, dispatch] = useReducer(reducer, []); const [loading, setLoading] = useState(true); + + useEffect(() => { setLoading(true); const fetchSession = async () => { @@ -115,22 +141,21 @@ const useWhatsApps = () => { }); socket.on("whatsappSession", data => { - if (data.action === "update") { - + if (data.action === "update") { dispatch({ type: "UPDATE_SESSION", payload: data.session }); } - }); + else if (data.action === "update_restore") { + dispatch({ type: "UPDATE_SESSION_RESTORE", payload: data.session }); + } + }); socket.on("whatsappSessionMonit", data => { if (data.action === "update") { - - - dispatch({ type: "UPDATE_WHATSAPPS_SESSION_MONIT", payload: data.whatsappSessionSize }); } - }); - - + }); + + return () => { socket.disconnect(); diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 2efc13d..9e968cb 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -114,8 +114,8 @@ const Connections = () => { const [confirmModalOpen, setConfirmModalOpen] = useState(false); const [diskSpaceInfo, setDiskSpaceInfo] = useState({}); - - + + const [disabled, setDisabled] = useState(true); const confirmationModalInitialState = { @@ -140,7 +140,9 @@ const Connections = () => { const handleRestartWhatsAppSession = async whatsAppId => { try { + await api.post(`/restartwhatsappsession/${whatsAppId}`); + } catch (err) { toastError(err); } @@ -329,6 +331,32 @@ const Connections = () => { }; + + + useEffect(() => { + + const delayDebounceFn = setTimeout(() => { + + const fetchQueries = async () => { + try { + + await api.post(`/restartwhatsappsession/0`, { params: { status: 'status' }, }); + + setDisabled(false) + + } catch (err) { + console.log(err); + } + }; + + fetchQueries(); + + }, 500); + return () => clearTimeout(delayDebounceFn); + + }, []); + + useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); @@ -407,37 +435,37 @@ const Connections = () => { perform="space-disk-info:show" yes={() => ( <> - - - - - Size - - - Used - - - Available - - - Use% - - - - - - - {diskSpaceInfo.size} - {diskSpaceInfo.used} - {diskSpaceInfo.available} - {diskSpaceInfo.use} - - -
-
- + + + + + Size + + + Used + + + Available + + + Use% + + + + + + + {diskSpaceInfo.size} + {diskSpaceInfo.used} + {diskSpaceInfo.available} + {diskSpaceInfo.use} + + +
+
+ )} - /> + /> @@ -533,9 +561,11 @@ const Connections = () => { role={user.profile} perform="connection-button:show" yes={() => ( + diff --git a/frontend/src/translate/languages/en.js b/frontend/src/translate/languages/en.js index a029743..9e8aac8 100644 --- a/frontend/src/translate/languages/en.js +++ b/frontend/src/translate/languages/en.js @@ -516,6 +516,8 @@ const messages = { ERR_NO_DIALOG_FOUND: "No Dialogflow found with this ID.", ERR_TEST_SESSION_DIALOG: "Error creating DialogFlow session", ERR_TEST_REPLY_DIALOG: "Error testing DialogFlow configuration", + ERR_WAPP_WRONG_SESSION_NAME: + "The number updated in the session name does not match the number read! To change the new number in the name disconnect the session!", }, }, }, diff --git a/frontend/src/translate/languages/es.js b/frontend/src/translate/languages/es.js index d5e4002..0a1484f 100644 --- a/frontend/src/translate/languages/es.js +++ b/frontend/src/translate/languages/es.js @@ -524,6 +524,9 @@ const messages = { ERR_NO_DIALOG_FOUND: "No se encontró Dialogflow con este ID.", ERR_TEST_SESSION_DIALOG: "Error al crear la sesión de dialogflow", ERR_TEST_REPLY_DIALOG: "Error al probar la configuración de DialogFlow", + ERR_WAPP_WRONG_SESSION_NAME: + "¡El número actualizado en el nombre de la sesión no coincide con el número leído! Para cambiar el nuevo número en el nombre desconecte la sesión!", + }, }, }, diff --git a/frontend/src/translate/languages/pt.js b/frontend/src/translate/languages/pt.js index cd6b102..6c579e8 100644 --- a/frontend/src/translate/languages/pt.js +++ b/frontend/src/translate/languages/pt.js @@ -527,6 +527,10 @@ const messages = { ERR_NO_DIALOG_FOUND: "Nenhuma Dialogflow encontrado com este ID", ERR_TEST_SESSION_DIALOG: "Erro ao criar sessão do DialogFlow", ERR_TEST_REPLY_DIALOG: "Erro ao testar configuração do DialogFlow", + ERR_WAPP_WRONG_SESSION_NAME: + "O numero atualizado no nome da sessão não corresponde ao numero lido! Para alterar o novo numero no nome desconecte a sessão!", + + }, }, },