Merge pull request #342 from dantecvip/master

Fix error CONTACT_NOT_FIND in older VCards and Add Receive Localizations Cards and Others Features
This commit is contained in:
Cassio Santos
2022-01-18 08:36:51 -03:00
committed by GitHub
8 changed files with 187 additions and 68 deletions

View File

@@ -1,5 +1,6 @@
import AppError from "../../errors/AppError";
import Contact from "../../models/Contact";
import CreateContactService from "./CreateContactService";
interface ExtraInfo {
name: string;
@@ -20,10 +21,18 @@ const GetContactService = async ({ name, number }: Request): Promise<Contact> =>
});
if (!numberExists) {
throw new AppError("CONTACT_NOT_FIND");
const contact = await CreateContactService({
name,
number,
})
if (contact == null)
throw new AppError("CONTACT_NOT_FIND")
else
return contact
}
return numberExists;
return numberExists
};
export default GetContactService;

View File

@@ -2,6 +2,7 @@ import { join } from "path";
import { promisify } from "util";
import { writeFile } from "fs";
import * as Sentry from "@sentry/node";
import {
Contact as WbotContact,
Message as WbotMessage,
@@ -114,6 +115,10 @@ const verifyMessage = async (
ticket: Ticket,
contact: Contact
) => {
if (msg.type === 'location')
msg = prepareLocation(msg);
const quotedMsg = await verifyQuotedMessage(msg);
const messageData = {
id: msg.id.id,
@@ -126,11 +131,21 @@ const verifyMessage = async (
quotedMsgId: quotedMsg?.id
};
await ticket.update({ lastMessage: msg.body });
await ticket.update({ lastMessage: msg.type === "location" ? msg.location.description ? "Localization - " + msg.location.description.split('\\n')[0] : "Localization" : msg.body });
await CreateMessageService({ messageData });
};
const prepareLocation = (msg: WbotMessage): WbotMessage => {
let gmapsUrl = "https://maps.google.com/maps?q=" + msg.location.latitude + "%2C" + msg.location.longitude + "&z=17&hl=pt-BR";
msg.body = "data:image/png;base64," + msg.body + "|" + gmapsUrl;
msg.body += "|" + (msg.location.description ? msg.location.description : (msg.location.latitude + ", " + msg.location.longitude))
return msg;
};
const verifyQueue = async (
wbot: Session,
msg: WbotMessage,
@@ -198,8 +213,9 @@ const isValidMsg = (msg: WbotMessage): boolean => {
msg.type === "image" ||
msg.type === "document" ||
msg.type === "vcard" ||
// msg.type === "multi_vcard" ||
msg.type === "sticker"
//msg.type === "multi_vcard" ||
msg.type === "sticker" ||
msg.type === "location"
)
return true;
return false;
@@ -225,13 +241,9 @@ 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"
// && msg.type !== "multi_vcard"
)
return;
if (!msg.hasMedia && msg.type !== "location" && msg.type !== "chat" && msg.type !== "vcard"
//&& msg.type !== "multi_vcard"
) return;
msgContact = await wbot.getContactById(msg.to);
} else {

View File

@@ -1 +1,2 @@
REACT_APP_BACKEND_URL = http://localhost:8080/
REACT_APP_BACKEND_URL = http://localhost:8080/
REACT_APP_HOURS_CLOSE_TICKETS_AUTO =

View File

@@ -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 (
<>
<div style={{
minWidth: "250px",
}}>
<div>
<div style={{ float: "left" }}>
<img src={image} onClick={handleLocation} style={{ width: "100px" }} />
</div>
{ description && (
<div style={{ display: "flex", flexWrap: "wrap" }}>
<Typography style={{ marginTop: "12px", marginLeft: "15px", marginRight: "15px", float: "left" }} variant="subtitle1" color="primary" gutterBottom>
<div dangerouslySetInnerHTML={{ __html: description.replace('\\n', '<br />') }}></div>
</Typography>
</div>
)}
<div style={{ display: "block", content: "", clear: "both" }}></div>
<div>
<Divider />
<Button
fullWidth
color="primary"
onClick={handleLocation}
disabled={!link}
>Visualizar</Button>
</div>
</div>
</div>
</>
);
};
export default LocationPreview;

View File

@@ -153,10 +153,13 @@ const MarkdownWrapper = ({ children }) => {
const boldRegex = /\*(.*?)\*/g;
const tildaRegex = /~(.*?)~/g;
if(children.includes('BEGIN:VCARD'))
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**");
}

View File

@@ -23,6 +23,7 @@ import {
import MarkdownWrapper from "../MarkdownWrapper";
import VcardPreview from "../VcardPreview";
import LocationPreview from "../LocationPreview";
import ModalImageCors from "../ModalImageCors";
import MessageOptionsMenu from "../MessageOptionsMenu";
import whatsBackground from "../../assets/wa-background.png";
@@ -414,7 +415,19 @@ const MessagesList = ({ ticketId, isGroup }) => {
};
const checkMessageMedia = (message) => {
if (message.mediaType === "vcard") {
if(message.mediaType === "location" && message.body.split('|').length >= 2) {
let locationParts = message.body.split('|')
let imageLocation = locationParts[0]
let linkLocation = locationParts[1]
let descriptionLocation = null
if(locationParts.length > 2)
descriptionLocation = message.body.split('|')[2]
return <LocationPreview image={imageLocation} link={linkLocation} description={descriptionLocation} />
}
else if (message.mediaType === "vcard") {
//console.log("vcard")
//console.log(message)
let array = message.body.split("\n");
@@ -604,7 +617,7 @@ const MessagesList = ({ ticketId, isGroup }) => {
{message.contact?.name}
</span>
)}
{(message.mediaUrl || message.mediaType === "vcard"
{(message.mediaUrl || message.mediaType === "location" || message.mediaType === "vcard"
//|| message.mediaType === "multi_vcard"
) && checkMessageMedia(message)}
<div className={classes.textContentItem}>
@@ -633,7 +646,7 @@ const MessagesList = ({ ticketId, isGroup }) => {
>
<ExpandMore />
</IconButton>
{(message.mediaUrl || message.mediaType === "vcard"
{(message.mediaUrl || message.mediaType === "location" || message.mediaType === "vcard"
//|| message.mediaType === "multi_vcard"
) && checkMessageMedia(message)}
<div

View File

@@ -4,56 +4,84 @@ import toastError from "../../errors/toastError";
import api from "../../services/api";
const useTickets = ({
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
}) => {
const [loading, setLoading] = useState(true);
const [hasMore, setHasMore] = useState(false);
const [tickets, setTickets] = useState([]);
const [loading, setLoading] = useState(true);
const [hasMore, setHasMore] = useState(false);
const [tickets, setTickets] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => {
setLoading(true);
const delayDebounceFn = setTimeout(() => {
const fetchTickets = async () => {
try {
const { data } = await api.get("/tickets", {
params: {
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
},
});
setTickets(data.tickets);
setHasMore(data.hasMore);
setLoading(false);
} catch (err) {
setLoading(false);
toastError(err);
}
};
fetchTickets();
}, 500);
return () => clearTimeout(delayDebounceFn);
}, [
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
]);
useEffect(() => {
setLoading(true);
const delayDebounceFn = setTimeout(() => {
const fetchTickets = async() => {
try {
const { data } = await api.get("/tickets", {
params: {
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
},
})
setTickets(data.tickets)
return { tickets, loading, hasMore };
let horasFecharAutomaticamente = process.env.REACT_APP_HOURS_CLOSE_TICKETS_AUTO
if (status === "open" && horasFecharAutomaticamente && horasFecharAutomaticamente !== "" &&
horasFecharAutomaticamente !== "0" && Number(horasFecharAutomaticamente) > 0) {
let dataLimite = new Date()
dataLimite.setHours(dataLimite.getHours() - Number(horasFecharAutomaticamente))
data.tickets.forEach(ticket => {
if (ticket.status !== "closed") {
let dataUltimaInteracaoChamado = new Date(ticket.updatedAt)
if (dataUltimaInteracaoChamado < dataLimite)
closeTicket(ticket)
}
})
}
setHasMore(data.hasMore)
setCount(data.count)
setLoading(false)
} catch (err) {
setLoading(false)
toastError(err)
}
}
const closeTicket = async(ticket) => {
await api.put(`/tickets/${ticket.id}`, {
status: "closed",
userId: ticket.userId || null,
})
}
fetchTickets()
}, 500)
return () => clearTimeout(delayDebounceFn)
}, [
searchParam,
pageNumber,
status,
date,
showAll,
queueIds,
withUnreadMessages,
])
return { tickets, loading, hasMore, count };
};
export default useTickets;
export default useTickets;

View File

@@ -54,13 +54,13 @@ const Dashboard = () => {
const GetTickets = (status, showAll, withUnreadMessages) => {
const { tickets } = useTickets({
const { count } = useTickets({
status: status,
showAll: showAll,
withUnreadMessages: withUnreadMessages,
queueIds: JSON.stringify(userQueueIds)
});
return tickets.length;
return count;
}
return (
@@ -114,4 +114,4 @@ const Dashboard = () => {
)
}
export default Dashboard
export default Dashboard