diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index 7ab9363..1b194a1 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -20,12 +20,18 @@ import { } from "../helpers/ContactsCache"; import { off } from "process"; +import GetContactService from "../services/ContactServices/GetContactService" type IndexQuery = { searchParam: string; pageNumber: string; }; +type IndexGetContactQuery = { + name: string; + number: string; +}; + interface ExtraInfo { name: string; value: string; @@ -84,6 +90,20 @@ export const index = async (req: Request, res: Response): Promise => { return res.json({ contacts, count, hasMore }); }; +export const getContact = async ( + req: Request, + res: Response +): Promise => { + const { name, number } = req.body as IndexGetContactQuery; + + const contact = await GetContactService({ + name, + number + }); + + return res.status(200).json(contact); +}; + export const store = async (req: Request, res: Response): Promise => { const newContact: ContactData = req.body; newContact.number = newContact.number.replace("-", "").replace(" ", ""); diff --git a/backend/src/helpers/CheckContactOpenTickets.ts b/backend/src/helpers/CheckContactOpenTickets.ts index 6cfc24d..303f4a7 100644 --- a/backend/src/helpers/CheckContactOpenTickets.ts +++ b/backend/src/helpers/CheckContactOpenTickets.ts @@ -15,9 +15,9 @@ const CheckContactOpenTickets = async ( if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") { let whats = await ListWhatsAppsNumber(whatsappId); - console.log("contactId: ", contactId, " | whatsappId: ", whatsappId); + // console.log("contactId: ", contactId, " | whatsappId: ", whatsappId); - console.log("WHATS: ", JSON.stringify(whats, null, 6)); + // console.log("WHATS: ", JSON.stringify(whats, null, 6)); ticket = await Ticket.findOne({ where: { diff --git a/backend/src/routes/contactRoutes.ts b/backend/src/routes/contactRoutes.ts index 158159c..8c77791 100644 --- a/backend/src/routes/contactRoutes.ts +++ b/backend/src/routes/contactRoutes.ts @@ -16,6 +16,8 @@ contactRoutes.get("/contacts/:contactId", isAuth, ContactController.show); contactRoutes.post("/contacts", isAuth, ContactController.store); +contactRoutes.post("/contact", isAuth, ContactController.getContact); + contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update); contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove); diff --git a/backend/src/services/ContactServices/GetContactService.ts b/backend/src/services/ContactServices/GetContactService.ts new file mode 100644 index 0000000..3865bc5 --- /dev/null +++ b/backend/src/services/ContactServices/GetContactService.ts @@ -0,0 +1,38 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import CreateContactService from "./CreateContactService"; + +interface ExtraInfo { + name: string; + value: string; +} + +interface Request { + name: string; + number: string; + email?: string; + profilePicUrl?: string; + extraInfo?: ExtraInfo[]; +} + +const GetContactService = async ({ name, number }: Request): Promise => { + const numberExists = await Contact.findOne({ + where: { number } + }); + + if (!numberExists) { + const contact = await CreateContactService({ + name, + number, + }) + + if (contact == null) + throw new AppError("CONTACT_NOT_FIND") + else + return contact + } + + return numberExists +}; + +export default GetContactService; \ No newline at end of file diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 55f0552..bb1c57e 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -98,6 +98,7 @@ import FindOrCreateTicketServiceBot from "../TicketServices/FindOrCreateTicketSe import ShowTicketService from "../TicketServices/ShowTicketService"; import ShowQueuesByUser from "../UserServices/ShowQueuesByUser"; import ListWhatsappQueuesByUserQueue from "../UserServices/ListWhatsappQueuesByUserQueue"; +import CreateContactService from "../ContactServices/CreateContactService" var lst: any[] = getWhatsappIds(); @@ -479,6 +480,7 @@ const isValidMsg = (msg: any): boolean => { msg.type === "image" || msg.type === "document" || msg.type === "vcard" || + // msg.type === "multi_vcard" || msg.type === "sticker" ) return true; @@ -608,7 +610,7 @@ const handleMessage = async ( return; } - } + } if (!isValidMsg(msg)) { return; @@ -643,7 +645,13 @@ const handleMessage = async ( // 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; + if ( + !msg.hasMedia && + msg.type !== "chat" && + msg.type !== "vcard" && + msg.type !== "multi_vcard" + ) + return; } else { console.log(`\n <<<<<<<<<< RECEIVING MESSAGE: Parcial msg and msgContact info: @@ -772,6 +780,34 @@ const handleMessage = async ( await verifyQueue(wbot, msg, ticket, contact); } + if (msg.type === "vcard") { + try { + const array = msg.body.split("\n"); + const obj = []; + let contact = ""; + for (let index = 0; index < array.length; index++) { + const v = array[index]; + const values = v.split(":"); + for (let ind = 0; ind < values.length; ind++) { + if (values[ind].indexOf("+") !== -1) { + obj.push({ number: values[ind] }); + } + if (values[ind].indexOf("FN") !== -1) { + contact = values[ind + 1]; + } + } + } + for await (const ob of obj) { + const cont = await CreateContactService({ + name: contact, + number: ob.number.replace(/\D/g, "") + }); + } + } catch (error) { + console.log(error); + } + } + const botInfo = await BotIsOnQueue("botqueue"); // Transfer to agent @@ -784,9 +820,7 @@ const handleMessage = async ( ticket.status == "pending" || ticket.status == "queueChoice") ) { - const filteredUsers = await findByContain("user:*", "name", msg?.body); - - console.log("######## filteredUsers: ", filteredUsers); + const filteredUsers = await findByContain("user:*", "name", msg?.body); if (filteredUsers && filteredUsers.length > 0) { if (botInfo.isOnQueue) { diff --git a/frontend/src/components/Audio/index.jsx b/frontend/src/components/Audio/index.jsx new file mode 100644 index 0000000..cc0c7cc --- /dev/null +++ b/frontend/src/components/Audio/index.jsx @@ -0,0 +1,62 @@ +import { Button } from "@material-ui/core"; +import React, { useRef } from "react"; +import { useEffect } from "react"; +import { useState } from "react"; + +const LS_NAME = 'audioMessageRate'; + +export default function({url}) { + const audioRef = useRef(null); + const [audioRate, setAudioRate] = useState( parseFloat(localStorage.getItem(LS_NAME) || "1") ); + const [showButtonRate, setShowButtonRate] = useState(false); + + useEffect(() => { + audioRef.current.playbackRate = audioRate; + localStorage.setItem(LS_NAME, audioRate); + }, [audioRate]); + + useEffect(() => { + audioRef.current.onplaying = () => { + setShowButtonRate(true); + }; + audioRef.current.onpause = () => { + setShowButtonRate(false); + }; + audioRef.current.onended = () => { + setShowButtonRate(false); + }; + }, []); + + const toogleRate = () => { + let newRate = null; + + switch(audioRate) { + case 0.5: + newRate = 1; + break; + case 1: + newRate = 1.5; + break; + case 1.5: + newRate = 2; + break; + case 2: + newRate = 0.5; + break; + default: + newRate = 1; + break; + } + + setAudioRate(newRate); + }; + + return ( + <> + + {showButtonRate && } + + ); +} \ No newline at end of file diff --git a/frontend/src/components/LocationPreview/index.js b/frontend/src/components/LocationPreview/index.js new file mode 100644 index 0000000..9187faf --- /dev/null +++ b/frontend/src/components/LocationPreview/index.js @@ -0,0 +1,53 @@ +import React, { useEffect } from 'react'; +import toastError from "../../errors/toastError"; + +import Typography from "@material-ui/core/Typography"; +import Grid from "@material-ui/core/Grid"; + +import { Button, Divider, } from "@material-ui/core"; + +const LocationPreview = ({ image, link, description }) => { + useEffect(() => {}, [image, link, description]); + + const handleLocation = async() => { + try { + window.open(link); + } catch (err) { + toastError(err); + } + } + + return ( + <> +
+
+
+ +
+ { description && ( +
+ +
') }}>
+
+
+ )} +
+
+ + +
+
+
+ + ); + +}; + +export default LocationPreview; \ No newline at end of file diff --git a/frontend/src/components/MarkdownWrapper/index.js b/frontend/src/components/MarkdownWrapper/index.js index 64764b2..3d2c01b 100644 --- a/frontend/src/components/MarkdownWrapper/index.js +++ b/frontend/src/components/MarkdownWrapper/index.js @@ -1,5 +1,5 @@ -import React from "react"; -import Markdown from "markdown-to-jsx"; +import React from "react" +import Markdown from "markdown-to-jsx" const elements = [ "a", @@ -139,25 +139,32 @@ const elements = [ "svg", "text", "tspan", -]; +] -const allowedElements = ["a", "b", "strong", "em", "u", "code", "del"]; +const allowedElements = ["a", "b", "strong", "em", "u", "code", "del"] const CustomLink = ({ children, ...props }) => ( {children} -); +) const MarkdownWrapper = ({ children }) => { - const boldRegex = /\*(.*?)\*/g; - const tildaRegex = /~(.*?)~/g; + const boldRegex = /\*(.*?)\*/g + const tildaRegex = /~(.*?)~/g + + if (children && children.includes('BEGIN:VCARD')) + //children = "Diga olá ao seu novo contato clicando em *conversar*!"; + children = null + + if (children && children.includes('data:image/')) + children = null if (children && boldRegex.test(children)) { - children = children.replace(boldRegex, "**$1**"); + children = children.replace(boldRegex, "**$1**") } if (children && tildaRegex.test(children)) { - children = children.replace(tildaRegex, "~~$1~~"); + children = children.replace(tildaRegex, "~~$1~~") } const options = React.useMemo(() => { @@ -167,20 +174,20 @@ const MarkdownWrapper = ({ children }) => { overrides: { a: { component: CustomLink }, }, - }; + } elements.forEach(element => { if (!allowedElements.includes(element)) { - markdownOptions.overrides[element] = el => el.children || null; + markdownOptions.overrides[element] = el => el.children || null } - }); + }) - return markdownOptions; - }, []); + return markdownOptions + }, []) - if (!children) return null; + if (!children) return null - return {children}; -}; + return {children} +} -export default MarkdownWrapper; +export default MarkdownWrapper \ No newline at end of file diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 236601a..9d25471 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -23,6 +23,11 @@ import { } from "@material-ui/icons"; import MarkdownWrapper from "../MarkdownWrapper"; +import VcardPreview from "../VcardPreview"; +import LocationPreview from "../LocationPreview"; +import Audio from "../Audio"; + + import ModalImageCors from "../ModalImageCors"; import MessageOptionsMenu from "../MessageOptionsMenu"; import whatsBackground from "../../assets/wa-background.png"; @@ -488,27 +493,109 @@ const MessagesList = ({ ticketId, isGroup }) => { setAnchorEl(null); }; + // const checkMessageMedia = (message) => { + // if (message.mediaType === "image") { + // return ; + // } + // if (message.mediaType === "audio") { + + // return ( + // + // ); + // } + + // if (message.mediaType === "video") { + // return ( + //