feat: Allow identifying messages sent by the agent and the IVR
parent
279c4697dd
commit
d47a36d8b2
|
@ -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;
|
||||
|
@ -35,7 +36,7 @@ type MessageData = {
|
|||
params: any;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId } = req.params;
|
||||
const { pageNumber } = req.query as IndexQuery;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -5,14 +5,12 @@ 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();
|
||||
const queues = await ListQueuesService();
|
||||
|
||||
return res.status(200).json(queues);
|
||||
};
|
||||
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const {
|
||||
pageNumber,
|
||||
status,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
};
|
|
@ -4,9 +4,8 @@ const fs = require("fs");
|
|||
import ListUsersService from "../services/UserServices/ListUsersService";
|
||||
import { get } from "./RedisClient";
|
||||
|
||||
const _botIsOnQueue = async (botName: string) => {
|
||||
|
||||
const botInfo = await get("botInfo");
|
||||
const _botIsOnQueue = async (botName: string) => {
|
||||
const botInfo = await get({ key: "botInfo", parse: true });
|
||||
|
||||
if (
|
||||
botInfo &&
|
||||
|
@ -19,8 +18,8 @@ const _botIsOnQueue = async (botName: string) => {
|
|||
botQueueId: botInfo.queueId,
|
||||
isOnQueue: botInfo.botIsOnQueue
|
||||
};
|
||||
}
|
||||
return { userIdBot: null, botQueueId: null, isOnQueue: false };
|
||||
}
|
||||
return { userIdBot: null, botQueueId: null, isOnQueue: false };
|
||||
};
|
||||
|
||||
export default _botIsOnQueue;
|
||||
|
|
|
@ -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) {
|
||||
if (key.includes("*")) {
|
||||
const keys = await redis.keys(key);
|
||||
export async function getSimple(key: string) {
|
||||
const value: any = await redis.get(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
// If there are keys, delete them
|
||||
export async function get({ key, value, parse }: getData) {
|
||||
if (key.includes("*")) {
|
||||
const keys = await redis.keys(key);
|
||||
if (keys.length > 0) {
|
||||
for (const key of keys) {
|
||||
const val = await redis.get(key);
|
||||
if (value == val) return value;
|
||||
const val = await redis.get(key);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ class Message extends Model<Message> {
|
|||
@Column
|
||||
fromMe: boolean;
|
||||
|
||||
@Default(false)
|
||||
@Column
|
||||
fromAgent: boolean;
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
body: string;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ interface MessageData {
|
|||
read?: boolean;
|
||||
mediaType?: string;
|
||||
mediaUrl?: string;
|
||||
fromAgent?: boolean;
|
||||
}
|
||||
interface Request {
|
||||
messageData: MessageData;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({
|
||||
|
@ -30,7 +29,7 @@ const UpdateQueueService = async (
|
|||
const queueWithSameName = await Queue.findOne({
|
||||
where: { name: value, id: { [Op.not]: queueId } }
|
||||
});
|
||||
|
||||
|
||||
return !queueWithSameName;
|
||||
}
|
||||
return true;
|
||||
|
@ -59,24 +58,25 @@ const UpdateQueueService = async (
|
|||
}
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
await queueSchema.validate({ color, name });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
|
||||
const queue = await ShowQueueService(queueId);
|
||||
|
||||
|
||||
await queue.update(queueData);
|
||||
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -29,9 +29,7 @@ const CheckIsValidContact = async (
|
|||
try {
|
||||
let _status: any;
|
||||
|
||||
if (!isValidNumber) {
|
||||
|
||||
console.log('kkkkkkkkkkkkkkkkkkkkkkkkkkkkk ')
|
||||
if (!isValidNumber) {
|
||||
|
||||
const { data, status } = await axios.post(
|
||||
`${process.env.WHATS_NUMBER_VALIDATOR_URL}/api/validate`,
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
Loading…
Reference in New Issue