Codelab: Gemini を使用して JavaScript で Chrome 拡張機能を作成する

1. はじめに

Meet 通話に参加したいけど、一番乗りは避けたいですか?そんなあなたにぴったりのソリューションがあります。

この Codelab では、最初の参加者が通話に参加したときにアラートを表示する Chrome 拡張機能を作成します。

Chrome 拡張機能のさまざまな要素について学習し、拡張機能の各部分を詳しく見ていきます。コンテンツ スクリプト、サービス ワーカー、メッセージ パッシングなどの拡張機能について説明します。

参加者が Meet 通話に参加するたびに通知を受け取るには、マニフェスト v3 リリースに準拠する必要があります。

2. 始める前に

前提条件

この Codelab は初心者向けですが、JavaScript の基礎知識があると、よりスムーズに進めることができます。

設定/要件

  • Chrome ブラウザ
  • ローカル システムに設定された IDE/エディタ。
  • gcloud を使用して Gemini API を有効にする場合は、gcloud CLI をインストールします。

Gemini API を有効にする

  • Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。
  • Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。
  • Gemini Marketplace ページに移動して API を有効にします。gcloud を使用して API を有効にすることもできます。gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID
  • 新しいタブで [Gemini for Cloud コンソール ページ] にアクセスし、[Start chatting] をクリックします。
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>"
}

この Codelab では、マニフェスト ファイルにこれらのフィールドがあることを前提としているため、マニフェスト ファイルで生成されたその他の追加フィールドは削除してください。

拡張機能が機能していることをテストするにはどうすればよいでしょうか?Gemini に聞いてみましょう。

プロンプト:

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

テスト方法の手順が表示されます。chrome://extensions に移動して "Extensions Page" に移動し、"Developer Mode" ボタンを有効にします。これにより、"Load unpacked" ボタンが表示されます。このボタンを使用して、拡張機能ファイルがローカルに保存されているフォルダに移動できます。移動すると、"Extensions Page" に拡張機能が表示されます。

3d802a497ce0cfc2.png

92db1999a1800ecd.png

これで、拡張機能が表示されました。機能を追加してみましょう。

4. コンテンツ スクリプトを追加する

https://meet.google.com でのみ JavaScript コードを実行したい場合は、コンテンツ スクリプトを使用します。拡張機能でこれを実現する方法を 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 ファイルに必要な変更と、content.js ファイルに必要な JavaScript を提供します。

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

これにより、サブドメイン「https://meet.google.com」のページに移動するたびに、コンテンツ スクリプト content.js が Chrome に挿入されます。このファイルを追加してテストしてみましょう。

このコードを content.js ファイルに追加しましょう。

console.log("Hello Gemini");

確かに、meet.google.com にアクセスすると、JavaScript コンソール(Mac: Cmd + Opt + J / Win/Linux: Ctrl + Shift + J)に「Hello Gemini」と表示されます。

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!");

6216bab627c31e6c.png

d61631cd9962ffe5.png

これで、アプリに JavaScript 固有の機能を追加できるようになりました。ここで、実現しようとしていることを考えてみましょう。

コンテンツ スクリプトの改善

会議ページ(会議に参加するオプションがある場所)で、誰かが会議に参加したときに通知を受け取れるようにしたいと考えています。これを実現するには、会議が空の場合と誰かが会議に参加した場合で、画面がどのように視覚的に変化するかを確認します。

会議に誰もいない場合は次のようになります。

fe5a0c95b20e7f72.png

会議に参加者がいる場合は次のようになります。

7a5ef60521d961cc.png

すぐにわかる 2 つの大きな違いがあります。

  1. ステータス テキストが「No one else is here」から「[User] is in this call」に変わります。
  2. 通話に参加したユーザーの画像が表示されます。

どちらの変更でも、誰かが会議に参加したかどうかを確認できますが、後者ではすでに参加しているユーザーに関する情報を取得できる可能性があるため、そちらを使用してみましょう。

ホットキー(Mac: Cmd + Opt + C / Win: Ctrl + Shift + C)を使用して Chrome で [要素の検査] を開き、参加したユーザーの画像をクリックします。

クラス文字列がいくつかある画像があり、画像のタイトル属性には会議に参加したユーザーの名前が含まれています。さらに、この画像タグはクラス U04fid の div でラップされています。テスト会議に参加者を追加すると、この親 div に複数の画像(異なるユーザーに対応)が表示されます。

そこで、次のような部分的な戦略を立てます。

  1. クラス U04fid の div に要素が含まれているかどうかを検出します。
  2. 含まれている場合は、会議に参加者がいることを意味します。
  3. ユーザーに通知します。

Gemini に戻って、この手順を段階的に実現する方法を尋ねてみましょう。

プロンプト:

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 ファイルにコピーし、拡張機能を再読み込みします(次に Meet ページを再読み込みします)。

これで、誰かが会議に参加すると、コンソールにログステートメントが表示されます。

5. ユーザーに通知を送信する

参加者が会議に参加したことを検出できるようになったので、Chrome 拡張機能に通知部分を追加してみましょう。Chrome 拡張機能のドキュメントを参照したり、プロンプトを調整して必要な情報を確認したりできますが、基本的には chrome.notifications.create API を使用する必要があります。このメソッドの呼び出しは、バックグラウンド Service Worker から行う必要があります。

プロンプト:

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

詳細な手順が表示されます。主なハイライトは次のとおりです。

  • マニフェスト ファイルに notifications 権限を追加します。
  • chrome.notifications.create を呼び出します。
  • 呼び出しはバックグラウンド スクリプトで行う必要があります。

manifest version 3 で Chrome 拡張機能にバックグラウンド スクリプトを追加するには、manifest.json ファイルに background.service_worker 宣言が必要です。

そこで、background.js というファイルを作成し、manifest.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 拡張機能のさまざまな部分(この場合はコンテンツ スクリプトからバックグラウンド Service Worker)間の通信が可能になります。その方法を Gemini に聞いてみましょう。

プロンプト:

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

Gemini は、chrome.runtime.sendMessagechrome.runtime.onMessage.addListener の関連する呼び出しを返します。

基本的には、sendMessage を使用して、誰かが Meet 通話に参加したというメッセージをコンテンツ スクリプトから送信します。onMessage.addListener は、コンテンツ スクリプトから送信されたメッセージに応答するイベント リスナーとして使用します。この場合、このイベント リスナーから sendNotification メソッドの呼び出しをトリガーします。

通知メッセージと action プロパティをバックグラウンド Service Worker に渡します。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"
    });
}

通知メッセージをカスタマイズして、一意の通知 ID を取得してみましょう。通知メッセージには、ユーザーの名前を含めることができます。前の手順で説明したように、画像のタイトル属性にユーザーの名前が表示されます。したがって、document.querySelector('div.U04fid > img').getAttribute('title'). を使用して参加者の名前を取得できます。

通知 ID については、コンテンツ スクリプトのタブ ID を取得して、通知 ID として使用できます。これは、sender.tab.id. を使用して、イベント リスナー chrome.runtime.onMessage.addListener 内で実現できます。

最終的に、ファイルは次のようになります。

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. 完了

短時間で Gemini を使用して Chrome 拡張機能を作成できました。Chrome 拡張機能の開発経験がある方も、拡張機能の世界に初めて触れる方も、Gemini は達成したいタスクをサポートします。

Chrome 拡張機能でできることについて質問することをおすすめします。chrome.storagealarms など、参照する価値のある API が多数あります。行き詰まった場合は、Gemini またはドキュメントを使用して、間違いを把握したり、問題を解決するさまざまな方法を収集したりできます。

必要なヘルプを得るにはプロンプトの変更が必要になることがよくありますが、コンテキストの履歴をすべて保持する 1 つのタブから変更できます。