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

1. はじめに

Meet 通話に参加する際に、一人で参加したくない場合は、これに該当する場合は、解決策があります。

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

Chrome 拡張機能のさまざまな要素について説明した後、拡張機能の各部分について詳しく説明します。コンテンツ スクリプト、Service Worker、メッセージの受け渡しなどの拡張関数について学習します。

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

2. 始める前に

前提条件

この Codelab は初心者に適していますが、JavaScript の基礎知識を身につけておくことで、使い勝手を大幅に向上させることができます。

設定/要件

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

Gemini API を有効にする

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. コンテンツ スクリプトを追加する

コンテンツ スクリプトを使用して、一部の 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 ファイルで必要な変更を正確に提供します。また、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"]
        }
    ]
}

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

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

console.log("Hello Gemini");

承知しました。meet.google.com にアクセスすると「Gemini に挨拶」と表示される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!");

6216bab627c31e6c.png

d61631cd9962ffe5.png

これでこれで、JavaScript 固有の機能をアプリに追加できるようになりました。ここで少し時間を取って、私たちが何を達成しようとしているのかを考えてみましょう。

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

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

会議に誰もいないときは、このように表示されます。

fe5a0c95b20e7f72.png

これは、会議の参加者が何人かの場合の映像です。

7a5ef60521d961cc.png

すぐに明らかになると、2 つの顕著な違いが 2 つあります。

  1. ステータス テキストが「誰もここにはいません」から変わる「[ユーザー] はこの通話に参加しています」に変更します。
  2. 通話に参加したユーザーの画像を確認できます。

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

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

画像にはクラス文字列がほとんどなく、画像のタイトル属性には会議に参加したユーザーの名前が入っています。さらに、このイメージタグは 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 を取得してみましょう。通知メッセージにはユーザー名を含めることができます。前のステップでは、画像の title 属性でユーザー名を確認できます。そのため、document.querySelector('div.U04fid > img').getAttribute('title'). を使用して参加者の名前を取得できます。

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

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

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 つのタブから行えば、すべてのコンテキスト ジャーニーを保持できます。