mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-18 19:59:20 +00:00
feat: start adding auto reply
This commit is contained in:
32
backend/src/services/QueueService/AssociateWhatsappQueue.ts
Normal file
32
backend/src/services/QueueService/AssociateWhatsappQueue.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import WhatsappQueue from "../../models/WhatsappQueue";
|
||||
|
||||
interface QueueData {
|
||||
id: number;
|
||||
optionNumber: number;
|
||||
}
|
||||
|
||||
const AssociateWhatsappQueue = async (
|
||||
whatsapp: Whatsapp,
|
||||
queuesData: QueueData[]
|
||||
): Promise<void> => {
|
||||
const queueIds = queuesData.map(({ id }) => id);
|
||||
|
||||
await whatsapp.$set("queues", queueIds);
|
||||
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
for (const queueData of queuesData) {
|
||||
await WhatsappQueue.update(
|
||||
{ optionNumber: queueData.optionNumber },
|
||||
{
|
||||
where: {
|
||||
whatsappId: whatsapp.id,
|
||||
queueId: queueData.id
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default AssociateWhatsappQueue;
|
||||
@@ -16,23 +16,19 @@ const FindOrCreateTicketService = async (
|
||||
[Op.or]: ["open", "pending"]
|
||||
},
|
||||
contactId: groupContact ? groupContact.id : contact.id
|
||||
},
|
||||
include: ["contact"]
|
||||
}
|
||||
});
|
||||
|
||||
if (ticket) {
|
||||
await ticket.update({ unreadMessages });
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
if (groupContact) {
|
||||
if (!ticket && groupContact) {
|
||||
ticket = await Ticket.findOne({
|
||||
where: {
|
||||
contactId: groupContact.id
|
||||
},
|
||||
order: [["updatedAt", "DESC"]],
|
||||
include: ["contact"]
|
||||
order: [["updatedAt", "DESC"]]
|
||||
});
|
||||
|
||||
if (ticket) {
|
||||
@@ -41,10 +37,10 @@ const FindOrCreateTicketService = async (
|
||||
userId: null,
|
||||
unreadMessages
|
||||
});
|
||||
|
||||
return ticket;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!ticket && !groupContact) {
|
||||
ticket = await Ticket.findOne({
|
||||
where: {
|
||||
updatedAt: {
|
||||
@@ -52,8 +48,7 @@ const FindOrCreateTicketService = async (
|
||||
},
|
||||
contactId: contact.id
|
||||
},
|
||||
order: [["updatedAt", "DESC"]],
|
||||
include: ["contact"]
|
||||
order: [["updatedAt", "DESC"]]
|
||||
});
|
||||
|
||||
if (ticket) {
|
||||
@@ -62,20 +57,20 @@ const FindOrCreateTicketService = async (
|
||||
userId: null,
|
||||
unreadMessages
|
||||
});
|
||||
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
|
||||
const { id } = await Ticket.create({
|
||||
contactId: groupContact ? groupContact.id : contact.id,
|
||||
status: "pending",
|
||||
isGroup: !!groupContact,
|
||||
unreadMessages,
|
||||
whatsappId
|
||||
});
|
||||
if (!ticket) {
|
||||
ticket = await Ticket.create({
|
||||
contactId: groupContact ? groupContact.id : contact.id,
|
||||
status: "pending",
|
||||
isGroup: !!groupContact,
|
||||
unreadMessages,
|
||||
whatsappId
|
||||
});
|
||||
}
|
||||
|
||||
ticket = await ShowTicketService(id);
|
||||
ticket = await ShowTicketService(ticket.id);
|
||||
|
||||
return ticket;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import Ticket from "../../models/Ticket";
|
||||
import AppError from "../../errors/AppError";
|
||||
import Contact from "../../models/Contact";
|
||||
import User from "../../models/User";
|
||||
import Queue from "../../models/Queue";
|
||||
|
||||
const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
||||
const ticket = await Ticket.findByPk(id, {
|
||||
@@ -16,6 +17,11 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
||||
model: User,
|
||||
as: "user",
|
||||
attributes: ["id", "name"]
|
||||
},
|
||||
{
|
||||
model: Queue,
|
||||
as: "queue",
|
||||
attributes: ["id", "name", "color"]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import ListWhatsAppsService from "../WhatsappService/ListWhatsAppsService";
|
||||
import { StartWhatsAppSession } from "./StartWhatsAppSession";
|
||||
|
||||
export const StartAllWhatsAppsSessions = async (): Promise<void> => {
|
||||
const whatsapps = await Whatsapp.findAll();
|
||||
const whatsapps = await ListWhatsAppsService();
|
||||
if (whatsapps.length > 0) {
|
||||
whatsapps.forEach(whatsapp => {
|
||||
StartWhatsAppSession(whatsapp);
|
||||
|
||||
@@ -19,6 +19,7 @@ import CreateMessageService from "../MessageServices/CreateMessageService";
|
||||
import { logger } from "../../utils/logger";
|
||||
import CreateOrUpdateContactService from "../ContactServices/CreateOrUpdateContactService";
|
||||
import FindOrCreateTicketService from "../TicketServices/FindOrCreateTicketService";
|
||||
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
||||
|
||||
interface Session extends Client {
|
||||
id?: number;
|
||||
@@ -126,6 +127,54 @@ const verifyMessage = async (
|
||||
await CreateMessageService({ messageData });
|
||||
};
|
||||
|
||||
const verifyQueue = async (
|
||||
wbot: Session,
|
||||
msg: WbotMessage,
|
||||
ticket: Ticket,
|
||||
contact: Contact
|
||||
) => {
|
||||
const { whatsappQueues, greetingMessage } = await ShowWhatsAppService(
|
||||
wbot.id!
|
||||
);
|
||||
|
||||
if (whatsappQueues.length === 1) {
|
||||
await ticket.$set("queue", whatsappQueues[0].queue);
|
||||
// TODO sendTicketQueueUpdate to frontend
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedOption = msg.body[0];
|
||||
|
||||
const validOption = whatsappQueues.find(
|
||||
q => q.optionNumber === +selectedOption
|
||||
);
|
||||
|
||||
if (validOption) {
|
||||
await ticket.$set("queue", validOption.queue);
|
||||
|
||||
const body = `\u200e ${validOption.queue.greetingMessage}`;
|
||||
|
||||
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body);
|
||||
|
||||
await verifyMessage(sentMessage, ticket, contact);
|
||||
|
||||
// TODO sendTicketQueueUpdate to frontend
|
||||
} else {
|
||||
let options = "";
|
||||
|
||||
whatsappQueues.forEach(whatsQueue => {
|
||||
options += `*${whatsQueue.optionNumber}* - ${whatsQueue.queue.name}\n`;
|
||||
});
|
||||
|
||||
const body = `\u200e ${greetingMessage}\n\n${options}`;
|
||||
|
||||
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body);
|
||||
|
||||
await verifyMessage(sentMessage, ticket, contact);
|
||||
}
|
||||
};
|
||||
|
||||
const isValidMsg = (msg: WbotMessage): boolean => {
|
||||
if (msg.from === "status@broadcast") return false;
|
||||
if (
|
||||
@@ -146,64 +195,64 @@ const handleMessage = async (
|
||||
msg: WbotMessage,
|
||||
wbot: Session
|
||||
): Promise<void> => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
(async () => {
|
||||
if (!isValidMsg(msg)) {
|
||||
return;
|
||||
if (!isValidMsg(msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let msgContact: WbotContact;
|
||||
let groupContact: Contact | undefined;
|
||||
|
||||
if (msg.fromMe) {
|
||||
// messages sent automatically by wbot have a special character in front of it
|
||||
// if so, this message was already been stored in database;
|
||||
if (/\u200e/.test(msg.body[0])) return;
|
||||
|
||||
// media messages sent from me from cell phone, first comes with "hasMedia = false" and type = "image/ptt/etc"
|
||||
// in this case, return and let this message be handled by "media_uploaded" event, when it will have "hasMedia = true"
|
||||
|
||||
if (!msg.hasMedia && msg.type !== "chat" && msg.type !== "vcard") return;
|
||||
|
||||
msgContact = await wbot.getContactById(msg.to);
|
||||
} else {
|
||||
msgContact = await msg.getContact();
|
||||
}
|
||||
|
||||
const chat = await msg.getChat();
|
||||
|
||||
if (chat.isGroup) {
|
||||
let msgGroupContact;
|
||||
|
||||
if (msg.fromMe) {
|
||||
msgGroupContact = await wbot.getContactById(msg.to);
|
||||
} else {
|
||||
msgGroupContact = await wbot.getContactById(msg.from);
|
||||
}
|
||||
|
||||
try {
|
||||
let msgContact: WbotContact;
|
||||
let groupContact: Contact | undefined;
|
||||
groupContact = await verifyContact(msgGroupContact);
|
||||
}
|
||||
|
||||
if (msg.fromMe) {
|
||||
// media messages sent from me from cell phone, first comes with "hasMedia = false" and type = "image/ptt/etc"
|
||||
// in this case, return and let this message be handled by "media_uploaded" event, when it will have "hasMedia = true"
|
||||
const contact = await verifyContact(msgContact);
|
||||
const ticket = await FindOrCreateTicketService(
|
||||
contact,
|
||||
wbot.id!,
|
||||
chat.unreadCount,
|
||||
groupContact
|
||||
);
|
||||
|
||||
if (!msg.hasMedia && msg.type !== "chat" && msg.type !== "vcard")
|
||||
return;
|
||||
if (msg.hasMedia) {
|
||||
await verifyMediaMessage(msg, ticket, contact);
|
||||
} else {
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
}
|
||||
|
||||
msgContact = await wbot.getContactById(msg.to);
|
||||
} else {
|
||||
msgContact = await msg.getContact();
|
||||
}
|
||||
|
||||
const chat = await msg.getChat();
|
||||
|
||||
if (chat.isGroup) {
|
||||
let msgGroupContact;
|
||||
|
||||
if (msg.fromMe) {
|
||||
msgGroupContact = await wbot.getContactById(msg.to);
|
||||
} else {
|
||||
msgGroupContact = await wbot.getContactById(msg.from);
|
||||
}
|
||||
|
||||
groupContact = await verifyContact(msgGroupContact);
|
||||
}
|
||||
|
||||
const contact = await verifyContact(msgContact);
|
||||
const ticket = await FindOrCreateTicketService(
|
||||
contact,
|
||||
wbot.id!,
|
||||
chat.unreadCount,
|
||||
groupContact
|
||||
);
|
||||
|
||||
if (msg.hasMedia) {
|
||||
await verifyMediaMessage(msg, ticket, contact);
|
||||
resolve();
|
||||
} else {
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
resolve();
|
||||
}
|
||||
} catch (err) {
|
||||
Sentry.captureException(err);
|
||||
logger.error(`Error handling whatsapp message: Err: ${err}`);
|
||||
reject(err);
|
||||
}
|
||||
})();
|
||||
});
|
||||
if (!ticket.queue && !chat.isGroup && !msg.fromMe) {
|
||||
await verifyQueue(wbot, msg, ticket, contact);
|
||||
}
|
||||
} catch (err) {
|
||||
Sentry.captureException(err);
|
||||
logger.error(`Error handling whatsapp message: Err: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMsgAck = async (msg: WbotMessage, ack: MessageAck) => {
|
||||
|
||||
@@ -2,10 +2,16 @@ import * as Yup from "yup";
|
||||
|
||||
import AppError from "../../errors/AppError";
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import AssociateWhatsappQueue from "../QueueService/AssociateWhatsappQueue";
|
||||
|
||||
interface QueueData {
|
||||
id: number;
|
||||
optionNumber: number;
|
||||
}
|
||||
interface Request {
|
||||
name: string;
|
||||
queueIds: number[];
|
||||
queuesData: QueueData[];
|
||||
greetingMessage?: string;
|
||||
status?: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
@@ -18,7 +24,8 @@ interface Response {
|
||||
const CreateWhatsAppService = async ({
|
||||
name,
|
||||
status = "OPENING",
|
||||
queueIds = [],
|
||||
queuesData = [],
|
||||
greetingMessage,
|
||||
isDefault = false
|
||||
}: Request): Promise<Response> => {
|
||||
const schema = Yup.object().shape({
|
||||
@@ -68,12 +75,13 @@ const CreateWhatsAppService = async ({
|
||||
{
|
||||
name,
|
||||
status,
|
||||
greetingMessage,
|
||||
isDefault
|
||||
},
|
||||
{ include: ["queues"] }
|
||||
);
|
||||
|
||||
await whatsapp.$set("queues", queueIds);
|
||||
await AssociateWhatsappQueue(whatsapp, queuesData);
|
||||
|
||||
await whatsapp.reload();
|
||||
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import Queue from "../../models/Queue";
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import WhatsappQueue from "../../models/WhatsappQueue";
|
||||
|
||||
const ListWhatsAppsService = async (): Promise<Whatsapp[]> => {
|
||||
const whatsapps = await Whatsapp.findAll({
|
||||
include: [
|
||||
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }
|
||||
]
|
||||
{
|
||||
model: WhatsappQueue,
|
||||
as: "whatsappQueues",
|
||||
attributes: ["optionNumber"],
|
||||
include: [
|
||||
{
|
||||
model: Queue,
|
||||
as: "queue",
|
||||
attributes: ["id", "name", "color", "greetingMessage"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
order: [["whatsappQueues", "optionNumber", "ASC"]]
|
||||
});
|
||||
|
||||
return whatsapps;
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import AppError from "../../errors/AppError";
|
||||
import Queue from "../../models/Queue";
|
||||
import WhatsappQueue from "../../models/WhatsappQueue";
|
||||
|
||||
const ShowWhatsAppService = async (id: string | number): Promise<Whatsapp> => {
|
||||
const whatsapp = await Whatsapp.findByPk(id, {
|
||||
include: [
|
||||
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }
|
||||
]
|
||||
{
|
||||
model: WhatsappQueue,
|
||||
as: "whatsappQueues",
|
||||
attributes: ["optionNumber"],
|
||||
include: [
|
||||
{
|
||||
model: Queue,
|
||||
as: "queue",
|
||||
attributes: ["id", "name", "color", "greetingMessage"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
order: [["whatsappQueues", "optionNumber", "ASC"]]
|
||||
});
|
||||
|
||||
if (!whatsapp) {
|
||||
|
||||
@@ -4,13 +4,19 @@ import { Op } from "sequelize";
|
||||
import AppError from "../../errors/AppError";
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import ShowWhatsAppService from "./ShowWhatsAppService";
|
||||
import AssociateWhatsappQueue from "../QueueService/AssociateWhatsappQueue";
|
||||
|
||||
interface QueueData {
|
||||
id: number;
|
||||
optionNumber: number;
|
||||
}
|
||||
interface WhatsappData {
|
||||
name?: string;
|
||||
status?: string;
|
||||
session?: string;
|
||||
isDefault?: boolean;
|
||||
queueIds?: number[];
|
||||
greetingMessage?: string;
|
||||
queuesData?: QueueData[];
|
||||
}
|
||||
|
||||
interface Request {
|
||||
@@ -32,7 +38,14 @@ const UpdateWhatsAppService = async ({
|
||||
isDefault: Yup.boolean()
|
||||
});
|
||||
|
||||
const { name, status, isDefault, session, queueIds = [] } = whatsappData;
|
||||
const {
|
||||
name,
|
||||
status,
|
||||
isDefault,
|
||||
session,
|
||||
greetingMessage,
|
||||
queuesData = []
|
||||
} = whatsappData;
|
||||
|
||||
try {
|
||||
await schema.validate({ name, status, isDefault });
|
||||
@@ -57,10 +70,11 @@ const UpdateWhatsAppService = async ({
|
||||
name,
|
||||
status,
|
||||
session,
|
||||
greetingMessage,
|
||||
isDefault
|
||||
});
|
||||
|
||||
await whatsapp.$set("queues", queueIds);
|
||||
await AssociateWhatsappQueue(whatsapp, queuesData);
|
||||
|
||||
await whatsapp.reload();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user