Bỏ qua tới nội dung

Sự kiện realtime

Có 4 cách nhận sự kiện, chọn theo nơi chạy code của bạn:

Cách Phù hợp Ghi chú
Socket.IO namespace /v1 App, worker giữ kết nối lâu Nhanh nhất, có cả conversation.typing
SSE GET /api/v1/events/stream Script, server không cài được socket.io Chạy tốt qua tunnel
Polling GET /api/v1/events?since= Cron, serverless, Google Apps Script Không cần giữ kết nối
Webhook Server có URL công khai Deplao chủ động POST, tự thử lại (xem hướng dẫn "Webhook")

Key cần scope events:subscribe (webhook cần webhooks:manage).

Cấu trúc một sự kiện

json
{
  "id": "evt_6b1f0c2d9a4e4c1b8f3a2d10",
  "seq": 1532,
  "type": "message.received",
  "ts": 1760412345678,
  "account_id": "0501234567",
  "channel": "zalo",
  "data": {
    "message": { "msg_id": "7712345678901", "thread_id": "8765432109876", "thread_type": "user", "text": "Shop ơi còn size M không?", "is_self": false },
    "conversation": { "thread_id": "8765432109876", "display_name": "Lan Nguyễn" }
  }
}
  • seq tăng dần và là "con trỏ" để nhận tiếp phần bị lỡ. Hãy lưu seq cuối cùng đã xử lý.
  • data.message có cùng dạng với GET .../messages, nên một hàm xử lý dùng được cho cả hai.
  • conversation.typing không được lưu (seq = 0) và chỉ có trên Socket.IO và SSE.
  • Danh mục đầy đủ kèm mô tả: GET /api/v1/events/types (không cần key).

Quy tắc

  • Key chỉ nhận sự kiện của tài khoản được cấp. Sự kiện toàn hệ thống (payment.received, plugin.*, ping) chỉ gửi cho key không giới hạn tài khoản.
  • Lọc loại: message.received (đúng tên), message.* (cả nhóm), * (tất cả).
  • Deplao chỉ ghi sự kiện khi có nơi nhận: key có scope sự kiện/webhook, kết nối đang mở hoặc webhook đang bật. Sự kiện xảy ra trước khi bạn tạo key đầu tiên sẽ không có.
  • Tin cũ hơn 5 phút (đồng bộ lịch sử, tải tin cũ) không phát message.received.
  • Sự kiện được giữ 7 ngày hoặc 50.000 bản ghi gần nhất (đổi trong Công cụ → Ứng dụng, API & Webhook → API → Nâng cao).
  • Giao ít nhất một lần: khi kết nối lại có thể nhận trùng. Khử trùng bằng id hoặc seq.

Socket.IO (Node.js)

js
// npm i socket.io-client@4
import { io } from 'socket.io-client';
import fs from 'node:fs';

let lastSeq = Number(fs.existsSync('seq.txt') ? fs.readFileSync('seq.txt', 'utf8') : 0);
const socket = io('http://127.0.0.1:9900/v1', {
  auth: { apiKey: process.env.DEPLAO_KEY },
  transports: ['websocket'],
});

socket.on('hello', ({ seq_now }) => {
  // Lần đầu bắt đầu từ hiện tại; các lần sau nhận cả phần bị lỡ lúc mất kết nối.
  socket.emit('subscribe', { types: ['message.*'], since: lastSeq || seq_now }, (ack) => console.log('subscribed', ack));
});

socket.on('event', (ev) => {
  if (ev.type === 'message.received') {
    console.log(ev.data.conversation.display_name, ':', ev.data.message.text);
  }
  if (ev.seq) {
    lastSeq = ev.seq;
    fs.writeFileSync('seq.txt', String(lastSeq));
  }
});

socket.on('connect_error', (err) => console.error('Không kết nối được:', err.message));
  • Lỗi kết nối trả qua connect_error: UNAUTHORIZED (sai key), FORBIDDEN_SCOPE (thiếu events:subscribe), FORBIDDEN_IP, API_DISABLED.
  • subscribe nhận { types, account_ids, since }. Gọi lại subscribe để đổi bộ lọc; unsubscribe để dừng.
  • Qua tunnel: io('https://<domain>/v1', ...) (path mặc định /socket.io).

SSE

bash
curl -N -H "Authorization: Bearer dpk_xxx" "http://127.0.0.1:9900/api/v1/events/stream?types=message.*&since=1500"
text
id: 1532
event: message.received
data: {"id":"evt_...","seq":1532,"type":"message.received",...}
  • Kết nối lại với header Last-Event-ID: <seq> hoặc ?since=<seq> để nhận phần bị lỡ.
  • Cứ 15 giây có dòng : ping để giữ kết nối qua proxy và tunnel.
  • EventSource của trình duyệt không gửi được header Authorization. Trong trình duyệt hãy dùng Socket.IO.

Python:

python
import json, requests

r = requests.get(
    "http://127.0.0.1:9900/api/v1/events/stream",
    params={"types": "message.received"},
    headers={"Authorization": "Bearer dpk_xxx"},
    stream=True, timeout=(5, 60),
)
for line in r.iter_lines(decode_unicode=True):
    if line and line.startswith("data: "):
        ev = json.loads(line[6:])
        print(ev["seq"], ev["data"]["message"]["text"])

Polling

bash
curl -H "Authorization: Bearer dpk_xxx" "http://127.0.0.1:9900/api/v1/events?since=0&types=message.received&limit=100"
json
{ "success": true, "data": [ { "seq": 1532, "type": "message.received" } ], "pagination": { "limit": 100, "has_more": false, "next_since": 1532 }, "request_id": "..." }

Gọi lại với since = pagination.next_since. Khi has_more = true gọi tiếp ngay, khi false đợi vài giây.

Chưa tìm thấy câu trả lời?

Nhắn hỗ trợ qua Telegram