إلى جانب WhatsApp، أضف فقاعة محادثة إلى موقعك أو اربط تطبيق الجوال مباشرة بـ Cxperium — الروبوت نفسه وصندوق الوارد نفسه.
يمكن أن يضم حساب Cxperium أكثر من قناة. ترتبط جميع القنوات بالروبوت نفسه وقائمة جهات الاتصال نفسها وصندوق الوارد نفسه؛ ويرى الموظفون المحادثة على شاشة واحدة أيًا كان مصدرها.
| النوع | الغرض | طريقة الربط |
|---|---|---|
| رقم عملك المعتمد من Meta | يتولى فريقنا الطلب؛ ويظهر الرقم المعتمد في قائمة القنوات. | |
| Web | فقاعة المحادثة في موقعك | تلصق كود التثبيت الذي توفره اللوحة في موقعك. دون برمجة. |
| Mobile (قناة مخصصة) | تطبيق الجوال أو عميلك الخاص | عبر REST وWebSocket؛ يتصل التطبيق مباشرة أو عبر خادمك. |
لإنشاء قناة جديدة:
ادخل إلى القنوات (Channels) من القائمة الجانبية وانقر على إضافة (Add) أعلى اليمين.
اختر بطاقة Web للمحادثة في موقعك، أو Mobile لتطبيق الجوال أو عميلك الخاص.
اكتب اسمًا للقناة وعنوان الروبوت الذي تُحوَّل إليه الرسائل الواردة على هذه القناة. اللغة الافتراضية اختيارية. انقر على إضافة قناة (Add Channel).
تعرض الخطوة الأخيرة مفتاح القناة والسر و(لنوع Web) كود تثبيت جاهزًا. يمكنك الوصول إليها لاحقًا من نافذة التفاصيل (Detail) في قائمة القنوات.
| القيمة | مثال | مكان الاستخدام |
|---|---|---|
مفتاح القناة (channelKey) | ch_c727ccd3ef5112a1 | يعرّف قناتك في العناوين وفي كود التثبيت. ليس سريًا. |
| السر (Secret) | 64 حرفًا ست عشريًا | يُضمَّن في كود التثبيت لقناة الويب. وفي قناة الجوال يُستخدم فقط عند التسجيل (التطبيق) أو لتوقيع الطلبات (الخادم). |
| عنوان الروبوت | https://bot.example.com/api/cxperium | نقطة نهاية روبوت المحادثة التي تُحوَّل إليها كل رسالة من هذه القناة. يمكنك تغييره بتعديل القناة. |
تضيف قناة الويب فقاعة محادثة إلى زاوية موقعك. ينقر الزائر على الفقاعة، ويكتب اسمه إن شئت، ويبدأ الحديث مع الروبوت أو مع موظفك. التثبيت مقطع كود واحد فقط.
<!-- 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>
انقر على نسخ في الخطوة الأخيرة من إنشاء القناة أو في نافذة التفاصيل في قائمة القنوات. إذا خصّصت المظهر فانسخ الكود من مصمم الأداة؛ فذلك الكود يتضمن تصميمك.
الصقه قبل وسم </body> مباشرة في كل صفحة تريد ظهور المحادثة فيها. يجب أن يكون في الصفحة مقطع Cxperium واحد فقط؛ احذف أي مقطع قديم.
يجب أن تظهر الفقاعة في الزاوية المختارة. إن لم تظهر فأعد التحميل دون ذاكرة التخزين المؤقت (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). إن كان فارغًا يُستخدم خط نظام الزائر. يجب أن يكون الخط محمّلًا في موقعك. |
| الإعداد | مفتاح الكود | الوظيفة |
|---|---|---|
| الموضع | position | right (أسفل اليمين) أو left (أسفل اليسار). |
| الهامش الجانبي / السفلي | sideOffset، bottomOffset | بُعد الفقاعة عن جانب الصفحة وأسفلها (0–200 بكسل). زد الهامش السفلي إن كان في موقعك شريط سفلي ثابت. |
| عرض / ارتفاع اللوحة | width، height | مقاس النافذة المفتوحة (280–600 / 400–900 بكسل). على الهواتف تملأ النافذة الشاشة. |
| استدارة الزوايا | radius | 0 لزوايا حادة، و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) |
| هل يُتحقق من أن المستخدم هو الشخص نفسه | لا؛ كافٍ للدعم المجهول | نعم؛ يتحقق خادمك من المستخدم |
إن لم يكن لديك خادم فاتبع المسار أ. تصف الخطوات أدناه المسار أ؛ وفي المسار ب تتغير ترويسة المصادقة فقط (انظر "إن كان لديك خادم: التوقيع").
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؛ فيُعاد الرمز نفسه وتستمر المحادثة من حيث توقفت. لا يمكن أن تحتوي هوية المستخدم على الحرف &.
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.
// 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 مجددًا لتصل الرسائل الفائتة.
يتحدث التطبيق مع خادمك، ويتحدث خادمك مع Cxperium. لا يصل السر إلى التطبيق أبدًا. يحمل كل طلب ترويسة X-Cxperium-Signature: t=<unix seconds>,v1=<hex> حيث v1 = HMAC-SHA256(secret, "<t>.<content>"). المحتوى هو نص JSON الخام لطلبات POST، والسلسلة "<channelKey>:<userId>" لاتصال WebSocket واستعلام السجل. التوقيع صالح لمدة 300 ثانية؛ أنشئ توقيعًا جديدًا لكل طلب.
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}`) }),
});
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 لكنه لا يُعالَج.
| النوع | الحقول | طريقة العرض |
|---|---|---|
text | text | فقاعة عادية. |
choices | text، choices[] (id، title) | أزرار تحت النص؛ عند النقر أرسل ردًا من نوع choice. |
media | media.kind، url، mime، caption، filename | بطاقة صورة أو فيديو أو ملف. |
link | text، link.url، link.label | زر رابط؛ النقر لا يرسل شيئًا إلى المحادثة بل يفتح العنوان. العنوان هو أيضًا السطر الأخير من text؛ احذف ذلك السطر إن عرضت الزر. |
form | text، form.screens، form.token، form.screen، form.data | انظر أدناه. |
يستطيع الروبوت إرسال نماذج إلى قناة الجوال بالكود نفسه الذي يستخدمه لـ WhatsApp Flows. يصل تعريف النموذج داخل الرسالة في form.screens، والشاشات بصيغة Meta Flow JSON (TextInput وDropdown وRadioButtonsGroup وCheckboxGroup وDatePicker وFooter). ابدأ بـ form.screen؛ وform.data هي بيانات الشاشة الأولى. إن لم تستطع عرض النماذج فاعرض حقل text على الأقل.
complete فأرسل الحقول المعبأة كرسالة عادية من نوع form؛ وtoken إلزامي.data_exchange فاستدعِ POST /custom/<KEY>/flows/exchange؛ تكون الاستجابة { "version": "3.0", "screen": "…", "data": { … } }. إذا كانت screen تساوي SUCCESS فأغلق النموذج وأرسل الكائن data.extension_message_response.params كإجابة.أكثر الحالات شيوعًا وحلولها.
| الحالة | الشرح / الحل |
|---|---|
| أعدت فتح المصمم فاختفت إعداداتي | تُحفظ المسودة في المتصفح نفسه فقط. الكود الموجود في موقعك لم يتغير وهو السجل الحقيقي. أعد إدخال القيم من الكود إلى المصمم. |
| التغيير لا يظهر في الموقع | تأكد من لصق الكود الجديد وحذف القديم؛ وأعد التحميل دون ذاكرة مؤقتة (Ctrl + F5). |
| تظهر الأداة بالأزرق الافتراضي | ربما نُسخ الكود من تفاصيل القناة؛ ذلك الكود يعطي المظهر الافتراضي دائمًا. الكود المخصص يُنسخ من المصمم. |
| لا يوجد زر "تخصيص المظهر" | يظهر الزر لقنوات Web فقط. تستخدم قنوات الجوال WebView Customizer. |
| لا تظهر الصورة / الأيقونة | يجب أن يكون العنوان كاملًا (https://…) ومتاحًا للعموم. |
تقول وحدة التحكم [cxperium] channelKey ve secret zorunlu | channelKey أو 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 ثوانٍ أو أعاد خطأ | اعرض "أعد المحاولة"؛ وتحقق من عنوان الروبوت في اللوحة. |
| الروبوت لا يرد أبدًا | القناة غير نشطة أو الروبوت متوقف | تواصل مع فريق الدعم مع ذكر اسم القناة. |
دع فريقنا التقني يرافقك في عملية التكامل — أرسل سؤالك وسنعاود التواصل معك في اليوم نفسه.
دعم فني متخصص · استشارات تكامل مجانية