تدفق البيانات
يوجّه تدفق البيانات الأحداث من Joryio إلى وجهات خارجية في وقت شبه حقيقي. يربط كل تدفق وجهةً بمجموعة من الأحداث، وتسلّم Joryio الأحداث المطابقة إلى تلك الوجهة على دفعات.
انتقل إلى البيانات → تدفق البيانات لإدارة تدفقاتك.
الوجهات
| الوجهة | النوع | وظيفتها |
|---|---|---|
| Webhook | نقطة نهاية HTTP | أرسل كل حدث بطريقة POST كـ JSON إلى أي عنوان HTTPS |
| Snowflake | مستودع بيانات | حمّل الأحداث باستمرار إلى جدول Snowflake |
| BigQuery | مستودع بيانات | ابث الأحداث إلى مجموعة بيانات BigQuery |
| Amazon Redshift | مستودع بيانات | حمّل الأحداث إلى عنقود Amazon Redshift |
| Amazon S3 | تخزين كائنات | اكتب ملفات JSON / NDJSON مجمعة إلى حاوية |
| Kafka | تدفق أحداث | انشر الأحداث إلى موضوع Kafka |
| Mixpanel | تحليلات المنتج | وجّه الأحداث إلى مشروع Mixpanel |
| Amplitude | تحليلات المنتج | وجّه الأحداث إلى مشروع Amplitude |
حقول الاتصال
تطلب كل وجهة تفاصيل اتصالها الخاصة. تُشفّر الأسرار (كلمات المرور والمفاتيح والرموز المميزة وJSON حساب الخدمة وقيم ترويسات Webhook) أثناء التخزين ولا تُعرض مرة أخرى أبدًا - راجع بيانات اعتماد بأقل الصلاحيات.
- Webhook - عنوان URL وسر التوقيع (اختياري) وترويسات إضافية (اختيارية، كائن JSON مثل
{"Authorization": "Bearer <token>"}؛ تُعامَل قيمها كأسرار) - Snowflake - الحساب واسم المستخدم وكلمة المرور والدور (اختياري - الدور الذي تُنفَّذ به الاستعلامات؛ إذا كان فارغًا يُطبَّق الدور الافتراضي للمستخدم) وWarehouse وقاعدة البيانات وSchema والجدول
- BigQuery - معرّف المشروع ومعرّف مجموعة البيانات ومعرّف الجدول وJSON حساب الخدمة
- Amazon Redshift - المضيف والمنفذ وقاعدة البيانات والمستخدم وكلمة المرور وSchema والجدول
- Amazon S3 - الحاوية والبادئة والمنطقة ونقطة النهاية (اختيارية، للتخزين المتوافق مع S3) ومعرّف مفتاح الوصول ومفتاح الوصول السري
- Kafka - الوسطاء والموضوع واسم مستخدم/كلمة مرور SASL وSSL
- Mixpanel - رمز المشروع والمنطقة (US / EU / India)
- Amplitude - مفتاح API والمنطقة (US / EU)
بيانات اعتماد بأقل الصلاحيات
امنح كل تدفق بيانات اعتماد خاصة به لا تستطيع إلا أمرًا واحدًا: الكتابة إلى الجدول أو مسار الحاوية أو الموضوع الذي يسلّم إليه التدفق. إذا تسرّبت بيانات الاعتماد يومًا، فهذا كل ما يمكن فعله بها - لا يمكنها قراءة بياناتك ولا المساس بأي شيء آخر. تستخدم الأمثلة أدناه أسماء توضيحية (example-bucket وanalytics.joryio_events وjoryio-events)؛ استبدلها بأسمائك.
كيف يتعامل Joryio مع بيانات الاعتماد المخزّنة
- مشفّرة. تُشفَّر كلمات المرور والمفاتيح والرموز المميزة وJSON حساب الخدمة وقيم ترويسات Webhook قبل تخزينها.
- لا تُعرض مرة أخرى أبدًا. يعرض المحرر وواجهة API السر المخزّن على شكل
********. لا يستطيع أحد - بما في ذلك أنت وفريق Joryio - قراءته مجددًا في لوحة التحكم أو عبر API. - تبقى عند تعديل حقول أخرى. حقل السر الذي تتركه فارغًا (أو
********) يحتفظ بالقيمة المخزّنة؛ ولا يستبدلها إلا قيمة تكتبها. إعادة تسمية التدفق أو تغيير أحداثه لا تمس بيانات اعتماده أبدًا. - تُحذف مع التدفق. يؤدي حذف التدفق إلى حذف بيانات اعتماده المخزّنة. وعند حذف الحساب، تُمحى مع بقية بيانات الحساب.
عند حذف تدفق، ألغِ بيانات اعتماده أو غيّرها أيضًا لدى الوجهة: لم تعد لدى Joryio، لكن المفتاح نفسه يبقى صالحًا حتى تلغيه.
ترويسات Webhook أسرار أيضًا. أدخلها على شكل كائن JSON، مثل {"Authorization": "Bearer <token>"}. تبقى أسماء الترويسات ظاهرة؛ وتُشفَّر كل قيمة وتظهر على شكل ********. عند تعديل التدفق، اترك ******** للإبقاء على القيمة، أو اكتب قيمة جديدة لاستبدالها، أو أزل الترويسة لحذفها. استخدم رمزًا مميزًا صادرًا لـ Joryio وحده، حتى تتمكن من إلغائه دون التأثير في أي شيء آخر.
Amazon S3
أنشئ مستخدم IAM (أو دورًا) لـ Joryio تكون صلاحيته الوحيدة s3:PutObject على حاوية التدفق وبادئته، واستخدم مفتاح الوصول الخاص به في التدفق. يكتب Joryio الكائنات تحت <prefix>/<date>/، لذا مع البادئة joryio-events:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "JoryioWriteEvents",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::example-bucket/joryio-events/*"
}
]
}
يتحقق اختبار الاتصال من وجود الحاوية، وهو ما لا يسمح به AWS إلا مع s3:ListBucket على الحاوية. تتيح هذه الصلاحية للمفتاح سرد أسماء الكائنات في الحاوية، لذا فهي اختيارية: بدونها يعمل التسليم ولا يُبلغ بالفشل إلا الاختبار. للسماح بالاختبار، أضف هذا البيان:
{
"Sid": "JoryioTestConnection",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-bucket"
}
للتخزين المتوافق مع S3، أنشئ رمزًا مقيّدًا بالحاوية الواحدة مع صلاحية كتابة الكائنات فقط.
Amazon Redshift
أنشئ مستخدم قاعدة بيانات مخصصًا لا يستطيع إلا الإدراج في جدول التدفق:
CREATE USER joryio_loader PASSWORD '<strong-password>';
GRANT USAGE ON SCHEMA analytics TO joryio_loader;
GRANT INSERT ON TABLE analytics.joryio_events TO joryio_loader;
أدخل joryio_loader في حقل المستخدم في التدفق.
Snowflake
أنشئ دورًا لا يستطيع إلا الإدراج في جدول التدفق (إضافة إلى صلاحيات USAGE التي يحتاجها Snowflake للوصول إليه)، ومستخدمًا يملك هذا الدور:
CREATE ROLE JORYIO_WRITER;
GRANT USAGE ON WAREHOUSE LOAD_WH TO ROLE JORYIO_WRITER;
GRANT USAGE ON DATABASE ANALYTICS TO ROLE JORYIO_WRITER;
GRANT USAGE ON SCHEMA ANALYTICS.EVENTS TO ROLE JORYIO_WRITER;
GRANT INSERT ON TABLE ANALYTICS.EVENTS.JORYIO_EVENTS TO ROLE JORYIO_WRITER;
CREATE USER JORYIO_LOADER PASSWORD = '<strong-password>'
DEFAULT_ROLE = JORYIO_WRITER DEFAULT_WAREHOUSE = LOAD_WH;
GRANT ROLE JORYIO_WRITER TO USER JORYIO_LOADER;
أدخل JORYIO_LOADER في اسم المستخدم وJORYIO_WRITER في الدور. حقل الدور اختياري: عند تعيينه، ينفّذ Joryio كل استعلام بهذا الدور؛ وعندما يكون فارغًا، يُطبَّق الدور الافتراضي للمستخدم.
BigQuery
أنشئ حساب خدمة لـ Joryio وامنحه BigQuery Data Editor (roles/bigquery.dataEditor) على مجموعة بيانات التدفق فقط - من إعدادات مشاركة مجموعة البيانات، لا على مستوى المشروع. ثم أنشئ له مفتاح JSON والصقه في JSON حساب الخدمة.
يرسل Joryio الصفوف عبر واجهة الإدراج في BigQuery ويقرأ البيانات الوصفية للجدول لاختبار الاتصال؛ ولا يشغّل أيٌّ منهما مهمة استعلام، لذا لا حاجة إلى BigQuery Job User. تتيح صلاحية Data Editor على مجموعة بيانات تعديل جداول أخرى فيها أيضًا، لذا ضع جدول التدفق في مجموعة بيانات خاصة به، أو امنح دورًا مخصصًا يقتصر على bigquery.tables.get وbigquery.tables.updateData على الجدول.
Kafka
أنشئ مستخدم SASL لـ Joryio (يصادق Joryio باستخدام SASL/PLAIN) واسمح له فقط بـ WRITE وDESCRIBE على موضوع التدفق. أنشئ الموضوع بنفسك مسبقًا - لا يحتاج Joryio إلى صلاحية إنشاء المواضيع.
kafka-acls --bootstrap-server <broker:port> --command-config admin.properties \
--add --allow-principal User:joryio \
--operation Write --operation Describe \
--topic joryio-events
Mixpanel وAmplitude وWebhooks
- Mixpanel - رمز المشروع لا يستطيع إلا إرسال الأحداث إلى مشروعه؛ ولا يمكنه قراءة البيانات. استخدم رمز المشروع الذي يجب أن يغذّيه التدفق.
- Amplitude - مفتاح API هو مفتاح الاستيعاب لمشروع واحد. استخدم المشروع الذي يجب أن يغذّيه التدفق.
- Webhook - استخدم سر توقيع ورمز
Authorizationصادرين لـ Joryio وحده، ولا تقبل إلا ما يرسله التدفق.
إنشاء تدفق
- انتقل إلى البيانات → تدفق البيانات وانقر إنشاء تدفق.
- سمِّ التدفق (يظهر في قائمة التدفقات) واختر وجهة.
- املأ حقول الاتصال الخاصة بالوجهة.
- اختر تنسيق الحمولة: JSON (كائن واحد لكل حدث) أو NDJSON (سطر لكل حدث).
- حدد الأحداث التي تريد بثها (انظر أدناه).
- راجع شريط الملخص وانقر إنشاء تدفق.
اختيار الأحداث
أنت تتحكم بدقة في الأحداث التي يستقبلها التدفق:
- All events - فعّله لبث كل حدث حالي ومستقبلي. أوقفه لاختيار أحداث محددة.
- الرسائل، حسب القناة - تُختار أحداث الرسائل لكل قناة على حدة (راجع الاختيار حسب القناة).
- مجموعات أخرى - In-App والرحلات والحملات وبطاقات Wallet والتجارة الإلكترونية والهوية. حدد مجموعة كاملة أو أحداثًا منفردة.
- الأحداث المخصصة - ضمن Custom Event، ابحث عن أحداثك أو اكتبها (تلك التي ترسلها باستخدام
track()، مثلpurchaseأوpage_view). تعرض القائمة الأحداث التي سجلتها مساحة العمل فعلًا؛ ويمكنك أيضًا كتابة حدث لم يُسجَّل بعد والنقر على Add.
الاختيار حسب القناة
يعرض المحرر قسمًا لكل قناة - البريد الإلكتروني وSMS وWhatsApp والإشعارات وViber وWebhook - ولا يضم إلا الأحداث الموجودة في تلك القناة (لا يوجد "فتح SMS"). داخل كل قسم تُجمَّع الأحداث هكذا:
| المجموعة | الخيارات |
|---|---|
| التفاعل | إرسال، تسليم، فتح (البريد الإلكتروني)، قراءة (WhatsApp)، نقرة |
| المشكلات | ارتداد، تحديد كبريد عشوائي، فشل، لم تُرسل، تأجيل، رد تلقائي (البريد الإلكتروني) |
| الموافقة | إلغاء الاشتراك، اشتراك |
| الواردة | رسالة واردة (SMS و WhatsApp و Viber) |
لكل قسم مربع تحديد الكل خاص به، ويحدد تحديد الكل في كل القنوات كل شيء دفعة واحدة. بجانب كل خيار يظهر الحجم الشهري التقديري - عدد هذه الأحداث التي سجلتها مساحة العمل في آخر 30 يومًا - لترى قبل الحفظ ما سيكلفه الاختيار لوجهتك.
يحمل الإرسال والتسليم والفتح علامة حجم كبير: كل منها صف واحد لكل رسالة. يبدأ التدفق الجديد بتحديد كل الخيارات الأخرى، وتُترك هذه الثلاثة لتضيفها بنفسك.
لا يتغير تنسيق البيانات. تحديد التسليم تحت SMS يبث الحدث المعتاد message.delivered الذي تكون خاصية channel فيه sms - وهو الاسم والقيم نفسها التي يستخدمها مخزن الأحداث و REST API وتكاملات Webhook. وتحتفظ الردود الواردة بأسمائها: sms.message.received و whatsapp.message.received و viber.message.received.
لا يزال بإمكان التدفق المُنشأ عبر الـ API اختيار حدث رسالة بالاسم ("events": ["message.delivered"]، مع إمكانية التضييق عبر "channels"). يستمر ذلك في العمل، ويعرضه المحرر كالحدث نفسه محددًا في كل قناة يغطيها.
تغيير حالة الاشتراك
يحتوي قسم الاشتراكات على خيار واحد، تغيير حالة الاشتراك: كل اشتراك وإلغاء اشتراك في كل قناة وقائمة. يبث حدثي الموافقة اللذين تسجلهما Joryio بالفعل، فلا يُحتسب شيء مرتين:
message.subscribed- اشترك شخص في قناة أو قائمة. يوضحstatusوpreviousStatusالتغيير، وscopeهوchannelأوlist(معlistId)، وsourceيوضح كيف حدث ذلك (apiوdashboardوpreference_centerوdouble_opt_inوkeywordوالاشتراك من متجر أو نموذج وغير ذلك).message.unsubscribed- ألغى شخص اشتراكه.scopeهوchannelأوlistأوglobal، وreasonيوضح طريقة الوصول (linkوkeyword...)، وunsubscribeSourceيحدد المسار الدقيق (زر إلغاء الاشتراك في تطبيق البريد، زر الصفحة، مركز التفضيلات، رد STOP).
الارتدادات وشكاوى البريد العشوائي أحداث مستقلة (message.bounced و message.spamreport). استيراد جهات الاتصال ومزامنة مستودع البيانات والمزامنة الأولى للمتجر تكتب الموافقة بشكل جماعي ولا تُصدر هذه الأحداث.
إيصالات التسليم
تقارير التسليم والارتدادات وشكاوى البريد العشوائي وإلغاء الاشتراك والرسائل التي لم ترسلها Joryio هي إيصالات (receipts). اخترها مثل أي حدث رسالة آخر، وتصل كالأحداث التالية:
| الحدث | القنوات | خصائص بارزة |
|---|---|---|
message.delivered | البريد الإلكتروني، SMS، Viber، الإشعارات، WhatsApp | messageId و campaignId و canvasId |
message.bounced | البريد الإلكتروني | bounceType (hard أو soft) و bounceSubType و dsnStatus و reason |
message.spamreport | البريد الإلكتروني | messageId و campaignId |
message.failed | كل القنوات | errorCode و errorReason |
message.skipped | كل القنوات | reason - سبب عدم الإرسال (حظر، حد التكرار، الموافقة، ساعات الهدوء) |
message.deferred | البريد الإلكتروني | المزوّد يعيد المحاولة بعد فشل مؤقت |
message.auto_replied | البريد الإلكتروني | رد غياب تلقائي - تم تسليم البريد |
message.unsubscribed | كل القنوات | راجع تغيير حالة الاشتراك |
message.opened (القناة whatsapp) | إيصال القراءة | |
sms.message.received و whatsapp.message.received و viber.message.received | SMS و WhatsApp و Viber | رد وارد؛ في SMS و Viber تكون keyword هي stop أو start أو help عندما يكون الرد كلمة امتثال |
تصل الإيصالات إلى تدفق البيانات فقط. لا تبدأ أي رحلة ولا تُشغِّل أي حملة، تمامًا كما كان من قبل.
قد يحدد تدفق محفوظ قبل وجود الإيصالات بعضًا منها مسبقًا (مثل message.bounced). لم تصل هذه أبدًا، ولن تبدأ بالوصول من تلقاء نفسها: يعرض التدفق إيصالات التسليم متاحة الآن، وتبدأ بالتدفق بمجرد حفظ التدفق مرة أخرى. تستقبل التدفقات الجديدة الإيصالات منذ البداية.
ما يحتويه حدث الرسالة المبثوث
- يحمل كل حدث معرّف جهة الاتصال في Joryio (
userId)، وتحمل أحداث الرسائل أيضًاexternalIdلجهة الاتصال عند وجوده. - تُضمَّن عناوين البريد الإلكتروني وأرقام الهواتف فقط عندما يملك آخر من حفظ التدفق صلاحية
pii:view(يملكها Admin و Owner دائمًا). وإلا فإنها تُزال. حفظ التدفق مرة أخرى يطبق القاعدة على من يحفظه. - لا يُضمَّن محتوى الرسالة (النص، العنوان، HTML) في أي إيصال.
- لا تُحذف عمليات الفتح والنقر التي تبدو آلية (ماسحات الأمان، خوادم خصوصية البريد)؛ بل تحمل
$is_bot: trueلتقرر أنت ما تفعله بها.
مجموعات الأحداث الأخرى
| المجموعة | الأحداث |
|---|---|
| In-App | in_app.displayed و in_app.clicked و in_app.dismissed و in_app.converted |
| الرحلات | canvas.experiment.entered و canvas.ai_decision.entered و canvas.step.entered و canvas.step.left |
| الحملات | campaign.sent و campaign.abtest.winner_declared و campaign.ai_decision.entered |
| بطاقات Wallet | pass.issued و pass.installed و pass.updated و pass.update_pushed و pass.removed و pass.voided و pass.expired و pass.scanned |
| التجارة الإلكترونية | Order Completed و Order Refunded و Order Cancelled |
| الهوية | $identify و $alias |
يمكنك أيضًا بث أي حدث مخصص ترسله باستخدام track().
التجميع في دفعات
تُخزن الأحداث مؤقتًا وتُسلّم على دفعات. يمكنك ضبط:
- حجم الدفعة - عدد الأحداث المتراكمة قبل الإرسال.
- فاصل الإرسال - أطول مدة انتظار قبل إرسال دفعة جزئية.
تُسلّم الدفعة عند بلوغ حجم الدفعة أو انقضاء فاصل الإرسال، أيهما يحدث أولًا.
آلية العمل
تُسلّم الأحداث إلى التدفقات عبر ناقل توزيع دائم ومنفصل عن تخزين التحليلات. يرى كل تدفق سيل الأحداث الكامل عبر كل القنوات، لذلك تستمر الوجهة في تلقي الأحداث بصورة مستقلة عن طريقة كتابة التحليلات.
عندما يطابق حدث اختيار أحداث التدفق، يُوضع في الطابور ويُسلّم إلى الوجهة في الدفعة التالية. يتضمن كل تسليم اسم الحدث وخصائصه وطابعه الزمني ومعرّف المستخدم، وقناة المصدر لأحداث الرسائل. تحمّل وجهات مستودعات البيانات والتخزين الدفعة كصفوف/ملفات، وتوجّه وجهات تحليلات المنتج كل حدث عبر API الإدخال، وتتلقى Webhooks نص JSON أو NDJSON.
يجمع كل تدفق (وفق حجم الدفعة وفاصل الإرسال) ويعيد المحاولة عند الفشل. إذا تعذر الوصول إلى وجهة، تُعاد الأحداث غير المسلّمة إلى الطابور وتُرسل من جديد عند تعافيها، فلا يفقد انقطاع الخدمة بياناتك.
ضمانات التسليم والتكرارات
التسليم مرة واحدة على الأقل (at-least-once). في حالات نادرة - إعادة محاولة بعد فشل شبكة، أو إعادة تشغيل عامل في منتصف دفعة - قد يُسلَّم الحدث نفسه إلى وجهتك أكثر من مرة.
هذا مقصود، وهو الضمان نفسه الذي تقدمه كل منصات البث. البديل، التسليم مرة واحدة على الأكثر، يعني إسقاط حدث بصمت عندما تكون نتيجة التسليم غير معروفة؛ ونحن نفضّل الإرسال مرتين على الفقدان.
كل حدث يحمل eventId ثابتًا. وهو القيمة نفسها في كل تسليم لذلك الحدث، فيمكنك استخدامه لإزالة التكرار:
| الوجهة | ما نقوم به |
|---|---|
| Amplitude | يُرسل كـ insert_id - يزيل Amplitude التكرار تلقائيًا |
| Mixpanel | يُرسل كـ $insert_id - يزيل Mixpanel التكرار تلقائيًا |
| S3 | الدفعة المُعاد تسليمها تُكتب على مفتاح الكائن نفسه، فتستبدل بدل أن تضيف ملفًا ثانيًا |
| BigQuery, Snowflake, Redshift | أزل التكرار حسب eventId عند التحميل، مثل ROW_NUMBER() OVER (PARTITION BY eventId) |
| Webhook | تجاهل أي eventId سبق أن عالجته |
بالنسبة لمستودعات البيانات والتخزين، التكرارات غير ضارة بعد إزالتها عند التحميل. أما webhooks التي تنفّذ إجراءً على كل حدث - إنشاء تذكرة، خصم مبلغ - فتحقق من eventId قبل التنفيذ.
المراقبة
تعرض قائمة التدفقات مؤشرات الأداء والحالة لكل تدفق:
- نشط - يُسلّم بصورة طبيعية
- متوقف مؤقتًا - متوقف مؤقتًا (يمكن استئنافه في أي وقت)
- خطأ - تفشل عمليات التسليم الأخيرة (تحقق من الاتصال)
يمكنك أيضًا رؤية إجمالي ما تم تسليمه وإجمالي ما فشل ومعدل التسليم ووقت آخر تسليم. افتح تدفقًا لعرض صفحة التفاصيل أو تعديله أو إيقافه/استئنافه أو حذفه.