Tôi cho Cursor đọc tài liệu Webhook — 30 phút sau có luôn relay Telegram sang Slack — UnifyPort
Muốn đưa tin nhắn Telegram vào Slack, con đường tiêu chuẩn là: tạo bot với @BotFather, cấu hình Webhook hoặc getUpdates polling, parse object Update lồng nhau của Telegram, format cho Slack Incoming Webhook, rồi xử lý rate limit. Chạy được, nhưng code hoàn toàn gắn chết với Telegram. Object Update có cấu trúc riêng, bot chỉ thấy được cuộc hội thoại của chính nó, và nếu sau này bạn muốn nhận thêm tin nhắn WhatsApp vào cùng kênh Slack — phải tích hợp từ đầu một API hoàn toàn khác.
Tôi chọn đường khác. Mở Cursor, dán tài liệu Webhook UnifyPort vào, yêu cầu nó build một relay Telegram sang Slack. Ba mươi phút sau tôi có một Express server xác minh chữ ký HMAC-SHA256 cho mọi delivery, format tin nhắn Telegram bằng Slack Block Kit và đẩy vào channel — khoảng 45 dòng code. Cùng relay đó xử lý luôn WhatsApp, LINE, TikTok, Zalo và X mà không cần sửa gì, vì cấu trúc event đầu vào giống hệt nhau cho cả sáu nền tảng.
Thành phẩm cuối cùng
Một Node.js Express server có thể:
- Nhận event
message.receivedtừ Webhook thống nhất của UnifyPort - Xác minh chữ ký mỗi delivery bằng HMAC-SHA256 với
signing_secretcủa bạn - Format mỗi tin nhắn thành Slack Block Kit payload với tag nền tảng, người gửi và timestamp
- Gửi vào kênh Slack qua Incoming Webhook
Thời gian: khoảng 30 phút. Bạn cần một workspace UnifyPort đã kết nối tài khoản Telegram (quét QR — không cần đăng ký bot) và một Slack Incoming Webhook URL.
Sao không dùng thẳng Bot API?
Tóm tắt nhanh những gì cần làm khi đi đường Telegram Bot API:
- Tạo bot qua @BotFather — nhận bot token, nhưng bot là một thực thể riêng biệt với tài khoản cá nhân của bạn
- Chọn phương thức nhận tin:
getUpdatespolling hoặc Webhook — cả hai đều cần cấu hình riêng cho Telegram - Parse object
Update: nội dung tin nhắn nằm ởupdate.message.text, người gửi ởupdate.message.from.id, cuộc chat ởupdate.message.chat.id— cấu trúc riêng của Telegram - Xử lý rate limit: 30 tin/giây toàn cục, 20 tin/phút cho cùng một nhóm
- Format và chuyển tiếp vào Slack
Code chạy tốt — cho đến khi bạn cần thêm tin nhắn WhatsApp trong cùng kênh. Lúc đó bạn phải tích hợp WhatsApp Cloud API riêng, parse một webhook payload khác, và duy trì hai code path mà cả hai đều xuất ra Slack message. Ba nền tảng thì ba code path.
Webhook UnifyPort gom tất cả lại. Mọi nền tảng đều gửi cùng event message.received — provider, from, text, message_id. Một handler phục vụ cả sáu kênh.
Chuẩn bị: đưa tài liệu vào Cursor
Trước khi viết prompt, đưa API reference vào context của Cursor. Dùng @Docs để thêm tài liệu Webhook UnifyPort — đặc biệt:
- Cấu trúc payload event
message.received - Header
x-unifyport-signaturevà phần giải thích xác minh HMAC-SHA256 - Lệnh tạo endpoint
POST /v1/webhook-endpoints
Hoặc dán trực tiếp các phần đó vào chat. Điểm mấu chốt: tên trường thật đi vào, code đúng đi ra. Nếu không có tài liệu, Cursor sẽ tự bịa ra callback telegram.onMessage() hoặc handler bot.on('text') — những thứ không tồn tại trong API UnifyPort.
Claude Code, Windsurf, Copilot đều hoạt động tương tự — file context, docs panel, hoặc dán vào chat. Công cụ là phụ; tài liệu mới là chính.
Quá trình build
Prompt đầu tiên — khung relay:
Dùng tài liệu UnifyPort webhook, viết một Express server với route POST /webhook. Khi event là “message.received”, gửi tin nhắn đã format vào Slack incoming webhook từ biến môi trường SLACK_WEBHOOK_URL. Bao gồm tên provider, người gửi và nội dung tin. Trả về 200 cho mọi event.
Cursor đọc tài liệu và tạo ra:
import express from "express";
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK_URL;
const app = express();
app.use(express.json());
app.post("/webhook", async (req, res) => {
const evt = req.body;
if (evt.event === "message.received") {
await fetch(SLACK_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `*[${evt.provider}]* ${evt.from}: ${evt.text}`,
}),
});
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
Mười lăm dòng handler. Vì cấu trúc event giống nhau trên cả sáu nền tảng, không cần parse gì riêng cho Telegram — evt.provider có giá trị "telegram", nhưng cấu trúc giống hệt tin nhắn WhatsApp hay LINE.
Prompt thứ hai — xác minh chữ ký:
Mỗi delivery bao gồm header x-unifyport-signature. Xác minh bằng HMAC-SHA256 với signing_secret từ biến môi trường. HMAC phải tính trên raw request body — không phải JSON đã re-serialize. Trả về 401 nếu thất bại. Dùng timing-safe comparison.
Cursor thêm lớp xác minh:
import crypto from "crypto";
const SIGNING_SECRET = process.env.UNIFYPORT_SIGNING_SECRET;
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf; }
}));
function isValid(req) {
const sig = req.get("x-unifyport-signature") || "";
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(req.rawBody)
.digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
Callback verify bắt raw buffer trước khi Express parse JSON. Nếu Cursor dùng req.body rồi re-serialize, sự khác biệt khoảng trắng sẽ khiến HMAC không bao giờ khớp. Prompt ghi “raw request body” nên nó xử lý đúng ngay lần đầu.
Prompt thứ ba — format Slack phong phú hơn:
Đổi Slack message sang Block Kit. Hiện provider dưới dạng tag, sender ID in đậm. Thêm context block với timestamp và message_id.
File server.js hoàn chỉnh:
import express from "express";
import crypto from "crypto";
const SIGNING_SECRET = process.env.UNIFYPORT_SIGNING_SECRET;
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK_URL;
const app = express();
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf; }
}));
function isValid(req) {
const sig = req.get("x-unifyport-signature") || "";
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(req.rawBody)
.digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
app.post("/webhook", async (req, res) => {
if (!isValid(req)) return res.sendStatus(401);
const evt = req.body;
if (evt.event === "message.received" && SLACK_WEBHOOK) {
await fetch(SLACK_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `*[${evt.provider}]* from *${evt.from}*\n${evt.text}`,
},
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text: `${evt.message_id} · ${new Date(evt.timestamp * 1000).toISOString()}`,
},
],
},
],
}),
});
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
Chưa đến 50 dòng. Xác minh chữ ký, format Slack Block Kit, chuyển tiếp mọi tin nhắn đầu vào. Handler không bao giờ kiểm tra giá trị provider vì không cần thiết.
Chạy và xem tin nhắn Telegram đến
Kết nối tài khoản Telegram trong workspace UnifyPort — Telegram hỗ trợ xác thực QR, quét từ app Telegram là xong trong vài giây. Không cần @BotFather, không cần bot token, không cần quản lý API credential. Đây là kết nối tài khoản Telegram thật của bạn, không phải bot riêng.
Đăng ký webhook endpoint trỏ đến server với subscribed_events: ["message.received"] và signing_secret. Khởi động server, rồi gửi một tin nhắn test đến tài khoản Telegram của bạn từ user khác. Event đến:
{
"event": "message.received",
"account_id": "acct_5Qm8nR",
"provider": "telegram",
"from": "user_7c3d9e",
"text": "Ngày mai 3 giờ mình còn gặp không?",
"timestamp": 1750521600,
"message_id": "tg_msg_2a6f4b"
}
Server xác minh chữ ký, format Block Kit payload và gửi vào Slack. Tin nhắn hiện trong channel với tag nền tảng, người gửi, timestamp và nội dung đầy đủ. Nếu xác minh chữ ký trả về 401 — kiểm tra UNIFYPORT_SIGNING_SECRET có khớp với giá trị đặt trên webhook endpoint không.
Lợi ích kiến trúc: thêm WhatsApp không cần sửa code
Đây là điểm relay này khác biệt so với cách build Bot API. Kết nối tài khoản WhatsApp trong cùng workspace, subscribe cùng webhook endpoint. Tin nhắn WhatsApp đến với cấu trúc y hệt:
{
"event": "message.received",
"account_id": "acct_3Xk1wL",
"provider": "whatsapp",
"from": "user_9b4e7a",
"text": "Hóa đơn đã gửi, xác nhận đã nhận giúp mình nhé",
"timestamp": 1750521660,
"message_id": "wa_msg_8d2c5f"
}
Cùng handler. Cùng Slack channel. Tag [whatsapp] phân biệt với [telegram] trong Slack, nhưng code path hoàn toàn giống nhau. Với Bot API relay, bạn cần thêm một tích hợp riêng — webhook payload khác, logic parse khác, xử lý lỗi khác. Relay này xử lý LINE, TikTok, Zalo và X theo cùng một cách, vì cấu trúc event không đổi giữa các nền tảng.
Đó là sự khác biệt giữa build trên API của một nền tảng và build trên lớp chuẩn hóa. Con đường Bot API gắn chết relay vào Telegram. Tài liệu Webhook UnifyPort cho Cursor — hoặc bất kỳ AI coding tool nào — một bề mặt đủ đơn giản để hoàn thành ngay lần đầu, và kết quả hoạt động cho mọi kênh bạn kết nối sau đó. Đưa tài liệu API UnifyPort cho công cụ của bạn, dán schema message.received vào, và relay sẽ chạy trước bữa trưa.