June 19, 2026 · 10 min read

ضخّ نتائج الاستطلاع المباشرة إلى لوحة المعلومات الداخلية عبر الـ webhooks

دليل مطوّر لاستهلاك webhooks الخاصة بـ PollsLive: تحقّق من توقيع HMAC، وتفاعل مع أحداث vote.created وأحداث الجلسة في الوقت الفعلي، واعتمد على استدعاء نقطة النتائج كخطة بديلة.

By Tom Becker · Full-stack developer·DevelopersHow-toTeams & meetings

نشغّل لوحة معلومات مثبّتة على الجدار في مكتبنا تعرض ما يحدث عبر المنتج. أردت نتائج الاستطلاع المباشرة عليها أثناء اجتماع الشركة العام - دون إرهاق الواجهة البرمجية بمؤقّت. جعلت webhooks الخاصة بـ PollsLive ذلك عملًا في ساعتين. إليك التكامل، بما في ذلك التحقّق من التوقيع، لأنه لا يجب أن تثق أبدًا بـ webhook غير مُتحقَّق منه.

Tip

لا تحتاج أن تكون تقنيًا لمتابعة هذا الدليل. كل خطوة تستخدم أزرارًا بسيطة في PollsLive - دون برمجة ودون تثبيت تطبيق لجمهورك.

الأحداث التي يمكنك الاشتراك فيها

تسجّل عنوان URL لنقطة نهاية ومجموعة من الأحداث في Studio ← Developers. الأحداث التي تهمّني للوحة معلومات مباشرة:

  • `session.started` / `session.ended` - اعرض أو أزل البطاقة المباشرة.
  • `response.created` - أجاب مشارك على شريحة في جلسة مباشرة (الحدث عالي التردّد).
  • `vote.created` - صوت جديد على استطلاع غير متزامن.
  • `poll.published` / `poll.closed` - دورة الحياة، مفيدة للأرشفة.

كل عملية تسليم هي طلب `POST` بصيغة JSON بنفس المظروف: اسم `event` وطابع زمني `createdAt` وكائن `data`.

POST /webhooks/pollslive  (from PollsLive → your server)
X-PollsLive-Signature: t=1750320000,v1=4f9a…c2

{
  "event": "response.created",
  "createdAt": "2026-06-19T16:00:00.000Z",
  "data": {
    "pollId": "poll_9aZ2kP",
    "sessionId": "ses_4dF1",
    "slideId": "fix",
    "kind": "multiple_choice"
  }
}

تحقّق من التوقيع أولًا - دائمًا

كل طلب يحمل `X-PollsLive-Signature: t=<unix>,v1=<hex>`. قيمة `v1` هي HMAC-SHA256 لـ `<t>.<rawBody>` باستخدام سرّ التوقيع الخاص بنقطة النهاية. يجب أن تحسبها على جسم الطلب الخام (وليس JSON المُحلَّل)، وأن تقارن في وقت ثابت، وأن ترفض أي شيء يكون طابعه الزمني قديمًا جدًا لمنع إعادة التشغيل.

import crypto from "node:crypto";

const SECRET = process.env.POLLSLIVE_WEBHOOK_SECRET; // from Studio → Developers

export function verifyPollsLive(rawBody, header, toleranceSec = 300) {
  // header looks like: "t=1750320000,v1=4f9a…c2"
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=")),
  );
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;

  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

الفخّ الوحيد في Express: تحتاج إلى البايتات الخام، لذا سجّل معالج التحقّق بـ `express.raw()` (أو التقط الـ buffer) بدلًا من `express.json()` لذلك المسار. ثم أقرّ بالاستلام بسرعة - أعِد `2xx` فورًا وقم بالعمل الفعلي بشكل غير متزامن، وإلا سيعتبر PollsLive الاستجابة البطيئة تسليمًا فاشلًا.

import express from "express";
import { verifyPollsLive } from "./verify.mjs";

const app = express();

app.post(
  "/webhooks/pollslive",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.get("X-PollsLive-Signature") ?? "";
    if (!verifyPollsLive(req.body.toString("utf8"), sig)) {
      return res.status(400).send("bad signature");
    }
    const evt = JSON.parse(req.body.toString("utf8"));

    // ACK first, then process out of band.
    res.sendStatus(200);
    queue.add(evt); // e.g. push to a worker / pub-sub / websocket fan-out
  },
);

app.listen(3000);

تحويل الأحداث إلى بطاقة مباشرة

لا أضع *عدد* الأصوات في حمولة الـ webhook عمدًا - الحدث هو تنبيه يقول "حدث تغيّر ما"، والـ worker لديّ يُهدّئ دفعة من أحداث `response.created` (إجابة واحدة لكل حدث قد تكون كثيرة أثناء اجتماع عام) في تحديث واحد يستدعي `GET /polls/{id}/results`. هذا يبقيني بأمان دون حد 120 طلبًا/دقيقة حتى مع استطلاع مزدحم.

const API = process.env.POLLSLIVE_API;
const auth = { Authorization: `Bearer ${process.env.POLLSLIVE_KEY}` };
const pending = new Map(); // pollId → timer

function onEvent(evt) {
  if (evt.event === "session.ended") return teardownTile(evt.data.pollId);
  const id = evt.data.pollId;
  clearTimeout(pending.get(id));
  pending.set(id, setTimeout(() => refresh(id), 750)); // debounce bursts
}

async function refresh(pollId) {
  const res = await fetch(`${API}/polls/${pollId}/results`, { headers: auth });
  if (res.status === 429) {                 // backed-off retry on rate limit
    return setTimeout(() => refresh(pollId), 2000);
  }
  const { data } = await res.json();
  pushToDashboard(pollId, data.voteCounts); // websocket → the wall display
}

ينتهي الأمر بالجدار يعرض البطاقة المباشرة أدناه - مُغذّاة بتنبيهات الـ webhook، لا بحلقة استدعاء متكرّرة. خلال آخر اجتماع عام تحدّثت خلال ثانية من كل صوت ولم تتجاوز حد المعدّل أبدًا:

Live poll

أين يجب أن نضع أسبوع الهاكاثون هذا العام؟

ميزات الذكاء الاصطناعي للمنتج الأساسي41% · 79
سداد الدين التقني / الموثوقية33% · 64
أدوات داخلية توفّر علينا الوقت19% · 37
بطاقة حرّة - أي شيء مقبول7% · 13
بطاقة لوحة معلومات مكتبنا، تتحدّث من GET /polls/{id}/results عند كل تنبيه webhook.

احتفظ دائمًا بخطة بديلة

قد تتأخّر الـ webhooks أو تُفقد - الشبكات شبكات. لذلك تعمل نفس دالة `refresh()` على فترة أمان بطيئة (كل 30 ثانية) طالما كانت الجلسة مباشرة. حزام وحمّالة: webhooks للتحديثات الفورية، واستدعاء لطيف كي لا تكون البطاقة خاطئة لمدة طويلة. إن فعلت واحدًا فقط، فليكن التحقّق؛ وإن فعلت اثنين، فأضف الخطة البديلة.

تعامل مع الـ webhook كإشارة "اذهب وانظر"، لا كمصدر للحقيقة. تحقّق منه، وهدّئه، ثم اقرأ نقطة النتائج - فتحصل على لوحة معلومات في الوقت الفعلي وصامدة أيضًا.

القائمة الكاملة للأحداث وصيغة التوقيع موجودة في دليل المطوّرين وفي مرجع الواجهة البرمجية التفاعلي. وإن كنت تؤتمت جانب *الإنشاء* أيضًا، فإن مقال زميلتي حول تشغيل اجتماعات الاستعراض من الواجهة البرمجية هو المقال المرافق.

Tip

حمولات webhook تتضمن لقطة النتيجة الكاملة - خزّنها، لا تستطلع API بمؤقت.

Results panel with stat tiles, breakdown by question, and export buttons.
The Results tab shows every answer and export options.
Session recap with final results, share buttons, and export options.
After a live session, recap shows final results and export options.
The PollsLive integrations page with PowerPoint, Google Slides, Keynote, Zoom, Microsoft Teams, Webex, and Google Meet companions.
PollsLive runs alongside your slides and meeting tools - your audience joins by QR or PIN.

اقرأ النتائج في Studio مع قراءة وتصدير نتائج الاستطلاع. راجع /integrations.

ابنِ على PollsLive - أنشئ استطلاعات، وشغّل جلسات مباشرة، واسحب النتائج، واستقبل الـ webhooks من كودك الخاص.

اقرأ وثائق المطوّرين