From 8a6324a98b5e07cc3aa4db06a6df733be86f1024 Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 18 Jun 2020 17:25:43 -0300 Subject: [PATCH] First github Commit --- backend/controllers/contact.js | 21 ++- backend/controllers/message.js | 14 +- backend/controllers/wbot.js | 99 ++++++----- backend/controllers/wbotMonitor.js | 63 +++---- backend/models/Whatsapp.js | 4 +- backend/package-lock.json | 24 +-- backend/package.json | 4 +- frontend/package-lock.json | 15 ++ frontend/package.json | 1 + frontend/src/components/Layout/MainDrawer.js | 2 +- .../src/components/Layout/MainListItems.js | 6 +- frontend/src/pages/Chat/Chat.js | 14 +- .../components/ContactsList/ContactsList.js | 155 +++++++++--------- .../components/MessagesInput/MessagesInput.js | 8 +- .../components/MessagesList/MessagesList.js | 39 +++-- frontend/src/pages/WhatsAuth/WhatsAuth.js | 72 ++++++-- .../pages/WhatsAuth/components/Bateryinfo.js | 34 ++++ .../src/pages/WhatsAuth/components/Qrcode.js | 25 +++ frontend/src/routes.js | 1 + 19 files changed, 391 insertions(+), 210 deletions(-) create mode 100644 frontend/src/pages/WhatsAuth/components/Bateryinfo.js create mode 100644 frontend/src/pages/WhatsAuth/components/Qrcode.js diff --git a/backend/controllers/contact.js b/backend/controllers/contact.js index 890540a..f83251b 100644 --- a/backend/controllers/contact.js +++ b/backend/controllers/contact.js @@ -3,12 +3,23 @@ const Message = require("../models/Message"); const Sequelize = require("sequelize"); exports.getContacts = async (req, res) => { + const { searchParam } = req.query; + + const lowerSerachParam = searchParam.toLowerCase(); + + const whereCondition = { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + "%" + lowerSerachParam + "%" + ), + }; + + //todo >> add contact number to search where condition + try { const contacts = await Contact.findAll({ - include: { - model: Message, - attributes: [], - }, + where: whereCondition, attributes: { include: [ [ @@ -25,7 +36,7 @@ exports.getContacts = async (req, res) => { ], ], }, - order: [[Message, "createdAt", "DESC"]], + order: [["updatedAt", "DESC"]], }); return res.json(contacts); diff --git a/backend/controllers/message.js b/backend/controllers/message.js index d1c9781..0f38c7d 100644 --- a/backend/controllers/message.js +++ b/backend/controllers/message.js @@ -3,7 +3,7 @@ const Message = require("../models/Message"); const Contact = require("../models/Contact"); const { getIO } = require("../socket"); const { getWbot } = require("./wbot"); -const sequelize = require("sequelize"); +const Sequelize = require("sequelize"); const { MessageMedia } = require("whatsapp-web.js"); @@ -44,13 +44,11 @@ exports.getContactMessages = async (req, res, next) => { const { contactId } = req.params; const { searchParam, pageNumber = 1 } = req.query; - console.log(req.body, req.query); - const lowerSerachParam = searchParam.toLowerCase(); const whereCondition = { - messageBody: sequelize.where( - sequelize.fn("LOWER", sequelize.col("messageBody")), + messageBody: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("messageBody")), "LIKE", "%" + lowerSerachParam + "%" ), @@ -90,7 +88,11 @@ exports.getContactMessages = async (req, res, next) => { }; }); - return res.json({ messages: serializedMessages.reverse(), messagesFound }); + return res.json({ + messages: serializedMessages.reverse(), + contact: contact, + messagesFound, + }); } catch (err) { next(err); } diff --git a/backend/controllers/wbot.js b/backend/controllers/wbot.js index 8ac023e..775c736 100644 --- a/backend/controllers/wbot.js +++ b/backend/controllers/wbot.js @@ -1,57 +1,72 @@ const qrCode = require("qrcode-terminal"); const { Client } = require("whatsapp-web.js"); const Whatsapp = require("../models/Whatsapp"); +const { getIO } = require("../socket"); let wbot; module.exports = { init: async () => { - let sessionCfg; + try { + let sessionCfg; - const dbSession = await Whatsapp.findOne({ where: { id: 1 } }); - if (dbSession && dbSession.session) { - sessionCfg = JSON.parse(dbSession.session); - } + const dbSession = await Whatsapp.findOne({ where: { id: 1 } }); + if (dbSession && dbSession.session) { + sessionCfg = JSON.parse(dbSession.session); + } - wbot = new Client({ - session: sessionCfg, - restartOnAuthFail: true, - }); - wbot.initialize(); - wbot.on("qr", async qr => { - qrCode.generate(qr, { small: true }); - await Whatsapp.upsert({ id: 1, qrcode: qr, status: "pending" }); - }); - wbot.on("authenticated", async session => { - console.log("AUTHENTICATED"); - await Whatsapp.upsert({ - id: 1, - session: JSON.stringify(session), - status: "authenticated", + wbot = new Client({ + session: sessionCfg, + restartOnAuthFail: true, }); - }); - wbot.on("auth_failure", async msg => { - console.error("AUTHENTICATION FAILURE", msg); - await Whatsapp.destroy({ where: { id: 1 } }); - }); - wbot.on("ready", async () => { - console.log("READY"); - await Whatsapp.update({ status: "online" }, { where: { id: 1 } }); - // const chats = await wbot.getChats(); // pega as mensagens nao lidas (recebidas quando o bot estava offline) - // let unreadMessages; // todo > salvar isso no DB pra mostrar no frontend - // for (let chat of chats) { - // if (chat.unreadCount > 0) { - // unreadMessages = await chat.fetchMessages({ - // limit: chat.unreadCount, - // }); - // } - // } + wbot.initialize(); + wbot.on("qr", async qr => { + qrCode.generate(qr, { small: true }); + await Whatsapp.upsert({ id: 1, qrcode: qr, status: "pending" }); + getIO().emit("qrcode", { + action: "update", + qr: qr, + }); + }); + wbot.on("authenticated", async session => { + console.log("AUTHENTICATED"); + await Whatsapp.upsert({ + id: 1, + session: JSON.stringify(session), + status: "authenticated", + }); + getIO().emit("whats_auth", { + action: "authentication", + session: dbSession, + }); + }); + wbot.on("auth_failure", async msg => { + console.error("AUTHENTICATION FAILURE", msg); + await Whatsapp.update({ session: "" }, { where: { id: 1 } }); + }); + wbot.on("ready", async () => { + console.log("READY"); + await Whatsapp.update( + { status: "online", qrcode: "" }, + { where: { id: 1 } } + ); + // const chats = await wbot.getChats(); // pega as mensagens nao lidas (recebidas quando o bot estava offline) + // let unreadMessages; // todo > salvar isso no DB pra mostrar no frontend + // for (let chat of chats) { + // if (chat.unreadCount > 0) { + // unreadMessages = await chat.fetchMessages({ + // limit: chat.unreadCount, + // }); + // } + // } - // console.log(unreadMessages); - wbot.sendPresenceAvailable(); - }); - - return wbot; + // console.log(unreadMessages); + wbot.sendPresenceAvailable(); + }); + return wbot; + } catch (err) { + console.log(err); + } }, getWbot: () => { diff --git a/backend/controllers/wbotMonitor.js b/backend/controllers/wbotMonitor.js index f30f64f..0c06eec 100644 --- a/backend/controllers/wbotMonitor.js +++ b/backend/controllers/wbotMonitor.js @@ -1,10 +1,6 @@ -const Contact = require("../models/Contact"); -const Message = require("../models/Message"); +const Whatsapp = require("../models/Whatsapp"); const wbotMessageListener = require("./wbotMessageListener"); -const path = require("path"); -const fs = require("fs"); - const { getIO } = require("../socket"); const { getWbot, init } = require("./wbot"); @@ -12,32 +8,43 @@ const wbotMonitor = () => { const io = getIO(); const wbot = getWbot(); - wbot.on("change_state", newState => { - console.log("monitor", newState); - }); + try { + wbot.on("change_state", newState => { + console.log("monitor", newState); + }); - wbot.on("change_battery", batteryInfo => { - // Battery percentage for attached device has changed - const { battery, plugged } = batteryInfo; - console.log(`Battery: ${battery}% - Charging? ${plugged}`); - }); + wbot.on("change_battery", async batteryInfo => { + // Battery percentage for attached device has changed + const { battery, plugged } = batteryInfo; + try { + await Whatsapp.update({ battery, plugged }, { where: { id: 1 } }); + } catch (err) { + console.log(err); + } - wbot.on("disconnected", reason => { - console.log("disconnected", reason); - wbot.destroy(); - setTimeout(() => - init() - .then(res => { - wbotMessageListener(); - wbotMonitor(); - }) - .catch(err => console.log(err)) - ); - }); + console.log(`Battery: ${battery}% - Charging? ${plugged}`); //todo> save batery state to db + }); - // setInterval(() => { - // wbot.resetState(); - // }, 20000); + wbot.on("disconnected", reason => { + console.log("disconnected", reason); //todo> save connection status to DB + setTimeout( + () => + init() + .then(res => { + wbotMessageListener(); + wbotMonitor(); + }) + .catch(err => console.log(err)), + 2000 + ); + }); + + // setInterval(() => { + // wbot.resetState(); + // }, 20000); + } catch (err) { + console.log(err); + } }; module.exports = wbotMonitor; diff --git a/backend/models/Whatsapp.js b/backend/models/Whatsapp.js index 503e16c..451146b 100644 --- a/backend/models/Whatsapp.js +++ b/backend/models/Whatsapp.js @@ -5,7 +5,9 @@ const sequelize = require("../util/database"); const Whatsapp = sequelize.define("whatsapp", { session: { type: Sequelize.TEXT() }, qrcode: { type: Sequelize.TEXT() }, - status: { type: Sequelize.STRING(20) }, + status: { type: Sequelize.STRING(60) }, + battery: { type: Sequelize.STRING(20) }, + plugged: { type: Sequelize.BOOLEAN() }, }); module.exports = Whatsapp; diff --git a/backend/package-lock.json b/backend/package-lock.json index 0c554f5..bcf5c50 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -31,9 +31,9 @@ "dev": true }, "@types/node": { - "version": "14.0.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.11.tgz", - "integrity": "sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg==" + "version": "14.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", + "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==" }, "@types/yauzl": { "version": "2.9.1", @@ -1046,9 +1046,9 @@ } }, "extract-zip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", - "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "requires": { "@types/yauzl": "^2.9.1", "debug": "^4.1.1", @@ -2342,9 +2342,9 @@ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" }, "sequelize": { - "version": "5.21.12", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.12.tgz", - "integrity": "sha512-WRTXLoH72HQnlEUbKI1Iev2yS1/epg72Chz0fHqk0MLn4Wj2pH/TS01lpMDGQqxZPXCxRGrwk1YxwUw2C0yI9A==", + "version": "5.21.13", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.13.tgz", + "integrity": "sha512-wpwSpxzvADmgPkcOGeer5yFdAVsYeA7NLEw4evSXw03OlGL41J4S8hVz2/nilSWlJSwumlDGC9QbdwAmkWGqJg==", "requires": { "bluebird": "^3.5.0", "cls-bluebird": "^2.1.0", @@ -2867,9 +2867,9 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "whatsapp-web.js": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/whatsapp-web.js/-/whatsapp-web.js-1.6.1.tgz", - "integrity": "sha512-pj0JEIuMsWTlqlNr7vRST9XrS+lj/E4i9B7c00FCzUGu6pbPr+95QrYYY56tH5bpCtpsl2NbIB3fbYe3RlxdkA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/whatsapp-web.js/-/whatsapp-web.js-1.7.0.tgz", + "integrity": "sha512-cha7byb+/xWZCB60JW/bSTGaiPgWlA4m37vXjGRkUvMT30/JZyVjQ9W2hazS4dBOtnx3/8DKSnn2AQEEb6Hi5w==", "requires": { "@pedroslopez/moduleraid": "^4.1.0", "jsqr": "^1.3.1", diff --git a/backend/package.json b/backend/package.json index e24d321..3d6d393 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,10 +23,10 @@ "multer": "^1.4.2", "mysql2": "^2.1.0", "qrcode-terminal": "^0.12.0", - "sequelize": "^5.21.12", + "sequelize": "^5.21.13", "sequelize-cli": "^5.5.1", "socket.io": "^2.3.0", - "whatsapp-web.js": "^1.6.1" + "whatsapp-web.js": "^1.7.0" }, "devDependencies": { "nodemon": "^2.0.4" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5f90e94..07a3d12 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10736,6 +10736,21 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, + "qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=" + }, + "qrcode.react": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.0.tgz", + "integrity": "sha512-jBXleohRTwvGBe1ngV+62QvEZ/9IZqQivdwzo9pJM4LQMoCM2VnvNBnKdjvGnKyDZ/l0nCDgsPod19RzlPvm/Q==", + "requires": { + "loose-envify": "^1.4.0", + "prop-types": "^15.6.0", + "qr.js": "0.0.0" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index c732f1d..5aecd32 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "emoji-mart": "^3.0.0", "moment": "^2.26.0", "moment-timezone": "^0.5.31", + "qrcode.react": "^1.0.0", "react": "^16.13.1", "react-audio-player": "^0.13.0", "react-bootstrap": "^1.0.1", diff --git a/frontend/src/components/Layout/MainDrawer.js b/frontend/src/components/Layout/MainDrawer.js index ffed0d5..97ce6ff 100644 --- a/frontend/src/components/Layout/MainDrawer.js +++ b/frontend/src/components/Layout/MainDrawer.js @@ -181,7 +181,7 @@ const MainDrawer = ({ appTitle, children }) => { {appTitle} - + diff --git a/frontend/src/components/Layout/MainListItems.js b/frontend/src/components/Layout/MainListItems.js index 5005051..5ba52e1 100644 --- a/frontend/src/components/Layout/MainListItems.js +++ b/frontend/src/components/Layout/MainListItems.js @@ -12,7 +12,7 @@ import Collapse from "@material-ui/core/Collapse"; import DashboardIcon from "@material-ui/icons/Dashboard"; import WhatsAppIcon from "@material-ui/icons/WhatsApp"; // import PeopleIcon from "@material-ui/icons/People"; -import BorderOuterIcon from "@material-ui/icons/BorderOuter"; +import SyncAltIcon from "@material-ui/icons/SyncAlt"; import ChatIcon from "@material-ui/icons/Chat"; import BarChartIcon from "@material-ui/icons/BarChart"; import LayersIcon from "@material-ui/icons/Layers"; @@ -71,8 +71,8 @@ const MainListItems = () => { } + primary="Conexão" + icon={} /> ({ const Chat = () => { const classes = useStyles(); - const [selectedContact, setSelectedContact] = useState(null); + const { contactId } = useParams(); return (
@@ -56,15 +57,12 @@ const Chat = () => { - + - {selectedContact ? ( + {contactId ? ( <> - + ) : ( ({ }, contactsList: { + position: "relative", borderTopLeftRadius: 0, borderTopRightRadius: 0, borderBottomRightRadius: 0, @@ -59,6 +61,7 @@ const useStyles = makeStyles(theme => ({ }, }, contactsSearchBox: { + position: "relative", background: "#fafafa", padding: "10px 13px", }, @@ -106,14 +109,24 @@ const useStyles = makeStyles(theme => ({ color: "white", backgroundColor: green[500], }, + circleLoading: { + color: green[500], + opacity: "70%", + position: "absolute", + top: 0, + left: "50%", + marginTop: 12, + // marginLeft: -12, + }, })); -const ContactsList = ({ selectedContact, setSelectedContact }) => { +const ContactsList = () => { const classes = useStyles(); const token = localStorage.getItem("token"); - + const { contactId } = useParams(); const [contacts, setContacts] = useState([]); - const [displayedContacts, setDisplayedContacts] = useState([]); + const [loading, setLoading] = useState(); + const [searchParam, setSearchParam] = useState(""); const history = useHistory(); @@ -126,21 +139,23 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { }, []); useEffect(() => { - const fetchContacts = async () => { - try { - const res = await api.get("/contacts"); - setContacts(res.data); - setDisplayedContacts(res.data); - } catch (err) { - if (err) { + setLoading(true); + const delayDebounceFn = setTimeout(() => { + const fetchContacts = async () => { + try { + const res = await api.get("/contacts", { + params: { searchParam }, + }); + setContacts(res.data); + setLoading(false); + } catch (err) { console.log(err); - alert(err); } - console.log(err); - } - }; - fetchContacts(); - }, [token, history]); + }; + fetchContacts(); + }, 1000); + return () => clearTimeout(delayDebounceFn); + }, [searchParam, token]); useEffect(() => { const socket = openSocket("http://localhost:8080"); @@ -154,60 +169,54 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { }); socket.on("appMessage", data => { - if ( - selectedContact && - data.message.contactId === selectedContact.id && - document.visibilityState === "visible" - ) { - return; + if (data.action === "create") { + updateUnreadMessagesCount(data); + if ( + contactId && + data.message.contactId === +contactId && + document.visibilityState === "visible" + ) + return; + showDesktopNotification(data); } - - let contactImageUrl = profileDefaultPic; - let contactName = "Novo Contato"; - - const contactIndex = contacts.findIndex( - contact => contact.id === data.message.contactId - ); - - if (contactIndex !== -1) { - contactImageUrl = contacts[contactIndex].imageURL; - contactName = contacts[contactIndex].name; - setDisplayedContacts(prevState => { - let aux = [...prevState]; - aux.unshift(aux.splice(contactIndex, 1)[0]); - return aux; - }); - setContacts(prevState => { - let aux = [...prevState]; - aux[contactIndex].unreadMessages++; - aux.unshift(aux.splice(contactIndex, 1)[0]); - return aux; - }); - } else { - setContacts(prevState => [data.contact, ...prevState]); - setDisplayedContacts(prevState => [data.contact, ...prevState]); - } - showNotification(data, contactName, contactImageUrl); }); return () => { socket.disconnect(); }; - }, [selectedContact, contacts]); + }, [contactId]); - const showNotification = (data, contactName, contactImageUrl) => { + const updateUnreadMessagesCount = data => { + setContacts(prevState => { + const contactIndex = prevState.findIndex( + contact => contact.id === data.message.contactId + ); + + if (contactIndex !== -1) { + let aux = [...prevState]; + aux[contactIndex].unreadMessages++; + aux[contactIndex].lastMessage = data.message.messageBody; + aux.unshift(aux.splice(contactIndex, 1)[0]); + return aux; + } else { + return [data.contact, ...prevState]; + } + }); + }; + + const showDesktopNotification = data => { const options = { body: `${data.message.messageBody} - ${moment(new Date()) .tz("America/Sao_Paulo") .format("DD/MM/YY - HH:mm")}`, - icon: contactImageUrl, + icon: data.contact.imageURL, }; - new Notification(`Mensagem de ${contactName}`, options); + new Notification(`Mensagem de ${data.contact.name}`, options); document.getElementById("sound").play(); }; const resetUnreadMessages = contactId => { - setDisplayedContacts(prevState => { + setContacts(prevState => { let aux = [...prevState]; let contactIndex = aux.findIndex(contact => contact.id === +contactId); aux[contactIndex].unreadMessages = 0; @@ -217,17 +226,12 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { }; const handleSelectContact = (e, contact) => { - setSelectedContact(contact); + history.push(`/chat/${contact.id}`); }; const handleSearchContact = e => { - let searchTerm = e.target.value.toLowerCase(); - - setDisplayedContacts( - contacts.filter(contact => - contact.name.toLowerCase().includes(searchTerm) - ) - ); + // let searchTerm = e.target.value.toLowerCase(); + setSearchParam(e.target.value.toLowerCase()); }; return ( @@ -246,12 +250,12 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { - {displayedContacts.map((contact, index) => ( + {contacts.map((contact, index) => ( handleSelectContact(e, contact)} - selected={selectedContact && selectedContact.id === contact.id} + selected={contactId && +contactId === contact.id} > { > {contact.lastMessage ||
} - {contact.unreadMessages > 0 && ( - - )} + } /> @@ -313,6 +315,11 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => {
))}
+ {loading ? ( +
+ +
+ ) : null}