fix: Correct bug in editing official WhatsApp and implement solution to validate number using remote WhatsApp API

feat-scaling-ticket-remote-creation
adriano 2024-03-06 11:00:03 -03:00
parent 6c5b89fd28
commit 943d121ab1
8 changed files with 158 additions and 116 deletions

View File

@ -126,21 +126,12 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
const validNumber = await CheckIsValidContact(newContact.number); const validNumber = await CheckIsValidContact(newContact.number);
// const validNumber: any = await CheckContactNumber(newContact.number)
if (!validNumber) { if (!validNumber) {
throw new AppError("ERR_WAPP_CHECK_CONTACT"); throw new AppError("ERR_WAPP_CHECK_CONTACT");
} }
const profilePicUrl = await GetProfilePicUrl(validNumber); const profilePicUrl = await GetProfilePicUrl(validNumber);
console.log("xxxxxxxxxxx profilePicUrl: ", profilePicUrl);
// console.log(`newContact.name: ${newContact.name}\n
// newContact.number: ${newContact.number}\n
// newContact.email: ${newContact.email}\n
// newContact.extraInfo: ${newContact.extraInfo}`)
let name = newContact.name; let name = newContact.name;
let number = validNumber; let number = validNumber;
let email = newContact.email; let email = newContact.email;

View File

@ -421,13 +421,14 @@ export const update = async (
return res.status(200).json({ message: invalidPhoneName }); return res.status(200).json({ message: invalidPhoneName });
} }
const { urlApi, isOfficial, phoneNumberId, wabaId } = whatsappData; const { urlApi, isOfficial, phoneNumberId, number, wabaId } = whatsappData;
const invalid = checkWhatsAppData({ const invalid = checkWhatsAppData({
urlApi, urlApi,
isOfficial, isOfficial,
phoneNumberId, phoneNumberId,
wabaId wabaId,
number
}); });
if (invalid) { if (invalid) {

View File

@ -13,20 +13,19 @@ import { WhatsIndex } from "./LoadBalanceWhatsSameQueue";
interface Request { interface Request {
userId?: string | number; userId?: string | number;
queueId?: string | number; queueId?: string | number;
ignoreNoWhatsappFound?: boolean
} }
//const GetDefaultWhatsApp = async (userId?: string | number): Promise<Whatsapp> => {
const GetDefaultWhatsApp = async ({ const GetDefaultWhatsApp = async ({
userId, userId,
queueId queueId,
ignoreNoWhatsappFound = false
}: Request): Promise<any> => { }: Request): Promise<any> => {
// test del
let defaultWhatsapp = await Whatsapp.findOne({ let defaultWhatsapp = await Whatsapp.findOne({
where: { isDefault: true } where: { isDefault: true }
}); });
if (!defaultWhatsapp) { if (!defaultWhatsapp && !ignoreNoWhatsappFound) {
if (userId) { if (userId) {
let whatsapps = await wbotByUserQueue({ userId, queueId }); let whatsapps = await wbotByUserQueue({ userId, queueId });
@ -56,17 +55,16 @@ const GetDefaultWhatsApp = async ({
} }
} else { } else {
defaultWhatsapp = await Whatsapp.findOne({ defaultWhatsapp = await Whatsapp.findOne({
where: { status: "CONNECTED" } where: { status: "CONNECTED", isOfficial: false }
}); });
} }
} }
if (!defaultWhatsapp) { if (!defaultWhatsapp && !ignoreNoWhatsappFound) {
throw new AppError("ERR_NO_DEF_WAPP_FOUND"); throw new AppError("ERR_NO_DEF_WAPP_FOUND");
} }
return defaultWhatsapp; return defaultWhatsapp;
//
}; };
export default GetDefaultWhatsApp; export default GetDefaultWhatsApp;

View File

@ -29,7 +29,8 @@ const FindOrCreateTicketServiceBot = async (
} }
}); });
const { queues, greetingMessage } = await ShowWhatsAppService(whatsappId); const { queues, greetingMessage, phoneNumberId } =
await ShowWhatsAppService(whatsappId);
//Habilitar esse caso queira usar o bot //Habilitar esse caso queira usar o bot
@ -107,7 +108,8 @@ const FindOrCreateTicketServiceBot = async (
userId: botInfo.userIdBot, userId: botInfo.userIdBot,
isGroup: !!groupContact, isGroup: !!groupContact,
unreadMessages, unreadMessages,
whatsappId whatsappId,
phoneNumberId
}); });
console.log('yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy') console.log('yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')

View File

@ -1,27 +1,54 @@
import axios from "axios";
import AppError from "../../errors/AppError"; import AppError from "../../errors/AppError";
import endPointQuery from "../../helpers/EndPointQuery"; import endPointQuery from "../../helpers/EndPointQuery";
import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp";
import { getWbot } from "../../libs/wbot"; import { getWbot } from "../../libs/wbot";
const CheckIsValidContact = async (number: string, ignoreThrow?:boolean): Promise<any> => { const CheckIsValidContact = async (
number: string,
ignoreThrow?: boolean
): Promise<any> => {
const defaultWhatsapp = await GetDefaultWhatsApp({
ignoreNoWhatsappFound: true
});
const defaultWhatsapp = await GetDefaultWhatsApp({}); let isValidNumber;
if (defaultWhatsapp) {
const wbot_url = await getWbot(defaultWhatsapp.id); const wbot_url = await getWbot(defaultWhatsapp.id);
const isValidNumber = await endPointQuery(`${wbot_url}/api/validate`, { mobile: `${number}`, }) let { data } = await endPointQuery(`${wbot_url}/api/validate`, {
mobile: `${number}`
});
if(ignoreThrow) return isValidNumber?.data?.number; if (data?.isValid) {
isValidNumber = data;
// console.log('isValidNumber.data.number: ', isValidNumber.data.number) }
}
try { try {
if (!isValidNumber) {
// const isValidNumber = await wbot.isRegisteredUser(`${number}@c.us`); const { data } = await axios.post(
`${process.env.WHATS_NUMBER_VALIDATOR_URL}/api/validate`,
{ mobile: number },
{
headers: {
"Content-Type": "application/json"
}
}
);
if (!isValidNumber || isValidNumber && !isValidNumber.data.isValid) { isValidNumber = data;
}
if (ignoreThrow) return isValidNumber?.number;
if (!isValidNumber || (isValidNumber && !isValidNumber.isValid)) {
throw new AppError("invalidNumber"); throw new AppError("invalidNumber");
} }
if (isValidNumber && isValidNumber?.isValid) return isValidNumber.number;
} catch (err: any) { } catch (err: any) {
if (err.message === "invalidNumber") { if (err.message === "invalidNumber") {
throw new AppError("ERR_WAPP_INVALID_CONTACT"); throw new AppError("ERR_WAPP_INVALID_CONTACT");
@ -29,10 +56,6 @@ const CheckIsValidContact = async (number: string, ignoreThrow?:boolean): Promis
throw new AppError("ERR_WAPP_CHECK_CONTACT"); throw new AppError("ERR_WAPP_CHECK_CONTACT");
} }
if (isValidNumber && isValidNumber.data.isValid)
return isValidNumber.data.number
}; };
export default CheckIsValidContact; export default CheckIsValidContact;

View File

@ -1,23 +1,47 @@
import axios from "axios";
import endPointQuery from "../../helpers/EndPointQuery"; import endPointQuery from "../../helpers/EndPointQuery";
import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp";
import { getWbot } from "../../libs/wbot"; import { getWbot } from "../../libs/wbot";
const GetProfilePicUrl = async (number: string): Promise<any> => { const GetProfilePicUrl = async (number: string): Promise<any> => {
const defaultWhatsapp = await GetDefaultWhatsApp({
ignoreNoWhatsappFound: true
});
const defaultWhatsapp = await GetDefaultWhatsApp({}); let profilePicUrl;
if (defaultWhatsapp) {
const wbot_url = await getWbot(defaultWhatsapp.id); const wbot_url = await getWbot(defaultWhatsapp.id);
let profilePicUrl = await endPointQuery(`${wbot_url}/api/GetProfilePicUrl`, { number: `${number}`, }) const {data} = await endPointQuery(`${wbot_url}/api/GetProfilePicUrl`, {
number: `${number}`
});
console.log('profilePicUrl.data.data: ', profilePicUrl.data.data) if (data?.data) {
profilePicUrl = data.data;
if (profilePicUrl && profilePicUrl.data.data) { }
return profilePicUrl.data.data;
} }
return null try {
if (!profilePicUrl) {
const { data } = await axios.post(
`${process.env.WHATS_NUMBER_VALIDATOR_URL}/api/GetProfilePicUrl`,
{ number },
{
headers: {
"Content-Type": "application/json"
}
}
);
profilePicUrl = data?.data;
}
if (profilePicUrl) {
return profilePicUrl;
}
} catch (error) {}
return null;
}; };
export default GetProfilePicUrl; export default GetProfilePicUrl;

View File

@ -1,26 +1,26 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react"
import * as Yup from "yup"; import * as Yup from "yup"
import { Formik, FieldArray, Form, Field } from "formik"; import { Formik, FieldArray, Form, Field } from "formik"
import { toast } from "react-toastify"; import { toast } from "react-toastify"
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles"
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors"
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button"
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField"
import Dialog from "@material-ui/core/Dialog"; import Dialog from "@material-ui/core/Dialog"
import DialogActions from "@material-ui/core/DialogActions"; import DialogActions from "@material-ui/core/DialogActions"
import DialogContent from "@material-ui/core/DialogContent"; import DialogContent from "@material-ui/core/DialogContent"
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle"
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography"
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton"
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress"
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n"
import api from "../../services/api"; import api from "../../services/api"
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError"
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
root: { root: {
@ -50,7 +50,7 @@ const useStyles = makeStyles(theme => ({
marginTop: -12, marginTop: -12,
marginLeft: -12, marginLeft: -12,
}, },
})); }))
const ContactSchema = Yup.object().shape({ const ContactSchema = Yup.object().shape({
name: Yup.string() name: Yup.string()
@ -63,72 +63,74 @@ const ContactSchema = Yup.object().shape({
.max(50, "Too Long!"), .max(50, "Too Long!"),
// email: Yup.string().email("Invalid email"), // email: Yup.string().email("Invalid email"),
}); })
const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
const classes = useStyles(); const classes = useStyles()
const isMounted = useRef(true); const isMounted = useRef(true)
const initialState = { const initialState = {
name: "", name: "",
number: "", number: "",
email: "", email: "",
useDialogflow: true, useDialogflow: true,
}; }
const [contact, setContact] = useState(initialState); const [contact, setContact] = useState(initialState)
const [isSaving, setSaving] = useState(false)
useEffect(() => { useEffect(() => {
return () => { return () => {
isMounted.current = false; isMounted.current = false
}; }
}, []); }, [])
useEffect(() => { useEffect(() => {
const fetchContact = async () => { const fetchContact = async () => {
if (initialValues) { if (initialValues) {
setContact(prevState => { setContact(prevState => {
return { ...prevState, ...initialValues }; return { ...prevState, ...initialValues }
}); })
} }
if (!contactId) return; if (!contactId) return
try { try {
const { data } = await api.get(`/contacts/${contactId}`); const { data } = await api.get(`/contacts/${contactId}`)
if (isMounted.current) { if (isMounted.current) {
setContact(data); setContact(data)
} }
} catch (err) { } catch (err) {
toastError(err); toastError(err)
}
} }
};
fetchContact(); fetchContact()
}, [contactId, open, initialValues]); }, [contactId, open, initialValues])
const handleClose = () => { const handleClose = () => {
onClose(); onClose()
setContact(initialState); setContact(initialState)
}; }
const handleSaveContact = async values => { const handleSaveContact = async values => {
try { try {
if (contactId) { if (contactId) {
await api.put(`/contacts/${contactId}`, values); await api.put(`/contacts/${contactId}`, values)
handleClose(); handleClose()
} else { } else {
const { data } = await api.post("/contacts", values); const { data } = await api.post("/contacts", values)
if (onSave) { if (onSave) {
onSave(data); onSave(data)
} }
handleClose(); handleClose()
} }
toast.success(i18n.t("contactModal.success")); toast.success(i18n.t("contactModal.success"))
setSaving(false)
} catch (err) { } catch (err) {
toastError(err); toastError(err)
}
} }
};
return ( return (
<div className={classes.root}> <div className={classes.root}>
@ -143,10 +145,11 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
enableReinitialize={true} enableReinitialize={true}
validationSchema={ContactSchema} validationSchema={ContactSchema}
onSubmit={(values, actions) => { onSubmit={(values, actions) => {
setSaving(true)
setTimeout(() => { setTimeout(() => {
handleSaveContact(values); handleSaveContact(values)
actions.setSubmitting(false); actions.setSubmitting(false)
}, 400); }, 400)
}} }}
> >
{({ values, errors, touched, isSubmitting }) => ( {({ values, errors, touched, isSubmitting }) => (
@ -256,14 +259,14 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
<Button <Button
type="submit" type="submit"
color="primary" color="primary"
disabled={isSubmitting} disabled={isSaving}
variant="contained" variant="contained"
className={classes.btnWrapper} className={classes.btnWrapper}
> >
{contactId {contactId
? `${i18n.t("contactModal.buttons.okEdit")}` ? `${i18n.t("contactModal.buttons.okEdit")}`
: `${i18n.t("contactModal.buttons.okAdd")}`} : `${i18n.t("contactModal.buttons.okAdd")}`}
{isSubmitting && ( {isSaving && (
<CircularProgress <CircularProgress
size={24} size={24}
className={classes.buttonProgress} className={classes.buttonProgress}
@ -276,7 +279,7 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
</Formik> </Formik>
</Dialog> </Dialog>
</div> </div>
); )
}; }
export default ContactModal; export default ContactModal