موصل Zoom
اربط Zoom بـ Brevo من خلال Tajo لمزامنة المشاركين في الاجتماعات وحضور الندوات كجهات اتصال تلقائيًا، وتشغيل تسلسلات متابعة ما بعد الاجتماع، وتتبع مقاييس التفاعل لأتمتة التسويق الخاصة بك.
نظرة عامة
| الخاصية | القيمة |
|---|---|
| المنصة | Zoom |
| الفئة | مؤتمرات الفيديو (مخصص) |
| تعقيد الإعداد | متوسط |
| تكامل رسمي | لا |
| البيانات المُزامَنة | مشاركون، أحداث، ندوات، جهات اتصال |
| طريقة المصادقة | OAuth 2.0 / Server-to-Server OAuth |
الميزات
- مزامنة المشاركين - إنشاء جهات اتصال Brevo تلقائيًا من المشاركين في الاجتماعات
- التقاط حضور الندوات - مزامنة المسجلين والحاضرين في الندوات
- محفزات أحداث الاجتماع - تشغيل الأتمتة عند بدء الاجتماع وانتهائه وأحداث التسجيل
- تتبع التفاعل - تتبع مدة الحضور ومقاييس المشاركة
- متابعة الندوات - تشغيل تسلسلات بريد إلكتروني مستهدفة بناءً على حضور الندوة
- إشعارات التسجيل - إرسال روابط التسجيل عبر حملات بريد إلكتروني Brevo
المتطلبات الأساسية
قبل البدء، تأكد من توفر ما يلي:
- حساب Zoom (خطة Pro أو أعلى)
- تطبيق Zoom Server-to-Server OAuth أو تطبيق OAuth عبر Zoom App Marketplace
- حساب Brevo مع وصول إلى API
- حساب Tajo مع صلاحيات الموصلات
المصادقة
Server-to-Server OAuth (موصى به)
# Create a Server-to-Server OAuth app at marketplace.zoom.usexport ZOOM_ACCOUNT_ID=your_account_idexport ZOOM_CLIENT_ID=your_client_idexport ZOOM_CLIENT_SECRET=your_client_secret// الحصول على رمز الوصول عبر Server-to-Server OAuthconst tokenResponse = await fetch('https://zoom.us/oauth/token', { method: 'POST', headers: { 'Authorization': `Basic ${Buffer.from( `${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}` ).toString('base64')}`, 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'account_credentials', account_id: process.env.ZOOM_ACCOUNT_ID })});
const { access_token } = await tokenResponse.json();OAuth 2.0 (على مستوى المستخدم)
// عنوان URL للتفويض لـ OAuth على مستوى المستخدمconst authUrl = 'https://zoom.us/oauth/authorize?' + new URLSearchParams({ client_id: process.env.ZOOM_CLIENT_ID, redirect_uri: 'https://your-app.com/callback', response_type: 'code' });
// استبدال الكود بالرموزconst tokenResponse = await fetch('https://zoom.us/oauth/token', { method: 'POST', headers: { 'Authorization': `Basic ${Buffer.from( `${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}` ).toString('base64')}`, 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: authorizationCode, redirect_uri: 'https://your-app.com/callback' })});الإعداد
الإعداد الأساسي
connectors: zoom: enabled: true account_id: "${ZOOM_ACCOUNT_ID}" client_id: "${ZOOM_CLIENT_ID}" client_secret: "${ZOOM_CLIENT_SECRET}"
sync: participants: true webinars: true recordings: true
webhook: secret_token: "${ZOOM_WEBHOOK_SECRET}" verification_token: "${ZOOM_VERIFICATION_TOKEN}"
lists: meeting_participants: 15 webinar_attendees: 16 webinar_registrants: 17تعيين الحقول
field_mapping: email: email name: FIRSTNAME join_time: MEETING_JOIN_DATE duration: MEETING_DURATION webinar_title: WEBINAR_NAME attendance_status: ATTENDANCE_STATUS registration_source: UTM_SOURCEنقاط نهاية واجهة البرمجة
| نقطة النهاية | الطريقة | الوصف |
|---|---|---|
https://api.zoom.us/v2/users | GET | سرد المستخدمين |
https://api.zoom.us/v2/users/{userId}/meetings | GET | سرد الاجتماعات |
https://api.zoom.us/v2/meetings/{meetingId} | GET | الحصول على تفاصيل الاجتماع |
https://api.zoom.us/v2/past_meetings/{meetingId}/participants | GET | سرد المشاركين في الاجتماعات السابقة |
https://api.zoom.us/v2/users/{userId}/webinars | GET | سرد الندوات |
https://api.zoom.us/v2/webinars/{webinarId}/registrants | GET | سرد المسجلين في الندوات |
https://api.zoom.us/v2/webinars/{webinarId}/participants | GET | سرد المشاركين في الندوات |
https://api.zoom.us/v2/meetings/{meetingId}/recordings | GET | الحصول على تسجيلات الاجتماع |
https://api.zoom.us/v2/webhooks | POST | الاشتراك في webhooks |
أمثلة على الكود
تهيئة الموصل
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('zoom', { accountId: process.env.ZOOM_ACCOUNT_ID, clientId: process.env.ZOOM_CLIENT_ID, clientSecret: process.env.ZOOM_CLIENT_SECRET});مزامنة المشاركين في الاجتماع
// استرجاع المشاركين في الاجتماعات السابقةconst response = await fetch( `https://api.zoom.us/v2/past_meetings/${meetingId}/participants`, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } });
const { participants } = await response.json();
for (const participant of participants) { if (participant.user_email) { await tajo.contacts.sync({ email: participant.user_email, attributes: { FIRSTNAME: participant.name, MEETING_DURATION: participant.duration, MEETING_JOIN_DATE: participant.join_time, ATTENDANCE_STATUS: 'attended' }, listIds: [15] }); }}مزامنة حضور الندوات
// الحصول على حضور الندوة ومزامنتهم مع Brevoconst attendeesResponse = await fetch( `https://api.zoom.us/v2/past_webinars/${webinarId}/participants`, { headers: { 'Authorization': `Bearer ${accessToken}` } });
const { participants: attendees } = await attendeesResponse.json();
for (const attendee of attendees) { await tajo.contacts.sync({ email: attendee.user_email, attributes: { FIRSTNAME: attendee.name, WEBINAR_NAME: webinarTitle, ATTENDANCE_STATUS: 'attended', MEETING_DURATION: attendee.duration }, listIds: [16] });}التعامل مع Zoom Webhooks
app.post('/webhooks/zoom', async (req, res) => { // التعامل مع تحدي التحقق من عنوان URL لـ Zoom if (req.body.event === 'endpoint.url_validation') { const hashForValidation = crypto .createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET) .update(req.body.payload.plainToken) .digest('hex');
return res.json({ plainToken: req.body.payload.plainToken, encryptedToken: hashForValidation }); }
// التحقق من توقيع webhook const message = `v0:${req.headers['x-zm-request-timestamp']}:${JSON.stringify(req.body)}`; const hash = crypto .createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET) .update(message) .digest('hex'); const signature = `v0=${hash}`;
if (req.headers['x-zm-signature'] !== signature) { return res.status(401).send('Unauthorized'); }
const { event, payload } = req.body;
await tajo.connectors.handleWebhook('zoom', { topic: event, payload: payload });
res.status(200).send('OK');});حدود المعدل
| الفئة | الحد | ملاحظات |
|---|---|---|
| استدعاءات API الخفيفة | 30 طلب/ثانية | GET للمستخدم، معلومات الاجتماع |
| استدعاءات API المتوسطة | 20 طلب/ثانية | سرد المشاركين والندوات |
| استدعاءات API الثقيلة | 10 طلبات/ثانية | التقارير والتسجيلات |
| الحد اليومي | 5,000+ | يعتمد على مستوى الخطة |
ترويسات حدود المعدل
يُعيد Zoom الترويسات X-RateLimit-Limit وX-RateLimit-Remaining وRetry-After. طبّق منطق التراجع بناءً على هذه الترويسات لتجنب أخطاء 429.
استكشاف الأخطاء وإصلاحها
| المشكلة | السبب | الحل |
|---|---|---|
| 401 Unauthorized | الرمز منتهي | حدّث رمز Server-to-Server OAuth |
| مشاركون مفقودون | الاجتماع لم ينتهِ | انتظر حتى ينتهي الاجتماع للحصول على البيانات الكاملة |
| فشل التحقق من Webhook | سر خاطئ | تحقق من سر webhook في Zoom Marketplace |
| لا توجد بيانات بريد إلكتروني | مشاركون ضيوف | فعّل التسجيل لالتقاط عناوين البريد الإلكتروني |
| حد المعدل 429 | طلبات كثيرة جدًا | طبّق التراجع الأُسّي |
وضع التصحيح
connectors: zoom: debug: true log_level: verbose log_webhooks: trueأفضل الممارسات
- استخدم Server-to-Server OAuth - مصادقة أبسط بدون تفاعل مستخدم
- فعّل تسجيل الندوات - مطلوب لالتقاط عناوين البريد الإلكتروني للحضور
- عالج بعد انتهاء الاجتماع - تكون بيانات المشاركين كاملة فقط بعد انتهاء الاجتماعات
- قسّم حسب نوع الحدث - خصّص قوائم Brevo مختلفة للاجتماعات مقابل الندوات
- تتبع مقاييس التفاعل - استخدم المدة ووقت الانضمام لتسجيل العملاء المحتملين
- أرسل متابعات التسجيل - أتمِت تسليم روابط التسجيل عبر Brevo
الأمان
- OAuth 2.0 - مصادقة Server-to-Server أو OAuth على مستوى المستخدم
- التحقق من Webhook - التحقق من توقيع HMAC-SHA256
- التحقق من URL - التحقق من التحدي والاستجابة لنقاط نهاية webhook
- صلاحيات مقيدة - اطلب أدنى نطاقات OAuth المطلوبة
- تدوير الرموز - رموز Server-to-Server تنتهي تلقائيًا (ساعة واحدة)
- نقل مشفر - TLS 1.2+ لجميع اتصالات API