diff --git a/backend/app.js b/backend/app.js index 6dca422..44cd1a0 100644 --- a/backend/app.js +++ b/backend/app.js @@ -7,6 +7,7 @@ const multer = require("multer"); const wBot = require("./controllers/wbot"); const Contact = require("./models/Contact"); const wbotMessageListener = require("./controllers/wbotMessageListener"); +const wbotMonitor = require("./controllers/wbotMonitor"); const messageRoutes = require("./routes/message"); const ContactRoutes = require("./routes/contacts"); @@ -61,9 +62,10 @@ sequelize }); }); - wBot.init(); - wbotMessageListener(); - + wBot.init().then(res => { + wbotMessageListener(); + wbotMonitor(); + }); console.log("Server started"); }) .catch(err => { diff --git a/backend/controllers/session.json b/backend/controllers/session.json deleted file mode 100644 index 06803dd..0000000 --- a/backend/controllers/session.json +++ /dev/null @@ -1 +0,0 @@ -{"WABrowserId":"\"E9dnt9Mm/JiFDCMJQHkXBw==\"","WASecretBundle":"{\"key\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\",\"encKey\":\"FrM3OnnEbuEr1JrtKypw5CPSc6rSD5bjbOGstv8ijk4=\",\"macKey\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\"}","WAToken1":"\"tjwS2T/ux5P+nyxYcBEj+gRIUS/XqREEEPqap787yzg=\"","WAToken2":"\"1@781NE8OipW/PigfWCI6tiz8fEpUOVVhyNEePCZMuEyC7aXG0cy1I75liSVz8z6DxVjXiw4iGI7c4Hg==\""} \ No newline at end of file diff --git a/backend/controllers/wbot.js b/backend/controllers/wbot.js index e06922a..8ac023e 100644 --- a/backend/controllers/wbot.js +++ b/backend/controllers/wbot.js @@ -1,39 +1,42 @@ -const path = require("path"); const qrCode = require("qrcode-terminal"); -const fs = require("fs"); const { Client } = require("whatsapp-web.js"); +const Whatsapp = require("../models/Whatsapp"); + let wbot; module.exports = { - init: () => { - const SESSION_FILE_PATH = path.join(__dirname, "/session.json"); + init: async () => { let sessionCfg; - if (fs.existsSync(SESSION_FILE_PATH)) { - sessionCfg = require(SESSION_FILE_PATH); + + 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", qr => { + wbot.on("qr", async qr => { qrCode.generate(qr, { small: true }); + await Whatsapp.upsert({ id: 1, qrcode: qr, status: "pending" }); }); - wbot.on("authenticated", session => { + wbot.on("authenticated", async session => { console.log("AUTHENTICATED"); - sessionCfg = session; - fs.writeFile(SESSION_FILE_PATH, JSON.stringify(sessionCfg), function ( - err - ) { - if (err) { - console.error(err); - } + await Whatsapp.upsert({ + id: 1, + session: JSON.stringify(session), + status: "authenticated", }); }); - wbot.on("auth_failure", msg => { + 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) { @@ -45,6 +48,7 @@ module.exports = { // } // console.log(unreadMessages); + wbot.sendPresenceAvailable(); }); return wbot; diff --git a/backend/controllers/wbotMessageListener.js b/backend/controllers/wbotMessageListener.js index d4e65a2..c5da672 100644 --- a/backend/controllers/wbotMessageListener.js +++ b/backend/controllers/wbotMessageListener.js @@ -5,7 +5,7 @@ const path = require("path"); const fs = require("fs"); const { getIO } = require("../socket"); -const { getWbot } = require("./wbot"); +const { getWbot, init } = require("./wbot"); const wbotMessageListener = () => { const io = getIO(); @@ -14,6 +14,9 @@ const wbotMessageListener = () => { wbot.on("message", async msg => { let newMessage; console.log(msg); + if (msg.from === "status@broadcast") { + return; + } const msgContact = await msg.getContact(); const imageUrl = await msgContact.getProfilePicUrl(); try { @@ -21,6 +24,10 @@ const wbotMessageListener = () => { where: { number: msgContact.number }, }); + if (contact) { + await contact.update({ imageURL: imageUrl }); + } + if (!contact) { try { contact = await Contact.create({ @@ -28,13 +35,6 @@ const wbotMessageListener = () => { number: msgContact.number, imageURL: imageUrl, }); - - // contact.dataValues.unreadMessages = 1; - - io.to("notification").emit("contact", { - action: "create", - contact: contact, - }); } catch (err) { console.log(err); } @@ -45,8 +45,7 @@ const wbotMessageListener = () => { if (media) { if (!media.filename) { let ext = media.mimetype.split("/")[1].split(";")[0]; - let aux = Math.random(5).toString(); - media.filename = aux.split(".")[1] + "." + ext; + media.filename = `${new Date().getTime()}.${ext}`; } fs.writeFile( @@ -72,10 +71,19 @@ const wbotMessageListener = () => { }); } - io.to(contact.id).to("notification").emit("appMessage", { - action: "create", - message: newMessage, - }); + io.to(contact.id) + .to("notification") + .emit("appMessage", { + action: "create", + message: { + ...newMessage.dataValues, + mediaUrl: `${ + newMessage.mediaUrl + ? `http://localhost:8080/public/${newMessage.mediaUrl}` + : "" + }`, + }, + }); let chat = await msg.getChat(); chat.sendSeen(); @@ -90,6 +98,7 @@ const wbotMessageListener = () => { where: { id: msg.id.id }, }); if (!messageToUpdate) { + // will throw an error is msg wasn't sent from app const error = new Error( "Erro ao alterar o ack da mensagem no banco de dados" ); diff --git a/backend/controllers/wbotMonitor.js b/backend/controllers/wbotMonitor.js new file mode 100644 index 0000000..dd76119 --- /dev/null +++ b/backend/controllers/wbotMonitor.js @@ -0,0 +1,40 @@ +const Contact = require("../models/Contact"); +const Message = require("../models/Message"); +const wbotMessageListener = require("./wbotMessageListener"); + +const path = require("path"); +const fs = require("fs"); + +const { getIO } = require("../socket"); +const { getWbot, init } = require("./wbot"); + +const wbotMonitor = () => { + const io = getIO(); + const wbot = getWbot(); + + wbot.on("change_state", newState => { + console.log(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("disconnected", reason => { + console.log("disconnected", reason); + wbot.destroy(); + setTimeout(() => + init() + .then(res => wbotMessageListener(), 2000) + .catch(err => console.log(err)) + ); + }); + + // setInterval(() => { + // wbot.resetState(); + // }, 20000); +}; + +module.exports = wbotMonitor; diff --git a/backend/models/Whatsapp.js b/backend/models/Whatsapp.js new file mode 100644 index 0000000..503e16c --- /dev/null +++ b/backend/models/Whatsapp.js @@ -0,0 +1,11 @@ +const Sequelize = require("sequelize"); + +const sequelize = require("../util/database"); + +const Whatsapp = sequelize.define("whatsapp", { + session: { type: Sequelize.TEXT() }, + qrcode: { type: Sequelize.TEXT() }, + status: { type: Sequelize.STRING(20) }, +}); + +module.exports = Whatsapp; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7c02bf5..5f90e94 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11319,6 +11319,14 @@ "prop-types": "^15.6.2" } }, + "react-web-notification": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/react-web-notification/-/react-web-notification-0.8.0.tgz", + "integrity": "sha512-c9oJEBHyI8P6ymA6lOdUKeOiLH3XkcsvV3nNfMK0D0aXh47sTxoGZLQnw/qO32bm5CmS7k66FSwvQXnTaBoJ3g==", + "requires": { + "core-js": "3" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index c1e4d6d..c732f1d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,6 +27,7 @@ "react-scripts": "3.4.1", "react-scroll-to-bottom": "^2.0.0", "react-toastify": "^6.0.4", + "react-web-notification": "^0.8.0", "socket.io-client": "^2.3.0" }, "scripts": { diff --git a/frontend/src/pages/Chat/components/ContactsList/ContactsList.js b/frontend/src/pages/Chat/components/ContactsList/ContactsList.js index 93bcdb0..daaa829 100644 --- a/frontend/src/pages/Chat/components/ContactsList/ContactsList.js +++ b/frontend/src/pages/Chat/components/ContactsList/ContactsList.js @@ -13,7 +13,6 @@ import List from "@material-ui/core/List"; import ListItem from "@material-ui/core/ListItem"; import ListItemText from "@material-ui/core/ListItemText"; import ListItemAvatar from "@material-ui/core/ListItemAvatar"; -import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; import Typography from "@material-ui/core/Typography"; import Avatar from "@material-ui/core/Avatar"; import Divider from "@material-ui/core/Divider"; @@ -24,11 +23,6 @@ import InputBase from "@material-ui/core/InputBase"; import ContactsHeader from "../ContactsHeader/ContactsHeader"; const useStyles = makeStyles(theme => ({ - badgeStyle: { - color: "white", - backgroundColor: green[500], - }, - contactsWrapper: { display: "flex", height: "100%", @@ -94,13 +88,24 @@ const useStyles = makeStyles(theme => ({ // display: "inline", }, - contactName: { - // flex: 1, - }, - lastMessageTime: { marginLeft: "auto", }, + + contactLastMessage: { + paddingRight: 20, + }, + + newMessagesCount: { + alignSelf: "center", + marginRight: 8, + marginLeft: "auto", + }, + + badgeStyle: { + color: "white", + backgroundColor: green[500], + }, })); const ContactsList = ({ selectedContact, setSelectedContact }) => { @@ -113,6 +118,14 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { const history = useHistory(); + useEffect(() => { + if (!("Notification" in window)) { + console.log("Esse navegador não suporte notificações"); + } else { + Notification.requestPermission(); + } + }, []); + useEffect(() => { const fetchContacts = async () => { try { @@ -139,9 +152,6 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { socket.emit("joinNotification"); socket.on("contact", data => { - if (data.action === "create") { - addContact(data.contact); - } if (data.action === "updateUnread") { resetUnreadMessages(data.contactId); } @@ -149,13 +159,40 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { socket.on("appMessage", data => { setNotification(prevState => !prevState); - // handleUnreadMessages(data.message.contactId); + if ( + selectedContact && + data.message.contactId === selectedContact.id && + document.visibilityState === "visible" + ) { + return; + } + + 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; + } + + const options = { + body: `${data.message.messageBody} - ${moment(new Date()) + .tz("America/Sao_Paulo") + .format("DD/MM/YY - HH:mm")}`, + icon: contactImageUrl, + }; + new Notification(`Mensagem de ${contactName}`, options); + document.getElementById("sound").play(); }); return () => { socket.disconnect(); }; - }, []); + }, [selectedContact, contacts]); const resetUnreadMessages = contactId => { setDisplayedContacts(prevState => { @@ -167,14 +204,8 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { }); }; - const addContact = contact => { - setContacts(prevState => [contact, ...prevState]); - setDisplayedContacts(prevState => [contact, ...prevState]); - }; - const handleSelectContact = (e, contact) => { setSelectedContact(contact); - // setNotification(prevState => !prevState); }; const handleSearchContact = e => { @@ -218,13 +249,9 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { > + { {contact.messages && contact.messages[0] && ( { .format("HH:mm")} )} - + } secondary={ - (contact.messages && - contact.messages[0] && - contact.messages[0].messageBody) || - "-" + + + {(contact.messages && + contact.messages[0] && + contact.messages[0].messageBody) ||
} +
+ {contact.unreadMessages > 0 && ( + + )} +
} /> - - {contact.unreadMessages > 0 && ( - - )} - ))} + ); }; diff --git a/frontend/src/pages/Chat/components/MessagesList/MessagesList.js b/frontend/src/pages/Chat/components/MessagesList/MessagesList.js index bc54cf5..c3e74f6 100644 --- a/frontend/src/pages/Chat/components/MessagesList/MessagesList.js +++ b/frontend/src/pages/Chat/components/MessagesList/MessagesList.js @@ -20,7 +20,6 @@ import openSocket from "socket.io-client"; import moment from "moment-timezone"; import InfiniteScrollReverse from "react-infinite-scroll-reverse"; import ModalImage from "react-modal-image"; -import ReactAudioPlayer from "react-audio-player"; import MessagesInput from "../MessagesInput/MessagesInput"; const useStyles = makeStyles(theme => ({ @@ -313,7 +312,11 @@ const MessagesList = ({ selectedContact }) => { ); } if (message.mediaType === "audio") { - return ; + return ( + + ); } if (message.mediaType === "video") { diff --git a/frontend/src/pages/Home/Dashboard.js b/frontend/src/pages/Home/Dashboard.js index e03fe5c..280c8a6 100644 --- a/frontend/src/pages/Home/Dashboard.js +++ b/frontend/src/pages/Home/Dashboard.js @@ -4,7 +4,9 @@ import MainDrawer from "../../components/Layout/MainDrawer"; const Dashboard = () => { return (
- + +

Todo Dashboard

+
); }; diff --git a/frontend/src/pages/Home/Notifications_button_24.png b/frontend/src/pages/Home/Notifications_button_24.png new file mode 100644 index 0000000..7409b40 Binary files /dev/null and b/frontend/src/pages/Home/Notifications_button_24.png differ diff --git a/frontend/src/util/Popcorn-Time-0.4.4-win32-Setup.exe b/frontend/src/util/Popcorn-Time-0.4.4-win32-Setup.exe new file mode 100644 index 0000000..7812556 Binary files /dev/null and b/frontend/src/util/Popcorn-Time-0.4.4-win32-Setup.exe differ diff --git a/frontend/src/util/sound.mp3 b/frontend/src/util/sound.mp3 new file mode 100644 index 0000000..bd770d8 Binary files /dev/null and b/frontend/src/util/sound.mp3 differ diff --git a/frontend/src/util/sound.ogg b/frontend/src/util/sound.ogg new file mode 100644 index 0000000..5a7c0ab Binary files /dev/null and b/frontend/src/util/sound.ogg differ