Skip to content

Chuẩn tích hợp kênh ​

Tài liệu này mô tả chuẩn giao tiếp giữa SieuTroly và các adapter kênh bên ngoài (Facebook, Telegram, Zalo, Instagram, v.v.).

Mỗi platform cần xây dựng một adapter nhỏ làm cầu nối:

  • Nhận webhook từ platform → chuẩn hóa → gửi vào SieuTroly
  • Nhận reply từ SieuTroly → gửi tin nhắn ra platform

Luồng tổng quan ​

Platform (FB / TG / ...) ──incoming──▶ Adapter ──▶ POST /webhooks/message (SieuTroly)
                                                            │
Platform (FB / TG / ...) ◀──outgoing── Adapter ◀── POST reply_url (SieuTroly → Adapter)

Cấu hình kênh trong SieuTroly ​

Khi tạo kênh (channel), trường config lưu JSON với các key:

KeyBắt buộcMô tả
webhook_tokenCóToken bí mật dùng để xác thực request 2 chiều
reply_urlCóURL của adapter nhận tin nhắn đi từ SieuTroly

Ví dụ config:

json
{
  "webhook_token": "sk_abc123xyz",
  "reply_url": "https://fb-adapter.example.com/reply"
}

Tin nhắn ĐẾN (Adapter → SieuTroly) ​

Endpoint: POST https://api.sieutroly.ai/webhooks/message

Header:

Content-Type: application/json
X-SieuTroly-Token: <webhook_token>

Body:

json
{
  "channel_id": "uuid-channel-trong-sieutroly",
  "sender": {
    "id": "platform-user-id (nếu is_group=true thì đây là ID của NHÓM)",
    "name": "Nguyễn Văn A",
    "avatar": "https://...",
    "is_group": false
  },
  "message": {
    "id": "platform-message-id",
    "type": "text",
    "content": "Xin chào!",
    "media_url": null,
    "timestamp": "2024-01-15T08:30:00Z",
    "sender_type": "contact",
    "reply_to": {
      "external_id": "platform-message-id-của-tin-gốc",
      "preview": "Nội dung tin gốc (fallback nếu không tìm thấy trong DB)",
      "sender_name": "Tên người gửi tin gốc (tuỳ chọn)"
    },
    "actor": {
      "id": "platform-user-id-của-thành-viên-vừa-gửi",
      "name": "Tên thành viên"
    }
  }
}

message.sender_type (tuỳ chọn, mặc định "contact"): set "agent" khi adapter phát hiện tin nhắn này thực ra do AGENT tự gửi qua app gốc của platform (vd trả lời trực tiếp bằng điện thoại Zalo, không qua SieuTroly) — lúc đó sender vẫn phải là thông tin ĐỐI PHƯƠNG trong hội thoại (không phải agent), để tin nhắn được gắn đúng vào hội thoại và hiển thị đúng chiều trong Inbox.

sender.is_group (tuỳ chọn, mặc định false): set true khi tin đến từ 1 nhóm chat — lúc đó sender.id/name/avatar là của CHÍNH cái nhóm (mọi tin trong nhóm gộp vào 1 conversation), còn message.actor (tuỳ chọn) là thành viên cụ thể vừa gửi tin đó, chỉ dùng để hiển thị tên người gửi trong Inbox — không tạo Customer riêng cho actor.

Chiều ngược lại (mục 4), recipient.is_group cũng phải set đúng để adapter biết gửi vào nhóm hay chat riêng (SieuTroly tự điền theo contact.is_group đã lưu khi nhận tin đến, adapter không cần tự suy luận).

message.reply_to (tuỳ chọn): set khi tin nhắn này là reply/quote 1 tin khác trên platform. SieuTroly tự tìm tin gốc trong DB theo {channel_id, external_id: reply_to.external_id} — tìm thấy thì hiển thị liên kết tới tin gốc thật, không thấy thì fallback hiển thị theo reply_to.preview/reply_to.sender_name (tin gốc có thể đã cũ, ngoài phạm vi lưu trữ, hoặc gửi trước khi tích hợp).

Các giá trị message.type:

typeMô tảcontentmedia_url
textTin nhắn văn bảnNội dung văn bảnnull
imageẢnhCaption hoặc [Ảnh]URL ảnh
videoVideoCaption hoặc [Video]URL video
audioTin nhắn thoại[Audio]URL audio
fileFile đính kèmTên fileURL file
stickerSticker / Emoji đặc biệt[Sticker]URL sticker

Response thành công (200):

json
{
  "ok": true,
  "conversation_id": "uuid",
  "message_id": "uuid"
}

Response lỗi:

json
{
  "error": "Mô tả lỗi"
}

Tin nhắn ĐI (SieuTroly → Adapter) ​

Khi agent gửi tin nhắn, SieuTroly tự động gọi reply_url của adapter.

Method: POST <reply_url>

Header:

Content-Type: application/json
X-SieuTroly-Token: <webhook_token>

Body:

json
{
  "conversation_id": "uuid",
  "recipient": {
    "id": "platform-user-id (id nhóm nếu is_group=true)",
    "name": "Nguyễn Văn A",
    "is_group": false
  },
  "message": {
    "type": "text",
    "content": "Chào bạn, tôi có thể giúp gì cho bạn?",
    "media_url": null
  },
  "sent_at": "2024-01-15T08:31:00Z"
}

Adapter phải trả về HTTP 200 để SieuTroly biết đã nhận. Nếu thất bại, lỗi được ghi log nhưng không ảnh hưởng tin nhắn trong hệ thống.


Xác thực (Authentication) ​

Cả 2 chiều đều dùng header X-SieuTroly-Token.

  • Adapter → SieuTroly: Adapter đặt token vào header khi gọi /webhooks/message
  • SieuTroly → Adapter: SieuTroly đặt cùng token khi gọi reply_url

Adapter phải xác minh token trước khi xử lý reply để tránh request giả mạo.


Ví dụ triển khai adapter (Telegram) ​

js
// telegram-adapter/index.js
const express = require('express')
const app = express()
app.use(express.json())

const SIEUTROLY_URL = 'https://api.sieutroly.ai/webhooks/message'
const CHANNEL_ID    = 'uuid-channel-telegram-trong-sieutroly'
const TOKEN         = 'sk_abc123xyz'
const BOT_TOKEN     = 'telegram-bot-token'

// 1. Nhận webhook từ Telegram → gửi vào SieuTroly
app.post('/telegram-webhook', async (req, res) => {
  const msg = req.body.message
  if (!msg?.text) return res.sendStatus(200)

  await fetch(SIEUTROLY_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-SieuTroly-Token': TOKEN },
    body: JSON.stringify({
      channel_id: CHANNEL_ID,
      sender: {
        id: String(msg.chat.id),
        name: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(' '),
      },
      message: {
        id: String(msg.message_id),
        type: 'text',
        content: msg.text,
        timestamp: new Date(msg.date * 1000).toISOString(),
      },
    }),
  })
  res.sendStatus(200)
})

// 2. Nhận reply từ SieuTroly → gửi ra Telegram
app.post('/reply', async (req, res) => {
  if (req.headers['x-sieutroly-token'] !== TOKEN) return res.sendStatus(401)
  const { recipient, message } = req.body

  await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: recipient.id, text: message.content }),
  })
  res.sendStatus(200)
})

app.listen(3001)

Ví dụ triển khai adapter (Facebook Messenger) ​

js
// facebook-adapter/index.js
const FB_ACCESS_TOKEN = 'EAAxxxxxxx'
const PAGE_VERIFY_TOKEN = 'sieutroly_verify'

// Xác thực webhook Facebook
app.get('/facebook-webhook', (req, res) => {
  if (req.query['hub.verify_token'] === PAGE_VERIFY_TOKEN)
    return res.send(req.query['hub.challenge'])
  res.sendStatus(403)
})

// 1. Nhận webhook từ Facebook → gửi vào SieuTroly
app.post('/facebook-webhook', async (req, res) => {
  for (const entry of req.body.entry || []) {
    for (const event of entry.messaging || []) {
      if (!event.message || event.message.is_echo) continue

      await fetch(SIEUTROLY_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-SieuTroly-Token': TOKEN },
        body: JSON.stringify({
          channel_id: CHANNEL_ID,
          sender: { id: event.sender.id, name: null },
          message: {
            id: event.message.mid,
            type: event.message.attachments ? event.message.attachments[0].type : 'text',
            content: event.message.text || '[Media]',
            media_url: event.message.attachments?.[0]?.payload?.url || null,
            timestamp: new Date(event.timestamp).toISOString(),
          },
        }),
      })
    }
  }
  res.sendStatus(200)
})

// 2. Nhận reply từ SieuTroly → gửi ra Facebook
app.post('/reply', async (req, res) => {
  if (req.headers['x-sieutroly-token'] !== TOKEN) return res.sendStatus(401)
  const { recipient, message } = req.body

  await fetch(`https://graph.facebook.com/v19.0/me/messages?access_token=${FB_ACCESS_TOKEN}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      messaging_type: 'RESPONSE',
      recipient: { id: recipient.id },
      message: message.type === 'image'
        ? { attachment: { type: 'image', payload: { url: message.media_url } } }
        : { text: message.content },
    }),
  })
  res.sendStatus(200)
})

Checklist cho bên tích hợp ​

  • [ ] Đăng ký kênh trên SieuTroly, lấy channel_id
  • [ ] Cấu hình webhook_token và reply_url trong config kênh
  • [ ] Deploy adapter, nhận webhook từ platform
  • [ ] Chuẩn hóa payload và POST vào POST /webhooks/message
  • [ ] Implement endpoint nhận reply_url, xác thực token, gửi tin ra platform
  • [ ] Test end-to-end: gửi tin từ platform → hiện trong SieuTroly → agent reply → hiện lại trên platform

Hỗ trợ: contact@sieutroly.ai