Codelab: создание расширения Chrome на JavaScript с помощью Gemini

1. Введение

Вы присоединяетесь к встрече, но не хотите делать это первым? Если это относится к вам, у нас есть решение для вас!

Следуя этой лабораторной работе, вы создадите расширение Chrome, которое будет предупреждать вас, когда к звонку присоединяется самый первый участник.

Вы изучите различные элементы расширения Chrome, а затем углубитесь в каждую часть расширения. Вы узнаете о функциях расширения, таких как сценарии контента, сервисные работники и передача сообщений.

Вам необходимо придерживаться версии манифеста v3, чтобы иметь возможность получать уведомление всякий раз, когда участник присоединяется к совещанию.

2. Прежде чем начать

Предварительные условия

Хотя эта лаборатория кода подходит для новичков, базовое понимание Javascript может значительно улучшить ваш опыт.

Настройка/Требования

  • Браузер Chrome
  • Настройка IDE/редактора в вашей локальной системе.
  • Установите интерфейс gcloud, если вы хотите включить Gemini API с помощью gcloud.

Включите 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. Пусть начнется веселье

Базовая установка расширения

Давайте создадим каталог, который мы будем использовать в качестве корня нашего проекта.

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

Прежде чем мы начнем задавать конкретные вопросы Gemini, давайте зададим несколько вопросов об общей структуре расширения Chrome.

Быстрый:

What are the important parts to build a chrome extension?

Мы получаем ответ, в котором указываются мелкие подробности о файле manifest , background script и сведения о пользовательском интерфейсе. Давайте более подробно рассмотрим эти конкретные файлы.

Быстрый:

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

В поле автора вы можете указать желаемое имя и адрес электронной почты.

Gemini возвращает необходимое нам содержимое файла манифеста, но мы получаем некоторые дополнительные поля, которые нам не понадобятся, например поле action . Также нам нужно описание. Давайте это исправим.

Быстрый:

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

Давайте поместим это содержимое в файл 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>"
}

На данный момент удалите все другие дополнительные поля, созданные в вашем файле манифеста, поскольку эта кодовая лаборатория предполагает наличие этих полей в вашем файле манифеста.

Как теперь проверить, работает ли расширение? Давайте спросим нашего друга Близнецов?

Быстрый:

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

Это дает мне несколько шагов, как это проверить. Давайте перейдем на "Extensions Page" перейдя по адресу chrome://extensions , и обязательно включите кнопку "Developer Mode" , после чего должна появиться кнопка "Load unpacked" , которую мы можем использовать для перехода к папке, содержащей файлы расширений локально. Как только мы это сделаем, мы сможем увидеть расширение на "Extensions Page" .

3d802a497ce0cfc2.png

92db1999a1800ecd.png

Большой! Мы видим наше расширение, но давайте начнем добавлять немного функционала.

4. Добавьте скрипт контента

Мы хотим запускать некоторый код JavaScript только на https://meet.google.com что мы можем сделать с помощью сценариев контента. Давайте спросим Gemini, как этого добиться в нашем расширении.

Быстрый:

How to add a content script in our chrome extension?

Или более конкретно:

Быстрый:

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

Или другая версия:

Быстрый:

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 дает нам точные изменения, которые нам нужно внести в наш файл Manifest.json, а также JavaScript, который нам нужен в нашем файле content.js .

С добавлением content_scripts наш файл манифеста станет таким:

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

Это указывает Chrome вставлять скрипт контента content.js всякий раз, когда мы переходим на страницу в поддомене « https://meet.google.com ». Давайте добавим этот файл и проверим его?

Давайте добавим этот код в файл content.js .

console.log("Hello Gemini");

Конечно же! Когда мы посещаем сайт meet.google.com, мы видим «Hello Gemini» на консоли JavaScript (Mac: Cmd + Opt + J / Win/Linux: Ctrl + Shift + J ).

манифест.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"

        ]

    }

]

}

контент.js

console.log("Привет, Близнецы!");

6216bab627c31e6c.png

d61631cd9962ffe5.png

Большой! Теперь мы можем добавить в наше приложение некоторые функции, специфичные для JavaScript. Давайте на минутку подумаем, чего мы пытаемся достичь.

Улучшение сценария контента

Мы хотим иметь возможность получать уведомление, когда кто-то присоединяется к собранию, когда мы находимся на странице собрания (где у нас есть возможность присоединиться к собранию). Чтобы достичь этого, давайте посмотрим, как визуально меняется экран, когда собрание пусто, и когда кто-то присоединился к собранию.

Вот так это выглядит, когда на собрании никого нет.

fe5a0c95b20e7f72.png

А это визуалы, когда на встрече есть какие-то участники.

7a5ef60521d961cc.png

Мы сразу видим два заметных различия:

  1. Текст статуса изменится с «Здесь больше никого нет» на «[Пользователь] участвует в этом звонке».
  2. Мы можем видеть изображения пользователей, присоединившихся к звонку.

Оба эти изменения подойдут нам, если мы хотим узнать, присоединился ли кто-нибудь к собранию, но у последнего есть некоторые возможности получить информацию об уже присоединившихся пользователях, поэтому давайте попробуем это использовать.

Откройте «Инспектор элементов» в Chrome с помощью горячей клавиши ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C ), затем щелкните изображение присоединившегося пользователя.

Мы можем заметить, что есть изображение с несколькими строками классов, а атрибут title изображения содержит имя пользователя, присоединившегося к собранию. Более того, этот тег изображения заключен в div с классом U04fid . Как только мы добавим несколько участников к нашей тестовой встрече, мы увидим, что в этом родительском div размещено несколько изображений (соответствующих разным пользователям).

Итак, у нас есть частичная стратегия:

  1. Обнаруживает, когда элемент div с классом U04fid содержит какие-либо элементы.
  2. Если да, то это означает, что на нашем собрании есть участники.
  3. Уведомить пользователя.

Давайте вернемся к Близнецам и спросим, ​​как мы можем достичь этого шаг за шагом.

Быстрый:

How to detect for element changes in the HTML page?

Это дает нам решение с использованием MutationObserver , но ничего особенного для наших требований, давайте попробуем изменить приглашение.

Быстрый:

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

На этот раз мы получаем решение, которое не включает MutationObserver , но проверяет соединения с помощью родительского div.

Вот что мы получили:

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

Попробуем еще больше модифицировать наш подход:

Быстрый:

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.

Он возвращается со следующим фрагментом кода:

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

Скопируйте этот код в файл content.js , перезагрузите расширение (затем перезагрузите страницу встречи).

Теперь, когда кто-то присоединяется к нашей встрече, мы можем видеть запись журнала в консоли.

5. Отправьте уведомление пользователю.

Теперь, когда мы можем определять, когда участник присоединился к собранию, давайте попробуем добавить часть уведомления в наше расширение Chrome. Мы можем просмотреть документацию по расширению Chrome или даже настроить подсказки, чтобы узнать, что мы ищем, но по сути нам нужно использовать API chrome.notifications.create , и вызов этого метода должен осуществляться работником фоновой службы.

Быстрый:

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

Мы видим несколько детальных шагов, основными из которых являются:

  • Добавьте разрешение notifications в файл манифеста.
  • Вызовите chrome.notifications.create
  • Вызов должен быть в фоновом сценарии.

Чтобы добавить фоновый скрипт к расширению Chrome в manifest version 3 , нам нужно объявление background.service_worker в нашем файле manifest.json .

Итак, мы создаем файл с именем background.js и добавляем следующее в наш файл манифеста.json.

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

С учетом вышеперечисленных дополнений наш файл манифеста станет:

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

Быстрый:

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

Сохраните это изображение в корне вашей папки и переименуйте его в success.png .

b2c22f064a3f2d9c.png

Затем добавьте следующий фрагмент кода в свой 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");

Теперь перезагрузите расширение со страницы расширений, и вы сразу же увидите всплывающее окно с уведомлением.

6. Добавьте передачу сообщений в расширение Chrome.

Теперь последний важный шаг, который нам нужен, — это соединить обнаружение участника сценарием содержимого и метод sendNotification в фоновом сценарии. В контексте расширений Chrome это можно сделать с помощью метода, называемого message passing .

Это обеспечивает связь между различными частями расширения Chrome, в нашем случае от сценария контента до работника фоновой службы. Давайте спросим нашего друга Близнецов, как этого добиться.

Быстрый:

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

Gemini отвечает соответствующими вызовами chrome.runtime.sendMessage и chrome.runtime.onMessage.addListener .

По сути, мы будем использовать sendMessage для отправки сообщения из сценария содержимого о том, что кто-то присоединился к вызову встречи, и onMessage.addListener в качестве прослушивателя событий, чтобы реагировать на сообщение, отправленное сценарием содержимого. В этом случае мы инициируем вызов метода sendNotification из этого прослушивателя событий.

Мы передадим сообщение уведомления и свойство action работнику фоновой службы. Свойство action описывает, на что реагирует фоновый сценарий.

Итак, вот наш код 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"
    });
}

А это наш код 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"
    });
}

Давайте попробуем настроить уведомление и получить уникальный идентификатор уведомления. В уведомляющее сообщение мы можем включить имя пользователя. Если вспомнить предыдущий шаг, мы могли увидеть имя пользователя в атрибуте заголовка изображения. Итак, мы можем получить имя участника, используя document.querySelector('div.U04fid > img').getAttribute('title').

Что касается идентификатора уведомления, мы можем получить идентификатор вкладки сценария содержимого и использовать его в качестве идентификатора уведомления. Это можно сделать внутри нашего прослушивателя событий chrome.runtime.onMessage.addListener , используя sender.tab.id.

Наконец, наши файлы должны выглядеть примерно так:

манифест.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"
    ]
}

контент.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",
    });
}

фон.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. Поздравления

За короткое время мы смогли создать расширение для Chrome с помощью Gemini. Независимо от того, являетесь ли вы опытным разработчиком расширений Chrome или новичком в мире расширений, Gemini может помочь вам с любыми задачами, которые вы хотите достичь.

Я бы посоветовал вам спросить о различных вещах, которые вы можете сделать с помощью расширения Chrome. Существует множество API-интерфейсов, которые стоит просмотреть, например chrome.storage , alarms и т. д. Если вы чувствуете, что застряли, используйте Gemini или документацию, чтобы узнать, что вы делаете неправильно, или найти различные способы решения проблемы.

Часто требуется изменить подсказку, чтобы получить необходимую помощь, но мы можем сделать это на одной вкладке, которая сохраняет все наше контекстное путешествие.