No sé cuál es la mejor manera de expresarlo, pero decidí hacer una publicación aquí ya que estuve buscando durante unos días y no pude encontrar una respuesta exacta a lo que quiero hacer.
Entonces, básicamente estoy tratando de desarrollar una extensión de Chrome simple para verificar si los usuarios de Reddit son bots o no. La extensión sería:
Al principio traté de hacer algo como esto en mi background.js :
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.status === 'complete'){ chrome.scripting.executeScript({ target: { tabId: tabId}, func: getAuthors, }, (authors) =>{ authors.forEach(element => { getAbout(element).then(about =>{ getComments(element).then(comments =>{ isBot(about,comments).then(response =>{ //ADD TAG }); }); }); }); }) } });La función getAuthors puede acceder con éxito al documento y recuperar los datos, el problema ocurre cuando tengo que acceder nuevamente para agregar las etiquetas.
Almacenar una matriz de autores y pasarla a otro chrome.scripting.executeScript no funciona, ya que la ejecución no esperará a que finalice el primer chrome.scripting.executeScript y lo ejecutará de inmediato.
Intenté invertir la lógica y usar mensajes también, esta vez en mi contentScript.js :
chrome.runtime.sendMessage('get-names', (names) => { console.log('received user data', names); names.forEach(element => { element.parentElement.appendChild(tag); }); });Ahora tengo una forma de agregar las etiquetas, pero no puedo enviar los datos para procesarlos en primer lugar, ya que, hasta donde yo sé, solo puede enviar una carga útil en respuesta . si envío un mensaje desde mi background.js , termino cayendo en una situación similar con el scripting y su ejecución asíncrona.
¿Hay una forma fácil de hacer esto?
Si bien es posible implementar esta tarea mediante una secuencia de comandos en segundo plano, una solución mejor y más simple es hacer todo en la secuencia de comandos de contenido.
La secuencia de comandos de contenido puede realizar cambios en DOM inmediatamente y puede realizar una solicitud de red a la URL del sitio actual para obtener los detalles del usuario. Sin embargo, la solicitud de red será asíncrona; de lo contrario, introduciría retrasos desagradables en la carga de la página.
Aquí está el esquema general:
document_start cuando DOM está vacío.window.localStorage del sitio porque es síncrono y pertenece al mismo proceso de pestaña interno, por lo que es rápido de leer.MutationObserver para detectar los elementos <a> agregados mientras se carga la página y también después para admitir la nueva interfaz de usuario de reddit (construye la página dinámicamente) y varios scripts de usuario/extensiones que expanden automáticamente los hilos en la antigua interfaz de usuario de reddit.manifiesto.json:
{ "content_scripts": [{ "matches": ["*://www.reddit.com/*", "*://old.reddit.com/*"], "js": ["content.js"], "css": ["content.css"], "run_at": "document_start" }],contenido.css:
a.is-bot::after { content: '[BOT]'; color: red; margin-left: .25em; }contenido.js:
const CLASS = 'is-bot'; const LS_KEY = 'b:'; const USER_URL = location.origin + '/user/'; const SEL = `a[href^="/user/"], a[href^="${USER_URL}"]`; const checkedElems = new WeakSet(); const promises = {}; new MutationObserver(onMutation) .observe(document, {subtree: true, childList: true}); function onMutation(mutations) { for (const {addedNodes} of mutations) { for (const n of addedNodes) { const toCheck = n.tagName === 'a' ? n.href.startsWith(USER_URL) && [n] : n.firstElementChild && n.querySelectorAll(SEL); for (const el of toCheck || []) { if (checkedElems.has(el)) continue; checkedElems.add(el); const name = el.href.slice(USER_URL.length); const isBot = localStorage[LS_KEY + name]; if (isBot === '1') { el.classList.add(CLASS); } else if (isBot !== '0') { (promises[name] || (promises[name] = checkUser(el, name))) .then(res => res && el.classList.add(CLASS)); } } } } } async function checkUser(el, name) { const isBot = await (await fetch(API_URL + name)).json(); localStorage[LS_KEY + name] = isBot ? '1' : '0'; delete promises[name]; return isBot; }