1. Introducción
¿Quieres unirte a una llamada de Meet, pero no quieres ser la primera persona en hacerlo? Si es así, tenemos la solución para ti.
Después de completar este codelab, crearás una extensión de Chrome que te alertará cuando el primer participante se una a la llamada.
Aprenderás sobre los diferentes elementos de una extensión de Chrome y, luego, profundizarás en cada parte de la extensión. Aprenderás sobre las funciones de extensión, como las secuencias de comandos de contenido, los service workers y el paso de mensajes.
Debes cumplir con el lanzamiento del manifiesto v3 para poder recibir una notificación cada vez que un participante se una a una llamada de Meet.
2. Antes de comenzar
Requisitos previos
Si bien este codelab es adecuado para principiantes, tener una comprensión básica de JavaScript puede mejorar en gran medida tu experiencia.
Configuración y requisitos
- El navegador Chrome
- Un IDE o editor configurado en tu sistema local
- Instala gcloud CLI si deseas habilitar la API de Gemini con gcloud.
Habilita la API de Gemini
- En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
- Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Obtén información sobre cómo verificar si la facturación está habilitada en un proyecto.
- Navega a la página de Gemini Marketplace para habilitar la API. También puedes usar gcloud para habilitar la API:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID - Visita la página de Gemini en la consola de Cloud en una pestaña nueva y haz clic en "Comenzar a chatear".
Note that if you're writing the code in the Cloud Shell editor, then you will have to download the folder somewhere on your local filesystem to test the extension locally.
3. Empecemos a celebrar
Instalación básica de extensiones
Creemos un directorio que usaremos como raíz de nuestro proyecto.
mkdir gemini-chrome-ext
cd gemini-chrome-ext
Antes de comenzar a hacerle preguntas específicas a Gemini, hagámosle algunas preguntas sobre la estructura genérica de una extensión de Chrome.
Instrucción:
What are the important parts to build a chrome extension?
Obtenemos una respuesta que especifica detalles menores sobre el archivo manifest, el background script y detalles sobre la interfaz de usuario. Analicemos estos archivos específicos con más detalle.
Instrucción:
Create a manifest.json file to build a chrome extension. Make the name of the extension "Meet Joinees Notifier" and the author "<YOUR_EMAIL>"
Puedes usar el nombre que desees y tu correo electrónico en el campo de autor.
Gemini devuelve el contenido del archivo de manifiesto que necesitamos, pero también obtenemos algunos campos adicionales que no necesitaremos, como el campo action. Además, necesitamos una descripción. Sin embargo, podemos solucionarlo.
Instrucción:
Remove the "action" field and make the description as "Adds the ability to receive a notification when a participant joins a Google meet".
Coloquemos este contenido en el archivo manifest.json en la raíz de tu proyecto.
En esta etapa, el archivo de manifiesto debería verse de la siguiente manera:
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>"
}
Por el momento, quita cualquier otro campo adicional que se haya generado en tu archivo de manifiesto, ya que este codelab supone que estos campos están en tu archivo de manifiesto.
Ahora, ¿cómo probamos que la extensión funciona? Preguntémosle a nuestro amigo Gemini, ¿de acuerdo?
Instrucción:
Guide me on the steps needed to test a chrome extension on my local filesystem.
Me indica algunos pasos para probarlo. Naveguemos a "Extensions Page". Para ello, ve a chrome://extensions y asegúrate de habilitar el botón "Developer Mode", que debería revelar el botón "Load unpacked", que podemos usar para navegar a la carpeta que contiene los archivos de extensión de forma local. Una vez que lo hagamos, deberíamos poder ver la extensión en "Extensions Page".


¡Genial! Podemos ver nuestra extensión, pero comencemos a agregarle algunas funciones.
4. Cómo agregar una secuencia de comandos de contenido
Queremos ejecutar código JavaScript solo en https://meet.google.com, lo que podemos hacer con secuencias de comandos de contenido. Preguntémosle a Gemini cómo lograrlo en nuestra extensión.
Instrucción:
How to add a content script in our chrome extension?
O, de forma más específica:
Instrucción:
How to add a content script to run on meet.google.com subdomain in our chrome extension?
O bien, otra versión:
Instrucción:
Help me add a content script named content.js to run on meet.google.com subdomain in our chrome extension. The content script should simply log "Hello Gemini" when we navigate to "meet.google.com".
Gemini nos indica los cambios exactos que debemos realizar en nuestro archivo manifest.json y también el código JavaScript que necesitamos en nuestro archivo content.js.
Con la incorporación de content_scripts, nuestro archivo de manifiesto se ve de la siguiente manera:
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "abc@example.com",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
]
}
Esto le indica a Chrome que inserte la secuencia de comandos de contenido content.js cada vez que navegamos a una página en el subdominio "https://meet.google.com". Agreguemos este archivo y probémoslo.
Agreguemos este código al archivo content.js.
console.log("Hello Gemini");
¡Por supuesto! Cuando visitamos meet.google.com, vemos "Hello Gemini" en la consola de JavaScript(Mac: Cmd + Opt + J; Win/Linux: Ctrl + Shift + J).
manifest.json
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "luke@cloudadvocacyorg.joonix.net",
"permissions": [
"tabs",
"notifications"
],
"content_scripts": [
{
"matches": [
"https://meet.google.com/*"
],
"js": [
"content.js"
]
}
]
}
content.js
console.log("Hello Gemini!");


¡Genial! Ahora estamos en condiciones de agregar algunas funciones específicas de JavaScript a nuestra app. Dediquemos un momento a pensar en lo que queremos lograr.
Mejora de la secuencia de comandos de contenido
Queremos poder recibir una notificación cuando alguien se une a una reunión mientras estamos en la página de la reunión(donde tenemos la opción de unirnos a la reunión). Para lograrlo, observemos cómo cambia visualmente la pantalla cuando la reunión está vacía y cuando alguien se une a ella.
Así se ve cuando no hay nadie en la reunión.

En cambio, estas son las imágenes cuando hay algunos participantes en la reunión.

A primera vista, podemos ver 2 diferencias notables:
- El texto de estado cambia de "No hay nadie más aquí" a "[Usuario] está en esta llamada".
- Podemos ver las imágenes de los usuarios que se unieron a la llamada.
Ambos cambios nos servirán si queremos saber si alguien se unió a la reunión, pero el último tiene algunas posibilidades de obtener información sobre los usuarios que ya se unieron, así que intentemos usarlo.
Abre el "Inspector de elementos" en Chrome con la tecla de acceso rápido ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C) y, luego, haz clic en la imagen del usuario que se unió.
Podemos observar que hay una imagen con pocas cadenas de clase y que el atributo title de la imagen tiene el nombre del usuario que se unió a la reunión. Además, esta etiqueta de imagen se incluye en un div con la clase U04fid. Una vez que agregamos algunos participantes a nuestra reunión de prueba, podemos ver que este elemento div principal aloja varias imágenes(que corresponden a diferentes usuarios).
Por lo tanto, tenemos una estrategia parcial en mente:
- Detecta cuándo el elemento div con la clase
U04fidcontiene elementos. - Si es así, significa que hay personas que se unieron a la reunión.
- Notifica al usuario.
Volvamos a Gemini y preguntemos cómo podemos lograr esto paso a paso.
Instrucción:
How to detect for element changes in the HTML page?
Nos brinda una solución que incluye MutationObserver, pero no es específica para nuestro requisito. Intentemos modificar la instrucción.
Instrucción:
How can I detect if an element "div.U04fid" has any child images in the HTML page?
Esta vez, obtenemos una solución que no incluye el MutationObserver, sino una que verifica los participantes con el div principal.
Esto es lo que recibimos:
const divU04fid = document.querySelector('div.U04fid');
const childImages = divU04fid.querySelectorAll('img');
if (childImages.length > 0) {
// div.U04fid has at least one child image.
}
Intentemos modificar aún más nuestro enfoque:
Instrucción:
Create a method which checks if the element div.U04fid has any child elements, if it does it should log it on the console. The method should be called checkJoinees and it should be called as a callback for MutationObserver which is observing the document.body.
Devuelve el siguiente fragmento de código:
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
// div.U04fid has at least one child element.
console.log('div.U04fid has at least one child element.');
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
Copia este código en el archivo content.js, vuelve a cargar la extensión(y, luego, vuelve a cargar la página de Meet).
Ahora, cuando alguien se une a nuestra reunión, podemos ver la instrucción de registro en la consola.
5. Envía una notificación al usuario
Ahora que podemos detectar cuándo un participante se unió a la reunión, intentemos agregar la parte de notificación en nuestra extensión de Chrome. Podemos explorar la documentación de la extensión de Chrome o incluso modificar las instrucciones para saber qué estamos buscando, pero, básicamente, necesitamos usar la API de chrome.notifications.create y la llamada a este método debe provenir de un service worker en segundo plano.
Instrucción:
Using the documentation for chrome notifications tell me how to use the chrome.notifications.create method.
Vemos algunos pasos detallados, y los aspectos más destacados son los siguientes:
- Agrega el permiso
notificationsal archivo de manifiesto. - Llama a chrome.notifications.create
- La llamada debe estar en una secuencia de comandos en segundo plano.
Para agregar una secuencia de comandos en segundo plano a una extensión de Chrome en manifest version 3, necesitamos la declaración background.service_worker en nuestro archivo manifest.json.
Por lo tanto, creamos un archivo llamado background.js y agregamos lo siguiente a nuestro archivo manifest.json.
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
Con las adiciones anteriores, nuestro archivo de manifiesto se ve de la siguiente manera:
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
}
Instrucción:
Create a method sendNotification that calls the chrome.notifications.create method with the message, "A user joined the call" for a chrome extension with manifest v3, the code is in the background service worker
Guarda esta imagen en la raíz de tu carpeta y cambia su nombre a success.png.

Luego, agrega el siguiente fragmento de código a tu archivo background.js.
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
sendNotification("notif-id", "test message");
Ahora vuelve a cargar la extensión desde la página de extensiones y deberías ver de inmediato una ventana emergente de notificación.
6. Agrega el paso de mensajes en tu extensión de Chrome
Ahora, el último paso importante que necesitamos es conectar la detección de un participante del script de contenido y el método sendNotification en el script en segundo plano. En el contexto de las extensiones de Chrome, la forma de hacerlo es a través de una técnica llamada message passing.
Esto permite la comunicación entre diferentes partes de una extensión de Chrome, en nuestro caso, desde la secuencia de comandos de contenido hasta el service worker en segundo plano. Preguntémosle a nuestro amigo Gemini cómo lograrlo.
Instrucción:
How to send a message from the content script to the background script in a chrome extension
Gemini responde con llamadas pertinentes a chrome.runtime.sendMessage y chrome.runtime.onMessage.addListener.
Básicamente, usaremos sendMessage para enviar un mensaje desde la secuencia de comandos de contenido que indique que alguien se unió a la llamada de Meet y onMessage.addListener como un objeto de escucha de eventos para reaccionar al mensaje enviado por la secuencia de comandos de contenido. En este caso, activaremos la llamada al método sendNotification desde este objeto de escucha de eventos.
Pasaremos el mensaje de notificación y una propiedad action al service worker en segundo plano. La propiedad action describe a qué responde la secuencia de comandos en segundo plano.
Este es nuestro código content.js:
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
// div.U04fid has at least one child element.
sendMessage();
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
function sendMessage() {
chrome.runtime.sendMessage({
txt: "A user has joined the call!",
action: "people_joined"
});
}
Este es nuestro código background.js:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "people_joined") {
sendNotification("notif-id", message.txt);
}
});
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
Intentemos personalizar el mensaje de notificación y obtener un ID de notificación único. En el mensaje de notificación, podemos incluir el nombre del usuario. Si recordamos un paso anterior, pudimos ver el nombre del usuario en el atributo title de la imagen. Por lo tanto, podemos recuperar el nombre del participante con document.querySelector('div.U04fid > img').getAttribute('title')..
En cuanto al ID de notificación, podemos recuperar el ID de la pestaña de la secuencia de comandos de contenido y usarlo como ID de notificación. Esto se puede lograr dentro de nuestro objeto de escucha de eventos chrome.runtime.onMessage.addListener usando sender.tab.id..
Por último, nuestros archivos deberían verse de la siguiente manera:
manifest.json
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
}
content.js
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
const name = document.querySelector('div.U04fid > img').getAttribute('title');
sendMessage(name);
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
function sendMessage(name) {
const joinee = (name === null ? 'Someone' : name),
txt = `${joinee} has joined the call!`;
chrome.runtime.sendMessage({
txt,
action: "people_joined",
});
}
background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "people_joined") {
sendNotification("" + sender.tab.id, message.txt); // We are casting this to string as notificationId is expected to be a string while sender.tab.id is an integer.
}
});
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
7. Felicitaciones
En poco tiempo, pudimos crear una extensión de Chrome con la ayuda de Gemini. Ya sea que seas un desarrollador experimentado de extensiones de Chrome o que recién te inicies en el mundo de las extensiones, Gemini puede ayudarte con las tareas que quieras realizar.
Te recomiendo que preguntes sobre las diferentes acciones que puedes realizar con una extensión de Chrome. Hay muchas APIs que vale la pena explorar, como chrome.storage, alarms, etcétera. Cuando te sientas atascado, usa Gemini o la documentación para saber qué estás haciendo mal o para recopilar diferentes formas de resolver el problema.
A menudo, es necesario modificar la instrucción para obtener la ayuda que se necesita, pero podemos hacerlo desde una sola pestaña que conserva todo nuestro recorrido contextual.