Discord 봇 AI 영상 생성: 응답을 미룬 뒤 수정하기

Discord 인터랙션에 3초 안에 지연 응답을 보내고 callback_url과 함께 POST /v1/videos를 제출한 뒤, Sume Job 웹훅이 오면 응답을 수정하세요.

읽는 시간 5분Sume
전체 글

AI 영상을 만드는 Discord 봇을 만들려면 슬래시 커맨드의 인터랙션에 3초 안에 type 5, 즉 DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE로 응답한 뒤 callback_url과 함께 POST /v1/videos를 제출하세요. Sume의 서명된 Job 웹훅이 도착하면, 15분짜리 인터랙션 토큰이 아직 유효할 때 지연 응답을 영상 URL로 수정하세요.

Sume에는 Discord 봇이 없습니다. 여러분 앱의 HTTP 인터랙션 엔드포인트가 HTTPS로 Sume를 직접 호출하는 방식입니다. Sume 관련 내용은 영상 생성 (영문)과 웹훅 (영문) 문서에서, Discord 관련 내용은 docs.discord.com에서 가져왔으며 모두 2026-09-27에 확인했습니다. Sume의 전달·서명 규칙은 Sume 영상 실행용 서명된 웹훅에서 다룹니다.

인터랙션 엔드포인트는 3초 안에 무엇을 해야 하나요?

Interactions Endpoint URL을 설정하면 Discord는 인터랙션마다 여러분의 서버로 POST를 보내며, 3초 안에 첫 응답을 보내지 않으면 인터랙션 토큰이 무효화됩니다. 영상 Job은 훨씬 오래 걸립니다. Sume 문서에 따르면 영상 생성은 보통 30초에서 몇 분이 걸립니다.

  • 모든 요청에서 앱의 공개 키로 X-Signature-Ed25519와 X-Signature-Timestamp를 검증하고, 검증에 실패하면 401로 응답하세요. Discord는 정기 점검으로 일부러 잘못된 서명을 보내며, 이 점검에 실패한 앱의 URL을 제거합니다.
  • PING(type: 1)에는 PONG(type: 1)으로 응답하세요.
  • 커맨드에는 { "type": 5 }로 응답하세요. 이 응답은 인터랙션을 받았다고 확인해 주며, 응답을 수정할 때까지 사용자에게는 로딩 상태가 보입니다.

봇은 영상 Job을 어떻게 시작하나요?

지연 응답을 보낸 뒤에는 커맨드 옵션의 value를 프롬프트로 보내세요. Sume 웹훅은 인터랙션이 아니라 Job을 가리키므로, 인터랙션의 application_id, token, channel_id를 반환된 Job id와 함께 저장하세요.

  • 인터랙션 id로 만든 Idempotency-Key가 있으면, 제출을 재시도해도 두 번째 Job을 시작하지 않고 원래 Job이 돌아옵니다.
  • callback_url은 공개 HTTPS URL이어야 합니다. localhost, 사설 네트워크, HTTPS가 아닌 URL은 거부됩니다.
  • 커맨드 하나하나가 워크스페이스 잔액으로 청구되는 Job을 시작합니다. Sume 문서는 요청을 전달하기 전에 사용자 입력을 검증하고 자체 인가를 적용하라고 안내하므로, 어떤 서버와 역할이 커맨드를 실행할 수 있는지 정하세요.
app.post("/interactions", express.raw({ type: "application/json" }), async (req, res) => {
  if (!verifyDiscord(req)) return res.status(401).end("invalid request signature"); // Ed25519
  const i = JSON.parse(req.body);
  if (i.type === 1) return res.json({ type: 1 }); // PING -> PONG
  res.json({ type: 5 }); // DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE, inside 3 seconds
  const r = await fetch("https://api.sume.com/v1/videos", {
    method: "POST",
    headers: {
      Authorization: "Bearer " + process.env.SUME_API_KEY,
      "Content-Type": "application/json",
      "Idempotency-Key": "discord-" + i.id,
    },
    body: JSON.stringify({
      model: "sume/auto",
      prompt: i.data.options[0].value,
      callback_url: "https://bot.example.com/hooks/sume",
    }),
  });
  const job = await r.json();
  if (!r.ok) return editReply(i.application_id, i.token, "Sume refused the job: " + job.error.code);
  await saveJob(job.id, { appId: i.application_id, token: i.token, channelId: i.channel_id, at: Date.now() });
});

영상이 준비되면 응답을 어떻게 수정하나요?

Sume는 Job이 종료 상태에 도달하면 Job 웹훅을 한 번 POST하고, 시도마다 10초 타임아웃으로 최대 10회 시도합니다. 원본 본문을 검증하고, 이벤트를 저장하고, 바로 2xx로 응답하고, job_id를 멱등성 키로 쓰세요.

  • PATCH /webhooks/{application_id}/{interaction_token}/messages/@original은 지연 응답을 그 자리에서 수정합니다. 인터랙션 토큰은 15분 동안 유효합니다.
  • 토큰이 만료된 뒤에 끝나는 Job은 대신 Discord의 Create Message 엔드포인트를 써야 합니다. 서버 채널에서는 이 호출에 SEND_MESSAGES 권한이 필요합니다.
  • job.completed에서는 type이 video인 payload.artifacts[] 항목에 URL이 담깁니다. 이 URL은 media.sume.com 아래의 공개 아티팩트이므로, 채널을 읽을 수 있는 사람은 누구나 열 수 있습니다.
  • verifyWebhook은 시크릿 교체 중에 sume-v1= 항목 중 어느 것이든 받아들입니다. 직접 구현한 검증은 헤더를 쉼표로 나눠야 합니다.
import { verifyWebhook } from "@sume-com/sdk";

app.post("/hooks/sume", express.raw({ type: "application/json" }), async (req, res) => {
  const ok = await verifyWebhook({
    body: req.body,
    headers: req.headers,
    secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET,
  });
  if (!ok) return res.status(401).end();
  const event = JSON.parse(req.body);
  const ctx = await claimJob(event.job_id); // your store; null if this job_id was handled
  res.status(204).end(); // Sume allows 10 s per attempt
  if (!ctx) return;
  const video = event.status === "OK" && event.payload.artifacts.find((a) => a.type === "video");
  const content = video ? video.url : "The video job failed.";
  // Tokens last 15 minutes; leave a margin, then fall back to Create Message.
  if (Date.now() - ctx.at > 14 * 60 * 1000) return createChannelMessage(ctx.channelId, content);
  await fetch("https://discord.com/api/v10/webhooks/" + ctx.appId + "/" + ctx.token + "/messages/@original", {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ content }),
  });
});

Discord 검증과 Sume 검증은 어떻게 다른가요?

봇은 들어오는 요청 두 종류를 서로 다른 방식 두 가지로 검증합니다. 두 요청은 별도 라우트에서 받으세요.

Discord의 Interactions Overview, Receiving and Responding 페이지와 Sume 웹훅 (영문) 페이지 기준, 2026-09-27 확인.
속성Discord 인터랙션Sume Job 웹훅
방식Ed25519 서명HMAC-SHA256
헤더X-Signature-Ed25519, X-Signature-Timestampx-sume-webhook-signature, x-sume-webhook-timestamp
서명 대상 바이트타임스탬프 뒤에 본문<timestamp>.<raw_body>
키앱의 공개 키워크스페이스 웹훅 서명 시크릿
응답 기한3초시도당 10초

어떤 한도가 있나요?

이 중 두 가지는 Discord가 아니라 Job 쪽에서 생기는 한도입니다.

  • Sume는 종료 Job 이벤트만 보내며 진행 상황은 전달하지 않습니다. GET /v1/jobs/{id}/status를 폴링하지 않는 한, 지연 응답과 수정 사이에 사용자가 보는 진행 표시는 Discord의 로딩 상태뿐입니다.
  • 끝내 도착하지 않은 전달은 Job을 바꾸지 않습니다. 완료된 Job을 GET /v1/jobs/{id}/result에서 읽은 뒤, 응답을 수정하거나 메시지를 게시하세요.
  • 인터랙션 토큰은 15분 동안 유지되는 반면, 영상 생성은 모델, 해상도, 서버 부하에 따라 몇 분이 걸릴 수 있습니다. Create Message 폴백은 남겨 두세요.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume