아이비봇

개발자 API 문서

아이비봇과 이 사이트를 잇는 API 예요. 직접 봇을 만드시거나, 아이비봇의 상태를 다른 곳에서 쓰고 싶으실 때 보세요.

주소
https://ibcytb.com/bot/api.php

동작은 주소 뒤 ?a= 로 고릅니다. 예) https://ibcytb.com/bot/api.php?a=ping
주고받는 것은 전부 JSON (UTF-8) 이고, 응답은 언제나 {"ok": true} 또는 {"ok": false} 로 시작해요.

1. 인증

헤더 두 개를 붙이세요.

헤더값필수
X-Ivy-Key ivy_ab12cd.xxxxxxxxxxxx예
X-Ivy-Ts 지금 시각 (초 단위 유닉스 시간)권장
Content-Type application/json; charset=UTF-8POST 일 때

키는 관리자 → 봇 연동 → 새 키 만들기 에서 받습니다. 만들 때 한 번만 보여 주고 저장은 해시로만 하니, 잃어버리면 새로 만드세요.

권한

권한쓸 수 있는 동작
botheartbeat · plans · room
bot,admin위의 전부 + setup · rooms
X-Ivy-Ts 가 왜 필요한가요
지나간 요청을 그대로 다시 보내는 것을 막습니다. 서버 시각과 5분 넘게 차이나면 clock_skew 로 거절해요. 폰 시계를 자동으로 맞춰 두세요. 헤더를 아예 안 보내면 이 검사를 건너뜁니다.

2. 호출 제한

3. 동작

동작방식권한하는 일
ping GET 없어도 됨 서버가 살아 있는지 봅니다. 키 없이 부를 수 있는 유일한 동작이에요.
heartbeat POST bot 봇이 살아 있다고 알립니다. 1분마다 보내세요. 가동률이 이 신호로 계산돼요.
plans GET bot 플랜, 기능 허용표, 제한값, 방별 플랜을 한 번에 받습니다. heartbeat 의 rev 가 바뀌었을 때만 다시 부르세요.
room GET bot 방 하나의 플랜과 열린 기능을 봅니다. 주소에 &key=방식별값 을 붙이세요.
setup POST admin 방을 등록하거나 플랜을 바꿉니다. $setup 이 쓰는 곳이에요. 방이 없으면 새로 만듭니다.
rooms GET admin 등록된 방을 전부 봅니다.

GET ?a=ping 없어도 됨

서버가 살아 있는지 봅니다. 키 없이 부를 수 있는 유일한 동작이에요.

돌아오는 것

{"ok":true,"pong":true,"time":"2026-09-27 01:00:00","rev":"6114d4b107de"}

POST ?a=heartbeat bot

봇이 살아 있다고 알립니다. 1분마다 보내세요. 가동률이 이 신호로 계산돼요.

보내는 것

{
  "device":  "phone-01",
  "label":   "아이비봇 폰",
  "version": "1.7.0",
  "uptime":  9000,
  "rooms":   ["1234567890", "9876543210"],
  "cpu":     "12.4%",
  "mem":     "2.1GB / 5.8GB (36%)",
  "ping":    38
}

돌아오는 것

{
  "ok": true,
  "server_time": "2026-09-27 01:00:00",
  "rev": "6114d4b107de",
  "online": true,
  "uptime_24h": 99.8
}
rooms 에 넣은 방들은 "마지막 신호" 시각이 갱신됩니다. 등록되지 않은 방 식별값은 조용히 무시해요.

GET ?a=plans bot

플랜, 기능 허용표, 제한값, 방별 플랜을 한 번에 받습니다. heartbeat 의 rev 가 바뀌었을 때만 다시 부르세요.

돌아오는 것

{
  "ok": true,
  "rev": "6114d4b107de",
  "plans": [
    {"code":"free","name":"무료","price":"0원","price_won":0,
     "final_won":0,"discount":null,"summary":"..."},
    {"code":"pro","name":"프로","price":"20,000원","price_won":20000,
     "final_won":16000,
     "discount":{"type":"percent","value":20,"label":"가을 할인","until":"2026-10-31 23:59:59"},
     "summary":"..."}
  ],
  "features": {"translate":{"name":"번역","group":"lookup","always":false}, "...": {}},
  "matrix":   {"free":["calc","weather"], "pro":["..."]},
  "limits":   {"free":{"notes_max":20}, "pro":{"notes_max":-1}},
  "limit_meta": {"notes_max":{"name":"노트 저장 개수","unit":"개","higher":true}},
  "always":   ["system","admin_tools"],
  "rooms":    {"1234567890":{"plan":"pro","active":true,"name":"컨볼 소통방"}},
  "default_plan": "free"
}

GET ?a=room bot

방 하나의 플랜과 열린 기능을 봅니다. 주소에 &key=방식별값 을 붙이세요.

돌아오는 것

{
  "ok": true,
  "rev": "6114d4b107de",
  "registered": true,
  "room": {"key":"1234567890","name":"컨볼 소통방","plan":"pro",
           "manager_link":true,"active":true},
  "plan": "pro",
  "features": ["translate","points","games","..."],
  "limits":   {"notes_max":-1,"gift_daily":10000}
}

POST ?a=setup admin

방을 등록하거나 플랜을 바꿉니다. $setup 이 쓰는 곳이에요. 방이 없으면 새로 만듭니다.

보내는 것

{
  "room_key":     "1234567890",
  "room_name":    "컨볼 소통방",
  "plan":         "standard",
  "manager_link": true,
  "by":           "아이비"
}

돌아오는 것

{
  "ok": true,
  "created": false,
  "rev": "8d62bb0012d4",
  "room": {"key":"1234567890","name":"컨볼 소통방","plan":"standard","manager_link":true},
  "features": ["translate","points","..."],
  "limits":   {"notes_max":200,"gift_daily":3000}
}
plan 을 빼면 플랜은 그대로 두고 이름이나 연동 여부만 바꿉니다. 없는 플랜 코드를 보내면 400 이에요.

GET ?a=rooms admin

등록된 방을 전부 봅니다.

돌아오는 것

{
  "ok": true, "rev": "...", "count": 3,
  "rooms": [
    {"key":"1234567890","name":"컨볼 소통방","plan":"pro",
     "manager_link":true,"active":true,"last_seen":"2026-09-27 00:59:12"}
  ]
}

4. rev 로 바뀐 것만 받기

모든 응답에는 rev 가 들어 있어요. 플랜, 기능 허용표, 제한값, 할인, 방 목록 중 무엇이든 바뀌면 이 값이 달라집니다.

봇은 이렇게 쓰면 됩니다.

  1. 1분마다 heartbeat 를 보낸다
  2. 응답의 rev 가 갖고 있던 값과 같으면 아무것도 하지 않는다
  3. 다르면 plans 를 한 번 불러 캐시를 갈아 끼운다

이러면 설정이 그대로인 동안에는 1분에 한 번의 작은 요청만 오갑니다.

5. 기능 키 목록 · 30종

matrix 와 features 에 나오는 키예요.

키이름분류항상 열림
system 봇 기본 명령어
.명령어 · .ping · .내해시 · .최적화 처럼 봇이 살아 있는지 확인하는 것들
기본 (항상 열림) 예
admin_tools 관리자 도구
블랙리스트와 셋업. 플랜과 무관하게 관리자만 씁니다.
기본 (항상 열림) 예
welcome 입장 시 안내 멘트
새로 들어온 사람에게 방 안내를 보냅니다.
플랜 기능 (이용약관 표) 아니오
levelup 레벨업
채팅 수를 세어 레벨과 랭킹을 매깁니다.
플랜 기능 (이용약관 표) 아니오
weather 날씨
.날씨 로 지역 날씨를 봅니다.
플랜 기능 (이용약관 표) 아니오
attendance 출석체크 및 순위 경쟁
.ㅊㅊ 출석체크와 연속 출석 순위입니다.
플랜 기능 (이용약관 표) 아니오
chatlog 채팅기록
채팅 원문을 저장합니다. 끄면 감지 결과와 채팅 수만 남깁니다.
플랜 기능 (이용약관 표) 아니오
fever 채팅랭킹 피버타임
특정 시간대에 포인트가 더 쌓입니다.
플랜 기능 (이용약관 표) 아니오
custom_cmd 사용자 지정 명령어
방마다 원하는 명령어를 직접 만듭니다.
플랜 기능 (이용약관 표) 아니오
translate 번역
.번역 · .번역목록
조회·번역 아니오
dict 사전
.뜻 · .사전 · .단어
조회·번역 아니오
spell 맞춤법 검사
.맞춤법 · .검사 · .교정
조회·번역 아니오
fx 환율·코인
.환율 · .코인 · .시세
조회·번역 아니오
calc 계산기
.계산
조회·번역 아니오
note 노트
.노트 개인 메모장
조회·번역 아니오
air 미세먼지
.미세먼지 · .먼지 · .대기질
생활 정보 아니오
time 시간·일출·일몰
.시간 · .일출 · .일몰 · .지구반대편
생활 정보 아니오
transit 교통
.버스 · .전철위치 · .길찾기
생활 정보 아니오
sky 하늘
.iss · .달 · .우주인 · .오로라 · .소행성 · .유성우
생활 정보 아니오
earth 지구
.지진 · .재난지도
생활 정보 아니오
textkit 글자 도구
.한영 · .영한 · .받침빼기 · .발음 · .글자수
생활 정보 아니오
netspeed 인터넷 속도 측정
.인터넷속도
생활 정보 아니오
points 포인트·상점·업적·칭호
.지갑 · .상점 · .업적 · .선물 · .경매
재미·게임 아니오
games 게임
.끝말잇기 · .마피아 · .러시안룰렛
재미·게임 아니오
events 이벤트
광복절 퀴즈 같은 기간 이벤트
재미·게임 아니오
mimic 따라쟁이·너찾는자
가벼운 반응 기능입니다.
재미·게임 아니오
antispam 도배 감지
같은 말을 연달아 보내면 방에 알립니다.
관리·감지 아니오
badword 욕설 감지
욕설을 걸러 방에 알립니다.
관리·감지 아니오
nickwatch 닉네임 변경 기록
.닉변 조회와 변경 자동 안내입니다.
관리·감지 아니오
warn 경고 부여
@닉네임 경고 로 경고를 줍니다.
관리·감지 아니오

6. 제한 키 목록 · 10종

limits 에 나오는 키예요. 값은 정수이고, -1 은 무제한, 0 은 항목마다 뜻이 다릅니다.

키이름단위0 의 뜻큰 값이 좋은가
rooms_max 등록 가능한 방
이 플랜으로 아이비봇을 들일 수 있는 방의 개수예요.
개 없음 예
notes_max 노트 저장 개수
한 사람이 저장할 수 있는 노트의 수예요.
개 쓸 수 없음 예
chatlog_days 채팅기록 보관 기간
채팅 원문을 며칠까지 보관하는지예요. 0이면 저장하지 않아요.
일 저장 안 함 예
rank_top 랭킹 표시 인원
.레벨 이나 순위에서 몇 등까지 보여줄지예요.
명 표시 안 함 예
gift_once 한 번에 선물 한도
.선물 로 한 번에 보낼 수 있는 포인트예요.
P 보낼 수 없음 예
gift_daily 하루 선물 한도
하루 동안 보낼 수 있는 포인트 합계예요.
P 보낼 수 없음 예
point_daily 하루 포인트 적립 상한
채팅으로 하루에 모을 수 있는 포인트예요.
P 적립 안 함 예
cmd_cooldown 명령어 쿨다운
같은 사람이 명령어를 다시 쓰기까지 기다리는 시간이에요. 짧을수록 좋아요.
초 없음 아니오 (작을수록 좋음)
custom_cmd_max 사용자 지정 명령어
방마다 직접 만들 수 있는 명령어의 수예요.
개 만들 수 없음 예
quiz_daily 하루 퀴즈 횟수
이벤트 퀴즈를 하루에 몇 번까지 낼 수 있는지예요.
회 쓸 수 없음 예

7. 오류

실패하면 이렇게 돌아옵니다. HTML 이 튀어나오는 일은 없어요.

{"ok":false,"error":"bad_key","message":"키가 맞지 않습니다."}
상태error뜻
401 no_key X-Ivy-Key 헤더가 없습니다.
401 bad_key 키가 맞지 않거나 꺼진 키입니다.
403 bad_request 이 키의 권한으로는 할 수 없는 동작입니다.
400 clock_skew 기기 시계가 서버와 5분 넘게 차이납니다.
400 bad_request 보낸 값이 모자라거나 잘못됐습니다.
404 unknown_action 그런 동작이 없습니다.
429 too_many 1분에 240회를 넘겼습니다.
503 not_upgraded 사이트 테이블이 아직 준비되지 않았습니다.
503 db 데이터베이스에 연결하지 못했습니다.
500 server 서버에서 문제가 생겼습니다.

8. 붙여 쓰는 예시

curl

curl -s "https://ibcytb.com/bot/api.php?a=ping"

curl -s -X POST "https://ibcytb.com/bot/api.php?a=heartbeat" \
  -H "X-Ivy-Key: ivy_ab12cd.xxxxxxxxxxxx" \
  -H "X-Ivy-Ts: $(date +%s)" \
  -H "Content-Type: application/json" \
  -d '{"device":"phone-01","version":"1.7.0","uptime":9000}'

메신저봇R (Rhino ES5)

네트워크는 반드시 별도 스레드에서 부르세요. 메인에서 부르면 봇이 멈춥니다.

function callIvy(action, body) {
    var conn = new java.net.URL("https://ibcytb.com/bot/api.php?a=" + action).openConnection();
    conn.setConnectTimeout(8000);
    conn.setReadTimeout(8000);
    conn.setRequestProperty("X-Ivy-Key", API_KEY);
    conn.setRequestProperty("X-Ivy-Ts", String(Math.floor(new Date().getTime() / 1000)));

    if (body) {
        var payload = new java.lang.String(JSON.stringify(body)).getBytes("UTF-8");
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        var os = conn.getOutputStream();
        os.write(payload); os.flush(); os.close();
    }

    var code = conn.getResponseCode();
    var br = new java.io.BufferedReader(new java.io.InputStreamReader(
        code >= 400 ? conn.getErrorStream() : conn.getInputStream(), "UTF-8"));
    var sb = new java.lang.StringBuilder(), line;
    while ((line = br.readLine()) !== null) { sb.append(line); }
    br.close();
    conn.disconnect();
    return JSON.parse(String(sb.toString()));
}

파이썬

import json, time, urllib.request

API = "https://ibcytb.com/bot/api.php"
KEY = "ivy_ab12cd.xxxxxxxxxxxx"

def call(action, body=None, **query):
    url = API + "?a=" + action
    for k, v in query.items():
        url += "&" + k + "=" + urllib.parse.quote(str(v))
    data = json.dumps(body, ensure_ascii=False).encode() if body else None
    req = urllib.request.Request(url, data=data, method="POST" if data else "GET")
    req.add_header("X-Ivy-Key", KEY)
    req.add_header("X-Ivy-Ts", str(int(time.time())))
    if data:
        req.add_header("Content-Type", "application/json")
    with urllib.request.urlopen(req, timeout=20) as r:
        return json.loads(r.read().decode())

print(call("ping"))
print(call("room", key="1234567890")["plan"])

도움이 필요하시면

막히는 곳이 있으면 부방장 아이비의 1:1 채팅으로 알려주세요. 키는 절대 다른 사람에게 보내지 마세요. 키가 있으면 플랜을 바꿀 수 있어요.

이 문서는 지금 돌아가는 서버 설정에서 바로 만들어져요. 기능이 늘면 표도 함께 늘어나요.