1. 简介
你是否想加入 Meet 通话,但不想率先加入?如果您属于这种情况,那我们就有适合您的解决方案!
完成此 Codelab 后,您将创建一个 Chrome 扩展程序,以便在第一个参与者加入通话时提醒您。
您将了解 Chrome 扩展程序的不同元素,然后深入了解该扩展程序的每个部分。您将了解扩展函数,例如内容脚本、Service Worker 和消息传递。
您需要遵循清单 v3 版本,才能在参与者加入 Meet 通话时收到通知。
2. 准备工作
前提条件
虽然此 Codelab 适合初学者,但掌握基本的 JavaScript 知识可以大大增强您的体验。
设置/要求
- Chrome 浏览器
- 在本地系统上设置 IDE/编辑器。
- 如果您想使用 gcloud 启用 Gemini API,请安装 gcloud cli。
启用 Gemini API
- 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。
- 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能。
- 前往 Gemini Marketplace 页面启用该 API。您也可以使用 gcloud 启用该 API:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID
- 访问新标签页中的 Cloud 控制台专用 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>"
}
暂时移除清单文件中生成的所有其他字段,因为此 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,每当我们导航到子网域“content.js
https://meet.google.com”。我们来添加此文件并进行测试,可以吗?
我们将此代码添加到 content.js
文件中。
console.log("Hello Gemini");
当然可以!当我们访问 meet.google.com 时,会看到“你好,Gemini”(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!");
??
太棒了!现在,我们可以为应用添加一些专门针对 JavaScript 的功能。让我们花点时间想一想,我们想要实现什么目标。
改进内容脚本
我们希望在通过会议页面(此处提供了加入会议的选项)时,当有人加入会议时,我们会收到通知。为此,让我们来观察一下,当会议没有人时以及有人加入会议时,屏幕的视觉变化情况。
这是会议中无人时的显示效果。
而这些是在有部分参与者参加会议时看到的视觉元素。
我们立刻就能看出 2 个显著差异:
- 状态文字会从“没有其他人在这里”变为“[用户] 已加入此通话”。
- 我们可以看到已加入通话的用户的图片。
如果我们想了解是否有人加入了会议,这两项更改对我们都有效,但后者还有可能获取已加入用户的信息,因此我们不妨使用该功能。
打开“元素检查器”在 Chrome 中使用热键(Mac:Cmd + Opt + C / Win: Ctrl + Shift + C
),然后点击已加入的用户的图片。
我们可以看到,图片中的类字符串很少,而且该图片的 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
方法的调用。
我们将通知消息和 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。这可以通过使用 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 扩展程序的其他功能。有很多 API 值得浏览,例如 chrome.storage
、alarms
等。无论遇到何种困难,您都可以使用 Gemini 或相关文档来了解您做错了什么,或收集不同的方法来解决问题。
通常需要修改提示以获得所需帮助,但我们可以从一个标签页执行此操作,该标签页保留了所有上下文信息。