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" }
}
}seqtăng dần và là "con trỏ" để nhận tiếp phần bị lỡ. Hãy lưuseqcuối cùng đã xử lý.data.messagecó cùng dạng vớiGET .../messages, nên một hàm xử lý dùng được cho cả hai.conversation.typingkhô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
idhoặcseq.
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ếuevents:subscribe),FORBIDDEN_IP,API_DISABLED. subscribenhận{ types, account_ids, since }. Gọi lạisubscribeđể đổ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. EventSourcecủa trình duyệt không gửi được headerAuthorization. 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