셀프구축 튜토리얼 — 수신→발신 봇
수신 웹훅을 받아 자동으로 답장하는 최소 봇을 한 벌 만듭니다. 이 예시는 에코 봇(고객이 보낸 말을 그대로 되돌려줌)이지만, 답장 부분을 여러분의 AI 에이전트 호출로 바꾸면 그대로 상담 봇이 됩니다.
준비물
전체 흐름
코드 (Node.js)
const crypto = require('crypto');
const express = require('express');
const BASE = 'https://api.talkbridge-dev.com';
const APIKEY = process.env.API_KEY; // blumnb-...
const WHSEC = process.env.WHSEC; // whsec_...
const BRAND = process.env.BRAND; // mybrand
const app = express();
const seen = new Set(); // Delivery-Id 멱등
app.post('/webhook', express.raw({ type: '*/*' }), async (req, res) => {
// 1) 서명 검증
const ts = req.get('X-Bridge-Timestamp') || '';
const sig = req.get('X-Bridge-Signature') || '';
const mac = crypto.createHmac('sha256', WHSEC)
.update('v0:' + ts + ':').update(req.body).digest('hex');
const expected = 'v0=' + mac;
if (sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('bad signature');
}
// 빠르게 2xx 응답하고 후처리는 비동기로(게이트웨이 타임아웃 회피)
res.sendStatus(200);
// 2) 멱등 & 이벤트 분기
const deliveryId = req.get('X-Bridge-Delivery-Id');
if (deliveryId && seen.has(deliveryId)) return;
if (deliveryId) seen.add(deliveryId);
const evt = JSON.parse(req.body.toString('utf8')); // { brand, userKey, kind, seq }
if (evt.kind !== 'message') return; // agent echo 등은 무시
// 3) 본문 조회
const url = `${BASE}/api/agent/rooms/${encodeURIComponent(evt.userKey)}/messages`
+ `?brand=${encodeURIComponent(evt.brand)}&max=5`;
const r = await fetch(url, { headers: { Authorization: `Bearer ${APIKEY}` } });
const data = await r.json();
const last = (data.messages || []).find(m => m.kind === 'message');
if (!last) return;
// 4) 답장 발신 (여기를 AI 호출로 교체하면 상담 봇)
const reply = `받은 메시지: ${last.text}`;
await fetch(`${BASE}/api/agent/send`, {
method: 'POST',
headers: { Authorization: `Bearer ${APIKEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ brandKey: evt.brand, userKey: evt.userKey, text: reply }),
});
});
app.listen(3000, () => console.log('webhook listening :3000'));
실행 & 노출
API_KEY=blumnb-... WHSEC=whsec_... BRAND=mybrand node bot.js
# 공개 주소가 필요하면 터널로 노출 후, 그 URL을 톡브릿지 센터 웹훅에 등록
확인 포인트
- 서명 실패는
401— 게이트웨이가 재시도하지만 계속 거부됩니다(정상). - 정상 처리는 즉시
200— 실제 작업(조회·발신)은 응답 후 비동기로. kind: "agent"를 반드시 걸러 무한 루프를 방지합니다.- 같은
Delivery-Id재수신은 스킵합니다.