API 레퍼런스

WEVEN 봇 API 문서

봇으로 메시지를 보내고, 슬래시 명령어에 응답하고, 서버를 관리하는 방법을 정리했어요. 디스코드 봇을 만들어 봤다면 거의 같은 흐름이에요.

REST
http://localhost:3000/api
Gateway
ws://localhost:3000/ws

빠른 시작#

  1. 내 애플리케이션에서 새 애플리케이션을 만들어요. 봇 계정이 함께 만들어지고 토큰이 한 번 표시돼요.
  2. 토큰을 안전한 곳(예: .env)에 저장해요. 토큰이 있으면 누구나 봇으로 행동할 수 있으니 절대 공개하지 마세요.
  3. 애플리케이션의 초대 링크 탭에서 필요한 권한을 고르고 링크를 열어 봇을 서버에 추가해요. 서버 관리 권한(MANAGE_SERVER)이 있는 서버에만 추가할 수 있어요.
  4. 예제 봇을 실행해요. 저장소에 있는 완성 예제는 BOT_TOKEN=토큰 node examples/bot.mjs로 실행할 수 있어요.
  5. 서버 채팅창에 /를 입력하면 봇의 명령어가 보여요. /ping을 보내 보세요.

인증과 요청#

모든 REST 요청은 http://localhost:3000/api 아래로 보내고, 봇 토큰을 Authorization 헤더에 넣어요. 본문은 JSON이에요.

bash
curl http://localhost:3000/api/bot/me \
  -H "Authorization: Bot <토큰>"

실패하면 HTTP 상태 코드와 함께 { "error": "코드" }를 돌려줘요. 코드 목록은 오류 코드에 있어요.

봇은 사람 계정 전용 기능(친구, DM, 결제, 계정 설정, 음성 참가)에는 접근할 수 없어요. 이런 경로를 부르면 403 bot_forbidden이 돌아와요. 토큰이 틀리거나 재발급돼 무효가 되면 401 invalid_token이에요.

방(room) 형식

메시지 API는 채널을 channel:<채널 ID> 형식의 room으로 가리켜요. 받은 메시지의 message.room을 그대로 쓰면 같은 채널에 답장할 수 있어요.

속도 제한

채널의 슬로우 모드는 봇에게도 적용돼요(MANAGE_MESSAGES나 MANAGE_CHANNELS 권한이 있으면 제외). 제한에 걸리면 429 slowmode와 retryAfter(초)가 돌아와요. 게이트웨이로 보내는 프레임도 너무 많으면 rate_limited 오류가 와요.

게이트웨이 (실시간)#

새 메시지나 명령어 사용 같은 이벤트는 웹소켓 게이트웨이로 받아요. 연결할 때 REST와 같은 Authorization 헤더를 보내요.

js
const socket = new WebSocket("ws://localhost:3000/ws", {
  headers: { Authorization: "Bot <토큰>" },
});

서버는 JSON 텍스트 프레임을 보내요. 모든 이벤트에는 type이 있어요. 봇은 참여 중인 서버의 채널 이벤트와 자기 자신에게 오는 이벤트를 받아요.

type설명
hello연결 직후. 접속 상태(presence)와 음성 채널 참가자 스냅샷
ready봇 전용. { user, applicationId, serverIds } 이 이벤트를 받으면 명령어를 등록하기 좋아요.
message_create{ message } 봇이 볼 수 있는 채널에 새 메시지
message_update{ message } 수정·반응·고정·투표 변경
message_delete{ room, messageId }
typing{ room, userId }
presence{ userId, status } ONLINE · IDLE · DND · OFFLINE
user_update{ user } 프로필 변경
server_sync{ serverId } 서버·채널·역할이 바뀌었어요. GET /api/servers/{id}로 다시 읽으세요.
members_sync{ serverId } 멤버 목록이 바뀌었어요.
server_removed{ serverId } 서버에서 추방·차단됐거나 서버가 삭제됐어요.
commands_sync{ serverId } 서버의 명령어 목록이 바뀌었어요.
interaction_create{ interaction } 누군가 내 슬래시 명령어를 사용했어요.
voice_state{ channelId, participants } 음성 채널 참가자 변경
error{ message } 보낸 프레임이 거부됐어요 (예: rate_limited, forbidden).

봇이 보낼 수 있는 프레임

프레임설명
{ "type": "typing", "room": "channel:…" }입력 중… 표시 (몇 초 동안)
{ "type": "voice_moderate", "userId", "mute"?, "deafen"?, "disconnect"? }음성 채널 멤버 서버 음소거 / 헤드셋 끄기 / 연결 끊기 (MUTE_MEMBERS · DEAFEN_MEMBERS · MOVE_MEMBERS)

연결 종료 코드

코드의미
1008토큰이 없거나 틀려요. 다시 연결하지 말고 토큰을 확인하세요.
4004토큰이 재발급됐거나 애플리케이션이 삭제됐어요.
그 밖서버 재시작 등 일시적인 끊김이에요. 잠시 뒤 다시 연결하세요.

주요 객체#

User

ts
{
  id: string;
  username: string;          // 고유한 사용자 이름
  displayName: string | null;
  accentColor: string;       // "#rrggbb"
  avatarId: string | null;   // 이미지: GET /api/uploads/{avatarId}
  bio: string;
  plan: "FREE" | "PLUS";
  bot: boolean;              // 봇·웹후크면 true
}

Message

ts
{
  id: string;
  room: "channel:<id>" | "dm:<id>";
  content: string;
  type: "DEFAULT" | "MEMBER_JOIN" | "PIN" | …;
  authorId: string;
  author: User;
  createdAt: string;            // ISO 8601
  editedAt: string | null;
  pinned: boolean;
  attachments: { id, name, size, type }[];   // 파일: GET /api/uploads/{id}
  reactions: { emoji, count, userIds }[];
  replyTo: { id, authorId, content } | null;
  poll: Poll | null;
  embeds: Embed[];
  interaction: { id, name, userId } | null;  // 슬래시 명령어 응답이면 설정됨
}

REST API#

봇이 쓸 수 있는 경로예요. 권한 열은 봇(정확히는 봇의 역할)이 그 서버나 채널에서 가져야 하는 권한이에요. 관리자(ADMINISTRATOR)는 모든 권한을 가진 것으로 취급돼요. 다른 멤버를 관리할 때는 대상보다 높은 역할이 필요해요(role_hierarchy).

봇 자신

메서드경로권한설명
GET/api/bot/me—봇 사용자, 애플리케이션, 참여 중인 서버(채널·역할 포함) 목록
GET/api/bot/commands—등록한 슬래시 명령어 목록
PUT/api/bot/commands—명령어 전체 덮어쓰기. 본문은 명령어 배열이에요. 슬래시 명령어 참고
POST/api/interactions/{id}/callback—명령어 사용에 응답 { content?, embeds?, ephemeral? }
GET/api/users/{id}—사용자 공개 프로필

메시지

메서드경로권한설명
GET/api/messages?room={room}&before={messageId}READ_MESSAGE_HISTORY최근 메시지 50개(오래된 순)와 hasMore. before로 이전 페이지를 불러와요.
POST/api/messagesSEND_MESSAGES{ room, content?, embeds?, replyToId?, attachmentIds?, poll?, nonce? } 임베드는 EMBED_LINKS, 첨부는 ATTACH_FILES, 투표는 SEND_POLLS가 필요해요.
PATCH/api/messages/{id}작성자{ content?, embeds? } 내가 보낸 메시지만 수정할 수 있어요.
DELETE/api/messages/{id}작성자또는 MANAGE_MESSAGES메시지 삭제
POST/api/messages/{id}/reactionsADD_REACTIONS{ emoji } 반응을 추가하거나, 이미 있으면 제거해요.
GET/api/messages/pins?room={room}READ_MESSAGE_HISTORY고정된 메시지
PUT/api/messages/{id}/pinPIN_MESSAGES또는 MANAGE_MESSAGES{ pinned: boolean }
GET/api/messages/search?room={room}&q={text}READ_MESSAGE_HISTORY채널 안에서 검색
POST/api/uploadsATTACH_FILESmultipart/form-data의 file 필드. 응답의 attachment.id를 attachmentIds에 넣어 보내요.

서버와 멤버

메서드경로권한설명
GET/api/servers/{id}멤버서버 정보, 채널, 카테고리, 역할, 내 권한
PATCH/api/servers/{id}MANAGE_SERVER{ name?, description?, systemChannelId?, defaultNotifications?, automodEnabled?, automodWords? }
GET/api/servers/{id}/members멤버멤버 목록
PATCH/api/servers/{id}/members/{userId}MANAGE_ROLES또는 MANAGE_NICKNAMES또는 MODERATE_MEMBERS바꾸는 항목마다 해당 권한이 필요해요. { roleIds?, nickname?, timeoutMinutes?, timeoutReason? } timeoutMinutes는 1·5·10·60·1440·10080 또는 null(해제)
DELETE/api/servers/{id}/members/{userId}KICK_MEMBERS추방
GET/api/servers/{id}/bansBAN_MEMBERS차단 목록
PUT/api/servers/{id}/bans/{userId}BAN_MEMBERS{ reason?, deleteMessageHours?: 0|1|24|168 }
DELETE/api/servers/{id}/bans/{userId}BAN_MEMBERS차단 해제
GET/api/servers/{id}/audit-logVIEW_AUDIT_LOG감사 로그
POST/api/servers/{id}/leave—서버에서 나가기 (봇의 관리 역할도 함께 지워져요)

역할

메서드경로권한설명
POST/api/servers/{id}/rolesMANAGE_ROLES{ name? }
PATCH/api/roles/{id}MANAGE_ROLES{ name?, color?, hoist?, mentionable?, permissions?, move?: "up"|"down" } 내가 가진 권한 비트만 켜고 끌 수 있어요.
DELETE/api/roles/{id}MANAGE_ROLES봇이 관리하는 역할은 지울 수 없어요.

채널과 카테고리

메서드경로권한설명
POST/api/servers/{id}/channelsMANAGE_CHANNELS{ name, type?: "TEXT"|"VOICE", categoryId?, topic?, isPrivate? }
PUT/api/servers/{id}/channelsMANAGE_CHANNELS{ channels: [{ id, categoryId, position }], categories? } 순서 바꾸기
PATCH/api/channels/{id}MANAGE_CHANNELS{ name?, topic?, categoryId?, slowmode? }
DELETE/api/channels/{id}MANAGE_CHANNELS채널 삭제
PUT/api/channels/{id}/overwritesMANAGE_ROLES{ targetType: "ROLE"|"MEMBER", targetId, allow, deny } 채널별 권한 덮어쓰기
POST/api/servers/{id}/categoriesMANAGE_CHANNELS{ name }
PATCHDELETE/api/categories/{id}MANAGE_CHANNELS카테고리 이름 바꾸기 / 삭제

웹후크

메서드경로권한설명
GET/api/servers/{id}/webhooksMANAGE_WEBHOOKS웹후크 목록 (실행 주소 포함)
POST/api/servers/{id}/webhooksMANAGE_WEBHOOKS{ channelId, name }
PATCH/api/webhooks/{id}MANAGE_WEBHOOKS{ name?, channelId?, accentColor?, resetToken? }
DELETE/api/webhooks/{id}MANAGE_WEBHOOKS웹후크 삭제 (보낸 메시지는 남아요)
POST/api/webhooks/{id}/{token}주소 = 비밀번호{ content?, embeds? } 인증 헤더 없이 메시지를 보내요.

임베드#

임베드는 색깔 띠가 있는 카드 형태의 메시지예요. 메시지 하나에 최대 10개까지 넣을 수 있어요. POST /api/messages, 메시지 수정, 명령어 응답, 웹후크에서 모두 같은 형식을 써요. 임베드 빌더로 미리 보면서 만들 수 있어요.

json
{
  "title": "제목",
  "description": "**마크다운**을 쓸 수 있어요",
  "url": "https://example.com",
  "color": "#5865F2",
  "timestamp": "2026-10-14T12:00:00.000Z",
  "author": { "name": "작성자", "url": "https://…", "iconUrl": "https://…" },
  "footer": { "text": "푸터", "iconUrl": "https://…" },
  "image": { "url": "https://…/big.png" },
  "thumbnail": { "url": "https://…/small.png" },
  "fields": [
    { "name": "시작", "value": "22:00", "inline": true },
    { "name": "길이", "value": "30분", "inline": true }
  ]
}
  • color는 "#rrggbb" 문자열이나 디스코드처럼 숫자(0x5865F2 = 5793266)로 보낼 수 있어요.
  • URL은 모두 http(s)로 시작해야 해요. 이미지는 받는 사람의 브라우저가 직접 불러와요.
  • inline: true 필드는 한 줄에 최대 3개까지 나란히 놓여요.
  • 제목·설명·필드·작성자·푸터·이미지 중 하나는 있어야 해요.
  • 임베드를 보내려면 채널에서 EMBED_LINKS 권한이 필요해요.
항목최대
title256자
description4096자
fields25개
field.name / field.value256자 / 1024자
footer.text2048자
author.name256자
메시지의 모든 임베드 글자 합6000자 (넘으면 embed_too_large)

슬래시 명령어#

슬래시 명령어는 PUT /api/bot/commands로 등록해요. 보낸 목록이 기존 목록을 통째로 대체하므로 봇이 켜질 때마다 전체 목록을 보내면 돼요. 봇이 들어가 있는 모든 서버에 바로 반영돼요.

json
[
  {
    "name": "날씨",
    "description": "도시의 날씨를 알려 줘요",
    "options": [
      { "name": "도시", "description": "도시 이름", "type": "string", "required": true },
      {
        "name": "단위", "description": "온도 단위", "type": "string",
        "choices": [{ "name": "섭씨", "value": "c" }, { "name": "화씨", "value": "f" }]
      }
    ]
  }
]
  • 이름은 1–32자이고 소문자, 숫자, 한글, -, _만 쓸 수 있어요. 설명은 1–100자예요.
  • 봇당 명령어 100개, 명령어당 옵션 25개, 옵션당 선택지 25개까지예요.
  • 필수 옵션은 선택 옵션보다 앞에 와야 해요.

옵션 타입

type봇이 받는 값사용자가 입력하는 방법
string문자열 (choices가 있으면 선택한 value)아무 텍스트
integer정수42
number숫자3.14
booleantrue / falsetrue, yes, 예, 네 / false, no, 아니요
user사용자 ID (resolved.users에 프로필)@사용자이름, 멘션, ID
channel채널 ID (resolved.channels)#채널이름, 멘션, ID
role역할 ID (resolved.roles)역할 이름, 멘션, ID

채팅창에서는 /날씨 서울처럼 쓰면 첫 번째 옵션에 전부 들어가고, /날씨 도시:서울 단위:섭씨처럼 이름:값으로 여러 옵션을 줄 수 있어요. 값이 맞지 않으면 사용자에게 바로 오류가 보이고 봇에게는 전달되지 않아요.

interaction_create

누군가 명령어를 쓰면 게이트웨이로 다음 이벤트가 와요.

json
{
  "type": "interaction_create",
  "interaction": {
    "id": "cm…",
    "applicationId": "cm…",
    "command": { "id": "cm…", "name": "날씨" },
    "options": { "도시": "서울", "단위": "c" },
    "resolved": { "users": {}, "channels": {}, "roles": {} },
    "room": "channel:cm…",
    "channelId": "cm…",
    "serverId": "cm…",
    "user": { "id": "cm…", "username": "minji", … },
    "member": { "nickname": null, "roleIds": ["cm…"] },
    "createdAt": "2026-10-14T12:00:00.000Z"
  }
}

응답하기

js
await fetch(`http://localhost:3000/api/interactions/${interaction.id}/callback`, {
  method: "POST",
  headers: { Authorization: "Bot <토큰>", "Content-Type": "application/json" },
  body: JSON.stringify({
    content: "서울은 맑아요 ☀️",
    embeds: [{ title: "서울 날씨", description: "18°C", color: "#FEE75C" }],
    ephemeral: false,
  }),
});
  • 응답은 한 번만, 명령어 사용 후 15분 안에 보낼 수 있어요. 두 번째 응답은 409 interaction_responded, 시간이 지나면 interaction_expired예요.
  • 응답 메시지 위에는 “누가 /명령어를 사용함” 표시가 붙어요.
  • ephemeral: true면 명령어를 쓴 사람에게만 보이고 저장되지 않아요(새로고침하면 사라져요).
  • 사용자는 10초 안에 응답이 없으면 “응답이 없어요” 안내를 봐요. 오래 걸리는 작업이라면 먼저 짧게 응답하고, 이어서 일반 메시지를 보내세요.
  • 명령어를 쓰려면 사용자에게 USE_APPLICATION_COMMANDS 권한이 필요해요.

웹후크#

웹후크는 봇을 띄우지 않고도 정해진 채널에 메시지를 보내는 주소예요. 서버 설정 → 연동에서 만들 수 있어요(MANAGE_WEBHOOKS 필요). 주소 자체가 비밀번호이므로 공개되면 주소 재설정을 누르세요.

bash
curl -X POST "http://localhost:3000/api/webhooks/<id>/<token>" \
  -H "Content-Type: application/json" \
  -d '{"content":"배포 완료!","embeds":[{"title":"v1.2.0","color":"#57F287"}]}'

웹후크 메시지는 웹후크의 이름과 아바타로 표시되고 APP 배지가 붙어요. 본문에는 content(최대 2000자)와 embeds를 넣을 수 있어요.

권한#

권한은 비트 필드(정수)예요. 여러 권한은 비트 OR로 합쳐요. 예를 들어 메시지 보내기 + 임베드는 8389632(= 1024 | 8388608)이에요. 봇을 초대할 때 고른 권한은 봇 전용 관리 역할로 만들어지고, 이 역할은 다른 멤버에게 줄 수 없고 봇이 나가면 함께 지워져요.

새 서버의 @everyone 기본값은 1137177792이에요. 채널별 권한 덮어쓰기는 @everyone → 역할 → 멤버 순서로 적용되고, 서버 주인과 관리자는 덮어쓰기를 무시해요.

일반 서버 권한

이름값설명
ADMINISTRATOR
관리자
1 (1 << 0)모든 권한을 가져요. 위험한 권한이니 신중하게 부여하세요.
VIEW_CHANNEL
채널 보기
262144 (1 << 18)기본적으로 채널을 볼 수 있어요. 비공개 채널은 따로 허용해야 해요.
MANAGE_SERVER
서버 관리하기
2 (1 << 1)서버 이름, 아이콘, 초대 코드 등을 바꿀 수 있어요.
MANAGE_ROLES
역할 관리하기
4 (1 << 2)자신보다 낮은 역할을 만들고 편집하고 부여할 수 있어요.
MANAGE_CHANNELS
채널 관리하기
8 (1 << 3)채널과 카테고리를 만들고 편집하고 삭제할 수 있어요.
MANAGE_EXPRESSIONS
표현 관리
536870912 (1 << 29)이 서버의 이모지, 스티커, 사운드를 올리고 지울 수 있어요.
MANAGE_WEBHOOKS
웹후크 관리
67108864 (1 << 26)웹후크를 만들고 고치고 지울 수 있어요. 웹후크는 외부 서비스가 채널에 메시지를 올리는 주소예요.
VIEW_AUDIT_LOG
감사 로그 보기
512 (1 << 9)서버의 관리 작업 기록을 볼 수 있어요.

멤버십 권한

이름값설명
CREATE_INVITE
초대 코드 만들기
64 (1 << 6)다른 사람을 서버에 초대할 수 있어요.
CHANGE_NICKNAME
별명 변경하기
128 (1 << 7)이 서버에서 자신의 별명을 바꿀 수 있어요.
MANAGE_NICKNAMES
별명 관리하기
256 (1 << 8)다른 멤버의 별명을 바꿀 수 있어요.
KICK_MEMBERS
멤버 추방하기
16 (1 << 4)자신보다 낮은 멤버를 서버에서 내보낼 수 있어요.
BAN_MEMBERS
멤버 차단하기
32 (1 << 5)멤버를 영구적으로 차단하고 차단 목록을 관리할 수 있어요.
MODERATE_MEMBERS
멤버 타임아웃
524288 (1 << 19)자신보다 낮은 멤버를 일정 시간 동안 채팅, 반응, 음성에서 제한할 수 있어요.

채팅 채널 권한

이름값설명
SEND_MESSAGES
메시지 보내기
1024 (1 << 10)채팅 채널에 메시지를 보낼 수 있어요.
EMBED_LINKS
임베드 보내기
8388608 (1 << 23)봇, 웹후크, API로 제목·색상·필드가 있는 임베드를 보낼 수 있어요.
ATTACH_FILES
파일 첨부하기
4096 (1 << 12)파일과 이미지를 업로드할 수 있어요.
ADD_REACTIONS
반응 추가하기
8192 (1 << 13)메시지에 새 이모지 반응을 추가할 수 있어요.
MENTION_EVERYONE
@everyone, @here, 모든 역할 멘션하기
16384 (1 << 14)모두에게 알림을 보낼 수 있어요.
MANAGE_MESSAGES
메시지 관리하기
2048 (1 << 11)다른 사람의 메시지를 삭제하고 고정할 수 있어요. 슬로우 모드도 적용되지 않아요.
PIN_MESSAGES
메시지 고정
134217728 (1 << 27)메시지를 고정하거나 고정을 해제할 수 있어요.
READ_MESSAGE_HISTORY
메시지 기록 보기
4194304 (1 << 22)채널에 예전에 올라온 메시지를 읽을 수 있어요. 없으면 들어와 있는 동안 온 메시지만 보여요.
SEND_POLLS
투표 만들기
16777216 (1 << 24)채널에 투표를 올릴 수 있어요.

앱 권한

이름값설명
USE_APPLICATION_COMMANDS
앱 명령어 사용
33554432 (1 << 25)서버에 있는 봇의 슬래시 명령어(/)를 쓸 수 있어요.

음성 채널 권한

이름값설명
CONNECT
연결
32768 (1 << 15)음성 채널에 참가할 수 있어요.
SPEAK
말하기
65536 (1 << 16)음성 채널에서 말할 수 있어요.
VIDEO
영상
131072 (1 << 17)카메라와 화면 공유를 사용할 수 있어요.
USE_SOUNDBOARD
사운드보드 사용
1073741824 (1 << 30)음성 채널에서 사운드보드를 재생할 수 있어요.
MUTE_MEMBERS
멤버 마이크 음소거
1048576 (1 << 20)음성 채널에서 다른 멤버의 마이크를 서버 전체에서 음소거할 수 있어요.
DEAFEN_MEMBERS
멤버 헤드셋 음소거
268435456 (1 << 28)음성 채널에서 다른 멤버가 아무것도 듣지 못하게 서버 전체에서 헤드셋을 음소거할 수 있어요.
MOVE_MEMBERS
멤버 연결 끊기
2097152 (1 << 21)음성 채널에 있는 다른 멤버의 연결을 끊을 수 있어요.
ADMINISTRATOR는 모든 권한과 채널 덮어쓰기 무시를 포함해요. 봇에게는 꼭 필요한 권한만 주세요.

오류 코드#

오류 응답은 { "error": "코드" } 형식이에요. 봇이 자주 만나는 코드예요.

코드의미
unauthenticated다시 로그인해 주세요.
invalid_token봇 토큰이 올바르지 않아요.
bot_forbidden봇은 이 기능을 쓸 수 없어요.
bot_only봇 토큰으로만 쓸 수 있는 기능이에요.
forbidden권한이 없어요.
invalid_input입력값을 확인해 주세요.
rate_limited너무 빨라요! 잠시 후 다시 시도해 주세요.
slowmode슬로우 모드가 켜져 있어요. 잠시 후에 다시 보내 주세요.
not_found찾을 수 없어요.
server_not_found서버를 찾을 수 없어요.
channel_not_found채널을 찾을 수 없어요.
message_not_found메시지를 찾을 수 없어요.
member_not_found멤버를 찾을 수 없어요.
role_not_found역할을 찾을 수 없어요.
invalid_room잘못된 대화방이에요.
message_empty빈 메시지는 보낼 수 없어요.
message_too_long메시지가 너무 길어요.
too_many_attachments첨부 파일은 최대 10개까지 보낼 수 있어요.
embed_invalid임베드 형식이 올바르지 않아요.
embed_too_large임베드 글자 수가 너무 많아요 (전체 6000자까지).
poll_invalid투표 형식이 올바르지 않아요.
automod_blockedAutoMod가 이 서버에서 금지된 단어가 들어간 메시지를 막았어요.
role_hierarchy내 역할보다 높거나 같은 대상은 관리할 수 없어요.
role_managed봇 전용 역할은 지우거나 다른 멤버에게 줄 수 없어요.
command_invalid명령어 정의가 올바르지 않아요.
command_not_found명령어를 찾을 수 없어요.
option_invalid명령어 옵션 값이 올바르지 않아요.
option_required필수 옵션을 입력해 주세요.
interaction_not_found명령어 요청을 찾을 수 없어요.
interaction_responded이미 응답한 명령어예요.
interaction_expired응답 시간이 지났어요 (15분).
cannot_dm_bot봇에게는 DM을 보낼 수 없어요.
cannot_friend_bot봇에게는 친구 요청을 보낼 수 없어요.
application_not_found앱을 찾을 수 없어요.
app_limit앱은 25개까지 만들 수 있어요.
app_name_length앱 이름은 2~32자여야 해요.
bot_already_added이미 이 서버에 있는 봇이에요.
webhook_not_found웹후크를 찾을 수 없어요.
webhook_limit채널 하나에 웹후크는 15개까지 만들 수 있어요.
webhook_name_length웹후크 이름은 1~80자여야 해요.

예제 봇#

Node.js 22+와 ws 패키지로 만든 최소 예제예요. 접속하면 명령어를 등록하고, /ping과 /안녕에 답하고, 채팅의 !ping에 답장해요.

bot.mjsjs
import WebSocket from "ws"; // npm install ws

const TOKEN = process.env.BOT_TOKEN;
const API = "http://localhost:3000/api";

async function api(method, path, body) {
  const res = await fetch(API + path, {
    method,
    headers: { Authorization: `Bot ${TOKEN}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error);
  return data;
}

const socket = new WebSocket("ws://localhost:3000/ws", { headers: { Authorization: `Bot ${TOKEN}` } });

socket.on("message", async (raw) => {
  const event = JSON.parse(raw);

  if (event.type === "ready") {
    await api("PUT", "/bot/commands", [
      { name: "ping", description: "봇이 살아 있는지 확인해요" },
      { name: "안녕", description: "인사해요", options: [{ name: "이름", description: "누구에게", type: "string", required: true }] },
    ]);
    console.log("준비 완료:", event.user.username);
  }

  if (event.type === "interaction_create") {
    const i = event.interaction;
    if (i.command.name === "ping") {
      await api("POST", `/interactions/${i.id}/callback`, { content: "퐁! 🏓" });
    }
    if (i.command.name === "안녕") {
      await api("POST", `/interactions/${i.id}/callback`, {
        embeds: [{ title: `안녕하세요, ${i.options["이름"]}님!`, color: "#5865F2" }],
      });
    }
  }

  if (event.type === "message_create" && !event.message.author.bot && event.message.content === "!ping") {
    await api("POST", "/messages", { room: event.message.room, content: "pong!", replyToId: event.message.id });
  }
});

주사위·정보·비밀(ephemeral)·투표 임베드 명령어와 재연결 처리가 포함된 완성 예제는 프로젝트의 examples/bot.mjs에 있어요.

bash
BOT_TOKEN=<토큰> WEVEN_URL=http://localhost:3000 WEVEN_GATEWAY=ws://localhost:3000/ws node examples/bot.mjs