درس تطبيقي حول الترميز: إنشاء إضافة Chrome في JavaScript باستخدام Gemini

1. مقدمة

هل ستنضم إلى مكالمة Meet ولكنك لا تريد أن تكون أول من ينضمّ إلى مكالمة Meet؟ إذا كان هذا الأمر ينطبق عليك، فلدينا الحل المناسب لك.

بعد هذا الدرس التطبيقي، ستنشئ إضافة Chrome تنبهك عند انضمام أول مشارك إلى المكالمة.

ستتعرف على العناصر المختلفة لإضافة Chrome ثم ستتعمق أكثر في كل جزء منها. وستتعرّف على وظائف الإضافات مثل النصوص البرمجية للمحتوى وعاملي الخدمة وتمرير الرسائل.

عليك الالتزام بإصدار البيان v3 لتتمكّن من تلقّي إشعار عند انضمام مشارك إلى مكالمة Meet.

2. قبل البدء

المتطلبات الأساسية

إنّ هذا الدرس التطبيقي حول الترميز مناسب للمبتدئين، إلا أنّ امتلاك فهم أساسي لـ JavaScript يمكن أن يحسّن تجربتك بشكل كبير.

الإعداد/المتطلبات

  • متصفح Chrome
  • إعداد IDE/Editor على نظامك المحلي
  • يُرجى تثبيت gcloud cli إذا أردت تفعيل واجهة Gemini API باستخدام gcloud.

تفعيل 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>"
}

عليك حاليًا إزالة أي حقول إضافية أخرى تم إنشاؤها في ملف البيان، لأنّ هذا الدرس التطبيقي يفترض توفُّر هذه الحقول في ملف البيان.

والآن، كيف نختبر عمل الإضافة؟ لنسأل صديقنا 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.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" على وحدة تحكم جافا سكريبت(Mac: Cmd + Opt + J / Win/Linux: Ctrl + Shift + J).

ملف البيان.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("مرحبًا Gemini!");

6216bab627c31e6c.png

d61631cd9962ffe5.png

رائع! نحن الآن في وضع يمكننا من خلاله إضافة بعض الوظائف الخاصة بJavaScript إلى تطبيقنا. لنتوقف لحظة ونفكر فيما نحاول تحقيقه.

تحسين النص البرمجي للمحتوى

نريد أن نتمكن من تلقّي إشعار عند انضمام شخص إلى اجتماع عندما نكون في صفحة الاجتماع(حيث يتوفّر لدينا خيار الانضمام إلى الاجتماع). لتحقيق ذلك، لنلاحظ كيف تتغير الشاشة مرئيًا عندما يكون الاجتماع فارغًا مقارنةً بانضمام شخص ما إلى الاجتماع.

هذه هي الطريقة التي يبدو عليها عندما لا يكون هناك أحد في الاجتماع.

fe5a0c95b20e7f72.png

بينما هذه هي العناصر المرئية عندما يكون هناك بعض المشاركين في الاجتماع.

7a5ef60521d961cc.png

ويمكننا ملاحظة اختلافين بارزين في الأغلب:

  1. سيتغيّر نص الحالة من "لا يوجد أحد آخر هنا" إلى "[مستخدم] في هذه المكالمة".
  2. يمكننا رؤية صور المستخدمين الذين انضموا إلى المكالمة.

سينجح كلا التغييرين إذا أردنا معرفة ما إذا كان أي شخص قد انضم إلى الاجتماع، ولكن لديه بعض الاحتمالات للحصول على معلومات حول المستخدمين الذين انضموا بالفعل، لذا دعنا نحاول استخدام ذلك.

افتح "Elements Inspector" (أداة فحص العناصر). في Chrome باستخدام مفتاح التشغيل السريع ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C) ثم انقر على صورة المستخدم الذي انضم.

يمكننا ملاحظة أن هناك صورة تحتوي على عدد قليل من سلاسل الفئات وأن سمة عنوان الصورة تحتوي على اسم المستخدم الذي انضم إلى الاجتماع. علاوة على ذلك، تكون علامة الصورة هذه ملفوفة في div مع الفئة U04fid. بمجرد أن نضيف بعض المشاركين إلى اجتماعنا التجريبي، يمكننا أن نرى أن القسم الرئيسي هذا يستضيف العديد من الصور(مقابلة لمستخدمين مختلفين).

إذن، لدينا استراتيجية جزئية:

  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 وأعِد تحميل الإضافة(ثم أعِد تحميل صفحة الاجتماع).

والآن عندما ينضم شخص إلى اجتماعنا، يمكننا الاطّلاع على بيان السجلّ في وحدة التحكّم.

5- إرسال إشعار إلى المستخدم

الآن وبعد أن تمكنا من اكتشاف وقت انضمام أحد المشاركين إلى الاجتماع، لنحاول إضافة جزء الإشعار في إضافة Chrome. يمكننا تصفّح مستندات إضافة Chrome أو حتى إجراء تعديلات على الطلبات لمعرفة ما نبحث عنه، ولكنّنا نحتاج في الأساس إلى استخدام واجهة برمجة تطبيقات 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 ونضيف ما يلي إلى ملفian.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، في هذه الحالة من النص البرمجي للمحتوى إلى مشغّل خدمات الخلفية. لنطلب من صديقنا 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 إلى مشغّل الخدمات في الخلفية. تصف السمة 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').

في ما يتعلق بمعرّف الإشعار، يمكننا جلب معرّف علامة التبويب للنص البرمجي للمحتوى واستخدامه كمعرّف للإشعار. يمكن تحقيق ذلك داخل أداة معالجة الأحداث 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. هناك العديد من واجهات برمجة التطبيقات التي يمكن تصفّحها، مثل chrome.storage وalarms وغيرهما. وعندما تواجه مشكلة، يمكنك استخدام Gemini أو المستندات لمعرفة الأخطاء التي ترتكبها أو جمع طرق مختلفة لحلّ المشكلة.

غالبًا ما يتطلب الأمر تعديل الطلب للحصول على المساعدة اللازمة، ولكن يمكننا إجراء ذلك من علامة تبويب واحدة تحتفظ بكلّ رحلتنا السياقية.