Atelier de programmation: Créer une extension Chrome en JavaScript à l'aide de Gemini

1. Introduction

Vous participez à un appel Meet, mais vous ne voulez pas être le premier à le faire ? Si cette description vous correspond, nous avons la solution pour vous !

À la suite de cet atelier de programmation, vous allez créer une extension Chrome qui vous avertit lorsque le tout premier participant rejoint l'appel.

Vous allez découvrir les différents éléments d'une extension Chrome, puis examiner en détail chacune d'elles. Vous découvrirez les fonctions d'extension telles que les scripts de contenu, les service workers et la transmission de messages.

Vous devez respecter la version 3 du fichier manifeste pour pouvoir recevoir une notification chaque fois qu'un participant rejoint un appel Meet.

2. Avant de commencer

Prérequis

Bien que cet atelier de programmation soit adapté aux débutants, maîtriser les bases de JavaScript peut considérablement améliorer votre expérience.

Configuration/Conditions requises

  • Le navigateur Google Chrome
  • Une configuration IDE/Éditeur sur votre système local
  • Installez la gcloud cli si vous souhaitez activer l'API Gemini à l'aide de gcloud.

Activer l'API Gemini

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. Que la fête commence

Installation de base des extensions

Créons un répertoire que nous utiliserons comme racine de notre projet.

mkdir gemini-chrome-ext
cd gemini-chrome-ext

Avant de commencer à poser des questions précises à Gemini, posons quelques questions sur la structure générique d'une extension Chrome.

Requête :

What are the important parts to build a chrome extension?

Nous obtenons une réponse spécifiant des détails mineurs sur le fichier manifest, le background script et des détails sur l'interface utilisateur. Examinons plus en détail ces fichiers spécifiques.

Requête :

Create a manifest.json file to build a chrome extension.
Make the name of the extension "Meet Joinees Notifier"
and the author "<YOUR_EMAIL>"

Vous pouvez saisir le nom de votre choix et votre adresse e-mail dans le champ "author".

Gemini renvoie le contenu du fichier manifeste dont nous avons besoin, mais nous obtenons des champs supplémentaires dont nous n'avons pas besoin, comme le champ action. Nous avons également besoin d'une description. Résolvons ce problème.

Requête :

Remove the "action" field and make the description as
"Adds the ability to receive a notification when a participant joins a Google meet".

Plaçons ce contenu dans le fichier manifest.json, à la racine de votre projet.

À ce stade, le fichier manifeste doit se présenter comme suit :

{
    "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>"
}

Pour l'instant, supprimez tous les autres champs supplémentaires générés dans votre fichier manifeste, car cet atelier de programmation suppose qu'ils figurent dans votre fichier manifeste.

Maintenant, comment vérifier que l'extension fonctionne ? Demandons à notre ami Gemini, d'accord.

Requête :

Guide me on the steps needed to test a chrome extension on my local filesystem.

Il m'indique quelques étapes pour le tester. Accédez à "Extensions Page" en accédant à chrome://extensions et veillez à activer le bouton "Developer Mode", qui devrait afficher le bouton "Load unpacked", que nous pouvons utiliser pour accéder localement au dossier qui contient les fichiers d'extension. L'extension devrait ensuite s'afficher dans "Extensions Page".

3d802a497ce0cfc2.png

92db1999a1800ecd.png

Parfait ! Nous pouvons voir notre extension, mais commençons par ajouter quelques fonctionnalités.

4. Ajouter un script de contenu

Nous voulons exécuter du code JavaScript uniquement sur https://meet.google.com, ce que nous pouvons faire à l'aide de scripts de contenu. Demandons à Gemini comment y parvenir dans notre extension.

Requête :

How to add a content script in our chrome extension?

Ou, plus précisément:

Requête :

How to add a content script to run on meet.google.com subdomain in our chrome extension?

Ou une autre version:

Requête :

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 nous fournit les modifications exactes à effectuer dans le fichier manifest.json ainsi que le code JavaScript dont nous avons besoin dans le fichier content.js.

Avec l'ajout de content_scripts, notre fichier manifeste devient:

{
    "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"]
        }
    ]
}

Cela indique à Chrome d'injecter le script de contenu content.js chaque fois que nous accédons à une page du sous-domaine " https://meet.google.com". Ajoutons ce fichier et testons-le.

Ajoutons ce code dans le fichier content.js.

console.log("Hello Gemini");

Bien sûr ! "Hello Gemini" s'affiche sur meet.google.com sur la console 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("Bonjour Gemini !');

6216bab627c31e6c.png

d61631cd9962ffe5.png

Parfait ! Nous sommes maintenant en mesure d'ajouter des fonctionnalités spécifiques au JavaScript à notre application. Prenons un moment pour réfléchir à ce que nous essayons de réaliser.

Améliorer le script du contenu

Nous souhaitons recevoir une notification lorsqu'un utilisateur rejoint une réunion alors que nous sommes sur la page de la réunion(si nous avons la possibilité de participer à la réunion). Pour ce faire, observons les changements visuels d'écran lorsque la réunion est vide par rapport à l'arrivée d'un participant.

Voici comment cela se présente lorsqu'il n'y a personne dans la réunion.

fe5a0c95b20e7f72.png

Alors qu'il s'agit des visuels quand certains participants à la réunion sont présents.

7a5ef60521d961cc.png

Nous constatons d'emblée deux différences notables:

  1. Le texte d'état passe de "Il n'y a personne d'autre". par "[L'utilisateur] participe à cet appel".
  2. Nous pouvons voir les images des utilisateurs qui ont rejoint l'appel.

Ces deux modifications fonctionnent pour nous si nous voulons savoir si quelqu'un a rejoint la réunion, mais cette dernière a la possibilité d'obtenir des informations sur les utilisateurs qui ont déjà rejoint la réunion. Essayons donc de les utiliser.

Ouvrir l'inspecteur d'éléments Dans Chrome à l'aide du raccourci clavier ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C), cliquez sur l'image de l'utilisateur qui vous a rejoint.

Nous remarquons qu'une image comporte peu de chaînes de classe et que l'attribut title de l'image contient le nom de l'utilisateur qui a rejoint la réunion. De plus, ce tag d'image est encapsulé dans un tag div avec la classe U04fid. Une fois que nous avons ajouté des participants à notre réunion de test, nous constatons que ce tag div parent héberge plusieurs images(correspondant à différents utilisateurs).

Nous avons donc une stratégie partielle en tête:

  1. Détecter les cas où le div avec la classe U04fid contient des éléments.
  2. Si c'est le cas, cela signifie que nous avons des participants à notre réunion.
  3. Informez l'utilisateur.

Revenons à Gemini pour voir comment nous pouvons y parvenir étape par étape.

Requête :

How to detect for element changes in the HTML page?

Cela nous donne une solution impliquant MutationObserver, mais sans rien de spécifique à notre exigence. Essayons de modifier la requête.

Requête :

How can I detect if an element "div.U04fid" has any child images in the HTML page?

Cette fois, nous obtenons une solution qui n'inclut pas MutationObserver, mais une solution qui vérifie les jointures à l'aide de l'élément div parent.

Voici ce que nous avons reçu:

const divU04fid = document.querySelector('div.U04fid');
const childImages = divU04fid.querySelectorAll('img');
if (childImages.length > 0) {
  // div.U04fid has at least one child image.
}

Essayons de modifier encore davantage notre approche:

Requête :

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.

Elle s'affiche avec l'extrait de code suivant:

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
});

Copiez ce code dans le fichier content.js, actualisez l'extension, puis actualisez la page Meet.

Désormais, lorsqu'un utilisateur rejoint notre réunion, nous pouvons voir l'instruction de journalisation dans la console.

5. Envoyer une notification à l'utilisateur

Maintenant que nous sommes en mesure de détecter si un participant a rejoint la réunion, essayons d'ajouter la partie notification dans notre extension Chrome. Nous pouvons parcourir la documentation de l'extension Chrome ou même modifier les invites afin de savoir ce que nous recherchons, mais nous devons essentiellement utiliser l'API chrome.notifications.create, et l'appel de cette méthode doit provenir d'un service worker d'arrière-plan.

Requête :

Using the documentation for chrome notifications tell me how to use the chrome.notifications.create method.

Voici quelques étapes détaillées, dont les principaux sont les suivants:

  • Ajoutez l'autorisation notifications dans le fichier manifeste.
  • Appelez la méthode chrome.notifications.create
  • L'appel doit être effectué dans un script d'arrière-plan.

Pour ajouter un script d'arrière-plan à une extension Chrome dans manifest version 3, nous avons besoin de la déclaration background.service_worker dans le fichier manifest.json.

Nous créons donc un fichier appelé background.js et ajoutons ce qui suit à notre fichier manifest.json.

"background": {
        "service_worker": "background.js"
},
"permissions": [
        "notifications"
]

Avec les ajouts ci-dessus, notre fichier manifeste se présente comme suit:

{
    "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"
    ]
}

Requête :

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

Enregistrez cette image à la racine de votre dossier et renommez-la success.png.

b2c22f064a3f2d9c.png

Ajoutez ensuite l'extrait de code suivant à votre 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");

À présent, actualisez l'extension à partir de la page des extensions. Un pop-up de notification doit immédiatement s'afficher.

6. Ajoutez la transmission de messages dans votre extension Chrome

La dernière étape majeure consiste à associer la détection d'un participant par le script de contenu à la méthode sendNotification du script en arrière-plan. Dans le contexte des extensions Chrome, la méthode est d'utiliser une technique appelée message passing.

Cela permet la communication entre les différentes parties d'une extension Chrome, dans notre cas du script de contenu au service worker d'arrière-plan. Demandons à notre ami Gemini comment y parvenir.

Requête :

How to send a message from the content script to the background script in a chrome extension

Gemini répond par des appels pertinents à chrome.runtime.sendMessage et chrome.runtime.onMessage.addListener.

Globalement, nous utiliserons sendMessage pour envoyer un message à partir du script de contenu indiquant qu'un utilisateur a rejoint l'appel Meet, et onMessage.addListener en tant qu'écouteur d'événements pour réagir au message envoyé par le script de contenu. Dans ce cas, nous allons déclencher l'appel de la méthode sendNotification à partir de cet écouteur d'événements.

Nous allons transmettre le message de notification et une propriété action au service worker d'arrière-plan. La propriété action décrit la réponse du script d'arrière-plan.

Voici notre code 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"
    });
}

Voici notre code 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"
    });
}

Essayons de personnaliser le message de notification et d'obtenir un ID de notification unique. Dans le message de notification, nous pouvons inclure le nom de l'utilisateur. Souvenez-vous qu'à une étape précédente, nous avons pu voir le nom de l'utilisateur dans l'attribut title de l'image. Nous pouvons donc récupérer le nom du participant en utilisant document.querySelector('div.U04fid > img').getAttribute('title')..

Concernant l'ID de notification, nous pouvons récupérer l'ID d'onglet du script de contenu et l'utiliser comme ID de notification. Vous pouvez le faire dans notre écouteur d'événements chrome.runtime.onMessage.addListener en utilisant sender.tab.id..

Enfin, nos fichiers devraient ressembler à ceci:

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. Félicitations

En peu de temps, nous avons pu créer une extension Chrome avec l'aide de Gemini. Que vous soyez un développeur d'extensions Chrome expérimenté ou que vous ne connaissiez pas bien le monde des extensions, Gemini peut vous aider dans toutes les tâches que vous souhaitez accomplir.

Je vous encourage à vous renseigner sur les différentes choses que vous pouvez faire avec une extension Chrome. De nombreuses API, comme chrome.storage, alarms, etc., valent la peine d'être explorées. Où que vous soyez, utilisez Gemini ou la documentation pour savoir ce que vous faites mal ou trouver différentes façons de résoudre le problème.

Il faut souvent modifier la requête pour obtenir l'aide nécessaire, mais nous pouvons le faire à partir d'un seul onglet qui conserve tout le parcours contextuel.