القنوات: الويب والجوال.

إلى جانب WhatsApp، أضف فقاعة محادثة إلى موقعك أو اربط تطبيق الجوال مباشرة بـ Cxperium — الروبوت نفسه وصندوق الوارد نفسه.

أنواع القنوات وإنشاؤها

يمكن أن يضم حساب Cxperium أكثر من قناة. ترتبط جميع القنوات بالروبوت نفسه وقائمة جهات الاتصال نفسها وصندوق الوارد نفسه؛ ويرى الموظفون المحادثة على شاشة واحدة أيًا كان مصدرها.

النوعالغرضطريقة الربط
WhatsAppرقم عملك المعتمد من Metaيتولى فريقنا الطلب؛ ويظهر الرقم المعتمد في قائمة القنوات.
Webفقاعة المحادثة في موقعكتلصق كود التثبيت الذي توفره اللوحة في موقعك. دون برمجة.
Mobile (قناة مخصصة)تطبيق الجوال أو عميلك الخاصعبر REST وWebSocket؛ يتصل التطبيق مباشرة أو عبر خادمك.

لإنشاء قناة جديدة:

  1. افتح صفحة القنوات

    ادخل إلى القنوات (Channels) من القائمة الجانبية وانقر على إضافة (Add) أعلى اليمين.

  2. اختر النوع

    اختر بطاقة Web للمحادثة في موقعك، أو Mobile لتطبيق الجوال أو عميلك الخاص.

  3. حدد اسمًا وعنوان الروبوت

    اكتب اسمًا للقناة وعنوان الروبوت الذي تُحوَّل إليه الرسائل الواردة على هذه القناة. اللغة الافتراضية اختيارية. انقر على إضافة قناة (Add Channel).

  4. احصل على بيانات الاتصال

    تعرض الخطوة الأخيرة مفتاح القناة والسر و(لنوع Web) كود تثبيت جاهزًا. يمكنك الوصول إليها لاحقًا من نافذة التفاصيل (Detail) في قائمة القنوات.

القيمةمثالمكان الاستخدام
مفتاح القناة (channelKey)ch_c727ccd3ef5112a1يعرّف قناتك في العناوين وفي كود التثبيت. ليس سريًا.
السر (Secret)64 حرفًا ست عشريًايُضمَّن في كود التثبيت لقناة الويب. وفي قناة الجوال يُستخدم فقط عند التسجيل (التطبيق) أو لتوقيع الطلبات (الخادم).
عنوان الروبوتhttps://bot.example.com/api/cxperiumنقطة نهاية روبوت المحادثة التي تُحوَّل إليها كل رسالة من هذه القناة. يمكنك تغييره بتعديل القناة.
إذا تسرّب السر: عدّل القناة في قائمة القنوات وانقر على تجديد السر، ثم حدّث كود التثبيت في موقعك أو القيمة في تطبيقك. يصبح السر القديم غير صالح فورًا.

قناة الويب: تثبيت الأداة

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

site-install-code.html
<!-- Cxperium chat -->
<script>
  window.cxperiumSettings = {
    channelKey: "ch_c727ccd3ef5112a1",
    secret: "f57b4e…",
    brand: "Acme Support",
    color: "#7c3aed",
    launcherText: "راسلنا",
    texts: { greetingMessage: "مرحبًا! كيف يمكننا مساعدتك؟" }
  };
  // Optional: introduce the signed-in user — the welcome form is skipped:
  // window.cxperiumSettings.user = { name: "Jane Doe", username: "jane.d" };
  (function (d) {
    var s = d.createElement("script");
    s.src = "https://cdn.cxperium.com/widget/v1/cxperium-widget.js";
    s.async = true;
    d.head.appendChild(s);
  })(document);
</script>
  1. انسخ الكود

    انقر على نسخ في الخطوة الأخيرة من إنشاء القناة أو في نافذة التفاصيل في قائمة القنوات. إذا خصّصت المظهر فانسخ الكود من مصمم الأداة؛ فذلك الكود يتضمن تصميمك.

  2. الصقه في موقعك

    الصقه قبل وسم </body> مباشرة في كل صفحة تريد ظهور المحادثة فيها. يجب أن يكون في الصفحة مقطع Cxperium واحد فقط؛ احذف أي مقطع قديم.

  3. أعد تحميل الصفحة

    يجب أن تظهر الفقاعة في الزاوية المختارة. إن لم تظهر فأعد التحميل دون ذاكرة التخزين المؤقت (Ctrl + F5).

تحفظ الأداة هوية الزائر ورمزه في المتصفح؛ وعندما يعود الزائر يمكنه متابعة محادثته السابقة أو فتح محادثة جديدة. إذا عرّفت مستخدمًا مسجّل الدخول عبر إعداد user فلن يظهر نموذج الترحيب إطلاقًا.

الأمان: يحتوي كود التثبيت على مفتاح القناة والسر. لا يستطيع السر الموجود في الصفحة سوى فتح سجل محادثة جديد؛ ولا يمكنه قراءة المحادثات الحالية. ومع ذلك جدّده إذا اعتقدت أنه تسرّب.
اللغة: نصوص الشاشة الافتراضية للأداة باللغة التركية. لموقع عربي، املأ قسم النصوص في المصمم (كائن texts في الكود)؛ وتبقى المفاتيح التي تتركها فارغة على قيمتها التركية الافتراضية.

الأداة نفسها في تطبيق الجوال: يمكنك أيضًا استخدام قناة الجوال داخل WebView. تضبط شاشة WebView Customizer لقناة الجوال في قائمة القنوات مظهر الصفحة التي تضمّنها في تطبيقك، فتضيف المحادثة دون كتابة تكامل SDK الموضح أدناه.

مظهر الأداة (مصمم الأداة)

تغيّر لون الفقاعة وموضعها ونصوصها وعلامتها التجارية من لوحة التحكم. لا يُفتح المصمم إلا لقنوات نوع Web.

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

لا يُخزَّن التصميم على الخادم. يعيش تصميمك داخل كود التثبيت الذي تلصقه في موقعك. يحتفظ المصمم بمسودة في هذا المتصفح فقط؛ وعند فتحه من حاسوب آخر تظهر القيم الافتراضية. بعد كل تغيير انسخ الكود مجددًا واستبدل به الكود الموجود في موقعك.

العلامة التجارية

الإعدادمفتاح الكودالوظيفة
اسم العلامةbrandالاسم الظاهر في عنوان النافذة وفوق فقاعات الروبوت.
صورة الرمزavatarUrlالشعار الدائري في الترويسة. إن كان فارغًا يظهر الحرف الأول من العلامة. مربعة، 96×96 بكسل على الأقل، عنوان كامل (https://…).
أيقونة الفقاعةiconUrlأيقونة فقاعة الفتح في الزاوية. إن كانت فارغة تظهر أيقونة المحادثة القياسية.
نص الفقاعةlauncherTextعبارة قصيرة بجانب الفقاعة (24 حرفًا كحد أقصى). إن كانت فارغة تظهر الأيقونة فقط.
نص التذييلfooterTextالسطر الصغير أسفل النافذة. اتركه فارغًا لإخفائه.

الألوان والخط

الإعدادمفتاح الكودالوظيفة
اللون الأساسيcolorالترويسة وفقاعات الزائر والأزرار وفقاعة الفتح.
التدرجcolorDarkالطرف الداكن لتدرج الترويسة. يُشتق من اللون الأساسي إن كان فارغًا.
الخلفيةbackgroundخلفية منطقة المحادثة.
النص / الباهتtextColor، mutedColorلون النص في فقاعات الروبوت، والنصوص الثانوية مثل الأوقات.
الخطfontاسم خط CSS (مثل Inter, sans-serif). إن كان فارغًا يُستخدم خط نظام الزائر. يجب أن يكون الخط محمّلًا في موقعك.

التخطيط والحجم

الإعدادمفتاح الكودالوظيفة
الموضعpositionright (أسفل اليمين) أو left (أسفل اليسار).
الهامش الجانبي / السفليsideOffset، bottomOffsetبُعد الفقاعة عن جانب الصفحة وأسفلها (0–200 بكسل). زد الهامش السفلي إن كان في موقعك شريط سفلي ثابت.
عرض / ارتفاع اللوحةwidth، heightمقاس النافذة المفتوحة (280–600 / 400–900 بكسل). على الهواتف تملأ النافذة الشاشة.
استدارة الزواياradius0 لزوايا حادة، و40 لزوايا شديدة الاستدارة.

النصوص والسلوك

الإعدادمفتاح الكودالوظيفة
سؤال الزائر عن اسمهaskNameعند التفعيل يطلب نموذج الترحيب الاسم ويجعله إلزاميًا. عند الإيقاف يبدأ الزائر باسم "زائر".
سؤال اسم المستخدمaskUsernameالقاعدة نفسها لاسم المستخدم. متوقف افتراضيًا؛ ترسله معظم المواقع من جلستها عبر إعداد user.
رسالة الترحيب في المحادثةtexts.greetingMessageأول رسالة تظهر كفقاعة روبوت عند بدء المحادثة. لا تظهر إن كانت فارغة.
عنوان / نص الترحيبtexts.welcomeTitle، texts.welcomeMessageالعنوان والوصف أعلى نموذج الترحيب.
تسمية حقل الاسم / مثالهtexts.nameLabel، texts.namePlaceholderتسمية مربع الاسم والنص الرمادي داخله.
زر البدءtexts.startButtonنص الزر الذي يرسل النموذج.
إرسال الملفات / الصورallowMediaيضيف زر إرفاق إلى مربع الرسالة. 2 ميغابايت كحد أقصى؛ صور وPDF.
نص مربع الرسالةtexts.inputPlaceholderالنص الرمادي في حقل كتابة الرسالة.

لا يتضمن كود التثبيت إلا الإعدادات المختلفة عن القيم الافتراضية، لذا يبقى قصيرًا. يمسح زر العودة إلى الافتراضي في الشريط العلوي جميع الإعدادات والمسودة.

تطبيق الجوال (القناة المخصصة)

تربط قناة الجوال (القناة المخصصة) تطبيقك على iOS أو Android أو React Native أو Flutter مباشرة بـ Cxperium. يكتب المستخدم رسالة في التطبيق، ويرد الروبوت أو الموظف، ويصل الرد إلى التطبيق فورًا. لا حاجة إلى خادم.

الغرضالعنوان
التسجيل والسجل وتأكيد التسليم (REST)https://provider.cxperium.com
المراسلة الحية (WebSocket، socket.io)https://ws.cxperium.com — مساحة الأسماء /custom

مساران للإعداد

القرار يتوقف على سؤال واحد: هل سيقف خادمك الخاص بين تطبيقك وCxperium؟

أ) تطبيق الجوال فقطب) لديك خادم خاص
مكان السرمضمَّن في التطبيق؛ يُستخدم عند التسجيل فقطعلى خادمك كمتغير بيئة
من يُنشئ هوية المستخدمCxperium؛ تُعاد في استجابة التسجيلأنت (مثل u_42)
ما الذي يصادق الطلباترمز المستخدم المأخوذ عند التسجيل (Authorization: Bearer)توقيع HMAC محسوب بالسر (X-Cxperium-Signature)
هل يُتحقق من أن المستخدم هو الشخص نفسهلا؛ كافٍ للدعم المجهولنعم؛ يتحقق خادمك من المستخدم

إن لم يكن لديك خادم فاتبع المسار أ. تصف الخطوات أدناه المسار أ؛ وفي المسار ب تتغير ترويسة المصادقة فقط (انظر "إن كان لديك خادم: التوقيع").

الخطوة 1: التسجيل (مرة واحدة عند أول تشغيل)

register.js
const res = await fetch("https://provider.cxperium.com/custom/ch_xxxxxxxx/register", {
  method: "POST",
  headers: { "apiKey": SECRET, "Content-Type": "application/json" },
  body: JSON.stringify({ user: { name: "Jane" } }),
}).then((r) => r.json());

// res = { status: 200, channelKey: "ch_xxxxxxxx",
//         contact: { id: "c_5d5cdeb1-…" }, token: "8abfc196…" }
save("contactId", res.contact.id);   // Keychain / EncryptedSharedPreferences
save("token", res.token);             // never expires, obtained once

إذا أُعيد تثبيت التطبيق وما زال contactId المحفوظ لديك، فضمّنه في جسم التسجيل باسم user.id؛ فيُعاد الرمز نفسه وتستمر المحادثة من حيث توقفت. لا يمكن أن تحتوي هوية المستخدم على الحرف &.

الخطوة 2: الاتصال وجلب السجل (عند كل تشغيل)

connect.js
import { io } from "socket.io-client";

const socket = io("https://ws.cxperium.com/custom", {
  transports: ["websocket"],
  auth: { channelKey: "ch_xxxxxxxx", user: contactId, token },
});

socket.on("connect", async () => {
  // ORDER MATTERS: connect first, THEN fetch history.
  let after = stored("lastSeq") || 0;
  for (;;) {
    const h = await fetch(
      `https://provider.cxperium.com/custom/ch_xxxxxxxx/messages?user=${contactId}&after=${after}&limit=50`,
      { headers: { Authorization: `Bearer ${token}` } },
    ).then((r) => r.json());
    for (const m of h.messages) render(m);
    if (!h.hasMore) break;
    after = h.cursor;   // advance from the page's cursor
  }
});

يعيد السجل الاتجاهين معًا: direction: "in" رسالة المستخدم نفسه (ارسمها يمينًا في واجهة LTR)، و"out" رد الروبوت أو الموظف. seq رقم تسلسلي متزايد لكل مستخدم. إن كنت تحفظ الرسائل محليًا فابدأ من آخر seq رأيته، وإلا فابدأ من 0.

الخطوة 3: الإرسال والاستقبال وتأكيد التسليم

chat.js
// Send — id is the replay guard: on a network error resend with the SAME id
socket.emit("message.send",
  { user: { name: "Jane" }, message: { id: uniqueId(), type: "text", text: "hello" } },
  (ack) => { /* ack = { status: 202, messageId: "…" } */ });

// Receive — m = { id, seq, ts, type, text, choices, media, link, form }
socket.on("message.created", (m) => {
  if (seen.has(m.id)) return;
  seen.add(m.id);
  render(m);
  save("lastSeq", m.seq);
  socket.emit("message.ack", { seq: m.seq }, () => {});
});

إذا انقطع WebSocket يمكن إرسال الأجسام نفسها عبر REST: POST /custom/<KEY>/messages وPOST /custom/<KEY>/ack مع ترويسة Authorization: Bearer <token>. يعيد socket.io الاتصال تلقائيًا؛ وعند حدث connect شغّل جلب السجل من الخطوة 2 مجددًا لتصل الرسائل الفائتة.

  • احفظ contactId والرمز بشكل دائم — يجب أن يبقيا بعد إغلاق التطبيق.
  • WebSocket أولًا ثم السجل — الترتيب المعاكس يفوّت الرسائل التي تصل في الأثناء.
  • أزل تكرار الرسائل بحسب id — يضمن النظام التسليم مرة واحدة على الأقل، لا مرة واحدة بالضبط.
  • عند التقسيم إلى صفحات تقدّم بـ after من cursor الاستجابة — لا من عداد تحتفظ به أنت.
  • لا داعي للقلق عند الانقطاع — اجلب السجل بعد إعادة الاتصال؛ لا تضيع أي رسالة.

إن كان لديك خادم: التوقيع (المسار ب)

يتحدث التطبيق مع خادمك، ويتحدث خادمك مع Cxperium. لا يصل السر إلى التطبيق أبدًا. يحمل كل طلب ترويسة X-Cxperium-Signature: t=<unix seconds>,v1=<hex> حيث v1 = HMAC-SHA256(secret, "<t>.<content>"). المحتوى هو نص JSON الخام لطلبات POST، والسلسلة "<channelKey>:<userId>" لاتصال WebSocket واستعلام السجل. التوقيع صالح لمدة 300 ثانية؛ أنشئ توقيعًا جديدًا لكل طلب.

sign.js
const crypto = require("crypto");
function sign(content) {
  const t = Math.floor(Date.now() / 1000);
  const v1 = crypto.createHmac("sha256", CHANNEL_SECRET).update(`${t}.${content}`).digest("hex");
  return `t=${t},v1=${v1}`;
}

// For WebSocket, auth must be a FUNCTION — the signature is refreshed on reconnect
const socket = io("https://ws.cxperium.com/custom", {
  transports: ["websocket"],
  auth: (cb) => cb({ channelKey: CHANNEL_KEY, user: userId, signature: sign(`${CHANNEL_KEY}:${userId}`) }),
});
لا تُعِد تحويل جسم POST إلى نص (stringify)؛ فإن تغيّر ترتيب المفاتيح لن يتطابق التوقيع. وقّع النص الذي ترسله بعينه.

أنواع الرسائل والنماذج

جسم الرسالة نفسه يُستخدم في حدث WebSocket message.created وفي استعلام السجل وفي webhook التسليم. إذا وصل type لا تعرفه فاعرض حقل text؛ تُضاف أنواع جديدة مع الوقت وtext ممتلئ دائمًا.

ما يمكن إرساله من المستخدم إلى الروبوت

النوعالجسمملاحظة
text{ "type": "text", "text": "hello" }نص عادي.
choice{ "type": "choice", "choice": { "id": "opt_yes", "title": "Yes" } }النقر على زر عرضه الروبوت.
media{ "type": "media", "media": { "kind": "image", "url": "https://…", "mime": "image/jpeg", "caption": "…" } }صورة أو ملف؛ يجب أن يكون العنوان متاحًا للعموم.
form{ "type": "form", "form": { "token": "…", "name": "signup_form", "version": 3, "response": { … } } }إجابة نموذج أرسله الروبوت؛ يُعاد token كما ورد.

نوع الموقع (location) غير مدعوم بعد؛ إن أرسلته تتلقى 202 لكنه لا يُعالَج.

ما قد يصل من الروبوت

النوعالحقولطريقة العرض
texttextفقاعة عادية.
choicestext، choices[] (id، title)أزرار تحت النص؛ عند النقر أرسل ردًا من نوع choice.
mediamedia.kind، url، mime، caption، filenameبطاقة صورة أو فيديو أو ملف.
linktext، link.url، link.labelزر رابط؛ النقر لا يرسل شيئًا إلى المحادثة بل يفتح العنوان. العنوان هو أيضًا السطر الأخير من text؛ احذف ذلك السطر إن عرضت الزر.
formtext، form.screens، form.token، form.screen، form.dataانظر أدناه.

النماذج (Flow)

يستطيع الروبوت إرسال نماذج إلى قناة الجوال بالكود نفسه الذي يستخدمه لـ WhatsApp Flows. يصل تعريف النموذج داخل الرسالة في form.screens، والشاشات بصيغة Meta Flow JSON (TextInput وDropdown وRadioButtonsGroup وCheckboxGroup وDatePicker وFooter). ابدأ بـ form.screen؛ وform.data هي بيانات الشاشة الأولى. إن لم تستطع عرض النماذج فاعرض حقل text على الأقل.

  • الإجابة: إذا كان إجراء Footer في الشاشة الأخيرة complete فأرسل الحقول المعبأة كرسالة عادية من نوع form؛ وtoken إلزامي.
  • طلب شاشة من الخادم: إذا كان إجراء Footer هو data_exchange فاستدعِ POST /custom/<KEY>/flows/exchange؛ تكون الاستجابة { "version": "3.0", "screen": "…", "data": { … } }. إذا كانت screen تساوي SUCCESS فأغلق النموذج وأرسل الكائن data.extension_message_response.params كإجابة.
  • تطبيق جاهز: تنفّذ أداة الويب هذا القسم بالكامل؛ ولا يحتاج تكامل WebView إلى عمل إضافي.

حل المشكلات

أكثر الحالات شيوعًا وحلولها.

أداة الويب

الحالةالشرح / الحل
أعدت فتح المصمم فاختفت إعداداتيتُحفظ المسودة في المتصفح نفسه فقط. الكود الموجود في موقعك لم يتغير وهو السجل الحقيقي. أعد إدخال القيم من الكود إلى المصمم.
التغيير لا يظهر في الموقعتأكد من لصق الكود الجديد وحذف القديم؛ وأعد التحميل دون ذاكرة مؤقتة (Ctrl + F5).
تظهر الأداة بالأزرق الافتراضيربما نُسخ الكود من تفاصيل القناة؛ ذلك الكود يعطي المظهر الافتراضي دائمًا. الكود المخصص يُنسخ من المصمم.
لا يوجد زر "تخصيص المظهر"يظهر الزر لقنوات Web فقط. تستخدم قنوات الجوال WebView Customizer.
لا تظهر الصورة / الأيقونةيجب أن يكون العنوان كاملًا (https://…) ومتاحًا للعموم.
تقول وحدة التحكم [cxperium] channelKey ve secret zorunluchannelKey أو secret فارغ في كود التثبيت. انسخ الكود مجددًا من تفاصيل القناة.

قناة الجوال

الخطأالسببالحل
401 Invalid channel secretترويسة apiKey مفقودة أو خاطئة عند التسجيلقارنها بالسر في اللوحة.
401 Invalid token / WS INVALID_TOKENالرمز مستخدم مع هوية أخرىيجب أن يستخدم كل مستخدم رمزه؛ أعد التسجيل عند الحاجة.
401 Invalid signature / WS INVALID_SIGNATUREأُعيد تحويل الجسم إلى نص أو التوقيع أقدم من 300 ثانيةوقّع النص الخام؛ وأنشئ توقيعًا لكل طلب.
WS UNAUTHORIZEDالقناة غير موجودة أو غير نشطة أو محذوفةتحقق من مفتاح القناة وحالتها في اللوحة.
WS INVALID_AUTHلم يُقدَّم رمز ولا توقيعصحّح حقل auth في الاتصال.
WS AUTH_UNAVAILABLEمشكلة مؤقتة في الخادمانتظر وأعد المحاولة بالطريقة نفسها.
يتصل WS أولًا ثم يُرفض باستمرارفي المسار ب قُدِّم auth ككائن ثابتيجب أن يكون auth دالة؛ فيتجدد التوقيع عند كل اتصال.
تضيع رسائل أحيانًالا يوجد جلب للسجل (سدّ الفجوات)اجمعها بـ after=<آخر seq> عند الاتصال.
تظهر الرسالة نفسها مرتينلا توجد إزالة تكرار بحسب idأزل تكرار الرسائل بحسب id.
400 FLOW_KEY_MISSINGلا يوجد مفتاح نماذج للروبوت على القناةأدخل مفتاح النماذج من القنوات ← تعديل.
502 BOT_UNREACHABLE / BOT_ERRORلم يرد الروبوت خلال 10 ثوانٍ أو أعاد خطأاعرض "أعد المحاولة"؛ وتحقق من عنوان الروبوت في اللوحة.
الروبوت لا يرد أبدًاالقناة غير نشطة أو الروبوت متوقفتواصل مع فريق الدعم مع ذكر اسم القناة.

لم تجد ما تبحث عنه؟

دع فريقنا التقني يرافقك في عملية التكامل — أرسل سؤالك وسنعاود التواصل معك في اليوم نفسه.

دعم فني متخصص · استشارات تكامل مجانية