الويبهوكس هي استدعاءات HTTP يتم إعدادها لكل حساب. يتم تفعيلها عند حدوث إجراءات مثل إنشاء رسالة في Super Chat. يمكن إنشاء عدة ويبهوكس لحساب واحد.
كيف يمكن إضافة ويبهوك؟
الخطوة 1. انتقل إلى الإعدادات → التكاملات → الويبهوكس. اضغط على زر "تهيئة".

الخطوة 2. اضغط على زر "إضافة ويبهوك جديد". ستظهر نافذة منبثقة. هنا، أدخل عنوان URL الذي يجب إرسال طلب POST إليه. بعد ذلك، تحتاج إلى اختيار الأحداث التي ترغب في الاشتراك بها. هذا الخيار يسمح لك بالاستماع فقط للأحداث ذات الصلة في Super Chat.

سيرسل Super Chat طلب POST بالحمولة التالية إلى عناوين URL التي تم إعدادها لمختلف التحديثات في حسابك.
مثال على حمولة ويبهوك
{
"event": "message_created", // اسم الحدث
"id": "1", // معرف الرسالة
"content": "Hi", // محتوى الرسالة
"created_at": "2020-03-03 13:05:57 UTC", // وقت إرسال الرسالة
"message_type": "incoming", // هذا سيكون نوع داخلي، خارجي أو قالب. المستخدم من الويدجت يرسل رسائل داخلية، والوكيل يرسل رسائل خارجية للمستخدم.
"content_type": "enum", // هذا نوع تعداد، يمكن أن يكون input_select أو cards أو form أو نص. سيكون نوع الرسالة "template" إذا كان content_type هو أحد هذه القيم. القيمة الافتراضية هي نص
"content_attributes": {} // سيكون كائنًا، القيم المختلفة معرفة أدناه
"source_id": "", // سيكون المعرف الخارجي إذا كان البريد الوارد تكامل مع تويتر أو فيسبوك.
"sender": { // تفاصيل الوكيل الذي أرسل الرسالة
"id": "1",
"name": "Agent",
"email": "agent@example.com"
},
"contact": { // تفاصيل المستخدم الذي أرسل الرسالة
"id": "1",
"name": "contact-name"
},
"conversation": { // تفاصيل المحادثة
"display_id": "1", // معرف المحادثة الذي يمكنك رؤيته في لوحة التحكم.
"additional_attributes": {
"browser": {
"device_name": "Macbook",
"browser_name": "Chrome",
"platform_name": "Macintosh",
"browser_version": "80.0.3987.122",
"platform_version": "10.15.2"
},
"referer": "<http://www.chatwoot.com>",
"initiated_at": "Tue Mar 03 2020 18:37:38 GMT-0700 (Mountain Standard Time)"
}
},
"account": { // تفاصيل الحساب
"id": "1",
"name": "Super Chat",
}
}
الأحداث المدعومة في Super Chat للويبهوك
ينشر Super Chat أحداثًا مختلفة إلى نقاط نهاية الويبهوك المحددة. إذا كنت تريد إعداد ويبهوك، يمكنك الرجوع إلى الدليل هنا.
كل حدث له بنية حمولة خاصة به بناءً على نوع النموذج الذي يعمل عليه. يصف القسم التالي الكائنات الرئيسية التي نستخدمها في Super Chat وخصائصها.
الكائنات
قد تتضمن حمولة الحدث أيًا من الكائنات التالية. أنواع الكائنات المختلفة التي يدعمها Super Chat مبينة أدناه.
الحساب
{
"id": "integer",
"name": "string"
}
البريد الوارد
{
"id": "integer",
"name": "string"
}
جهة الاتصال
{
"id": "integer",
"name": "string",
"avatar": "string",
"type": "contact",
"account": {
// <...Account Object>
}
}
المستخدم
{
"id": "integer",
"name": "string",
"email": "string",
"type": "user"
}
المحادثة
{
"additional_attributes": {
"browser": {
"device_name": "string",
"browser_name": "string",
"platform_name": "string",
"browser_version": "string",
"platform_version": "string"
},
"referer": "string",
"initiated_at": {
"timestamp": "iso-datetime"
}
},
"can_reply": "boolean",
"channel": "string",
"id": "integer",
"inbox_id": "integer",
"contact_inbox": {
"id": "integer",
"contact_id": "integer",
"inbox_id": "integer",
"source_id": "string",
"created_at": "datetime",
"updated_at": "datetime",
"hmac_verified": "boolean"
},
"messages": ["Array of message objects"],
"meta": {
"sender": {
// Contact Object
},
"assignee": {
// User Object
}
},
"status": "string",
"unread_count": "integer",
"agent_last_seen_at": "unix-timestamp",
"contact_last_seen_at": "unix-timestamp",
"timestamp": "unix-timestamp",
"account_id": "integer"
}
الرسالة
{
"id": "integer",
"content": "string",
"message_type": "integer",
"created_at": "unix-timestamp",
"private": "boolean",
"source_id": "string / null",
"content_type": "string",
"content_attributes": "object",
"sender": {
"type": "string - contact/user"
// User or Contact Object
},
"account": {
// Account Object
},
"conversation": {
// Conversation Object
},
"inbox": {
// Inbox Object
}
}
مثال على حمولة ويبهوك
{
"event": "event_name"
// خصائص تتعلق بالحدث
}
أحداث الويبهوك
يدعم Super Chat أحداث الويبهوك التالية. يمكنك الاشتراك فيها أثناء إعداد الويبهوك في لوحة التحكم أو باستخدام واجهة البرمجة (API).
conversation_created
سيتم تفعيل هذا الحدث عند إنشاء محادثة جديدة في الحساب. حمولة الحدث كالتالي.
{
"event": "conversation_created"
// <...Conversation Attributes>
}
conversation_updated
سيتم تفعيل هذا الحدث عند حدوث تغيير في أي من خصائص المحادثة.
{
"event": "conversation_updated",
"changed_attributes": [
{
"<attribute_name>": {
"current_value": "",
"previous_value": ""
}
}
]
// <...Conversation Attributes>
}
conversation_status_changed
سيتم تفعيل هذا الحدث عند تغيير حالة المحادثة.
ملاحظة: إذا كنت تستخدم واجهات البرمجة الخاصة بروبوت الوكلاء بدلاً من الويبهوكس، هذا الحدث غير مدعوم حالياً.
{
"event": "conversation_status_changed"
// <...Conversation Attributes>
}
message_created
سيتم تفعيل هذا الحدث عند إنشاء رسالة في محادثة. حمولة الحدث كالتالي.
{
"event": "message_created"
// <...Message Attributes>
}
message_updated
سيتم تفعيل هذا الحدث عند تحديث رسالة في محادثة. حمولة الحدث كالتالي.
{
"event": "message_updated"
// <...Message Attributes>
}
webwidget_triggered
سيتم تفعيل هذا الحدث عند فتح المستخدم النهائي لويدجت الدردشة المباشرة.
{
"event": "webwidget_triggered",
"id": "",
"contact": {
// <...Contact Object>
},
"inbox": {
// <...Inbox Object>
},
"account": {
// <...Account Object>
},
"current_conversation": {
// <...Conversation Object>
},
"source_id": "string",
"event_info": {
"initiated_at": {
"timestamp": "date-string"
},
"referer": "string",
"widget_language": "string",
"browser_language": "string",
"browser": {
"browser_name": "string",
"browser_version": "string",
"device_name": "string",
"platform_name": "string",
"platform_version": "string"
}
}
}
conversation_typing_on
يتم تفعيل هذا الحدث عندما يبدأ الوكيل بالكتابة في محادثة. يمكن أن تكون ملاحظة خاصة أو رسالة للعميل. يمكنك استخدام علم is_private للتمييز بينهما.
{
"event": "conversation_typing_on",
"conversation": { ...<Conversation Object> },
"user": { ... <User / AgentBot / Captain Object> },
"is_private": true
}
conversation_typing_off
يتم تفعيل هذا الحدث عندما يتوقف الوكيل عن الكتابة أو عند مغادرة نافذة المحادثة.
{
"event": "conversation_typing_off",
"conversation": { ...<Conversation Object> },
"user": { ... <User / AgentBot / Captain Object> },
"is_private": true
}
التحقق من الويبهوكس
يقوم Super Chat بتوقيع كل طلب ويبهوك صادر حتى يتمكن الخادم لديك من التحقق من أن الحمولة تم إرسالها من قبل Super Chat وأنها لم يتم العبث بها. يظهر السر لك عند إنشاء الويبهوك، ويمكنك مشاهدته مرة أخرى في نموذج تحرير الويبهوك.
يرسل كل طلب ويبهوك الرؤوس التالية، والتي يمكن استخدامها لحساب توقيع HMAC الخاص بالحمولة
-
X-Super Chat-Signature: توقيع HMAC-SHA256 مسبوق بـsha256= -
X-Super Chat-Timestamp: الطابع الزمني (ثواني) عند توقيع الطلب -
X-Super Chat-Delivery: معرف تسليم فريد لحدث الويبهوك (عند توفره)
يتم احتساب التوقيع كما يلي:
sha256=HMAC-SHA256(webhook_secret, "{timestamp}.{raw_body}")
حيث:
-
webhook_secretهو السر المرتبط بالويبهوك -
timestampهو قيمة رأسX-Super Chat-Timestamp -
raw_bodyهو نص الطلب JSON الخام (غير محلل/غير مُعاد تسلسله)
خطوات التحقق
-
استخرج
X-Super Chat-SignatureوX-Super Chat-Timestampمن رؤوس الطلب -
اقرأ نص الطلب الخام كبايتات (لا تقم بتحليل وإعادة تسلسلها)
-
احسب التوقيع المتوقع:
sha256=HMAC-SHA256(secret, "{timestamp}.{raw_body}") -
قارن التوقيع المحسوب مع التوقيع المستلم باستخدام مقارنة وقت ثابت
-
اختياريًا، ارفض الطلبات التي يكون طابعها الزمني قديمًا جدًا لمنع هجمات إعادة الطلب
أمثلة
روبي
def verify_signature(raw_body, timestamp, received_signature, secret)
expected = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{raw_body}")}"
ActiveSupport::SecurityUtils.secure_compare(expected, received_signature)
end
بايثون
import hmac
import hashlib
def verify_signature(raw_body: bytes, timestamp: str, received_signature: str, secret: str) -> bool:
message = f"{timestamp}.".encode() + raw_body
expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
Node.js
const crypto = require("crypto");
function verifySignature(rawBody, timestamp, receivedSignature, secret) {
const message = `${timestamp}.${rawBody}`;
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(message).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(receivedSignature)
);
}
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifySignature(rawBody []byte, timestamp, receivedSignature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(fmt.Sprintf("%s.%s", timestamp, rawBody)))
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(receivedSignature))
}
ملاحظات هامة
-
استخدم دائمًا نص الطلب الخام للتحقق. تحليل JSON وإعادة تسلسله قد يغير ترتيب المفاتيح أو الفراغات أو ترميز يونيكود، مما سيؤدي إلى توقيع مختلف.
-
استخدم دائمًا مقارنة وقت ثابت (مثل
hmac.compare_digest،crypto.timingSafeEqual،ActiveSupport::SecurityUtils.secure_compare) لمنع هجمات التوقيت. -
فكر في رفض الطلبات التي تحمل طوابع زمنية أقدم من 5 دقائق لتقليل مخاطر هجمات إعادة الإرسال.