Codelab: פיתוח תוסף ל-Chrome ב-JavaScript באמצעות Gemini

1. מבוא

הצטרפת לשיחה ב-Meet אבל לא רוצה להיות הראשון שעושה זאת? אם תיאור זה מתאים לך, יש לנו פתרון שמתאים לך.

בהמשך ל-Codelab הזה, יוצרים תוסף ל-Chrome ששולח התראה כשהמשתתף הראשון מצטרף לשיחה.

תלמדו על הרכיבים השונים של תוסף ל-Chrome ותעמקו בכל חלק של התוסף. תלמדו על פונקציות של תוספים כגון סקריפטים של תוכן, Service Worker והעברת הודעות.

כדי לקבל התראה בכל פעם שמשתתף מצטרף לשיחה ב-Meet, צריך לפעול בהתאם לגרסה 3 של המניפסט.

2. לפני שמתחילים

דרישות מוקדמות

ה-Codelab הזה מתאים למתחילים, אבל הבנה בסיסית של JavaScript יכולה לשפר מאוד את החוויה שלך.

הגדרה/דרישות

  • דפדפן Chrome
  • הגדרת סביבת פיתוח משולבת (IDE)/עריכה במערכת המקומית.
  • כדי להפעיל את Gemini API באמצעות gcloud, מתקינים את 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.

כן, יוצגו לי כמה שלבים לבדיקה. כדי לנווט אל "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 מספק לנו את השינויים המדויקים שאנחנו צריכים לבצע בקובץ המניפסט.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, אנחנו רואים את ההודעה 'שלום 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

ניתן לראות מיד שני הבדלים עיקריים:

  1. טקסט הסטטוס משתנה מ'אין כאן אף אחד אחר'. ל"[משתמש] נמצא בשיחה הזו".
  2. אנחנו יכולים לראות את התמונות של המשתמשים שהצטרפו לשיחה.

שני השינויים האלה יעזרו לנו אם נרצה לדעת אם מישהו הצטרף לפגישה, אבל כדי לעשות זאת יש כמה אפשרויות לקבל מידע על המשתמשים שכבר הצטרפו. ננסה להשתמש בזה.

פתיחת הכלי לבדיקת הרכיבים ב-Chrome באמצעות מקש הקיצור ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C), ולאחר מכן לוחצים על התמונה של המשתמש שהצטרף.

שמנו לב שיש תמונה עם מעט מחרוזות כיתתיות, ובמאפיין הכותרת של התמונה מופיע השם של המשתמש שהצטרף לפגישה. בנוסף, תג התמונה הזה מוקף ב-div עם מחלקה U04fid. אחרי שנוסיף כמה משתתפים לפגישת הניסיון, נוכל לראות שה-div הזה, ההורה, מארח כמה תמונות(בהתאם למשתמשים שונים).

לכן, יש לנו אסטרטגיה חלקית:

  1. זיהוי מקרים שבהם ה-div עם המחלקה U04fid מכיל רכיבים.
  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 או אפילו לשנות את ההנחיות כדי להבין מה אנחנו מחפשים, אבל בעיקרון אנחנו צריכים להשתמש ב-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, במקרה שלנו, מהסקריפט של התוכן ועד ל-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 כדי לשלוח הודעה מתסריט התוכן שמישהו הצטרף לשיחה בפגישה, וב-onMessage.addListener כמאזינים לאירוע כדי להגיב להודעה שנשלחה על ידי תסריט התוכן. במקרה הזה, נפעיל את הקריאה ל-method sendNotification מה-event listener הזה.

אנחנו נעביר את הודעת ההתראה והמאפיין action ל-Worker Service ברקע. המאפיין 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').

בנוגע למזהה ההתראה, אנחנו יכולים לאחזר את מזהה הכרטיסייה של סקריפט התוכן ולהשתמש בו כמזהה התראה. ניתן לעשות זאת מתוך ה-event listener 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. מזל טוב

תוך זמן קצר הצלחנו לפתח תוסף ל-Chrome בעזרת Gemini. גם מפתחים מנוסים של תוספים ל-Chrome וגם מפתחים חדשים בעולם התוספים – Gemini יכול לעזור לכם בביצוע כל המשימות שרוצים.

אני ממליץ לך לשאול מה אפשר לעשות באמצעות תוסף ל-Chrome. יש הרבה ממשקי API ששווה לעיין בהם, כמו chrome.storage, alarms וכו'. בכל מקום שבו נתקלת בבעיה, אפשר להשתמש ב-Gemini או במסמכי התיעוד כדי לדעת מה הייתה הבעיה או לאסוף דרכים שונות לפתרון הבעיה.

לעיתים קרובות צריך לשנות את ההנחיה כדי לקבל את העזרה הנדרשת, אבל אפשר לעשות את זה דרך כרטיסייה אחת ששומרת את כל התהליך לאורך ההקשר.