feat: Allow identifying messages sent by the agent and the IVR

feat-scaling-ticket-remote-creation
adriano 2024-03-13 11:19:40 -03:00
parent 279c4697dd
commit d47a36d8b2
14 changed files with 198 additions and 96 deletions

View File

@ -21,6 +21,7 @@ import sendWhatsAppMessageOfficialAPI from "../helpers/sendWhatsAppMessageOffici
import Whatsapp from "../models/Whatsapp";
import checkLastClientMsg24hs from "../helpers/CheckLastClientMsg24hs";
import AppError from "../errors/AppError";
import { get } from "../helpers/RedisClient";
type IndexQuery = {
pageNumber: string;
@ -97,7 +98,8 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
}
const name = params.find((p: any) => p?.template_name);
const { language }: any = params?.find((p: any) => p?.language) || 'pt_BR'
const { language }: any =
params?.find((p: any) => p?.language) || "pt_BR";
const { template_name } = name;

View File

@ -5,11 +5,9 @@ import DeleteQueueService from "../services/QueueService/DeleteQueueService";
import ListQueuesService from "../services/QueueService/ListQueuesService";
import ShowQueueService from "../services/QueueService/ShowQueueService";
import UpdateQueueService from "../services/QueueService/UpdateQueueService";
import Queue from "../models/Queue"
import AppError from "../errors/AppError"
import { get, set } from "../helpers/RedisClient";
import Queue from "../models/Queue";
import AppError from "../errors/AppError";
import { del, get, set } from "../helpers/RedisClient";
export const index = async (req: Request, res: Response): Promise<Response> => {
const queues = await ListQueuesService();
@ -125,7 +123,7 @@ export const customization = async (
await set("ura", ura);
const _ura = await get("ura");
const _ura = await get({ key: "ura", parse: true });
console.log("_URA: ", _ura);
return res.status(200).json({ new_queues });
@ -164,6 +162,8 @@ export const remove = async (
await DeleteQueueService(queueId);
await del(`queue:${queueId}`);
const io = getIO();
io.emit("queue", {
action: "delete",

View File

@ -75,8 +75,21 @@ 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"
export const index = async (req: Request, res: Response): Promise<Response> => {
const QueuesGreetingMessage = await get({
key: "whatsapp:*",
value: "*1* - MESA DE SERVICIO"
});
console.log(
" TTTTTTTTTTTTTTTT EEEEEEEEEEEEE SSSSSSSSSSS TTTTTTTTTT KKKKKKKK: ",
QueuesGreetingMessage
);
const {
pageNumber,
status,

View File

@ -230,7 +230,10 @@ export const weebhook = async (
req.body.entry[0].changes[0].value.metadata.display_phone_number;
let type = message.type;
const contact_to_exist = await get("whatsapp:*", `${contact_to}`);
const contact_to_exist = await get({
key: "whatsapp:*",
value: `${contact_to}`
});
if (contact_to_exist == null) {
console.log(
@ -408,7 +411,15 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}`
});
} else {
await set(`whatsapp:${whatsapp.id}`, `${number}`);
await set(
`whatsapp:${whatsapp.id}`,
JSON.stringify({
number: whatsapp?.number,
id: whatsapp?.id,
greetingMessage: whatsapp?.greetingMessage,
phoneNumberId: whatsapp?.phoneNumberId
})
);
}
const io = getIO();
@ -484,7 +495,15 @@ export const update = async (
client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}`
});
} else {
await set(`whatsapp:${whatsapp.id}`, `${number}`);
await set(
`whatsapp:${whatsapp.id}`,
JSON.stringify({
number: whatsapp?.number,
id: whatsapp?.id,
greetingMessage: whatsapp?.greetingMessage,
phoneNumberId: whatsapp?.phoneNumberId
})
);
}
const io = getIO();

View File

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

View File

@ -5,8 +5,7 @@ import ListUsersService from "../services/UserServices/ListUsersService";
import { get } from "./RedisClient";
const _botIsOnQueue = async (botName: string) => {
const botInfo = await get("botInfo");
const botInfo = await get({ key: "botInfo", parse: true });
if (
botInfo &&

View File

@ -8,6 +8,12 @@ type WhatsappData = {
value?: string;
};
type getData = {
key: string;
value?: string;
parse?: boolean;
};
export async function set(key: string, value: string | object) {
if (typeof value == "object") await redis.set(key, JSON.stringify(value));
else {
@ -15,21 +21,30 @@ export async function set(key: string, value: string | object) {
}
}
export async function get(key: string, value?: string) {
export async function getSimple(key: string) {
const value: any = await redis.get(key);
return value;
}
export async function get({ key, value, parse }: getData) {
if (key.includes("*")) {
const keys = await redis.keys(key);
// If there are keys, delete them
if (keys.length > 0) {
for (const key of keys) {
const val = await redis.get(key);
if (value == val) return value;
if (val.includes(value)) {
if (parse) return JSON.parse(val);
return val;
}
}
}
return null;
} else {
const value: any = await redis.get(key);
return JSON.parse(value);
if (parse) return JSON.parse(value);
return value;
}
}

View File

@ -31,6 +31,10 @@ class Message extends Model<Message> {
@Column
fromMe: boolean;
@Default(false)
@Column
fromAgent: boolean;
@Column(DataType.TEXT)
body: string;

View File

@ -27,6 +27,7 @@ import { clearAllKeys, get, set } from "./helpers/RedisClient";
import ShowUserService from "./services/UserServices/ShowUserService";
import { json } from "sequelize";
import { setBotInfo } from "./helpers/SetBotInfo";
import Queue from "./models/Queue";
const server = app.listen(process.env.PORT, () => {
logger.info(`Server started on port: ${process.env.PORT}`);
@ -47,7 +48,7 @@ gracefulShutdown(server);
(async () => {
console.log("os.tmpdir(): ", os.tmpdir());
await clearAllKeys("user:*", "whatsapp:*");
await clearAllKeys("user:*", "whatsapp:*", "queue:*");
const users = await User.findAll();
@ -62,23 +63,37 @@ gracefulShutdown(server);
await set(`user:${id}`, { id, name });
}
// const queues = await Queue.findAll();
// for (const queue of queues) {
// const { id, greetingMessage, name } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
// }
loadSettings();
let whatsapps: any = await Whatsapp.findAll({
attributes: ["id", "url", "phoneNumberId", "number"]
attributes: ["id", "url", "phoneNumberId", "number", "greetingMessage"]
});
if (whatsapps && whatsapps.length > 0) {
for (let i = 0; i < whatsapps.length; i++) {
try {
const { phoneNumberId } = whatsapps[i];
const { phoneNumberId, id, greetingMessage } = whatsapps[i];
if (phoneNumberId) {
await set(
`whatsapp:${whatsapps[i].dataValues.id}`,
`${whatsapps[i].dataValues.number}`
JSON.stringify({
number: whatsapps[i].dataValues.number,
id,
greetingMessage,
phoneNumberId
})
);
}
if (phoneNumberId) {
continue;
}

View File

@ -13,6 +13,7 @@ interface MessageData {
read?: boolean;
mediaType?: string;
mediaUrl?: string;
fromAgent?: boolean;
}
interface Request {
messageData: MessageData;

View File

@ -1,6 +1,7 @@
import * as Yup from "yup";
import AppError from "../../errors/AppError";
import Queue from "../../models/Queue";
import { set } from "../../helpers/RedisClient";
interface QueueData {
name: string;
@ -9,68 +10,67 @@ interface QueueData {
}
const CreateQueueService = async (queueData: QueueData): Promise<Queue> => {
try {
const { color, name } = queueData;
const queueSchema = Yup.object().shape({
name: Yup.string()
.min(2, "ERR_QUEUE_INVALID_NAME")
.required("ERR_QUEUE_INVALID_NAME")
.test(
"Check-unique-name",
"ERR_QUEUE_NAME_ALREADY_EXISTS",
async value => {
if (value) {
const queueWithSameName = await Queue.findOne({
where: { name: value }
});
const queueSchema = Yup.object().shape({
name: Yup.string()
.min(2, "ERR_QUEUE_INVALID_NAME")
.required("ERR_QUEUE_INVALID_NAME")
.test(
"Check-unique-name",
"ERR_QUEUE_NAME_ALREADY_EXISTS",
async value => {
if (value) {
const queueWithSameName = await Queue.findOne({
where: { name: value }
});
return !queueWithSameName;
return !queueWithSameName;
}
return false;
}
),
color: Yup.string()
.required("ERR_QUEUE_INVALID_COLOR")
.test("Check-color", "ERR_QUEUE_INVALID_COLOR", async value => {
if (value) {
const colorTestRegex = /^#[0-9a-f]{3,6}$/i;
return colorTestRegex.test(value);
}
return false;
}
),
color: Yup.string()
.required("ERR_QUEUE_INVALID_COLOR")
.test("Check-color", "ERR_QUEUE_INVALID_COLOR", async value => {
if (value) {
const colorTestRegex = /^#[0-9a-f]{3,6}$/i;
return colorTestRegex.test(value);
}
return false;
})
.test(
"Check-color-exists",
"ERR_QUEUE_COLOR_ALREADY_EXISTS",
async value => {
if (value) {
const queueWithSameColor = await Queue.findOne({
where: { color: value }
});
return !queueWithSameColor;
})
.test(
"Check-color-exists",
"ERR_QUEUE_COLOR_ALREADY_EXISTS",
async value => {
if (value) {
const queueWithSameColor = await Queue.findOne({
where: { color: value }
});
return !queueWithSameColor;
}
return false;
}
return false;
}
)
});
)
});
try {
await queueSchema.validate({ color, name });
} catch (err: any) {
throw new AppError(err.message);
}
try {
await queueSchema.validate({ color, name });
} catch (err: any) {
throw new AppError(err.message);
}
const queue = await Queue.create(queueData);
const queue = await Queue.create(queueData);
return queue;
// const { id, greetingMessage } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
return queue;
} catch (error: any) {
console.error('===> Error on CreateQueueService.ts file: \n', error)
console.error("===> Error on CreateQueueService.ts file: \n", error);
throw new AppError(error.message);
}
};
export default CreateQueueService;

View File

@ -3,6 +3,7 @@ import * as Yup from "yup";
import AppError from "../../errors/AppError";
import Queue from "../../models/Queue";
import ShowQueueService from "./ShowQueueService";
import { set } from "../../helpers/RedisClient"
interface QueueData {
name?: string;
@ -14,9 +15,7 @@ const UpdateQueueService = async (
queueId: number | string,
queueData: QueueData
): Promise<Queue> => {
try {
const { color, name } = queueData;
const queueSchema = Yup.object().shape({
@ -70,13 +69,14 @@ const UpdateQueueService = async (
await queue.update(queueData);
return queue;
// const { id, greetingMessage } = queue;
// await set(`queue:${id}`, { id, name, greetingMessage });
return queue;
} catch (error: any) {
console.error('===> Error on UpdateQueueService.ts file: \n', error)
console.error("===> Error on UpdateQueueService.ts file: \n", error);
throw new AppError(error.message);
}
};
export default UpdateQueueService;

View File

@ -31,8 +31,6 @@ const CheckIsValidContact = async (
if (!isValidNumber) {
console.log('kkkkkkkkkkkkkkkkkkkkkkkkkkkkk ')
const { data, status } = await axios.post(
`${process.env.WHATS_NUMBER_VALIDATOR_URL}/api/validate`,
{ mobile: number },

View File

@ -92,7 +92,8 @@ import {
createObject,
findByContain,
findObject,
get
get,
getSimple
} from "../../helpers/RedisClient";
import FindOrCreateTicketServiceBot from "../TicketServices/FindOrCreateTicketServiceBot";
import ShowTicketService from "../TicketServices/ShowTicketService";
@ -175,9 +176,14 @@ const verifyMediaMessage = async (
mediaUrl: media.filename,
mediaType: media.mimetype.split("/")[0],
quotedMsgId: quotedMsg,
phoneNumberId: msg?.phoneNumberId
phoneNumberId: msg?.phoneNumberId,
fromAgent: false
};
if (msg?.fromMe) {
messageData = { ...messageData, fromAgent: true };
}
if (!ticket?.phoneNumberId) {
if (!media.filename) {
const ext = media.mimetype.split("/")[1].split(";")[0];
@ -280,18 +286,33 @@ const verifyMessage = async (
contact: Contact,
quotedMsg?: any
) => {
const messageData = {
let messageData = {
id: msg.id.id,
ticketId: ticket.id,
contactId: msg.fromMe ? undefined : contact.id,
body: msg.body,
fromMe: msg.fromMe,
fromAgent: false,
mediaType: msg.type,
read: msg.fromMe,
quotedMsgId: quotedMsg,
phoneNumberId: msg?.phoneNumberId
};
if (msg?.fromMe) {
const botInfo = await BotIsOnQueue("botqueue");
if (botInfo.isOnQueue) {
const ura: any = await get({ key: "ura" });
if (ura && !ura.includes(JSON.stringify(msg?.body))) {
messageData = { ...messageData, fromAgent: true };
}
} else if (msg?.body?.trim().length > 0 && !/\u200e/.test(msg.body[0])) {
messageData = { ...messageData, fromAgent: true };
}
}
await ticket.update({ lastMessage: msg.body });
await CreateMessageService({ messageData });
@ -349,7 +370,7 @@ const verifyQueue = async (
ticketId: ticket.id
});
const data = await get("ura");
const data = await get({ key: "ura", parse: true });
await createObject({
whatsappId: `${ticket.whatsappId}`,
@ -965,7 +986,7 @@ const handleMessage = async (
const menu = async (userTyped: string, whatsappId: any, contactId: any) => {
let lastId = await findObject(whatsappId, contactId, "ura");
const data: any = await get("ura");
const data: any = await get({ key: "ura", parse: true });
console.log("lastId[0]: ", lastId[0]);