merge changes, to merge the updated versions

Merge branch 'el_lojas_melhorias' of github.com:AdrianoRobson/projeto-hit into el_lojas_melhorias
feat-scaling-ticket-remote-creation
gustavo-gsp 2024-04-18 10:19:36 -03:00
commit cc12cafb99
156 changed files with 16245 additions and 2326 deletions

View File

@ -0,0 +1,8 @@
PORT=8019
PORT_START=8020
BASE_URL=http://localhost
PASS="strongpassword, strongpassword32"
DB_MONGO_URL=mongodb://localhost:27017
DB_MONGO_NAME=session_out_omnihit_db
DB_MONGO_NAME_CAMPAIGN=broker_omnihit

View File

@ -0,0 +1,520 @@
const express = require('express')
const bodyparser = require('body-parser')
const dotenv = require('dotenv')
dotenv.config({ path: '.env' })
const copyFolder = require('./helpers/copyFolder')
const createDir = require('./helpers/createDir')
const createFile = require('./helpers/createFile')
const path = require('path')
const db_info = require('./db_conn')
const fs = require('fs')
let mysql_conn = require('./helpers/mysql_conn.js')
const { exec, execSync, spawn } = require('child_process')
const startPm2Process = require('./helpers/startPm2Process')
const removeDir = require('./helpers/remove_dir')
const setSessionName = require('./helpers/setSessionNumber')
const getNumberFromName = require('./helpers/getNumberSequence')
const pm2 = require('pm2')
const bcrypt = require('bcrypt')
const OmnihitDBConn = require('./model/db_conn')
const app = express()
app.use(bodyparser.json())
app.get('/', function (req, res) {
return res.send('Express + TypeScript Server')
})
app.post('/api/session', async function (req, res) {
let { app_name, whatsappId, client_url, number } = req.body
let oldNumber = ''
if (app_name) {
app_name = app_name.trim()
}
console.log('__dirname: ', path.join(__dirname, '..', app_name))
console.log(
'app_name: ',
app_name,
' | whatsappId: ',
whatsappId,
' | client_url: ',
client_url
)
const sessionsPath = path.join(__dirname, '..', 'sessions')
const directoriesInDIrectory = fs
.readdirSync(sessionsPath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.map((item) => item.name)
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
let dirSessionsApp = path.join(sessionsPath, app_name)
if (dirExist.length == 0) {
let create = createDir(dirSessionsApp)
if (!create) {
res.status(500).json({ message: 'Cannot create the directory path!' })
return
}
}
let appPort = []
let existSubDir = false
for (let i = 0; i < directoriesInDIrectory.length; i++) {
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
const subDir = fs
.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), {
withFileTypes: true,
})
.filter((item) => item.isDirectory())
.map((item) => item.name)
for (let x = 0; x < subDir.length; x++) {
console.log('subdir: ', subDir[x])
let whatsId = subDir[x].split('_')[0]
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
let currPath = path.join(
sessionsPath,
directoriesInDIrectory[i],
subDir[x]
)
console.log(
'PATH: ',
path.join(sessionsPath, directoriesInDIrectory[i], subDir[x])
)
oldNumber = subDir[x].split('_')[1]
if (oldNumber != number) {
deletePm2Process(subDir[x], currPath)
removeDir(currPath)
} else {
res.send('ok')
return
}
}
let auxPort = subDir[x].split('_')[3]
console.log('---------> auxPort: ' + auxPort)
if (auxPort) {
auxPort = +auxPort.trim()
if (!isNaN(auxPort)) {
appPort.push(auxPort)
}
}
existSubDir = true
}
}
appPort = existSubDir ? Math.max(...appPort) + 1 : process.env.PORT_START
console.log('new port: ', appPort)
let dirSessionAppName
let numberSession = 1
let lstPass = process.env.PASS
if (!lstPass) {
console.log('PASS VARIABLE NOT FOUND INTO .ENV!')
return res.send('OK')
}
let db_credentials
try {
db_credentials = await OmnihitDBConn.findOne({ client_url })
if (!db_credentials) {
db_credentials = new OmnihitDBConn({
client_url: client_url,
db_conf: {
DB: '',
DB_HOST: '',
DB_USER: '',
DB_PASS: '',
DB_PORT: '',
},
})
await db_credentials.save()
return res.send('ok')
}
} catch (error) {
console.log(error)
}
if (db_credentials && db_credentials.db_conf.DB.trim().length > 0) {
lstPass = lstPass.split(',')
let password = null
// password = await lstPass.find(
// async (pass) =>
// await bcrypt.compare(pass.trim(), db_credentials.db_conf.DB_PASS)
// )
for (let i = 0; i < lstPass.length; i++) {
const hasPass = await bcrypt.compare(
lstPass[i].trim(),
db_credentials.db_conf.DB_PASS
)
if (hasPass) {
password = lstPass[i].trim()
break
}
}
if (password) {
db_credentials.db_conf.DB_PASS = password
} else {
return res.send('ok')
}
}
if (
db_credentials.db_conf &&
Object.keys(db_credentials.db_conf).length > 0
) {
const whatsapp_numbers = await new Promise((resolve, reject) => {
mysql_conn(db_credentials.db_conf).query(
'SELECT name, number FROM Whatsapps WHERE name LIKE ?',
[`%${number}%`],
(err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
}
}
)
})
let session_num = []
if (whatsapp_numbers && whatsapp_numbers.length > 0) {
console.log('whatsapp_numbers.length: ', whatsapp_numbers.length)
if (whatsapp_numbers.length == 5) {
res.status(400).json({
message: 'Cannot create more than 4 sessions from the same number',
})
return
}
let regex = /-> [a-zA-Z]\d$/
let numbered_sessions = whatsapp_numbers.filter((e) => regex.test(e.name))
console.log('numbered_sessions: ', numbered_sessions)
session_num = numbered_sessions.map((e) =>
parseInt(
e.name
.split('->')
[e.name.split('->').length - 1].trim()
.match(/\d+/)[0]
)
)
console.log('session_num', session_num)
}
let index = 1
while (index <= 4) {
if (!session_num.includes(index)) {
console.log(index)
numberSession = index
break
}
index++
}
}
// numberSession = Math.max(...session_number) + 1
console.log('Number session: ', numberSession)
// }
dirSessionAppName = `${whatsappId}_${number}_${numberSession}_${appPort}`
destDir = path.join(dirSessionsApp, dirSessionAppName)
originDir = path.join(__dirname, '..', 'whats')
copyFolder(originDir, destDir)
if (
db_credentials.db_conf &&
Object.keys(db_credentials.db_conf).length > 0
) {
console.log('***SUCCESS SEED DIRECTORY CREATED***')
let whatsName
const whatsapp = await new Promise((resolve, reject) => {
mysql_conn(db_credentials.db_conf).query(
'SELECT name from Whatsapps where id = ?',
[whatsappId],
(err, result) => {
if (err) {
reject(err)
} else {
resolve(result)
}
}
)
})
if (whatsapp[0]['name']?.split('->')?.length > 0) {
whatsName = `${whatsapp[0]['name'].split('->')[0]} -> S${numberSession}`
} else {
whatsName = `${whatsapp[0]['name']} -> S${numberSession}`
}
console.log('whatsName: ', whatsName)
console.log(
`url: ${process.env.BASE_URL}:${appPort}\n whatsname: ${whatsName}\n whatsappId: ${whatsappId}`
)
await new Promise((resolve, reject) => {
mysql_conn(db_credentials.db_conf).query(
'UPDATE Whatsapps SET url = ?, name = ? where id = ?',
[`${process.env.BASE_URL}:${appPort}`, `${whatsName}`, whatsappId],
function (err, result) {
if (err) {
reject(err)
console.log('===> ERROR: ' + err)
} else {
resolve(result)
// console.log('RESULT: ', result)
}
// else
// console.log('myslq result: ', result);
}
)
})
let whatsappName = `${number} - s${numberSession}`
console.log('-------------- numberSession', numberSession)
if (whatsapp.length > 0) {
if (whatsapp[0]['name'].split(' ').length > 0) {
whatsappName = `${whatsapp[0]['name'].split(' ')[0]
} - S${numberSession}`
}
}
console.log('whatsapp: ', whatsapp)
console.log("whatsapp[0]['name']: ", whatsapp[0]['name'])
const keys = Object.keys(db_credentials.db_conf)
var stream = fs.createWriteStream(path.join(destDir, '.env'))
stream.once('open', function (fd) {
stream.write('# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE\n')
stream.write(`MOBILEUID=${number}\n`)
stream.write(`MOBILENAME=${whatsappName}\n`)
stream.write(`OLD_MOBILEUID=${oldNumber}\n`)
stream.write('\n')
stream.write('# PORT NUMBER FOR THIS API\n')
stream.write(`PORT=${appPort}\n`)
stream.write('\n')
stream.write('# URL FROM THE OMNIHIT BACKEND API\n')
stream.write(`CLIENT_URL=${client_url}\n`)
stream.write('\n')
stream.write('# OMNIHIT DATABASE\n')
keys.forEach((key, index) => {
stream.write(`${key}=${db_credentials.db_conf[key]}\n`)
})
stream.write('\n')
stream.write(
`# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE\n`
)
stream.write(`WHATSAPP_ID=${whatsappId}\n`)
stream.write('\n')
stream.write('# MONGO CONNECTION\n')
stream.write(`DB_MONGO_URL=${process.env.DB_MONGO_URL}\n`)
stream.write('\n')
stream.write('# MONGO COLLECTION\n')
stream.write(`DB_MONGO_NAME=${process.env.DB_MONGO_NAME_CAMPAIGN}\n`)
stream.write('\n')
stream.end()
})
console.log('----------------destDir: ', destDir)
execSync(`npm install`, { cwd: destDir }, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`)
return
}
if (stderr) {
console.log(`stderr: ${stderr}`)
return
}
console.log(`stdout: ${stdout}`)
})
const env = {
PORT: appPort,
DB_MONGO_URL: process.env.DB_MONGO_URL,
DB_MONGO_NAME: process.env.DB_MONGO_NAME_CAMPAIGN
}
startPm2Process(dirSessionAppName, 'app.js', destDir, env)
}
res.send('OK')
})
app.post('/api/session/edit', async function (req, res) {
const { app_name, whatsappId, client_url, number } = req.body
})
app.post('/api/session/del', async function (req, res) {
let { whatsappId, app_name } = req.body
if (app_name) {
app_name = app_name.trim()
}
const sessionsPath = path.join(__dirname, '..', 'sessions')
const directoriesInDIrectory = fs
.readdirSync(sessionsPath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.map((item) => item.name)
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
console.log('dirExist: ', dirExist)
if (dirExist.length == 0) res.send('ok')
for (let i = 0; i < directoriesInDIrectory.length; i++) {
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
const subDir = fs
.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), {
withFileTypes: true,
})
.filter((item) => item.isDirectory())
.map((item) => item.name)
for (let x = 0; x < subDir.length; x++) {
console.log('subdir: ', subDir[x])
let whatsId = subDir[x].split('_')[0]
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
let currPath = path.join(
sessionsPath,
directoriesInDIrectory[i],
subDir[x]
)
deletePm2Process(subDir[x], currPath)
console.log('currPath: ', currPath)
removeDir(currPath)
return res.send('ok')
}
}
}
res.send('ok')
})
app.listen(process.env.PORT || 8003, function () {
console.log(
'\u26A1[server]: Server is running at Port ::: ' + process.env.PORT || 8003
)
})
process.on('uncaughtException', function (err) {
console.error(' ')
console.error(
'----- ' + new Date().toUTCString() + ' ----------------------------------'
)
console.error('Erro uncaughtException: ', err.message)
console.error(err.stack)
console.error(' ')
return
})
function deletePm2Process(process_name, currPath) {
pm2.connect(function (err) {
if (err) {
console.error(err)
}
pm2.list(function (err, processes) {
if (err) {
console.error(err)
}
processes.forEach(function (process) {
console.log('.........process.name: ', process.name)
if (process.name === process_name) {
execSync(
`pm2 delete ${process_name} && pm2 save --force`,
{ cwd: currPath },
(error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`)
return
}
if (stderr) {
console.log(`stderr: ${stderr}`)
return
}
console.log(`stdout: ${stdout}`)
}
)
}
})
pm2.disconnect()
})
})
}

View File

@ -0,0 +1,11 @@
const mongoose = require('mongoose')
require('dotenv').config({ path: `${process.cwd()}/.env` })
async function main(){
await mongoose.connect(process.env.DB_MONGO_URL, { dbName: process.env.DB_MONGO_NAME })
console.log('Conectou ao Mongoose!')
}
main().catch((err)=>console.log(err))
module.exports = mongoose

View File

@ -0,0 +1,27 @@
const db = [
{
client_url: "http://localhost:8080",
db_conf: {
DB: "whaticket",
DB_HOST: "localhost",
DB_USER: "whaticket",
DB_PASS: "strongpassword",
DB_PORT: "3306"
}
},
{
client_url: "http://localhost:8081",
db_conf: {
DB: "whaticket",
DB_HOST: "localhost",
DB_USER: "whaticket",
DB_PASS: "strongpassword",
DB_PORT: "3306"
}
}
]
module.exports = db;

View File

@ -0,0 +1,20 @@
const bcrypt = require('bcrypt')
// const pass = async () => {
// // create a password
// const salt = await bcrypt.genSalt(12)
// const passwordHash = await bcrypt.hash('7901228899', salt)
// console.log(passwordHash)
// }
// pass()
const passDec = async () => {
const _pass = await bcrypt.compare(
'strongpassword',
'$2b$12$PZ8N1jU77nnNUCCGyKTMNOi2QI7X/SgPsISVQfr.cQ/jgdx5Z7AqC'
)
console.log('_pass: ', _pass)
}
passDec()
console.log('process.cwd(): ', process.cwd())

View File

@ -0,0 +1,17 @@
const fsPromises = require("fs/promises");
const fs = require('fs-extra')
// Delete a directory and its children
function copyFolder(sourcePath, destPath) {
fs.copySync(sourcePath, destPath, { overwrite: true }, (err) => {
if (err) {
console.error(err);
} else {
console.log("Copy dir success!");
}
});
}
module.exports = copyFolder;

View File

@ -0,0 +1,22 @@
const fs = require('fs');
function createDir(dir) {
// create new directory
try {
// check if directory already exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
console.log("Directory is created.");
} else {
console.log("Directory already exists.");
}
} catch (err) {
console.log(err);
return false
}
return true
}
module.exports = createDir;

View File

@ -0,0 +1,17 @@
const fs = require('fs');
function createFile(saveDir, rows, fileName) {
var stream = fs.createWriteStream(path.join(saveDir, fileName));
stream.once('open', function (fd) {
rows.forEach(element => {
stream.write(element);
});
stream.end();
});
}
module.exports = createFile

View File

@ -0,0 +1,46 @@
const pm2 = require('pm2');
function findAndDeletePm2Process(pm2_process_name) {
pm2.connect(function (err) {
if (err) {
console.error(err);
// process.exit(2);
}
pm2.list(function (err, processes) {
if (err) {
console.error(err);
// process.exit(2);
}
const processToDelete = processes.find(process => process.name === pm2_process_name);
if (!processToDelete) {
console.error('Process not found');
// process.exit(2);
}
else {
pm2.delete(processToDelete.pm_id, function (err) {
if (err) {
console.error(err);
// process.exit(2);
}
else{
console.log(`Process deleted: ${pm2_process_name}`)
}
pm2.disconnect();
});
}
});
});
}
module.exports = findAndDeletePm2Process

View File

@ -0,0 +1,39 @@
function getNumberFromName(name) {
name = name + ' '
let number = name.split('')
let newNumber = ''
let list = []
for (let i = 0; i < number.length; i++) {
if (!isNaN(Number(number[i])) && number[i].trim().length > 0) {
newNumber += number[i]
}
else {
if (!isNaN(Number(newNumber)) && newNumber.trim().length > 0) {
list.push(newNumber)
}
newNumber = ''
}
}
// longestString = list.filter(str => str.length === Math.max(...list.map(s => s.length)))[0];
// console.log('list: ', list)
let longestString = ""; // variable to store the longest string
for (let i = 0; i < list.length; i++) {
if (list[i].length > longestString.length) {
longestString = list[i];
}
}
return longestString
}
module.exports = getNumberFromName;

View File

@ -0,0 +1,109 @@
const dotenv = require('dotenv');
dotenv.config({ path: `${process.cwd()}/.env` });
const path = require('path')
function mysql_conn(config) {
// Ubicua Plataform - MYSQL Module
try {
var mysql_npm = require('mysql');
} catch (err) {
console.log("Cannot find `mysql` module. Is it installed ? Try `npm install mysql` or `npm install`.");
}
var db_config = {
host: config.DB_HOST,
user: config.DB_USER,
password: config.DB_PASS,
database: config.DB,
charset: 'utf8mb4_general_ci',
port: config.DB_PORT
};
//-
//- Create the connection variable
//-
var connection = mysql_npm.createPool(db_config);
//-
//- Establish a new connection
//-
connection.getConnection(function (err) {
if (err) {
// mysqlErrorHandling(connection, err);
console.log("\n\t *** Cannot establish a connection with the database. ***");
connection = reconnect(connection);
} else {
console.log("\n\t *** New connection established with the database. ***")
}
});
//-
//- Reconnection function
//-
function reconnect(connection) {
console.log("\n New connection tentative...");
//- Create a new one
connection = mysql_npm.createPool(db_config);
//- Try to reconnect
connection.getConnection(function (err) {
if (err) {
//- Try to connect every 2 seconds.
setTimeout(reconnect(connection), 2000);
} else {
console.log("\n\t *** New connection established with the database. ***")
return connection;
}
});
}
//-
//- Error listener
//-
connection.on('error', function (err) {
//-
//- The server close the connection.
//-
if (err.code === "PROTOCOL_CONNECTION_LOST") {
console.log("/!\\ Cannot establish a connection with the database. /!\\ (" + err.code + ")");
return reconnect(connection);
}
else if (err.code === "PROTOCOL_ENQUEUE_AFTER_QUIT") {
console.log("/!\\ Cannot establish a connection with the database. /!\\ (" + err.code + ")");
return reconnect(connection);
}
else if (err.code === "PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR") {
console.log("/!\\ Cannot establish a connection with the database. /!\\ (" + err.code + ")");
return reconnect(connection);
}
else if (err.code === "PROTOCOL_ENQUEUE_HANDSHAKE_TWICE") {
console.log("/!\\ Cannot establish a connection with the database. /!\\ (" + err.code + ")");
}
else {
console.log("/!\\ Cannot establish a connection with the database. /!\\ (" + err.code + ")");
return reconnect(connection);
}
});
return connection
}
//-
//- Export
//-
module.exports = mysql_conn;

View File

@ -0,0 +1,17 @@
import axios from "axios";
async function postData(url, body = {}) {
let response;
try {
response = await axios.post(url, body);
console.log(response.data); // handle successful response
} catch (error) {
console.error(error); // handle error
}
return response
}
module.exports = postData

View File

@ -0,0 +1,28 @@
const fsPromises = require("fs/promises");
const fs = require('fs')
// Delete a directory and its children
const removeDir = async (dirPath) => {
if (fs.existsSync(dirPath)) {
try {
await fsPromises.rm(dirPath, { recursive: true, force: true });
console.log("Directory removed!");
return true
}
catch (err) {
console.log('An error occurred while removing the directory: ', err);
}
}
else {
console.log('Directory not found to remove: ', dirPath)
}
return false
}
module.exports = removeDir ;

View File

@ -0,0 +1,152 @@
import os from 'os';
import dir from 'path';
import fs from 'fs';
export const setJSON = (obj) => {
const sessionControlFile = dir.join(process.cwd(), `sessionsDir.json`);
try {
if (fs.existsSync(sessionControlFile)) {
const sessionsDir = fs.readFileSync(sessionControlFile, { encoding: 'utf8', flag: 'r' });
let lstRestore = JSON.parse(sessionsDir)
lstRestore.push(obj)
fs.writeFileSync(sessionControlFile, JSON.stringify(lstRestore), "utf8");
} else {
console.log('sessionsDir.json file not found! It will be created.');
if (Array.isArray(obj)) {
fs.writeFileSync(sessionControlFile, JSON.stringify(obj), "utf8");
}
else {
fs.writeFileSync(sessionControlFile, JSON.stringify([obj]), "utf8");
}
}
} catch (error) {
console.log('There was an error on try to read the sessionsDir.json file: ', error)
}
}
export const shifRestoreControll = () => {
const sessionControlFile = dir.join(os.tmpdir(), `sessionsDir.json`);
try {
if (fs.existsSync(sessionControlFile)) {
const sessionsDir = fs.readFileSync(sessionControlFile, { encoding: 'utf8', flag: 'r' });
let lstRestore: any = JSON.parse(sessionsDir)
let whatsapp: any = lstRestore.shift()
fs.writeFileSync(sessionControlFile, JSON.stringify(lstRestore), "utf8");
return whatsapp
}
} catch (error) {
console.log('There was an error on try to read the sessionsDir.json file: ', error)
}
return {}
}
export const delRestoreControllFile = () => {
const sessionControlFile = dir.join(os.tmpdir(), `sessionsDir.json`);
try {
if (fs.existsSync(sessionControlFile)) {
fs.unlinkSync(sessionControlFile)
} else {
console.log('sessionsDir.json file not found!');
}
} catch (error) {
console.log('There was an error on try delete the sessionsDir.json file: ', error)
}
}
export const getRestoreControll = () => {
const sessionControlFile = dir.join(os.tmpdir(), `sessionsDir.json`);
try {
if (fs.existsSync(sessionControlFile)) {
const sessionsDir = fs.readFileSync(sessionControlFile, { encoding: 'utf8', flag: 'r' });
let lstRestore: any = JSON.parse(sessionsDir)
return lstRestore
} else {
console.log('sessionsDir.json file not found!');
}
} catch (error) {
console.log('There was an error on try to read the sessionsDir.json file: ', error)
}
return []
}
export const _restore = async (whatsapp: Whatsapp, msg_file_title: string) => {
return
if (whatsapp.status != 'RESTORING' && whatsapp.status != 'qrcode') {
console.log('THE WHATSAAP ID: ', whatsapp.id, ' WILL BE RESTORED SOON!')
await whatsapp.update({ status: "RESTORING", });
const io = getIO();
io.emit("whatsappSession", {
action: "update",
session: whatsapp
});
// await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { status: "RESTORING", })
setTimeout(async () => await autoRestore(whatsapp.id, msg_file_title), 95000);
}
}

View File

@ -0,0 +1,105 @@
const fs = require('fs');
let mysql_conn = require('./mysql_conn.js');
async function sessionNumber(db_info, whatsappId, number, dirSessionsApp) {
let numberSession = 1
let whatsappName
const dirSessionsNumberAppDirectories = fs.readdirSync(dirSessionsApp, { withFileTypes: true })
.filter((item) => item.isDirectory() && item.name.includes(`${number}`))
.map((item) => item.name);
if (dirSessionsNumberAppDirectories.length > 0) {
let session_number = dirSessionsNumberAppDirectories.map((e) => +e.split('_')[2])
numberSession = Math.max(...session_number) + 1
console.log('Number session: ', numberSession)
if (numberSession > 4) {
res.status(400).json({ message: 'Cannot create more than 4 sessions from the same number' })
return
}
}
let db = db_info.filter((e) => e.client_url == client_url)
if (db && db.length > 0) {
db = db[0].db_conf
let whatsName
const whatsapp = await new Promise((resolve, reject) => {
mysql_conn(db).query("SELECT name from Whatsapps where id = ?", [whatsappId], (err, result) => {
if (err) {
reject(err)
}
else {
resolve(result)
}
});
})
if (whatsapp[0]["name"].split('->').length > 0) {
whatsName = `${whatsapp[0]["name"].split('->')[0]} -> S${numberSession}`
}
else {
whatsName = `${whatsapp[0]["name"]} -> S${numberSession}`
}
console.log('whatsName: ', whatsName)
console.log(`url: ${process.env.BASE_URL}:${appPort}\n whatsname: ${whatsName}\n whatsappId: ${whatsappId}`)
// await new Promise((resolve, reject) => {
// mysql_conn(db).query("UPDATE Whatsapps SET name = ? where id = ?", [ `${whatsName}`, whatsappId],
// function (err, result) {
// if (err) {
// reject(err)
// console.log("===> ERROR: " + err);
// }
// else {
// resolve(result)
// console.log('RESULT: ', result)
// }
// // else
// // console.log('myslq result: ', result);
// });
// })
whatsappName = `${number} - s${numberSession}`
console.log('-------------- numberSession', numberSession)
if (whatsapp.length > 0) {
if (whatsapp[0]['name'].split(' ').length > 0) {
whatsappName = `${whatsapp[0]['name'].split(' ')[0]} - S${numberSession}`
}
}
}
console.log('---------> whatsappName', whatsappName)
return whatsappName
}
module.exports = sessionNumber

View File

@ -0,0 +1,50 @@
const pm2 = require('pm2')
const { execSync } = require("child_process")
function startPm2Process(process_name, file, path, env) {
pm2.connect(function (err) {
if (err) {
console.error(err)
// process.exit(2);
}
console.log('ENV PM2: ', env)
pm2.start({
name: process_name,
script: file,
cwd: path,
env
// env: {
// NODE_ENV: 'production',
// PORT: port,
// }
// additional options here if needed
}, function (err, apps) {
if (err) {
console.error(err)
// process.exit(2);
}
else {
execSync(`pm2 save --force`, { cwd: path }, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`)
return
}
if (stderr) {
console.log(`stderr: ${stderr}`)
return
}
console.log(`stdout: ${stdout}`)
})
}
pm2.disconnect()
})
})
}
module.exports = startPm2Process

View File

@ -0,0 +1,33 @@
const mongoose = require('../db/connMongo')
const { Schema } = mongoose
const OmnihitDBConn = mongoose.model(
'Omnihit_db_conn',
new Schema(
{
client_url: {
type: String,
},
db_conf: {
DB: {
type: String,
},
DB_HOST: {
type: String,
},
DB_USER: {
type: String,
},
DB_PASS: {
type: String,
},
DB_PORT: {
type: String,
},
},
},
{ timestamps: true }
)
)
module.exports = OmnihitDBConn

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "api-sessions-controller",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Adriano <adriano08andrade@hotmail.com>",
"license": "MIT",
"dependencies": {
"bcrypt": "^5.1.0",
"body-parser": "^1.20.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"fs-extra": "^11.1.0",
"mongoose": "^7.4.0",
"mysql": "^2.18.1",
"nodemon": "^2.0.20",
"socket.io": "^4.5.4"
}
}

View File

@ -0,0 +1,25 @@
# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE
MOBILEUID=5517988310949
MOBILENAME=test - S1
# PORT NUMBER FOR THIS API
PORT=8029
# URL FROM THE OMNIHIT BACKEND API
CLIENT_URL=http://localhost:8080
# OMNIHIT DATABASE
DB=whaticket
DB_HOST=localhost
DB_USER=whaticket
DB_PASS=strongpassword
DB_PORT=3306
# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE
WHATSAPP_ID=223
# MONGO CONNECTION
DB_MONGO_URL=mongodb://localhost:27017
# MONGO COLLECTION
DB_MONGO_NAME=broker_omnihit

View File

@ -0,0 +1,31 @@
# dependencies
node_modules
/node_modules
/medias/*.*
/medias/in/*.*
/WWebJS/session-OmniHIT/Default/**
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
DevToolsActivePort*
npm-debug.log*
yarn-debug.log*
yarn-error.log*

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
const dotenv = require('dotenv');
dotenv.config({ path: `${process.cwd()}/.env` });
const path = require('path')
const MongoClient = require( 'mongodb' ).MongoClient;
const url = process.env.DB_URL;
var _db;
module.exports = {
connectToServer: function( callback ) {
MongoClient.connect( url, { useNewUrlParser: true }, function( err, client ) {
_db = client.db(process.env.DB_NAME);
return callback( err );
} );
},
getDb: function() {
return _db;
}
};
// // PRODUCTION CONNECTION
// const MongoClient = require( 'mongodb' ).MongoClient;
// const url = "mongodb://admin:d1nf54012022prod*@172.31.187.8:27017";
// var _db;
// module.exports = {
// connectToServer: function( callback ) {
// MongoClient.connect( url, { useNewUrlParser: true }, function( err, client ) {
// _db = client.db('db_omnihit');
// return callback( err );
// } );
// },
// getDb: function() {
// return _db;
// }
// };
// LOCA CONNECTION
// const MongoClient = require( 'mongodb' ).MongoClient;
// const url = 'mongodb://localhost:27017';
// var _db;
// module.exports = {
// connectToServer: function( callback ) {
// MongoClient.connect( url, { useNewUrlParser: true }, function( err, client ) {
// _db = client.db('db_omnihit');
// return callback( err );
// } );
// },
// getDb: function() {
// return _db;
// }
// }
/*
const MongoClient = require( 'mongodb' ).MongoClient;
const url = "mongodb://admin:d1nf54012022*@172.31.187.2:27017";
var _db;
module.exports = {
connectToServer: function( callback ) {
MongoClient.connect( url, { useNewUrlParser: true }, function( err, client ) {
_db = client.db('db_omnihit_todoo');
return callback( err );
} );
},
getDb: function() {
return _db;
}
};
*/
// const MongoClient = require( 'mongodb' ).MongoClient;
// const url = "mongodb://admin:d1nf5401@192.168.15.13/admin?retryWrites=true&w=majority";
// var _db;
// module.exports = {
// connectToServer: function( callback ) {
// MongoClient.connect( url, { useNewUrlParser: true }, function( err, client ) {
// _db = client.db('db_omnihit');
// return callback( err );
// } );
// },
// getDb: function() {
// return _db;
// }
// };

View File

@ -0,0 +1,18 @@
const { MongoClient } = require('mongodb')
const uri = process.env.DB_MONGO_URL
const mongo = new MongoClient(uri)
async function run() {
try {
await mongo.connect()
console.log('Connectado ao mongo db')
} catch (err) {
console.log(err)
}
}
run()
module.exports = mongo

View File

@ -0,0 +1,85 @@
const os = require('os');
const dbcloud = require('./dbcloud.js');
const dotenv = require('dotenv');
const axios = require('axios').default;
dotenv.config({path: '../.env'});
module.exports = {
// qrcode: async function(mobileuid, mobilename, qr ) {
// payload = {}
// payload['mobileuid'] = mobileuid;
// payload['mobilename'] = mobilename;
// payload['qrcode'] = qr;
// await axios.post(process.env.URL_QRCODE, payload, { timeout: 2000, headers: { 'content-type': 'application/json' } });
// console.log(new Date().toISOString() + " >>> SEND QR CODE ---------------------------------------");
// return 200;
// },
monitor: async function(mobileuid, mobilename, stat) {
let _totalmem = parseInt(os.totalmem());
let _freemem = parseInt(os.freemem());
let _memory = 100 - (_freemem / _totalmem * 100);
payload = {}
payload['mobileuid'] = mobileuid;
payload['mobilename'] = mobilename;
payload['memory'] = _memory;
let db = dbcloud.getDb();
let mco = await db.collection('tab_counts').find({ "_id": mobileuid }).limit(1).toArray();
if ( mco.length == 0 ) {
payload['_id'] = mobileuid;
payload['hour'] = 0;
payload['in'] = 0;
payload['out'] = 0;
payload['lastmessage'] = new Date(new Date() + 'UTC');
payload['in_today'] = 0;
payload['out_today'] = 0;
await db.collection('tab_counts').insertOne(payload);
}else{
payload['hour'] = mco[0]['hour'];
payload['in'] = mco[0]['in'];
payload['out'] = mco[0]['out'];
payload['lastmessage'] = mco[0]['lastmessage']
payload['in_today'] = mco[0]['in_today'];
payload['out_today'] = mco[0]['out_today'];
}
payload['dt'] = new Date(new Date() + 'UTC');
payload['status'] = stat;
console.log(new Date().toISOString() + " >>> SEND MONITOR ALARM ---------------------------------------");
console.log(new Date().toISOString() + " >>> payload: ",payload);
let monitor = await db.collection('tab_monitor').find({ "_id": mobileuid }).limit(1).toArray();
if ( monitor.length == 0 ) {
payload['_id'] = mobileuid
await db.collection('tab_monitor').insertOne(payload);
}
else{
await db.collection('tab_monitor').updateOne({ "_id": mobileuid }, { $set: payload }, true);
}
return 200;
}
}

View File

@ -0,0 +1,43 @@
module.exports = function() {
this.getTimestamp = function() {
var date = new Date();
var year = date.getFullYear();
var month = ("0"+(date.getMonth()+1)).substr(-2);
var day = ("0"+date.getDate()).substr(-2);
var hour = ("0"+date.getHours()).substr(-2);
var minutes = ("0"+date.getMinutes()).substr(-2);
var seconds = ("0"+date.getSeconds()).substr(-2);
return year+"-"+month+"-"+day+" "+hour+":"+minutes+":"+seconds;
};
this.log = function(desc, message) {
console.log(getTimestamp() + ' >> ' + desc + " :: ");
console.log(message);
};
this.getTime = function() {
var date = new Date();
var hour = ("0"+date.getHours()).substr(-2);
var minutes = ("0"+date.getMinutes()).substr(-2);
var seconds = ("0"+date.getSeconds()).substr(-2);
return hour+":"+minutes+":"+seconds;
};
this.forceGC = function() {
if (global.gc) {
console.log(getTimestamp() + " >> Starting Garbage Collector...");
global.gc();
} else {
console.warn("Garbage Collector não habilitado! Execute seu programa com node --expose-gc app.js.");
}
};
this.isJSON = function(str) {
try {
return (JSON.parse(str) && !!str);
} catch (e) {
return false;
}
}
}

View File

@ -0,0 +1,39 @@
const removeDir = require('./remove_dir');
const copyFolder = require('./copyFolder');
const path = require('path');
const fs = require('fs');
async function backup_session(destroy, save_session_after, save_first_read_only=false) {
console.log('process.cwd(): ', process.cwd())
const sessionBackupPath = path.join(process.cwd(), `session_backup`, `session-omnihit_sesssion`)
if (fs.existsSync(sessionBackupPath) && save_first_read_only) return
destroy = setTimeout(async () => {
const sessionPath = path.join(process.cwd(), '.wwebjs_auth', 'session-omnihit_sesssion')
if (fs.existsSync(path.join(process.cwd(), '.wwebjs_auth'))) {
await removeDir(sessionBackupPath)
// copy the good session for backup dir
copyFolder(sessionPath, sessionBackupPath)
}
else {
console.log('Directory not found to copy backup_session: ', sessionPath)
}
}, save_session_after);
return destroy
}
module.exports = backup_session;

View File

@ -0,0 +1,41 @@
const http = require('http')
const checkInternetConnection = async () => {
const options = {
hostname: 'www.google.com',
port: 80,
method: 'HEAD'
}
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
resolve(true)
} else {
resolve(false)
}
req.abort()
})
req.on('error', (err) => {
resolve(false)
})
req.end()
})
};
// (async () => {
// try {
// const isConnected = await checkInternetConnection()
// if (isConnected) {
// console.log('Internet connection is available.')
// } else {
// console.log('Internet connection is not available.')
// }
// } catch (error) {
// console.error('Error checking internet connection:', error)
// }
// })()
module.exports = checkInternetConnection

View File

@ -0,0 +1,17 @@
const fsPromises = require("fs/promises");
const fs = require('fs-extra')
// Delete a directory and its children
function copyFolder(sourcePath, destPath) {
fs.copySync(sourcePath, destPath, { overwrite: true }, (err) => {
if (err) {
console.error(err);
} else {
console.log("Copy dir success!");
}
});
}
module.exports = copyFolder;

View File

@ -0,0 +1,36 @@
const multer = require('multer')
const path = require('path')
//Destination to store the images
const imageStorage = multer.diskStorage({
destination: function(req, file, cb){
// let folder = ""
// if(req.baseUrl.includes("users")){
// folder = "users"
// }else if(req.baseUrl.includes("pets")){
// folder = "pets"
// }
cb(null, path.join(process.cwd(),'medias', 'out'))
},
filename: function(req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname))
}
})
const imageUpload = multer({
storage: imageStorage,
// fileFilter(req, file, cb){
// if (!file.originalname.match(/\.(jpg|jpeg|png)$/)){
// return cb(new Error('Por favor, envie apenas jpg ou png!'))
// }
// cb(undefined, true)
// }
})
module.exports = { imageUpload }

View File

@ -0,0 +1,129 @@
const dotenv = require('dotenv');
dotenv.config({ path: `${process.cwd()}/.env` });
const path = require('path')
// Ubicua Plataform - MYSQL Module
try{
var mysql_npm = require('mysql');
}catch(err){
console.log("Cannot find `mysql` module. Is it installed ? Try `npm install mysql` or `npm install`.");
}
var db_config = {
host : process.env.DB_HOST,
user : process.env.DB_USER,
password : process.env.DB_PASS,
database : process.env.DB,
charset : 'utf8mb4_general_ci',
port : process.env.DB_PORT
};
//-
//- Connection configuration
//-
// var db_config = {
// host : 'localhost',
// user : 'whaticket',
// password : '9147teste',
// database : 'db_cdnwork',
// charset : 'utf8mb4_general_ci',
// port : '6603'
// };
// var db_config = {
// host : '172.31.187.7',
// user : 'todoo',
// password : '7901228899',
// database : 'db_cdnwork',
// charset : 'utf8mb4_general_ci'
// };
//-
//- Create the connection variable
//-
var connection = mysql_npm.createPool(db_config);
//-
//- Establish a new connection
//-
connection.getConnection(function(err){
if(err) {
// mysqlErrorHandling(connection, err);
console.log("\n\t *** Cannot establish a connection with the database. ***");
connection = reconnect(connection);
}else {
console.log("\n\t *** New connection established with the database. ***")
}
});
//-
//- Reconnection function
//-
function reconnect(connection){
console.log("\n New connection tentative...");
//- Create a new one
connection = mysql_npm.createPool(db_config);
//- Try to reconnect
connection.getConnection(function(err){
if(err) {
//- Try to connect every 2 seconds.
setTimeout(reconnect(connection), 2000);
}else {
console.log("\n\t *** New connection established with the database. ***")
return connection;
}
});
}
//-
//- Error listener
//-
connection.on('error', function(err) {
//-
//- The server close the connection.
//-
if(err.code === "PROTOCOL_CONNECTION_LOST"){
console.log("/!\\ Cannot establish a connection with the database. /!\\ ("+err.code+")");
return reconnect(connection);
}
else if(err.code === "PROTOCOL_ENQUEUE_AFTER_QUIT"){
console.log("/!\\ Cannot establish a connection with the database. /!\\ ("+err.code+")");
return reconnect(connection);
}
else if(err.code === "PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR"){
console.log("/!\\ Cannot establish a connection with the database. /!\\ ("+err.code+")");
return reconnect(connection);
}
else if(err.code === "PROTOCOL_ENQUEUE_HANDSHAKE_TWICE"){
console.log("/!\\ Cannot establish a connection with the database. /!\\ ("+err.code+")");
}
else{
console.log("/!\\ Cannot establish a connection with the database. /!\\ ("+err.code+")");
return reconnect(connection);
}
});
//-
//- Export
//-
module.exports = connection;

View File

@ -0,0 +1,28 @@
const fsPromises = require("fs/promises");
const fs = require('fs')
// Delete a directory and its children
const removeDir = async (dirPath) => {
if (fs.existsSync(dirPath)) {
try {
await fsPromises.rm(dirPath, { recursive: true, force: true });
console.log("Directory removed!");
return true
}
catch (err) {
console.log('An error occurred while removing the directory: ', err);
}
}
else {
console.log('Directory not found to remove: ', dirPath)
}
return false
}
module.exports = removeDir ;

View File

@ -0,0 +1,39 @@
const removeDir = require('./remove_dir');
const copyFolder = require('./copyFolder');
const path = require('path');
const fs = require('fs');
async function restore(client) {
try {
await client.destroy()
} catch (error) {
console.error(`Error on try destroy client: ${error}`)
}
const sessionBackupPath = path.join(process.cwd(), `session_backup`, `session-omnihit_sesssion`)
const sessionPath = path.join(process.cwd(), '.wwebjs_auth', 'session-omnihit_sesssion')
if (fs.existsSync(path.join(process.cwd(), `session_backup`, `session-omnihit_sesssion`))) {
await removeDir(sessionPath)
// copy the good session for backup dir
copyFolder(sessionBackupPath, sessionPath)
}
else {
console.log('Directory not found to copy: ', sessionPath)
}
setTimeout(() => {
console.log('process.exit: kkkkkkkkkkkkkkkkkkkkk')
process.exit()
}, 5000)
}
module.exports = restore;

View File

@ -0,0 +1,67 @@
var io = require('socket.io-client');
var lst = []
const _clear_lst = () => {
if (lst.length <= 199) return
const chunk = Math.floor((lst.length / 2))
lst = lst.slice(chunk, chunk + lst.length);
}
const multisessionIdControll = (msgId) => {
_clear_lst()
let index = lst.findIndex((x) => x.id == msgId)
console.log('INDEX: ', index)
if (index == -1) {
lst.push({ id: msgId })
}
else {
console.log('IGNORED ID: ', msgId)
return
}
// console.log('LIST OF ID MESSAGE lst: ', lst)
console.log('PASSOU.................................ID: ', msgId)
}
const initIO = (url) => {
const socket = io(url, { reconnect: true });
socket.on('connect', async () => {
console.log('Made socket connection2');
});
socket.on('messageId', messageId => {
console.log('-------> messageId: ', messageId);
multisessionIdControll(messageId)
console.log('socket lst: ', lst)
});
return socket;
};
module.exports = { initIO, lst }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
{
"name": "omnihit",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon ./app.js"
},
"author": "Edson da Silva",
"license": "ISC",
"dependencies": {
"axios": "^0.21.4",
"body-parser": "^1.19.0",
"dotenv": "^16.0.0",
"express": "^4.17.1",
"form-data": "^4.0.0",
"fs-extra": "^11.1.0",
"logger": "^0.0.1",
"mime": "^2.4.5",
"mongodb": "^4.1.1",
"mongoose": "^7.4.3",
"multer": "^1.4.4",
"mysql": "^2.18.1",
"node-os-utils": "^1.3.5",
"qr-encode": "^0.3.0",
"qrcode-terminal": "^0.12.0",
"socket.io": "^4.5.4",
"socket.io-client": "^4.5.4"
},
"devDependencies": {
"nodemon": "^2.0.20"
}
}

View File

@ -183,11 +183,17 @@ socketIo.on('connect_error', async function (err) {
})
//
const wwebVersion = '2.2402.5';
//NOVA OPÇÃO MD
client = new Client({
authStrategy: new LocalAuth({ clientId: 'omnihit_sesssion' }),
puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome-stable' },
webVersionCache: {
type: 'remote',
remotePath: `https://raw.githubusercontent.com/wppconnect-team/wa-version/main/html/${wwebVersion}.html`,
},
})
client.initialize()

View File

@ -20,11 +20,13 @@ import {
} from "../helpers/ContactsCache";
import { off } from "process";
import GetContactService from "../services/ContactServices/GetContactService"
import GetContactService from "../services/ContactServices/GetContactService";
import ContactQueue from "../models/ContactQueues";
type IndexQuery = {
searchParam: string;
pageNumber: string;
userId?:string;
};
type IndexGetContactQuery = {
@ -41,10 +43,11 @@ interface ContactData {
number: string;
email?: string;
extraInfo?: ExtraInfo[];
queueIds?: number[];
}
export const index = async (req: Request, res: Response): Promise<Response> => {
let { searchParam, pageNumber } = req.query as IndexQuery;
let { searchParam, pageNumber, userId } = req.query as IndexQuery;
console.log("PAGE NUMBER CONTACT: ", pageNumber);
@ -80,11 +83,10 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
}
}
console.log("QUERY CONTACTS FROM DATABASE SEARCH PARAM: ", searchParam);
const { contacts, count, hasMore } = await ListContactsService({
searchParam,
pageNumber
pageNumber,
userId
});
return res.json({ contacts, count, hasMore });
@ -142,7 +144,8 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
number,
email,
profilePicUrl: profilePicUrl,
extraInfo
extraInfo,
queueIds: newContact?.queueIds
});
const io = getIO();
@ -208,6 +211,8 @@ export const remove = async (
await DeleteContactService(contactId);
await ContactQueue.destroy({ where: { contactId } });
const io = getIO();
io.emit("contact", {
action: "delete",

View File

@ -0,0 +1,113 @@
import * as Yup from "yup";
import { Request, Response } from "express";
import { getIO } from "../libs/socket";
import AppError from "../errors/AppError";
import ListPositionService from "../services/PositionService/ListPositionService";
import ShowPositionService from "../services/PositionService/ShowPositionService";
import CreatePositionService from "../services/PositionService/CreatePositionService";
import UpdatePositionService from "../services/PositionService/UpdatePositionService";
import DeletePositionService from "../services/PositionService/DeletePositionService";
type IndexQuery = {
searchParam: string;
pageNumber: string;
};
interface PositionData {
name: string;
}
export const index = async (req: Request, res: Response): Promise<Response> => {
const { searchParam, pageNumber } = req.query as IndexQuery;
const { positions, count, hasMore } = await ListPositionService({
searchParam,
pageNumber
});
return res.json({ positions, count, hasMore });
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const newPosition: PositionData = req.body;
const PositionSchema = Yup.object().shape({
name: Yup.string().required()
});
try {
await PositionSchema.validate(newPosition);
} catch (err: any) {
throw new AppError(err.message);
}
const position = await CreatePositionService({
...newPosition
});
const io = getIO();
io.emit("position", {
action: "create",
position
});
return res.status(200).json(position);
};
export const show = async (req: Request, res: Response): Promise<Response> => {
const { positionId } = req.params;
const position = await ShowPositionService(positionId);
return res.status(200).json(position);
};
export const update = async (
req: Request,
res: Response
): Promise<Response> => {
const positionData: PositionData = req.body;
const schema = Yup.object().shape({
name: Yup.string().required()
});
try {
await schema.validate(positionData);
} catch (err: any) {
throw new AppError(err.message);
}
const { positionId } = req.params;
const position = await UpdatePositionService({
positionData,
positionId
});
const io = getIO();
io.emit("position", {
action: "update",
position
});
return res.status(200).json(position);
};
export const remove = async (
req: Request,
res: Response
): Promise<Response> => {
const { positionId } = req.params;
await DeletePositionService(positionId);
const io = getIO();
io.emit("position", {
action: "delete",
positionId
});
return res.status(200).json({ message: "Position deleted" });
};

View File

@ -8,6 +8,11 @@ import UpdateQueueService from "../services/QueueService/UpdateQueueService";
import Queue from "../models/Queue";
import AppError from "../errors/AppError";
import { del, get, set } from "../helpers/RedisClient";
import { Op } from "sequelize";
import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService";
import Whatsapp from "../models/Whatsapp";
import QuickAnswerQueue from "../models/QuickAnswerQueue";
import ContactQueue from "../models/ContactQueues";
export const index = async (req: Request, res: Response): Promise<Response> => {
const queues = await ListQueuesService();
@ -15,10 +20,68 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
return res.status(200).json(queues);
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const { name, color, greetingMessage } = req.body;
export const listQueues = async (
req: Request,
res: Response
): Promise<Response> => {
const whatsapps = await Whatsapp.findAll({
where: {
name: { [Op.ne]: "botqueue" },
number: { [Op.ne]: "" },
phoneNumberId: false
},
attributes: ["number"],
include: [
{
model: Queue,
as: "queues",
attributes: ["id", "name"]
}
]
});
const queue = await CreateQueueService({ name, color, greetingMessage });
const whats = whatsapps
?.filter((w: any) => w?.queues?.length > 0)
?.map((w: any) => {
const { number, queues } = w;
return {
number,
queues: queues?.map((q: any) => {
const { id, name } = q;
return { id, name };
})
};
});
let _queues: any = [];
for (const w of whats) {
const { queues } = w;
for (const q of queues) {
const { id: queueId, name } = q;
const auxQueue = _queues.findIndex((q: any) => q.queueId == queueId);
if (auxQueue == -1) {
_queues.push({ queueId, name });
}
}
}
return res.status(200).json(_queues);
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const { name, color, greetingMessage, farewellMessage, cc } = req.body;
const queue = await CreateQueueService({
name,
color,
greetingMessage,
cc,
farewellMessage
});
const io = getIO();
io.emit("queue", {
@ -60,43 +123,43 @@ export const customization = async (
if (new_queues && new_queues.length > 0) {
const db_queues: any = await Queue.findAll();
for (const i in new_queues) {
let { queueName: name, color, greetingMessage } = new_queues[i];
// for (const i in new_queues) {
// let { queueName: name, color, greetingMessage } = new_queues[i];
name = name?.trim()?.replace(/\s+/g, " ");
// name = name?.trim()?.replace(/\s+/g, " ");
const update = db_queues.find(
(q: any) => q.name?.trim()?.replace(/\s+/g, " ") == name
);
// const update = db_queues.find(
// (q: any) => q.name?.trim()?.replace(/\s+/g, " ") == name
// );
if (update) {
const { id } = update;
// UPDATE
// const queue = await UpdateQueueService(id, {
// name,
// color,
// greetingMessage
// });
// if (update) {
// const { id } = update;
// // UPDATE
// // const queue = await UpdateQueueService(id, {
// // name,
// // color,
// // greetingMessage
// // });
// const io = getIO();
// io.emit("queue", {
// action: "update",
// queue
// });
} else {
// CREATE
// const queue = await CreateQueueService({
// name,
// color,
// greetingMessage
// });
// const io = getIO();
// io.emit("queue", {
// action: "update",
// queue
// });
}
}
// // const io = getIO();
// // io.emit("queue", {
// // action: "update",
// // queue
// // });
// } else {
// // CREATE
// // const queue = await CreateQueueService({
// // name,
// // color,
// // greetingMessage
// // });
// // const io = getIO();
// // io.emit("queue", {
// // action: "update",
// // queue
// // });
// }
// }
let remove_queues = db_queues.filter(
(q: any) =>
@ -162,6 +225,9 @@ export const remove = async (
await DeleteQueueService(queueId);
await QuickAnswerQueue.destroy({ where: { queueId } });
await ContactQueue.destroy({ where: { queueId } });
await del(`queue:${queueId}`);
const io = getIO();

View File

@ -9,23 +9,27 @@ import UpdateQuickAnswerService from "../services/QuickAnswerService/UpdateQuick
import DeleteQuickAnswerService from "../services/QuickAnswerService/DeleteQuickAnswerService";
import AppError from "../errors/AppError";
import QuickAnswerQueue from "../models/QuickAnswerQueue";
type IndexQuery = {
searchParam: string;
pageNumber: string;
userId?: string;
};
interface QuickAnswerData {
shortcut: string;
message: string;
queueIds?: number[];
}
export const index = async (req: Request, res: Response): Promise<Response> => {
const { searchParam, pageNumber } = req.query as IndexQuery;
const { searchParam, pageNumber, userId } = req.query as IndexQuery;
const { quickAnswers, count, hasMore } = await ListQuickAnswerService({
searchParam,
pageNumber
pageNumber,
userId
});
return res.json({ quickAnswers, count, hasMore });
@ -41,7 +45,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
try {
await QuickAnswerSchema.validate(newQuickAnswer);
} catch (err:any) {
} catch (err: any) {
throw new AppError(err.message);
}
@ -59,9 +63,9 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
};
export const show = async (req: Request, res: Response): Promise<Response> => {
const { quickAnswerId } = req.params;
const { quickAnswerId, userId } = req.params;
const quickAnswer = await ShowQuickAnswerService(quickAnswerId);
const quickAnswer = await ShowQuickAnswerService(quickAnswerId, userId);
return res.status(200).json(quickAnswer);
};
@ -103,10 +107,12 @@ export const remove = async (
req: Request,
res: Response
): Promise<Response> => {
const { quickAnswerId } = req.params;
const { quickAnswerId, queueId } = req.params;
await DeleteQuickAnswerService(quickAnswerId);
await QuickAnswerQueue.destroy({ where: { quickAnswerId } });
const io = getIO();
io.emit("quickAnswer", {
action: "delete",

View File

@ -20,6 +20,7 @@ import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
import { getIO } from "../libs/socket";
import { Json } from "sequelize/types/lib/utils";
import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService";
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService";
type IndexQuery = {
userId: string;
@ -262,8 +263,6 @@ export const reportMessagesUserByDateStartDateEnd = async (
}
data_query_messages[i].id = i + 1;
console.log("data_query_messages: ", data_query_messages[i]);
}
return res.status(200).json(data_query_messages);
@ -331,10 +330,6 @@ export const reportServiceByQueue = async (
const { startDate, endDate, queueId } = req.query as IndexQuery;
console.log(
`startDate: ${startDate} | endDate: ${endDate} | queueId: ${queueId}`
);
const reportService = await ReportByNumberQueueService({
startDate,
endDate,
@ -343,3 +338,29 @@ export const reportServiceByQueue = async (
return res.status(200).json({ reportService });
};
export const reportTicksCountByStatusChatEnds = async (
req: Request,
res: Response
): Promise<Response> => {
if (
req.user.profile !== "master" &&
req.user.profile !== "admin" &&
req.user.profile !== "supervisor"
) {
throw new AppError("ERR_NO_PERMISSION", 403);
}
const { startDate, endDate } = req.query as IndexQuery;
const dateToday = splitDateTime(
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
);
const reportStatusChatEnd = await CountStatusChatEndService(
startDate || dateToday.fullDate,
endDate || dateToday.fullDate
);
return res.status(200).json({ reportStatusChatEnd });
};

View File

@ -6,7 +6,7 @@ import ListSchedulingNotifyContactService from "../services/SchedulingNotifyServ
import CreateSchedulingNotifyService from "../services/SchedulingNotifyServices/CreateSchedulingNotifyService";
import ShowSchedulingNotifyService from "../services/SchedulingNotifyServices/ShowSchedulingNotifyService";
import { deleteScheduleByTicketIdCache } from "../helpers/SchedulingNotifyCache";
import ShowStatusChatEndService from "../services/StatusChatEndService/ShowStatusChatEndService";
type IndexQuery = {
contactNumber: string;
@ -14,58 +14,58 @@ type IndexQuery = {
endDate: string;
};
export const reportScheduleNotifyByDateStartDateEnd = async (
req: Request,
res: Response
): Promise<Response> => {
const { contactNumber, startDate, endDate } = req.query as IndexQuery;
export const reportScheduleNotifyByDateStartDateEnd = async (req: Request, res: Response): Promise<Response> => {
const { contactNumber, startDate, endDate } = req.query as IndexQuery
const data_query = await ListSchedulingNotifyContactService(contactNumber, startDate, endDate);
// console.group('DATA QUERY SCHEDULE:\n',data_query)
const data_query = await ListSchedulingNotifyContactService(
contactNumber,
startDate,
endDate
);
return res.status(200).json(data_query);
};
export const createOrUpdateScheduleNotify = async (req: Request, res: Response): Promise<Response> => {
export const createOrUpdateScheduleNotify = async (
req: Request,
res: Response
): Promise<Response> => {
const scheduleData = req.body;
const statusChatEnd = await ShowStatusChatEndService({
name: scheduleData.statusChatEndName
});
const schedulingNotifyCreate = await CreateSchedulingNotifyService(
{
const schedulingNotifyCreate = await CreateSchedulingNotifyService({
schedulingNotifyId: scheduleData.schedulingNotifyId,
ticketId: scheduleData.ticketId,
statusChatEndId: scheduleData.statusChatEndId,
statusChatEndId: statusChatEnd.id,
schedulingDate: scheduleData.schedulingDate,
schedulingTime: scheduleData.schedulingTime,
message: scheduleData.message
}
)
});
// console.group(':::::::::::::::::: DATA schedulingNotifyCreate:\n',schedulingNotifyCreate)
// const io = getIO();
// io.emit("schedulingNotify", {action: "update", schedulingNotifyCreate });
return res.status(200).json(schedulingNotifyCreate);
};
export const remove = async (req: Request, res: Response): Promise<Response> => {
export const remove = async (
req: Request,
res: Response
): Promise<Response> => {
const { scheduleId } = req.params;
let schedule: any = await ShowSchedulingNotifyService(scheduleId)
let schedule: any = await ShowSchedulingNotifyService(scheduleId);
await deleteScheduleByTicketIdCache(schedule.ticketId)
await deleteScheduleByTicketIdCache(schedule.ticketId);
await DeleteSchedulingNotifyService(scheduleId);
return res.status(200).send();
};

View File

@ -18,9 +18,7 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
const settings = await ListSettingsService();
// const config = await SettingTicket.findAll();
return res.status(200).json({ settings, });
return res.status(200).json({ settings });
};
export const ticketSettings = async (
@ -40,6 +38,7 @@ export const updateTicketSettings = async (
): Promise<Response> => {
const {
number,
saturdayBusinessTime,
outBusinessHours,
ticketExpiration,
weekend,
@ -58,6 +57,14 @@ export const updateTicketSettings = async (
});
}
if (saturdayBusinessTime && Object.keys(saturdayBusinessTime).length > 0) {
await updateSettingTicket({
...saturdayBusinessTime,
key: "saturdayBusinessTime",
number
});
}
if (ticketExpiration && Object.keys(ticketExpiration).length > 0) {
await updateSettingTicket({
...ticketExpiration,

View File

@ -1,12 +1,131 @@
import { Request, Response } from "express";
import AppError from "../errors/AppError";
import * as Yup from "yup";
type IndexQuery = {
searchParam: string;
pageNumber: string;
};
interface StatusChatEndData {
name: string;
farewellMessage: string;
isDefault: boolean;
}
import ListStatusChatEndService from "../services/StatusChatEndService/ListStatusChatEndService";
import ShowStatusChatEndService from "../services/StatusChatEndService/ShowStatusChatEndService";
import UpdateStatusChatEndService from "../services/StatusChatEndService/UpdateStatusChatEndService";
import { getIO } from "../libs/socket";
import StatusChatEnd from "../models/StatusChatEnd";
import CreateStatusChatEndService from "../services/StatusChatEndService/CreateStatusChatEndService";
import { del } from "../helpers/RedisClient";
// export const show = async (req: Request, res: Response): Promise<Response> => {
// const { statusChatEnd, count, hasMore } = await ListStatusChatEndService({ searchParam: "", pageNumber: "1" });
// return res.status(200).json(statusChatEnd);
// };
export const index = async (req: Request, res: Response): Promise<Response> => {
const { searchParam, pageNumber } = req.query as IndexQuery;
const { statusChatEnd, count, hasMore } = await ListStatusChatEndService({
searchParam,
pageNumber
});
return res.json({ statusChatEnd, count, hasMore });
};
export const show = async (req: Request, res: Response): Promise<Response> => {
const { statusChatEndId } = req.params;
const { statusChatEnd, count, hasMore } = await ListStatusChatEndService({ searchParam: "", pageNumber: "1" });
let statushCatEnd;
const isNumber = (str: any) => !isNaN(str);
if (isNumber(statusChatEndId))
statushCatEnd = await ShowStatusChatEndService({ id: statusChatEndId });
else statushCatEnd = await ShowStatusChatEndService({ isDefault: true });
return res.status(200).json(statushCatEnd);
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const newStatusChatEnd: StatusChatEndData = req.body;
const StatusChatEndSchema = Yup.object().shape({
name: Yup.string().required()
});
try {
await StatusChatEndSchema.validate(newStatusChatEnd);
} catch (err: any) {
throw new AppError(err.message);
}
const statusChatEnd = await CreateStatusChatEndService({
...newStatusChatEnd
});
const io = getIO();
io.emit("statusChatEnd", {
action: "create",
statusChatEnd
});
return res.status(200).json(statusChatEnd);
};
export const update = async (
req: Request,
res: Response
): Promise<Response> => {
const statusChatEndData: StatusChatEndData = req.body;
const schema = Yup.object().shape({
name: Yup.string()
});
try {
await schema.validate(statusChatEndData);
} catch (err: any) {
throw new AppError(err.message);
}
const { statusChatEndId } = req.params;
const statusChatEnd = await UpdateStatusChatEndService({
statusChatEndData,
statusChatEndId
});
const io = getIO();
io.emit("statusChatEnd", {
action: "update",
statusChatEnd
});
return res.status(200).json(statusChatEnd);
};
export const remove = async (
req: Request,
res: Response
): Promise<Response> => {
const { statusChatEndId } = req.params;
await StatusChatEnd.destroy({ where: { id: statusChatEndId } });
await del(`statusChatEnd:${statusChatEndId}`);
const io = getIO();
io.emit("statusChatEnd", {
action: "delete",
statusChatEndId
});
return res.status(200).json({ message: "Status chat deleted" });
};

View File

@ -22,7 +22,7 @@ import format from "date-fns/format";
import ListTicketsServiceCache from "../services/TicketServices/ListTicketServiceCache";
import { searchTicketCache, loadTicketsCache } from "../helpers/TicketCache";
import { Op } from "sequelize";
import { Op, where } from "sequelize";
type IndexQuery = {
searchParam: string;
@ -75,7 +75,10 @@ import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
import CreateContactService from "../services/ContactServices/CreateContactService";
import { botSendMessage } from "../services/WbotServices/wbotMessageListener";
import WhatsappQueue from "../models/WhatsappQueue";
import { get } from "../helpers/RedisClient"
import { get } from "../helpers/RedisClient";
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService";
import Queue from "../models/Queue";
import StatusChatEnd from "../models/StatusChatEnd";
export const index = async (req: Request, res: Response): Promise<Response> => {
const {
@ -118,10 +121,19 @@ export const remoteTicketCreation = async (
req: Request,
res: Response
): Promise<Response> => {
const { contact_from, contact_to, msg, contact_name }: any = req.body;
let { queueId, contact_from, cc, contact_to, msg, contact_name }: any =
req.body;
const validate = ["contact_from", "contact_to", "msg"];
const validateOnlyNumber = ["contact_from", "contact_to"];
let whatsappId: any;
if (!queueId && !contact_from && !cc) {
return res.status(400).json({
error: `Property 'queueId' or 'contact_from' or 'cc' is required.`
});
}
const validate = ["contact_to", "msg"];
const validateOnlyNumber = ["queueId", "contact_to", "contact_from"];
for (let prop of validate) {
if (!req.body[prop])
@ -138,19 +150,61 @@ export const remoteTicketCreation = async (
}
}
if (queueId) {
const whatsapps = await ListWhatsAppsForQueueService(queueId, "CONNECTED");
if (!whatsapps || whatsapps?.length == 0) {
return res.status(500).json({
msg: `queueId ${queueId} does not have a WhatsApp number associated with it or the number's session is disconnected.`
});
}
const { id } = whatsapps[0];
whatsappId = id;
} else if (contact_from) {
const whatsapp = await Whatsapp.findOne({
where: { number: contact_from, status: "CONNECTED" }
});
if (whatsapp) {
const { id: whatsappId, number, status } = whatsapp;
const queue: any = await WhatsappQueue.findOne({
where: { whatsappId },
attributes: ["queueId"]
if (!whatsapp) {
return res.status(404).json({
msg: `Whatsapp number ${contact_from} not found or disconnected!`
});
}
const { queueId } = queue;
const { id } = whatsapp;
const { queues } = await ShowWhatsAppService(id);
if (!queues || queues.length == 0) {
return res.status(500).json({
msg: `The WhatsApp number ${contact_from} is not associated with any queue! `
});
}
queueId = queues[0].id;
whatsappId = id;
} else if (cc) {
const queue = await Queue.findOne({ where: { cc } });
if (!queue) {
return res.status(404).json({
msg: `Queue with cc ${cc} not found! `
});
}
queueId = queue.id;
const whatsapps = await ListWhatsAppsForQueueService(queueId, "CONNECTED");
if (whatsapps.length === 0) {
return res.status(500).json({
msg: `No WhatsApp found for this cc ${cc} or the WhatsApp number is disconnected! `
});
}
whatsappId = whatsapps[0].id;
}
// const validNumber = await CheckIsValidContact(contact_to, true);
const validNumber = contact_to;
@ -210,9 +264,10 @@ export const remoteTicketCreation = async (
whatsappId,
0,
undefined,
queueId
queueId,
true
);
botSendMessage(ticket, msg);
botSendMessage(ticket, `${msg}`);
}
const io = getIO();
@ -233,14 +288,6 @@ export const remoteTicketCreation = async (
return res
.status(500)
.json({ msg: `The number ${contact_to} does not exist on WhatsApp` });
}
console.log(
`REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 500 | MSG: Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit`
);
return res.status(500).json({
msg: `Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit`
});
};
export const store = async (req: Request, res: Response): Promise<Response> => {
@ -340,34 +387,75 @@ export const update = async (
// lembrete
const scheduleData = JSON.parse(schedulingNotifyData);
const statusChatEndName = await ShowStatusChatEndService(
scheduleData.statusChatEndId
);
console.log("scheduleData: ", scheduleData);
const statusChatEnd = await ShowStatusChatEndService({
name: scheduleData.statusChatEndName
});
const { ticket } = await UpdateTicketService({
ticketData: {
status: status,
userId: userId,
statusChatEnd: statusChatEndName.name
statusChatEnd: statusChatEnd.name,
statusChatEndId: statusChatEnd.id
},
ticketId
});
if (scheduleData.farewellMessage) {
let _farewellMessage;
if (getSettingValue("farewellMessageByStatusChatEnd")?.value == "enabled") {
const statusChatEndData = await get({
key: `statusChatEnd:${statusChatEnd.id}`,
parse: true
});
if (
statusChatEndData &&
statusChatEndData?.farewellMessage &&
statusChatEndData?.farewellMessage?.trim()?.length > 0
) {
const { farewellMessage } = statusChatEndData;
_farewellMessage = farewellMessage;
}
} else if (getSettingValue("farewellMessageByQueue")?.value == "enabled") {
const queueData = await get({
key: `queue:${ticket.queueId}`,
parse: true
});
if (
queueData &&
queueData?.farewellMessage &&
queueData?.farewellMessage?.trim()?.length > 0
) {
const { farewellMessage } = queueData;
_farewellMessage = farewellMessage;
}
} else if (scheduleData.farewellMessage) {
const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
const { farewellMessage } = whatsapp;
if (farewellMessage) {
await SendWhatsAppMessage({ body: farewellMessage, ticket });
_farewellMessage = farewellMessage;
}
}
// lembrete // agendamento
if (_farewellMessage) {
await SendWhatsAppMessage({ body: `\u200e${_farewellMessage}`, ticket });
}
console.log("tatusChatEnd.name: ", statusChatEnd.name);
if (
scheduleData.statusChatEndId === "2" ||
scheduleData.statusChatEndId === "3"
statusChatEnd.name === "LEMBRETE" ||
statusChatEnd.name === "AGENDAMENTO À CONFIRMAR"
) {
// lembrete // agendamento
if (
isScheduling(scheduleData.schedulingDate, scheduleData.schedulingTime)
) {
@ -378,7 +466,7 @@ export const update = async (
const schedulingNotifyCreate = await CreateSchedulingNotifyService({
ticketId: scheduleData.ticketId,
statusChatEndId: scheduleData.statusChatEndId,
statusChatEndId: `${statusChatEnd.id}`,
schedulingDate: scheduleData.schedulingDate,
schedulingTime: scheduleData.schedulingTime,
message: scheduleData.message
@ -406,7 +494,6 @@ export const update = async (
for (const w of whatsappsByqueue) {
let whats = await ListWhatsAppsNumber(w.id);
console.log("-------> WHATS: ", JSON.stringify(whats, null, 6));
const ticket = await Ticket.findOne({
where: {
[Op.and]: [

View File

@ -138,8 +138,15 @@ export const all = async (req: Request, res: Response): Promise<Response> => {
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const { email, password, name, profile, positionCompany, queueIds } =
req.body;
const {
email,
password,
name,
profile,
positionCompany,
positionId,
queueIds
} = req.body;
console.log("===========> req.url: ", req.url);
@ -163,6 +170,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
password,
name,
positionCompany,
positionId,
profile,
queueIds
});

View File

@ -15,6 +15,9 @@ import SchedulingNotify from "../models/SchedulingNotify";
import StatusChatEnd from "../models/StatusChatEnd";
import UserOnlineTime from "../models/UserOnlineTime";
import SettingTicket from "../models/SettingTicket";
import QuickAnswerQueue from "../models/QuickAnswerQueue";
import Position from "../models/Position"
import ContactQueue from "../models/ContactQueues"
// eslint-disable-next-line
const dbConfig = require("../config/database");
// import dbConfig from "../config/database";
@ -33,11 +36,13 @@ const models = [
WhatsappQueue,
UserQueue,
QuickAnswer,
QuickAnswerQueue,
SchedulingNotify,
StatusChatEnd,
UserOnlineTime,
SettingTicket
SettingTicket,
Position,
ContactQueue
];
sequelize.addModels(models);

View File

@ -0,0 +1,17 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Tickets", "statusChatEndId", {
type: DataTypes.INTEGER,
references: { model: "StatusChatEnds", key: "id" },
onUpdate: "CASCADE",
onDelete: "SET NULL",
allowNull: true
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Tickets", "statusChatEndId");
}
};

View File

@ -0,0 +1,14 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Queues", "cc", {
type: DataTypes.STRING,
allowNull: true
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Queues", "cc");
}
};

View File

@ -0,0 +1,28 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("QuickAnswerQueues", {
quickAnswerId: {
type: DataTypes.INTEGER,
primaryKey: true
},
queueId: {
type: DataTypes.INTEGER,
primaryKey: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("QuickAnswerQueues");
}
};

View File

@ -0,0 +1,14 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Queues", "farewellMessage", {
type: DataTypes.STRING,
allowNull: true
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Queues", "farewellMessage");
}
};

View File

@ -0,0 +1,14 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("StatusChatEnds", "farewellMessage", {
type: DataTypes.STRING,
allowNull: true
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("StatusChatEnds", "farewellMessage");
}
};

View File

@ -0,0 +1,15 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("StatusChatEnds", "isDefault", {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("StatusChatEnds", "isDefault");
}
};

View File

@ -0,0 +1,30 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("Positions", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("Positions");
}
};

View File

@ -0,0 +1,19 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Users", "positionId", {
type: DataTypes.INTEGER,
references: { model: "Positions", key: "id" },
onUpdate: "CASCADE",
onDelete: "SET NULL"
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Users", "positionId");
}
};

View File

@ -0,0 +1,28 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.createTable("ContactQueues", {
contactId: {
type: DataTypes.INTEGER,
primaryKey: true
},
queueId: {
type: DataTypes.INTEGER,
primaryKey: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.dropTable("ContactQueues");
}
};

View File

@ -0,0 +1,15 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Tickets", "isRemote", {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: false
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Tickets", "isRemote");
}
};

View File

@ -1,26 +0,0 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkInsert('People', [{
name: 'John Doe',
isBetaMember: false
}], {});
*/
},
down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkDelete('People', null, {});
*/
}
};

View File

@ -0,0 +1,25 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"SettingTickets",
[
{
message: "",
startTime: new Date(),
endTime: new Date(),
value: "disabled",
key: "saturdayBusinessTime",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("SettingTickets", {});
}
};

View File

@ -0,0 +1,22 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Settings",
[
{
key: "quickAnswerByQueue",
value: "disabled",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Settings", {});
}
};

View File

@ -0,0 +1,22 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Settings",
[
{
key: "farewellMessageByQueue",
value: "disabled",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Settings", {});
}
};

View File

@ -0,0 +1,22 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Settings",
[
{
key: "farewellMessageByStatusChatEnd",
value: "disabled",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Settings", {});
}
};

View File

@ -0,0 +1,22 @@
import { QueryInterface } from "sequelize"
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Settings",
[
{
key: "contactByqueues",
value: "disabled",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
)
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Settings", {})
}
}

View File

@ -0,0 +1,29 @@
import QuickAnswer from "../models/QuickAnswer";
export default function quickAnswearByQueueFiltered(
queueIds: any[],
quickAnswers: QuickAnswer[]
) {
let auxQuickAnswear = [];
let repet: any[] = [];
const userQueues = queueIds.map((uq: any) => uq.queueId);
for (const quickAnswer of quickAnswers) {
const { queues, id } = quickAnswer;
if (queues.length == 0) {
auxQuickAnswear.push(quickAnswer);
continue;
}
for (const q of queues) {
if (userQueues.includes(q.id)) {
if (repet.includes(id)) continue;
repet.push(id);
auxQuickAnswear.push(quickAnswer);
}
}
}
return auxQuickAnswear;
}

View File

@ -5,6 +5,8 @@ interface SerializedUser {
id: number;
name: string;
positionCompany: string;
positionId: string | number;
position: object;
email: string;
profile: string;
queues: Queue[];
@ -15,8 +17,11 @@ export const SerializeUser = (user: User): SerializedUser => {
id: user.id,
name: user.name,
positionCompany: user.positionCompany,
positionId: user.positionId,
position: user.position,
email: user.email,
profile: user.profile,
queues: user.queues
queues: user.queues,
};
};

View File

@ -62,21 +62,8 @@ const isWeekend = async (number: string | number) => {
weekend.value == "enabled" &&
weekend.message?.trim()?.length > 0
) {
// Specify your desired timezone
const brazilTimeZone = "America/Sao_Paulo";
const currentDateUtc = new Date();
// Convert UTC date to Brazil time zone
const currentDate = utcToZonedTime(currentDateUtc, brazilTimeZone);
// Format the date using the desired format
const formattedDate = _format(currentDate, "yyyy-MM-dd HH:mm:ssXXX");
const parsedDate = parseISO(formattedDate);
// Convert parsed date to Brazil time zone
const localDate = utcToZonedTime(parsedDate, brazilTimeZone);
const localDate = localDateConvert();
// Check if it's Saturday or Sunday
if (isSaturday(localDate)) {
@ -173,8 +160,104 @@ async function isOutBusinessTime(number: string | number) {
return obj;
}
export {
isWeekend,
isHoliday,
isOutBusinessTime
};
async function isOutBusinessTimeSaturday(number: string | number) {
let obj = { set: false, msg: "" };
// Convert parsed date to Brazil time zone
const localDate = localDateConvert();
// Check if it's Saturday or Sunday
if (!isSaturday(localDate)) {
return obj;
}
const outBusinessHoursSaturday = await SettingTicket.findOne({
where: { key: "saturdayBusinessTime", number }
});
let isWithinRange = false;
if (
outBusinessHoursSaturday &&
outBusinessHoursSaturday.value == "enabled" &&
outBusinessHoursSaturday?.message?.trim()?.length > 0
) {
const ticketDateTimeUpdate = splitDateTime(
new Date(
_format(new Date(), "yyyy-MM-dd HH:mm:ss", {
locale: ptBR
})
)
);
const startTime = splitDateTime(
new Date(
_format(
new Date(outBusinessHoursSaturday.startTime),
"yyyy-MM-dd HH:mm:ss",
{
locale: ptBR
}
)
)
);
const endTime = splitDateTime(
new Date(
_format(
new Date(outBusinessHoursSaturday.endTime),
"yyyy-MM-dd HH:mm:ss",
{
locale: ptBR
}
)
)
);
const format = "HH:mm:ss";
const parsedStartTime = parse(
ticketDateTimeUpdate.fullTime,
format,
new Date()
);
const parsedEndTime = parse(startTime.fullTime, format, new Date());
const parsedTimeToCheck = parse(endTime.fullTime, format, new Date());
const timeInterval = { start: parsedStartTime, end: parsedEndTime };
// If the time range spans across different days, handle the date part
if (parsedEndTime < parsedStartTime) {
const nextDay = new Date(parsedStartTime);
nextDay.setDate(nextDay.getDate() + 1);
timeInterval.end = nextDay;
}
isWithinRange = isWithinInterval(parsedTimeToCheck, timeInterval);
if (!isWithinRange) {
obj.set = true;
obj.msg = outBusinessHoursSaturday.message;
}
}
return obj;
}
function localDateConvert() {
const brazilTimeZone = "America/Sao_Paulo";
const currentDateUtc = new Date();
// Convert UTC date to Brazil time zone
const currentDate = utcToZonedTime(currentDateUtc, brazilTimeZone);
// Format the date using the desired format
const formattedDate = _format(currentDate, "yyyy-MM-dd HH:mm:ssXXX");
const parsedDate = parseISO(formattedDate);
// Convert parsed date to Brazil time zone
const localDate = utcToZonedTime(parsedDate, brazilTimeZone);
return localDate;
}
export { isWeekend, isHoliday, isOutBusinessTime, isOutBusinessTimeSaturday };

View File

@ -1,11 +1,16 @@
import axios from "axios";
import https from "https"
import http from "http"
const api = axios.create({
baseURL: process.env.URL_WHATSAPP_API,
headers: {
Accept: "application/json",
Authorization: `Bearer ${process.env.TOKEN}`
}
},
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true })
});
export default api;

View File

@ -22,7 +22,8 @@ const isAuth = (req: Request, res: Response, next: NextFunction): void => {
const [, token] = authHeader.split(" ");
if (
req.originalUrl == "/tickets/remote/create" &&
(req.originalUrl == "/queue/remote/list" ||
req.originalUrl == "/tickets/remote/create") &&
token === process.env.TOKEN_REMOTE_TICKET_CREATION
) {
return next();

View File

@ -9,10 +9,13 @@ import {
AllowNull,
Unique,
Default,
HasMany
HasMany,
BelongsToMany
} from "sequelize-typescript";
import ContactCustomField from "./ContactCustomField";
import Ticket from "./Ticket";
import Queue from "./Queue"
import ContactQueue from "./ContactQueues"
@Table
class Contact extends Model<Contact> {
@ -52,6 +55,12 @@ class Contact extends Model<Contact> {
@HasMany(() => ContactCustomField)
extraInfo: ContactCustomField[];
@BelongsToMany(() => Queue, () => ContactQueue)
queues: Array<Queue & { WhatsappQueue: ContactQueue }>;
@HasMany(() => ContactQueue)
contactQueue: ContactQueue[];
}
export default Contact;

View File

@ -0,0 +1,33 @@
import {
Table,
Column,
CreatedAt,
UpdatedAt,
Model,
ForeignKey,
BelongsTo
} from "sequelize-typescript";
import Queue from "./Queue";
import Contact from "./Contact"
@Table
class ContactQueue extends Model<ContactQueue> {
@ForeignKey(() => Contact)
@Column
contactId: number;
@ForeignKey(() => Queue)
@Column
queueId: number;
@CreatedAt
createdAt: Date;
@UpdatedAt
updatedAt: Date;
@BelongsTo(() => Queue)
queue: Queue;
}
export default ContactQueue;

View File

@ -0,0 +1,48 @@
import {
Table,
Column,
CreatedAt,
UpdatedAt,
Model,
PrimaryKey,
ForeignKey,
BelongsTo,
HasMany,
HasOne,
AutoIncrement,
Default,
DataType,
Unique
} from "sequelize-typescript";
import Contact from "./Contact";
import Message from "./Message";
import Queue from "./Queue";
import User from "./User";
import Whatsapp from "./Whatsapp";
import SchedulingNotify from "./SchedulingNotify";
import StatusChatEnd from "./StatusChatEnd";
@Table
class Position extends Model<Position> {
@PrimaryKey
@AutoIncrement
@Column
id: number;
@Unique
@Column(DataType.TEXT)
name: string;
@CreatedAt
createdAt: Date;
@UpdatedAt
updatedAt: Date;
@HasMany(() => User)
users: User[];
}
export default Position;

View File

@ -36,6 +36,12 @@ class Queue extends Model<Queue> {
@Column
greetingMessage: string;
@Column
farewellMessage: string;
@Column
cc: string;
@CreatedAt
createdAt: Date;

View File

@ -6,8 +6,12 @@ import {
UpdatedAt,
Model,
PrimaryKey,
AutoIncrement
AutoIncrement,
BelongsToMany,
HasMany
} from "sequelize-typescript";
import Queue from "./Queue";
import QuickAnswerQueue from "./QuickAnswerQueue";
@Table
class QuickAnswer extends Model<QuickAnswer> {
@ -27,6 +31,12 @@ class QuickAnswer extends Model<QuickAnswer> {
@UpdatedAt
updatedAt: Date;
@BelongsToMany(() => Queue, () => QuickAnswerQueue)
queues: Array<Queue & { QuickAnswerQueue: QuickAnswerQueue }>;
@HasMany(() => QuickAnswerQueue)
quickAnswerQueue: QuickAnswerQueue[];
}
export default QuickAnswer;

View File

@ -0,0 +1,34 @@
import {
Table,
Column,
CreatedAt,
UpdatedAt,
Model,
ForeignKey,
BelongsTo
} from "sequelize-typescript";
import Queue from "./Queue";
import Whatsapp from "./Whatsapp";
import QuickAnswer from "./QuickAnswer"
@Table
class QuickAnswerQueue extends Model<QuickAnswerQueue> {
@ForeignKey(() => QuickAnswer)
@Column
quickAnswerId: number;
@ForeignKey(() => Queue)
@Column
queueId: number;
@CreatedAt
createdAt: Date;
@UpdatedAt
updatedAt: Date;
@BelongsTo(() => Queue)
queue: Queue;
}
export default QuickAnswerQueue;

View File

@ -7,12 +7,13 @@ import {
Model,
PrimaryKey,
HasMany
} from "sequelize-typescript";
} from "sequelize-typescript";
import SchedulingNotify from "./SchedulingNotify";
import SchedulingNotify from "./SchedulingNotify";
import Ticket from "./Ticket";
@Table
class StatusChatEnd extends Model<StatusChatEnd> {
@Table
class StatusChatEnd extends Model<StatusChatEnd> {
@PrimaryKey
@AutoIncrement
@Column
@ -21,6 +22,12 @@ import {
@Column
name: string;
@Column
farewellMessage: string;
@Column
isDefault: boolean;
@CreatedAt
createdAt: Date;
@ -29,7 +36,9 @@ import {
@HasMany(() => SchedulingNotify)
SchedulingNotifies: SchedulingNotify[];
}
export default StatusChatEnd;
@HasMany(() => Ticket)
tickets: Ticket[];
}
export default StatusChatEnd;

View File

@ -21,6 +21,7 @@ import User from "./User";
import Whatsapp from "./Whatsapp";
import SchedulingNotify from "./SchedulingNotify";
import StatusChatEnd from "./StatusChatEnd"
@Table
class Ticket extends Model<Ticket> {
@ -42,6 +43,14 @@ class Ticket extends Model<Ticket> {
@Column
isGroup: boolean;
@Default(false)
@Column
isRemote: boolean;
@ForeignKey(() => StatusChatEnd)
@Column
statusChatEndId: number;
@Column
statusChatEnd: string;

View File

@ -12,12 +12,15 @@ import {
Default,
HasMany,
BelongsToMany,
BelongsTo,
ForeignKey
} from "sequelize-typescript";
import { hash, compare } from "bcryptjs";
import Ticket from "./Ticket";
import Queue from "./Queue";
import UserQueue from "./UserQueue";
import UserOnlineTime from "./UserOnlineTime";
import Position from "./Position"
@Table
class User extends Model<User> {
@ -66,6 +69,13 @@ class User extends Model<User> {
@BelongsToMany(() => Queue, () => UserQueue)
queues: Queue[];
@ForeignKey(() => Position)
@Column
positionId: number;
@BelongsTo(() => Position)
position: Position;
@BeforeUpdate
@BeforeCreate
static hashPassword = async (instance: User): Promise<void> => {

View File

@ -0,0 +1,30 @@
import express from "express";
import isAuth from "../middleware/isAuth";
import * as PositionController from "../controllers/PositionController";
const positionRoutes = express.Router();
positionRoutes.get("/positions", isAuth, PositionController.index);
positionRoutes.get(
"/positions/:positionId",
isAuth,
PositionController.show
);
positionRoutes.post("/positions", isAuth, PositionController.store);
positionRoutes.put(
"/positions/:positionId",
isAuth,
PositionController.update
);
positionRoutes.delete(
"/positions/:positionId",
isAuth,
PositionController.remove
);
export default positionRoutes;

View File

@ -15,7 +15,7 @@ import schedulingNotifiyRoutes from "./SchedulingNotifyRoutes";
import statusChatEndRoutes from "./statusChatEndRoutes";
import wbotMonitorRoutes from "./wbotMonitorRoutes";
import iamRoutesEL from "./iamRoutesEL";
import positionRoutes from "./PositionRoutes"
const routes = Router();
@ -35,5 +35,6 @@ routes.use(schedulingNotifiyRoutes);
routes.use(reportRoutes);
routes.use(statusChatEndRoutes);
routes.use(wbotMonitorRoutes);
routes.use(positionRoutes);
export default routes;

View File

@ -11,6 +11,8 @@ queueRoutes.post("/queue", isAuth, QueueController.store);
queueRoutes.post("/queue/customization", QueueController.customization);
queueRoutes.get("/queue/remote/list", isAuth, QueueController.listQueues);
queueRoutes.get("/queue/:queueId", isAuth, QueueController.show);
queueRoutes.put("/queue/:queueId", isAuth, QueueController.update);

View File

@ -7,11 +7,19 @@ import * as ReportController from "../controllers/ReportController";
const reportRoutes = express.Router();
reportRoutes.get("/reports", isAuth, ReportController.reportUserByDateStartDateEnd);
reportRoutes.get(
"/reports",
isAuth,
ReportController.reportUserByDateStartDateEnd
);
reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue);
reportRoutes.get("/reports/user/services", isAuth, ReportController.reportUserService);
reportRoutes.get(
"/reports/user/services",
isAuth,
ReportController.reportUserService
);
reportRoutes.get(
"/reports/services/numbers",
@ -19,8 +27,22 @@ reportRoutes.get(
ReportController.reportService
);
reportRoutes.get("/reports/services/queues", isAuth, ReportController.reportServiceByQueue);
reportRoutes.get(
"/reports/services/queues",
isAuth,
ReportController.reportServiceByQueue
);
reportRoutes.get("/reports/messages", isAuth, ReportController.reportMessagesUserByDateStartDateEnd);
reportRoutes.get(
"/reports/messages",
isAuth,
ReportController.reportMessagesUserByDateStartDateEnd
);
reportRoutes.get(
"/reports/count/statusChatEnd",
isAuth,
ReportController.reportTicksCountByStatusChatEnds
);
export default reportRoutes;

View File

@ -5,6 +5,27 @@ import * as StatusChatEnd from "../controllers/StatusChatEndController";
const statusChatEndRoutes = Router();
statusChatEndRoutes.get("/statusChatEnd", isAuth, StatusChatEnd.show);
statusChatEndRoutes.post("/statusChatEnd", isAuth, StatusChatEnd.store);
// statusChatEndRoutes.get("/statusChatEnd", isAuth, StatusChatEnd.show);
statusChatEndRoutes.get("/statusChatEnd", isAuth, StatusChatEnd.index);
statusChatEndRoutes.get(
"/statusChatEnd/:statusChatEndId",
isAuth,
StatusChatEnd.show
);
statusChatEndRoutes.put(
"/statusChatEnd/:statusChatEndId",
isAuth,
StatusChatEnd.update
);
statusChatEndRoutes.delete(
"/statusChatEnd/:statusChatEndId",
isAuth,
StatusChatEnd.remove
);
export default statusChatEndRoutes;

View File

@ -28,6 +28,7 @@ import ShowUserService from "./services/UserServices/ShowUserService";
import { json } from "sequelize";
import { setBotInfo } from "./helpers/SetBotInfo";
import Queue from "./models/Queue";
import StatusChatEnd from "./models/StatusChatEnd";
const server = app.listen(process.env.PORT, () => {
logger.info(`Server started on port: ${process.env.PORT}`);
@ -48,7 +49,14 @@ gracefulShutdown(server);
(async () => {
console.log("os.tmpdir(): ", os.tmpdir());
await clearAllKeys("user:*", "whatsapp:*", "queue:*");
await clearAllKeys("user:*", "whatsapp:*", "queue:*", "statusChatEnd:*");
const statusChatEnds = await StatusChatEnd.findAll();
for (const statusChatEnd of statusChatEnds) {
const { id, name, farewellMessage } = statusChatEnd;
await set(`statusChatEnd:${id}`, { id, name, farewellMessage });
}
const users = await User.findAll();
@ -63,12 +71,12 @@ gracefulShutdown(server);
await set(`user:${id}`, { id, name });
}
// const queues = await Queue.findAll();
const queues = await Queue.findAll();
// for (const queue of queues) {
// const { id, greetingMessage, name } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
// }
for (const queue of queues) {
const { id, greetingMessage, name, farewellMessage } = queue;
await set(`queue:${id}`, { id, name, greetingMessage, farewellMessage });
}
loadSettings();

View File

@ -0,0 +1,12 @@
import Contact from "../../models/Contact";
const AssociateContatctQueue = async (
contact: Contact,
queueIds: number[]
): Promise<void> => {
await contact.$set("queues", queueIds);
await contact.reload();
};
export default AssociateContatctQueue;

View File

@ -1,8 +1,9 @@
import AppError from "../../errors/AppError";
import Contact from "../../models/Contact";
import { createOrUpdateContactCache } from '../../helpers/ContactsCache'
import { createOrUpdateContactCache } from "../../helpers/ContactsCache";
import GetProfilePicUrl from "../WbotServices/GetProfilePicUrl";
import AssociateContatctQueue from "./AssociateContatctQueue";
interface ExtraInfo {
name: string;
@ -15,18 +16,18 @@ interface Request {
email?: string;
profilePicUrl?: string;
extraInfo?: ExtraInfo[];
queueIds?: number[];
}
const CreateContactService = async ({
name,
number,
email = "",
profilePicUrl='',
extraInfo = []
profilePicUrl = "",
extraInfo = [],
queueIds = []
}: Request): Promise<Contact> => {
try {
const numberExists = await Contact.findOne({
where: { number }
});
@ -48,21 +49,25 @@ const CreateContactService = async ({
}
);
await AssociateContatctQueue(contact, queueIds);
// TEST DEL
await createOrUpdateContactCache(`contact:${contact.id}`, {id: contact.id, name, number, profilePicUrl, isGroup:'false', extraInfo, email })
await createOrUpdateContactCache(`contact:${contact.id}`, {
id: contact.id,
name,
number,
profilePicUrl,
isGroup: "false",
extraInfo,
email
});
//
return contact;
} catch (error: any) {
console.error('===> Error on CreateContactService.ts file: \n', error)
console.error("===> Error on CreateContactService.ts file: \n", error);
throw new AppError(error.message);
}
};
export default CreateContactService;

View File

@ -1,9 +1,16 @@
import { Sequelize, Op } from "sequelize";
import Contact from "../../models/Contact";
import Queue from "../../models/Queue";
import { getSettingValue } from "../../helpers/WhaticketSettings";
// import ListContactsService from "../services/ContactServices/ListContactsService";
import ListWhatsappQueueByUserQueue from "../WhatsappService/ListWhatsAppsForQueueService";
import QueuesByUser from "../UserServices/ShowQueuesByUser";
import { number } from "yup";
interface Request {
searchParam?: string;
pageNumber?: string;
userId?: string;
}
interface Response {
@ -14,13 +21,14 @@ interface Response {
const ListContactsService = async ({
searchParam = "",
pageNumber = "1"
pageNumber = "1",
userId
}: Request): Promise<Response> => {
const whereCondition = {
[Op.or]: [
{
name: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("name")),
Sequelize.fn("LOWER", Sequelize.col("Contact.name")),
"LIKE",
`%${searchParam.toLowerCase().trim()}%`
)
@ -31,20 +39,59 @@ const ListContactsService = async ({
const limit = 20;
const offset = limit * (+pageNumber - 1);
const { count, rows: contacts } = await Contact.findAndCountAll({
let { count, rows: contacts } = await Contact.findAndCountAll({
where: whereCondition,
limit,
include: [
{
model: Queue,
as: "queues",
// where: whereConditionQueue,
attributes: ["id", "name", "color", "greetingMessage"]
}
],
offset,
order: [["name", "ASC"]]
});
const hasMore = count > offset + contacts.length;
if (getSettingValue("contactByqueues")?.value == "enabled") {
const queueIds = await QueuesByUser({ userId });
contacts = contactQueueFilter(queueIds, contacts);
}
return {
contacts,
count,
hasMore
};
};
export default ListContactsService;
function contactQueueFilter(queueIds: any[], contacts: Contact[]) {
let auxContact: any[] = [];
let repet: any[] = [];
const userQueues = queueIds.map((uq: any) => uq.queueId);
for (const contact of contacts) {
const { queues, id } = contact;
if (queues.length == 0) {
auxContact.push(contact);
continue;
}
for (const q of queues) {
if (userQueues.includes(q.id)) {
if (repet.includes(id)) continue;
repet.push(id);
auxContact.push(contact);
}
}
}
return auxContact;
}

View File

@ -1,8 +1,20 @@
import Contact from "../../models/Contact";
import AppError from "../../errors/AppError";
import { getSettingValue } from "../../helpers/WhaticketSettings";
import Queue from "../../models/Queue";
const ShowContactService = async (id: string | number): Promise<Contact> => {
const contact = await Contact.findByPk(id, { include: ["extraInfo"] });
let includeQueue: any[] = [];
if (getSettingValue("contactByqueues")?.value == "enabled") {
includeQueue = [
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }
];
}
const contact = await Contact.findByPk(id, {
include: ["extraInfo", ...includeQueue]
});
if (!contact) {
throw new AppError("ERR_NO_CONTACT_FOUND", 404);

View File

@ -2,9 +2,10 @@ import AppError from "../../errors/AppError";
import Contact from "../../models/Contact";
import ContactCustomField from "../../models/ContactCustomField";
import { updateTicketsByContactsCache } from '../../helpers/TicketCache'
import { updateContactCacheById } from '../../helpers/ContactsCache'
import { updateTicketsByContactsCache } from "../../helpers/TicketCache";
import { updateContactCacheById } from "../../helpers/ContactsCache";
import { tr } from "date-fns/locale";
import AssociateContatctQueue from "./AssociateContatctQueue";
interface ExtraInfo {
id?: number;
@ -16,6 +17,7 @@ interface ContactData {
number?: string;
name?: string;
extraInfo?: ExtraInfo[];
queueIds?: number[];
}
interface Request {
@ -23,15 +25,12 @@ interface Request {
contactId: string;
}
const UpdateContactService = async ({
contactData,
contactId
}: Request): Promise<Contact> => {
try {
const { email, name, number, extraInfo } = contactData;
const { email, name, number, extraInfo, queueIds } = contactData;
// console.log('email, name, number, extraInfo: ', email, name, number, extraInfo)
@ -54,7 +53,9 @@ const UpdateContactService = async ({
await Promise.all(
contact.extraInfo.map(async oldInfo => {
const stillExists = extraInfo.findIndex(info => info.id === oldInfo.id);
const stillExists = extraInfo.findIndex(
info => info.id === oldInfo.id
);
if (stillExists === -1) {
await ContactCustomField.destroy({ where: { id: oldInfo.id } });
@ -63,11 +64,10 @@ const UpdateContactService = async ({
);
}
const oldNumber = contact.number
const oldNumber = contact.number;
//Solução para o erro tcp_wrap.cc
console.log('----------> oldNumber: ', oldNumber)
console.log("----------> oldNumber: ", oldNumber);
if (number) {
const numberExists = await Contact.findOne({
where: { number }
@ -78,7 +78,6 @@ const UpdateContactService = async ({
}
}
await contact.update({
name,
number,
@ -86,29 +85,28 @@ const UpdateContactService = async ({
});
if (queueIds) await AssociateContatctQueue(contact, queueIds);
//TEST DEL
await updateTicketsByContactsCache(oldNumber, contact.name, contact.number)
await updateTicketsByContactsCache(oldNumber, contact.name, contact.number);
//
await contact.reload({
attributes: ["id", "name", "number", "email", "profilePicUrl"],
include: ["extraInfo"]
});
// console.log('contactcontactcontactcontact: ',flatten(JSON.parse(JSON.stringify(contact))))
await updateContactCacheById(contact.id, JSON.parse(JSON.stringify(contact)))
await updateContactCacheById(
contact.id,
JSON.parse(JSON.stringify(contact))
);
return contact;
} catch (error: any) {
console.error('===> Error on UpdateContactService.ts file: \n', error)
console.error("===> Error on UpdateContactService.ts file: \n", error);
throw new AppError(error.message);
}
};
export default UpdateContactService;

View File

@ -0,0 +1,23 @@
import AppError from "../../errors/AppError";
import Position from "../../models/Position";
import AssociateQuickAnswerQueue from "../QueueService/AssociateQuickAnswerQueue";
interface Request {
name: string;
}
const CreatePositionService = async ({ name }: Request): Promise<Position> => {
const nameExists = await Position.findOne({
where: { name }
});
if (nameExists) {
throw new AppError("ERR_POSITION_DUPLICATED");
}
const position = await Position.create({ name });
return position;
};
export default CreatePositionService;

View File

@ -0,0 +1,16 @@
import Position from "../../models/Position";
import AppError from "../../errors/AppError";
const DeletePositionService = async (id: string): Promise<void> => {
const position = await Position.findOne({
where: { id }
});
if (!position) {
throw new AppError("ERR_NO_POSITION_FOUND", 404);
}
await position.destroy();
};
export default DeletePositionService;

View File

@ -0,0 +1,48 @@
import { Op, Sequelize } from "sequelize";
import Position from "../../models/Position";
interface Request {
searchParam?: string;
pageNumber?: string;
}
interface Response {
positions: Position[];
count: number;
hasMore: boolean;
}
const ListPositionService = async ({
searchParam = "",
pageNumber = "1"
}: Request): Promise<Response> => {
console.log("searchParam: ", searchParam);
const whereCondition = {
message: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("name")),
"LIKE",
`%${searchParam.toLowerCase().trim()}%`
)
};
const limit = 20;
const offset = limit * (+pageNumber - 1);
let { count, rows: positions } = await Position.findAndCountAll({
where: whereCondition,
limit,
offset,
order: [["name", "ASC"]]
});
const hasMore = count > offset + positions.length;
return {
positions,
count,
hasMore
};
};
export default ListPositionService;

View File

@ -0,0 +1,16 @@
import AppError from "../../errors/AppError";
import Position from "../../models/Position";
const ShowPositionService = async (
id: string,
): Promise<Position> => {
const position = await Position.findByPk(id);
if (!position) {
throw new AppError("ERR_NO_POSITION_FOUND", 404);
}
return position;
};
export default ShowPositionService;

View File

@ -0,0 +1,38 @@
import Position from "../../models/Position";
import AppError from "../../errors/AppError";
import AssociateQuickAnswerQueue from "../QueueService/AssociateQuickAnswerQueue";
interface PositionData {
name: string;
}
interface Request {
positionData: PositionData;
positionId: string;
}
const UpdatePositionService = async ({
positionData,
positionId
}: Request): Promise<Position> => {
const { name } = positionData;
const position = await Position.findOne({
where: { id: positionId }
});
if (!position) {
throw new AppError("ERR_NO_POSITION_FOUND", 404);
}
await position.update({
name
});
await position.reload({
attributes: ["id", "name"]
});
return position;
};
export default UpdatePositionService;

View File

@ -0,0 +1,12 @@
import QuickAnswer from "../../models/QuickAnswer";
const AssociateQuickAnswerQueue = async (
QuickAnswer: QuickAnswer,
queueIds: number[]
): Promise<void> => {
await QuickAnswer.$set("queues", queueIds);
await QuickAnswer.reload();
};
export default AssociateQuickAnswerQueue;

View File

@ -7,6 +7,8 @@ interface QueueData {
name: string;
color: string;
greetingMessage?: string;
farewellMessage?: string;
cc?: string;
}
const CreateQueueService = async (queueData: QueueData): Promise<Queue> => {
@ -63,8 +65,13 @@ const CreateQueueService = async (queueData: QueueData): Promise<Queue> => {
const queue = await Queue.create(queueData);
// const { id, greetingMessage } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
const { id, greetingMessage, farewellMessage } = queue;
await set(`queue:${id}`, {
id,
name,
greetingMessage,
farewellMessage
});
return queue;
} catch (error: any) {

View File

@ -4,28 +4,28 @@ import UserQueue from "../../models/UserQueue";
import ListTicketsServiceCache from "../TicketServices/ListTicketServiceCache";
import { deleteTicketsFieldsCache } from '../../helpers/TicketCache'
import { deleteTicketsFieldsCache } from "../../helpers/TicketCache";
import { del } from "../../helpers/RedisClient";
const DeleteQueueService = async (queueId: number | string): Promise<void> => {
const queue = await ShowQueueService(queueId);
if (queue.id) {
const tickets = await ListTicketsServiceCache({ queueId });
const tickets = await ListTicketsServiceCache({ queueId })
await deleteTicketsFieldsCache(tickets, ['queue.id', 'queue.name', 'queue.color'])
await deleteTicketsFieldsCache(tickets, [
"queue.id",
"queue.name",
"queue.color"
]);
}
try {
await UserQueue.destroy({ where: { queueId: queueId } });
del(`queue:${queueId}`);
} catch (error) {
console.log('Error on delete UserQueue by queueId: ', queueId)
console.log("Error on delete UserQueue by queueId: ", queueId);
}
await queue.destroy();

View File

@ -3,12 +3,14 @@ import * as Yup from "yup";
import AppError from "../../errors/AppError";
import Queue from "../../models/Queue";
import ShowQueueService from "./ShowQueueService";
import { set } from "../../helpers/RedisClient"
import { set } from "../../helpers/RedisClient";
interface QueueData {
name?: string;
color?: string;
greetingMessage?: string;
farewellMessage?: string;
cc?: string;
}
const UpdateQueueService = async (
@ -69,8 +71,13 @@ const UpdateQueueService = async (
await queue.update(queueData);
// const { id, greetingMessage } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
const { greetingMessage, farewellMessage } = queue;
await set(`queue:${queueId}`, {
id: queueId,
name,
greetingMessage,
farewellMessage
});
return queue;
} catch (error: any) {

View File

@ -1,14 +1,17 @@
import AppError from "../../errors/AppError";
import QuickAnswer from "../../models/QuickAnswer";
import AssociateQuickAnswerQueue from "../QueueService/AssociateQuickAnswerQueue";
interface Request {
shortcut: string;
message: string;
queueIds?: number[];
}
const CreateQuickAnswerService = async ({
shortcut,
message
message,
queueIds = []
}: Request): Promise<QuickAnswer> => {
const nameExists = await QuickAnswer.findOne({
where: { shortcut }
@ -20,6 +23,8 @@ const CreateQuickAnswerService = async ({
const quickAnswer = await QuickAnswer.create({ shortcut, message });
await AssociateQuickAnswerQueue(quickAnswer, queueIds);
return quickAnswer;
};

Some files were not shown because too many files have changed in this diff Show More