إعداد الإشعارات الفورية وتلقّيها

يمكنك استخدام الطرق في مجموعة Watches لتلقّي إشعارات عند تغيُّر البيانات في النماذج. تقدّم هذه الصفحة نظرة عامة على الإشعارات الفورية وتعليمات حول كيفية إعدادها وتلقّيها.

نظرة عامة

تتيح ميزة الإشعارات الفورية في Google Forms API للتطبيقات الاشتراك في تلقّي إشعارات عند حدوث تغييرات في البيانات في النماذج. يتم تسليم الإشعارات إلى موضوع Cloud Pub/Sub، عادةً في غضون دقائق من حدوث التغيير.

لتلقّي إشعارات فورية، عليك إعداد موضوع في Cloud Pub/Sub وتقديم اسم هذا الموضوع عند إنشاء مراقبة لنوع الحدث المناسب.

في ما يلي تعريفات للمفاهيم الرئيسية المستخدَمة في هذه المستندات:

  • الهدف هو المكان الذي يتم إرسال الإشعارات إليه. الهدف الوحيد المتاح هو موضوع Cloud Pub/Sub.
  • نوع الحدث هو فئة من الإشعارات التي يمكن لتطبيق تابع لجهة خارجية الاشتراك فيها.
  • المراقبة هي تعليمات موجّهة إلى Forms API لإرسال إشعارات بشأن نوع حدث معيّن في نموذج معيّن إلى هدف.

بعد إنشاء مراقبة لنوع حدث في نموذج معيّن، يتلقّى هدف المراقبة (وهو موضوع في Cloud Pub/Sub) إشعارات من هذه الأحداث في هذا النموذج إلى أن تنتهي صلاحية المراقبة. تدوم مدة صلاحية الساعة أسبوعًا، ولكن يمكنك تمديدها في أي وقت قبل انتهاء صلاحيتها من خلال إرسال طلب إلى watches.renew().

يتلقّى موضوع Cloud Pub/Sub إشعارات فقط بشأن النماذج التي يمكنك الاطّلاع عليها باستخدام بيانات الاعتماد التي تقدّمها. على سبيل المثال، إذا ألغى المستخدم الإذن من تطبيقك أو فقد إذن التعديل في نموذج تتم مراقبته، لن يتم إرسال الإشعارات بعد ذلك.

أنواع الأحداث المتاحة

توفّر Google Forms API حاليًا فئتَين من الأحداث:

  • EventType.SCHEMA، التي تُرسل إشعارات بشأن التعديلات التي يتم إجراؤها على محتوى النموذج وإعداداته.
  • EventType.RESPONSES، الذي يرسل إشعارًا عند إرسال ردود النموذج (الجديدة والمعدَّلة).

ردود الإشعارات

يتم ترميز الإشعارات باستخدام JSON وتحتوي على ما يلي:

  • رقم تعريف النموذج الذي يؤدي إلى تشغيل الإجراء
  • رقم تعريف المشاهدة التي أدّت إلى ظهور الإعلان
  • نوع الحدث الذي أدى إلى ظهور الإشعار
  • الحقول الأخرى التي يتم ضبطها من خلال Cloud Pub/Sub، مثل messageId وpublishTime

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

يوضّح المقتطف التالي نموذج إشعار بتغيير في المخطط:

{
  "attributes": {
    "eventType": "SCHEMA",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "892515d1-a902-444f-a2fe-42b718fe8159"
  },
  "messageId": "767437830649",
  "publishTime": "2021-03-31T01:34:08.053Z"
}

يوضّح المقتطف التالي نموذج إشعار بردّ جديد:

{
  "attributes": {
    "eventType": "RESPONSES",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "5d7e5690-b1ff-41ce-8afb-b469912efd7d"
  },
  "messageId": "767467004397",
  "publishTime": "2021-03-31T01:43:57.285Z"
}

إعداد موضوع Cloud Pub/Sub

يتم إرسال الإشعارات إلى مواضيع Cloud Pub/Sub. من خلال Cloud Pub/Sub، يمكنك تلقّي إشعارات على رابط ويب أو عن طريق طلب البيانات بشكل متكرّر من نقطة نهاية الاشتراك.

لإعداد موضوع Cloud Pub/Sub، اتّبِع الخطوات التالية:

  1. أكمِل المتطلبات الأساسية في Cloud Pub/Sub.
  2. إعداد عميل Cloud Pub/Sub
  3. راجِع أسعار Cloud Pub/Sub، وفعِّل الفوترة لمشروعك على Developer Console.
  4. يمكنك إنشاء موضوع Cloud Pub/Sub بإحدى الطرق الثلاث التالية:

  5. إنشاء اشتراك في Cloud Pub/Sub لتحديد طريقة تسليم الإشعارات من خلال Cloud Pub/Sub

  6. أخيرًا، قبل إنشاء عمليات مراقبة تستهدف موضوعك، عليك منح الإذن لحساب خدمة الإشعارات في "نماذج Google" (forms-notifications@system.gserviceaccount.com) بالنشر في موضوعك.

إنشاء ساعة

بعد الحصول على موضوع يمكن لحساب خدمة الإشعارات الفورية في Forms API نشره، يمكنك إنشاء إشعارات باستخدام طريقة watches.create(). تتحقّق هذه الطريقة من إمكانية وصول حساب خدمة الإشعارات الفورية إلى موضوع Cloud Pub/Sub المقدَّم، وفي حال عدم إمكانية الوصول إلى الموضوع، سيحدث خطأ، مثلاً إذا كان الموضوع غير متوفّر أو لم تمنحه إذن النشر في هذا الموضوع.

Python

forms/snippets/create_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)

service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

watch = {
    "watch": {
        "target": {"topic": {"topicName": "<YOUR_TOPIC_PATH>"}},
        "eventType": "RESPONSES",
    }
}

form_id = "<YOUR_FORM_ID>"

# Print JSON response after form watch creation
result = service.forms().watches().create(formId=form_id, body=watch).execute()
print(result)

Node.js

forms/snippets/create_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Creates a watch on a form to get notifications for new responses.
 */
async function createWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // The request body to create a watch.
  const watchRequest = {
    watch: {
      target: {
        topic: {
          // TODO: Replace with a valid Cloud Pub/Sub topic name.
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      // The event type to watch for. 'RESPONSES' is the only supported type.
      eventType: 'RESPONSES',
    },
  };

  // Send the request to create the watch.
  const result = await formsClient.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });

  console.log(result.data);
  return result.data;
}

حذف ساعة

Python

forms/snippets/delete_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after deleting a form watch
result = (
    service.forms().watches().delete(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

forms/snippets/delete_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Deletes a watch from a form.
 */
async function deleteWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to delete the watch.
  const result = await formsClient.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

التفويض

مثل جميع طلبات البيانات من Forms API، يجب أن يتم السماح بإرسال الطلبات إلى watches.create() باستخدام رمز موافقة مميز. يجب أن تتضمّن الرمز المميّز نطاقًا يمنح إذن الوصول للقراءة إلى البيانات التي يتم إرسال إشعارات بشأنها.

  • بالنسبة إلى تغييرات المخطط، يعني ذلك أي نطاق يمنح إذن الوصول للقراءة إلى النماذج باستخدام forms.get().
  • بالنسبة إلى الردود، يعني ذلك أي نطاق يمنح إذن الوصول للقراءة إلى ردود النماذج، على سبيل المثال باستخدام forms.responses.list().

لكي يتم تسليم الإشعارات، يجب أن يحتفظ التطبيق بإذن OAuth من المستخدم المفوَّض بالنطاقات المطلوبة. في حال قطع المستخدم اتصال التطبيق، تتوقف الإشعارات وقد يتم تعليق الساعة مع ظهور خطأ. لاستئناف تلقّي الإشعارات بعد استعادة التفويض، يُرجى الاطّلاع على مقالة تجديد تفويض ساعة.

عرض قائمة بالساعات التي تمّت إضافتها إلى نموذج

Python

forms/snippets/list_watches.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"

# Print JSON list of form watches
result = service.forms().watches().list(formId=form_id).execute()
print(result)

Node.js

forms/snippets/list_watches.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Lists the watches for a given form.
 */
async function listWatches() {
  // Authenticate with Google and get an authorized client.
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth,
  });

  // Get the list of watches for the form.
  const result = await formsClient.forms.watches.list({
    formId: formID,
  });

  console.log(result.data);
  return result.data;
}

تجديد اشتراك في ساعة

Python

forms/snippets/renew_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after renewing a form watch
result = (
    service.forms().watches().renew(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

forms/snippets/renew_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Renews a watch on a form.
 */
async function renewWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to renew the watch.
  const result = await formsClient.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

التقييد

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

بسبب الحدّ من عدد الإشعارات، قد يتوافق إشعار واحد مع أحداث متعددة. بعبارة أخرى، يشير الإشعار إلى وقوع حدث واحد أو أكثر منذ آخر إشعار.

الحدود

في أي وقت، يمكن أن يتضمّن كل مشروع على Cloud Console نموذجًا ونوع حدث معيّنًا مما يلي:

  • ما يصل إلى 20 مشاهدة إجمالاً
  • مشاهدة واحدة كحدّ أقصى لكل مستخدم نهائي

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

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

الموثوقية

يتم إعلام كل ساعة مرة واحدة على الأقل بعد كل حدث في جميع الحالات باستثناء الحالات الاستثنائية. في معظم الحالات، يتم إرسال إشعار في غضون دقائق من وقوع الحدث.

الأخطاء

إذا تعذّر بشكل متكرّر إرسال الإشعارات إلى الساعة، ستصبح حالة الساعة SUSPENDED وسيتم ضبط الحقل errorType في الساعة. لإعادة ضبط حالة الساعة المعلقة إلى ACTIVE واستئناف الإشعارات، يُرجى الاطّلاع على تجديد اشتراك ساعة.

الاستخدام المقترَح

  • استخدام موضوع Cloud Pub/Sub واحد كهدف للعديد من عمليات المراقبة
  • عند تلقّي إشعار بشأن موضوع معيّن، يتم تضمين معرّف النموذج في حمولة الإشعار. استخدِمها مع نوع الحدث لمعرفة البيانات التي يجب استرجاعها والشكل الذي يجب استرجاعها منه.
  • لاسترداد البيانات المعدَّلة بعد تلقّي إشعار يتضمّن EventType.RESPONSES، استخدِم الدالة forms.responses.list().
    • اضبط الفلتر على الطلب على timestamp > timestamp_of_the_last_response_you_fetched.
  • لجلب البيانات المعدَّلة بعد تلقّي إشعار يتضمّن EventType.SCHEMA، استخدِم الدالة forms.get().