1. 简介
您是否要加入 Meet 通话,但不想成为第一个加入的人?如果您是这种情况,我们为您提供了解决方案!
完成本 Codelab 后,您将创建一个 Chrome 扩展程序,以便在第一个参与者加入通话时提醒您。
您将了解 Chrome 扩展程序的各种元素,然后深入了解扩展程序的每个部分。您将了解扩展程序功能,例如内容脚本、Service Worker 和消息传递。
您需要遵守 清单 v3 版本,才能在参与者加入 Meet 通话时收到通知。
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 控制台页面,然后点击“开始聊天”。
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" 中看到该扩展程序。


太棒了!我们可以看到自己的扩展程序,但我们先来添加一些功能。
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"]
}
]
}
这会告知 Chrome,每当我们前往子网域“https://meet.google.com”中的页面时,都注入内容脚本 content.js。我们来添加此文件并进行测试吧?
我们在 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!");


太棒了!现在,我们可以向应用添加一些 JavaScript 特定功能。我们花点时间思考一下我们想要实现的目标。
改进内容脚本
我们希望能够在会议页面(我们可以在其中选择加入会议)上收到有人加入会议的通知。为了实现此目的,我们来观察一下,当会议为空时与有人加入会议时,屏幕在视觉上是如何变化的。
会议中无人时,屏幕如下所示。

而当会议中有参与者时,屏幕如下所示。

我们可以立即看到 2 个显著差异:
- 状态文本从“No one else is here”更改为“[User] is in this call”。
- 我们可以看到已加入通话的用户的图片。
如果我们想知道是否有人加入了会议,这两种更改都适用,但后者有可能获取有关已加入会议的用户的信息,因此我们尝试使用后者。
使用热键(Mac:Cmd + Opt + C / Win: Ctrl + Shift + C)在 Chrome 中打开“元素检查器”,然后点击已加入用户的图片。
我们可以注意到,有一张图片,其中包含一些类字符串,并且图片的 title 属性包含已加入会议的用户的名称。此外,此图片标记封装在类为 U04fid 的 div 中。向测试会议添加一些加入者后,我们可以看到此父 div 托管多个图片(对应于不同的用户)。
因此,我们有了一个初步策略:
- 检测类为
U04fid的 div 何时包含任何元素。 - 如果包含,则表示会议中有加入者。
- 通知用户。
我们回到 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。

然后,将以下代码段添加到 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.sendMessage 和 chrome.runtime.onMessage.addListener 的相关调用。
本质上,我们将使用 sendMessage 从内容脚本发送消息,表明有人加入了 Meet 通话,并使用 onMessage.addListener 作为事件监听器来响应内容脚本发送的消息。在本例中,我们将从此事件监听器触发对 sendNotification 方法的调用。
我们将向后台 Service Worker 传入通知消息和 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"
});
}
我们尝试自定义通知消息并获取唯一的通知 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 扩展程序可以执行的不同操作。有很多值得浏览的 API,例如 chrome.storage、alarms 等。无论您遇到什么问题,都可以使用 Gemini 或文档来了解自己做错了什么,或者收集解决问题的不同方法。
通常需要修改提示才能获得所需的帮助,但我们可以在一个标签页中完成此操作,该标签页会保留我们的所有上下文历程。