I'm trying to create a chrome extension for sending messages. I just want to add my name above the messages.
The problem is that I don't know how to send my name that comes from the variable (nomeAtendente).
I would be very grateful for any help, as I've been searching for how to do this on the internet for a few days now and haven't found anything.
const interval = setInterval(() => {
const menu = document.querySelector("#app > div > div > div._2Ts6i._3RGKj > header > div._604FD > div > span > span");
const content = document.querySelector("#app > div > div > div._2Ts6i._2xAQV");
if (menu && content) {
clearInterval(interval);
const btnMenu = document.createElement("button");
btnMenu.innerHTML = "7C";
btnMenu.classList.add("btnMenu7Carros");
menu.appendChild(btnMenu);
const divLateralHTML = `
Nome do atendente: <br>
<input type="text" id="nomeAtendente">
<br><br>
<button id="buttonSalvar" name="button" class="">Salvar</button>
`;
content.insertAdjacentHTML('afterend', `<div id="divLateral" style="display: none;">${divLateralHTML}</div>`);
//Abre e fecha o menu.
const divLateral = document.getElementById("divLateral");
let divLateralVisivel = false;
btnMenu.addEventListener("click", function () {
divLateralVisivel = !divLateralVisivel;
divLateral.style.display = divLateralVisivel ? "block" : "none";
});
//Salva os dados
const buttonSalvar = document.getElementById("buttonSalvar");
buttonSalvar.addEventListener("click", function () {
localStorage.setItem("nomeAtendente", document.getElementById("nomeAtendente").value);
alert("Salvo com sucesso!")
});
// Obtém o valor do localStorage e define no input
document.getElementById("nomeAtendente").value = localStorage.getItem('nomeAtendente') || "";
const nomeAtendente = localStorage.getItem("nomeAtendente");
document.addEventListener("keydown", function (e) {
handleEvent(e);
});
document.addEventListener("keyup", function (e) {
handleEvent(e);
});
document.addEventListener("keypress", function (e) {
handleEvent(e);
});
function handleEvent(e) {
const elemento = document.querySelector("#main > footer > div._2lSWV._3cjY2.copyable-area > div > span:nth-child(2) > div > div._1VZX7 > div._3Uu1_ > div > div > p > span");
if (elemento !== null) {
mensagemOriginal = elemento.textContent.trim();
localStorage.setItem("mensagemOriginal", mensagemOriginal);
//console.log("Original: " + mensagemOriginal);
}
if (e.key === "Enter" || e.code === "Enter") {
mensagemOriginal = localStorage.getItem('mensagemOriginal') || ""
const novaMensagem = `*[${nomeAtendente}]*\n${mensagemOriginal}`;
console.log(novaMensagem);
var elementoRemover = document.querySelector("#main > footer > div._2lSWV._3cjY2.copyable-area > div > span:nth-child(2) > div > div._1VZX7 > div._3Uu1_ > div > div.lhggkp7q.qq0sjtgm.jxacihee.c3x5l3r8.b9fczbqn.t35qvd06.m62443ks.rkxvyd19.c5h0bzs2.bze30y65.kao4egtt.kh4n4d4z.tt14wmjx");
if (elementoRemover) {
elementoRemover.parentNode.removeChild(elementoRemover);
}
// Selecione o elemento com a classe "selectable-text" e classe "copyable-text"
var elementoModificar = document.querySelector("#main > footer > div._2lSWV._3cjY2.copyable-area > div > span:nth-child(2) > div > div._1VZX7 > div._3Uu1_ > div > div > p");
if (elementoModificar) {
elementoModificar.innerHTML = elementoModificar.innerHTML.replace(/<br>/g, '');
elementoModificar.innerHTML += `<span class="selectable-text copyable-text" data-lexical-text="true">${novaMensagem}</span>`;
}
localStorage.setItem("mensagemOriginal", "");
}
}
}
}, 1000);
My first thought was to hook the function WhatsApp Web used to submit the message, or change the internal state of the app. Unfortunately I was not able to actually pin-point them after fiddling with the page.
What I tried after, which seems to work, is this:
const editorElement = document.querySelector('#main div[data-lexical-editor]');
function modifyMessage() {
const editorSpan = editorElement.querySelector('span[data-lexical-text]'); // get span[data-lexical-text]
console.log('editorSpan:', editorSpan);
if (editorSpan) { // might not exist if the input field is empty
const origMessage = editorSpan.childNodes[0].data; // get original message from text node (first child)
const newMessage = '*AUTHOR*\n' + origMessage;
editorSpan.childNodes[0].data = newMessage; // alter it!
console.log('origMessage:', `"${origMessage}"`, 'newMessage:', `"${editorSpan.childNodes[0].data}"`);
console.log('origMessage:', `"${origMessage}"`, 'newMessage:', `"${editorSpan.childNodes[0].data}"`);
}
}
function handleEvent(e) {
console.log('handleEvent:', e);
if (e.type === 'keydown') {
if (e.key === 'Enter') {
modifyMessage();
}
}
}
const USE_CAPTURE = true;
editorElement.addEventListener('keydown', handleEvent, {capture:USE_CAPTURE}); // NOTE: 'capture:true' may be needed in this case, to ensure the key event is captured before WA handles it */
// editorElement.removeEventListener('keydown', handleEvent, USE_CAPTURE);
With a few adjustments the same logic could be used to handle 'onClick' on the send button.
Hopefully this is helpful, or at least a good starting point. ;D
UPDATE #0
And that ~would work for a single chat... but what if you selected another chat?!
Enter MutationObserver...
UPDATE #1
The MutationObserver API let's you listen for changes to the DOM and react to them.
With the following code we listen for a new chat element being added to the DOM (matching the '#main' selector), and wire the events to the new editor element.
// ~SAME CODE AS BEFORE
function modifyMessage() {
const editorSpan = editorElement.querySelector('span[data-lexical-text]'); // get span[data-lexical-text]
console.log('editorSpan:', editorSpan);
if (editorSpan) { // might not exist if the input field is empty
const origMessage = editorSpan.childNodes[0].data; // get original message from text node (first child)
const newMessage = '*AUTHOR* 😎\n' + origMessage;
editorSpan.childNodes[0].data = newMessage; // alter it!
console.log('origMessage:', `"${origMessage}"`, 'newMessage:', `"${editorSpan.childNodes[0].data}"`);
}
}
function handleEvent(e) {
console.log('handleEvent:', e);
if (e.type === 'keydown') {
if (e.key === 'Enter') {
modifyMessage();
}
}
}
// NEW CODE
function waitForElement(selector, callback) {
// check if element matching selector is already in the DOM
let matchedElement = document.querySelector(selector);
if (matchedElement) {
callback(matchedElement);
}
// setup a MutationObserver that listens to changes to the DOM
let mutObserver = new MutationObserver((mutationRecords, observer) => {
for (const mutation of mutationRecords) {
for (const addedNode of mutation.addedNodes) {
if (addedNode.matches(selector)) {
callback(addedNode);
}
}
}
});
const options = {
childList: true,
subtree: true,
};
mutObserver.observe(document.documentElement, options);
return mutObserver; // so that you can call .disconnect() to stop observing
}
let editorElement = null; // this will store the editorElement for the current chat
// watch for chat change
let observer = waitForElement('#main', (matchedElement) => {
console.log('matched (new chat clicked):', matchedElement);
let newEditorElement = document.querySelector('#main div[data-lexical-editor]');
if (editorElement != newEditorElement) { // check if it's actually a different editorElement
editorElement = newEditorElement;
console.log('new editorElement:', editorElement);
const USE_CAPTURE = true;
editorElement.addEventListener('keydown', handleEvent, {capture:USE_CAPTURE}); // NOTE: 'capture:true' may be needed in this case, to ensure the key event is captured before WA handles it */
// editorElement.removeEventListener('keydown', handleEvent, USE_CAPTURE);
}
});
// observer.disconnect();
UPDATE #2
Slightly altered code.
Now the approach is to listen for added nodes inside the right panes (matched by '._2xAQV' - check if that's the case on your end too), and rewire the events as needed.
Also adds/removes a click event listener to the send buttons, and keydown events on the correct text-inputs (should work for attachments too now).
// ~SAME CODE AS BEFORE
function modifyMessage() {
const editorSpan = editorElement.querySelector('span[data-lexical-text]'); // get span[data-lexical-text]
console.log('editorSpan:', editorSpan);
if (editorSpan) { // might not exist if the input field is empty
const origMessage = editorSpan.childNodes[0].data; // get original message from text node (first child)
const newMessage = '*AUTHOR* 😎\n' + origMessage;
editorSpan.childNodes[0].data = newMessage; // alter it!
console.log('origMessage:', `"${origMessage}"`, 'newMessage:', `"${editorSpan.childNodes[0].data}"`);
}
}
function handleEvent(e) {
console.log('handleEvent:', e);
if ((e.type === 'keydown' && e.key === 'Enter') || e.type === 'click') {
modifyMessage();
}
}
// SLIGHTLY MODIFIED CODE (should also handle send button and text messages below images)
function waitForElement(selector, callback) {
// check if element matching selector is already in the DOM
let matchedElement = document.querySelector(selector);
if (matchedElement) {
callback(matchedElement);
}
// setup a MutationObserver that listens to changes to the DOM
let mutObserver = new MutationObserver((mutationRecords, observer) => {
for (const mutation of mutationRecords) {
if (mutation.addedNodes?.length > 0) console.log('addedNodes:', mutation.addedNodes);
for (const addedNode of mutation.addedNodes) {
if (addedNode.matches(selector)) {
callback(addedNode);
}
}
/*if (mutation.removedNodes?.length > 0) console.log('removedNodes:', mutation.removedNodes);
for (const removedNode of mutation.removedNodes) {
if (removedNode.matches(selector)) {
callback(removedNode);
}
}*/
}
});
const options = {
childList: true,
subtree: true,
};
mutObserver.observe(document.documentElement, options);
return mutObserver; // so that you can call .disconnect() to stop observing
}
let editorElement = null; // this will store the editorElement for the current chat
let sendButtonElement = null; // this will store the sendButtonElement for the current chat
// watch for changes to the DOM (for elements added inside "._2xAQV" (which match the right panes))
let observer = waitForElement('._2xAQV *', (matchedElement) => {
console.log('matchedElement:', matchedElement);
// editor
const editorSelector = '._2xAQV .lexical-rich-text-input'; //'#main div[data-lexical-editor]'
let newEditorElement = document.querySelector(editorSelector);
if (editorElement != newEditorElement) { // check if it's actually a different editorElement
editorElement = newEditorElement;
console.log('new editorElement:', editorElement);
const USE_CAPTURE = true;
if (editorElement) editorElement.removeEventListener('keydown', handleEvent, USE_CAPTURE); // remove event listener
editorElement.addEventListener('keydown', handleEvent, {capture:USE_CAPTURE}); // NOTE: 'capture:true' may be needed in this case, to ensure the key event is captured before WA handles it */
}
// send button
const sendButtonSelector = '._2xAQV span[data-icon="send"]'; //'#main div[data-lexical-editor]'
let newSendButtonElement = document.querySelector(sendButtonSelector)?.parentElement;
if (sendButtonElement != newSendButtonElement) { // check if it's actually a different sendButtonElement
sendButtonElement = newSendButtonElement;
console.log('new newSendButtonElement:', sendButtonElement);
const USE_CAPTURE = true;
if (sendButtonElement) sendButtonElement.removeEventListener('click', handleEvent, USE_CAPTURE); // remove event listener
sendButtonElement.addEventListener('click', handleEvent, {capture:USE_CAPTURE}); // NOTE: 'capture:true' may be needed in this case, to ensure the click event is captured before WA handles it */
}
});
// observer.disconnect();
Could be refactored a bit, but you should get the gist of it. Anyway let me know if it works for you.