개발자 문서
한국어

봇 개발

봇 만들기

@BotFather 로 봇을 만들고, 메시지를 받고 답하고, 버튼·명령어·사진·그룹까지 — 처음부터 끝까지 따라 하는 튜토리얼.

이 튜토리얼을 끝내면 메시지에 답하고, 버튼을 달고, 명령어 메뉴와 사진을 다루고, 그룹에서도 일하는 봇을 갖게 됩니다. 필요한 것은 파발 앱과 봇 프로그램을 돌릴 컴퓨터, 그리고 Python 3.10 이상이나 Node.js 18 이상 가운데 하나입니다.

봇은 어떻게 동작하나

파발의 봇은 프로그램이 조종하는 계정입니다. 봇에게 보낸 메시지는 봇의 메시지함에 쌓이고, 봇 프로그램은 서버에 "새로 온 것 있나요?"(getUpdates) 하고 물어 가져간 뒤, 답을 보냅니다(sendMessage). 봇 프로그램은 서버 밖, 여러분의 컴퓨터에서 돕니다.

사람 (앱) 파발 서버 봇 프로그램 ① getUpdates?offset=0&timeout=30 새 소식이 올 때까지 최대 30초 기다림 ② "안녕" 보냄 ③ [{update_id: 1, message: "안녕"}] ④ sendMessage(chat_id, "안녕하세요!") ⑤ 답이 앱에 도착 (실시간) ⑥ getUpdates?offset=2 — 1번은 받았음, 다음 것
getUpdates 로 받고 sendMessage 로 답하는 기본 순서

다이어그램 설명

  • 세 줄기: 왼쪽은 앱을 쓰는 사람, 가운데는 파발 서버, 오른쪽은 여러분의 봇 프로그램입니다. 세로 점선은 시간이 흐르는 방향입니다.
  • 회색 화살표는 봇이 먼저 하는 요청, 파란 화살표는 그 결과로 오가는 메시지입니다. 봇 프로그램은 요청만 보내고, 서버가 봇에게 먼저 연락하지 않습니다(웹훅을 쓰지 않는 한).
  • ①의 기다림(롱 폴링)이 핵심입니다: timeout=30 을 주면 새 소식이 없을 때 서버가 바로 빈 답을 주지 않고 최대 30초 쥐고 있다가, 메시지가 오는 순간(②) 돌려줍니다(③). 그래서 반응이 빠르고 요청 수는 적습니다.
  • ⑥의 offset 이 "받았음" 표시입니다: 마지막으로 처리한 update_id 에 1을 더해 보내면 그 이하는 서버에서 지워집니다. offset 을 올리지 않으면 같은 업데이트를 계속 받습니다.
  • ⑤는 사람이 보낸 메시지와 같은 길입니다: 봇의 답은 앱의 모든 기기에 푸시되고 대화 목록에도 올라갑니다.

1. @BotFather 로 봇 만들기

봇은 파발 앱 안의 @BotFather 와 대화해서 만듭니다. BotFather 는 파발 서버에 들어 있는 봇입니다.

  1. 앱 검색창에 BotFather 를 넣고 BotFather 를 엽니다. 시작을 누르면 명령 목록이 옵니다.
  2. /newbot 을 보냅니다.
  3. 봇의 이름을 보냅니다. 대화 목록에 보이는 이름이라 한글도 됩니다.
  4. 봇의 사용자명을 보냅니다. 영문자로 시작하는 영문자·숫자·밑줄 5~32자이고, 반드시 bot 으로 끝나야 합니다.
  5. 토큰이 담긴 답이 오면 끝입니다. 토큰을 복사해 두세요.
/newbot
BotFather새 봇을 만듭니다. 봇의 이름을 알려 주세요. (대화 목록에 보이는 이름이에요)
안녕 봇
BotFather좋아요. 이제 봇의 사용자명을 정해 주세요. …
hello_test_bot
BotFather완료! 새 봇 @hello_test_bot 를 만들었어요. 검색해서 대화를 시작할 수 있어요. 봇 토큰: 100003:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789 토큰은 비밀번호처럼 안전하게 보관하세요. …
BotFather 명령하는 일
/newbot새 봇 만들기 (이름 → 사용자명 → 토큰)
/mybots내가 만든 봇 목록
/token봇 토큰 다시 보기
/revoke토큰 재발급 — 이전 토큰은 즉시 무효, 이전 토큰으로 붙어 있던 연결도 끊김
/setcommands명령어 메뉴 설정 (명령어 - 설명 을 한 줄에 하나)
/deletebot봇 삭제 — 네, 삭제합니다 로 확인. 사용자명은 다시 쓸 수 있게 풀림
/cancel진행 중인 작업 취소

/token @hello_test_bot 처럼 사용자명을 붙여 보내면 "어느 봇인가요?" 단계를 건너뜁니다.

2. 토큰 다루기

토큰은 <봇 ID>:<비밀> 꼴입니다. 앞의 숫자가 봇의 사용자 ID 이고, 뒤가 비밀입니다. 토큰 하나로 봇을 완전히 조종할 수 있으므로 비밀번호처럼 다루세요.

  • 코드에 적지 말고 환경 변수(BOT_TOKEN)나 비밀 저장소에 두세요. 공개 저장소에 올리지 마세요.
  • 새어 나갔다면 BotFather 에 /revoke. 이전 토큰은 즉시 거절(401 Unauthorized)되고, 이전 토큰으로 MTProto 에 붙어 있던 봇 세션도 끊깁니다.
  • 토큰은 주소(URL) 안에 들어가므로, 봇 프로그램이 요청 주소를 로그에 남기지 않게 하세요. 파발 서버도 Bot API 주소를 로그에 남기지 않습니다.

3. 첫 요청 — getMe

모든 요청의 주소는 https://pabal.me/bot<토큰>/<메서드> 입니다. 토큰이 맞는지 getMe 로 확인해 봅시다.

export BOT_TOKEN='100003:AbCdEf…'
curl -s "https://pabal.me/bot$BOT_TOKEN/getMe"
# pip install requests
import os
import requests

r = requests.get(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/getMe", timeout=10)
print(r.json())
// Node.js 18 이상 — fetch 가 들어 있음
const res = await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/getMe`);
console.log(await res.json());

성공하면 이렇게 옵니다. 모든 응답은 okresult(성공) 또는 error_code·description(실패)을 담은 JSON 입니다.

{
  "ok": true,
  "result": {
    "id": 100003,
    "is_bot": true,
    "first_name": "안녕 봇",
    "username": "hello_test_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": true,
    "supports_inline_queries": false,
    "can_connect_to_business": false,
    "has_main_web_app": false
  }
}

토큰이 틀리면 HTTP 401 과 함께 {"ok": false, "error_code": 401, "description": "Unauthorized"} 가 옵니다.

4. 메시지 받기 — getUpdates

앱에서 봇을 열고 시작을 누르거나 아무 말이나 보낸 뒤, 새 소식을 가져옵니다.

curl -s "https://pabal.me/bot$BOT_TOKEN/getUpdates?timeout=30"
{
  "ok": true,
  "result": [
    {
      "update_id": 1,
      "message": {
        "message_id": 1,
        "from": { "id": 100001, "is_bot": false, "first_name": "하나" },
        "chat": { "id": 100001, "first_name": "하나", "type": "private" },
        "date": 1789805661,
        "text": "/start",
        "entities": [ { "type": "bot_command", "offset": 0, "length": 6 } ]
      }
    }
  ]
}
  • update_id: 업데이트마다 1씩 커지는 번호. 처리한 뒤 다음 요청에 offset=update_id+1 을 주면 그 이하가 "받았음"으로 지워집니다.
  • timeout: 새 소식이 없을 때 기다릴 초(0~50). 0 이면 바로 빈 목록을 돌려줍니다. 25~30 을 권합니다.
  • chat.id: 답장을 보낼 곳. 1:1 대화면 사람의 ID(양수), 그룹이면 음수입니다.
  • 받을 수 있는 업데이트는 message(새 메시지), edited_message(고친 메시지), callback_query(버튼 누름) 세 가지입니다.
대기열은 서버 메모리에 있습니다

가져가지 않은 업데이트는 봇마다 최근 1,000개까지 서버 메모리에 쌓입니다. 서버를 다시 켜면 가져가지 않은 것은 사라집니다(메시지 자체는 대화에 남습니다). 봇을 오래 꺼 두지 마세요.

5. 답장 보내기 — sendMessage

curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" \
  -H 'Content-Type: application/json' \
  -d '{"chat_id": 100001, "text": "안녕하세요!"}'
requests.post(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/sendMessage",
              json={"chat_id": 100001, "text": "안녕하세요!"}, timeout=10)
await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/sendMessage`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ chat_id: 100001, text: '안녕하세요!' }),
});

파라미터는 JSON 본문, 폼(application/x-www-form-urlencoded), 파일을 올릴 때의 multipart/form-data, 주소 뒤의 쿼리 문자열 중 편한 것으로 보내면 됩니다. 결과는 보낸 메시지(Message)입니다.

봇은 먼저 말을 걸 수 없습니다

사람이 봇에게 한 번이라도 메시지를 보낸 뒤에만 그 사람에게 보낼 수 있습니다. 아니면 403 Forbidden: bot can't initiate conversation with a user 입니다. 텔레그램과 같은 규칙입니다.

6. 따라 말하는 봇 완성

받기와 보내기를 반복하면 봇이 됩니다. 라이브러리 없이 쓴 전체 코드입니다.

# echo.py — pip install requests
# 실행: BOT_TOKEN='100003:…' python3 echo.py
import os
import requests

API = f"https://pabal.me/bot{os.environ['BOT_TOKEN']}"


def call(method, **params):
    r = requests.post(f"{API}/{method}", json=params, timeout=60)
    data = r.json()
    if not data["ok"]:
        raise RuntimeError(f"{method}: {data['description']}")
    return data["result"]


offset = 0
print("봇이 켜졌습니다. 멈추려면 Ctrl+C")
while True:
    for update in call("getUpdates", offset=offset, timeout=30):
        offset = update["update_id"] + 1          # 받았음 표시
        message = update.get("message")
        if message and "text" in message:
            call("sendMessage", chat_id=message["chat"]["id"], text=message["text"])
// echo.mjs — Node.js 18 이상, 라이브러리 없이
// 실행: BOT_TOKEN='100003:…' node echo.mjs
const API = `https://pabal.me/bot${process.env.BOT_TOKEN}`;

async function call(method, params = {}) {
  const res = await fetch(`${API}/${method}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  });
  const data = await res.json();
  if (!data.ok) throw new Error(`${method}: ${data.description}`);
  return data.result;
}

let offset = 0;
console.log('봇이 켜졌습니다. 멈추려면 Ctrl+C');
for (;;) {
  const updates = await call('getUpdates', { offset, timeout: 30 });
  for (const update of updates) {
    offset = update.update_id + 1;              // 받았음 표시
    const message = update.message;
    if (message?.text) {
      await call('sendMessage', { chat_id: message.chat.id, text: message.text });
    }
  }
}

7. 라이브러리로 만들기

텔레그램용 봇 라이브러리는 서버 주소를 바꾸는 설정이 있습니다. 그 설정 하나로 파발에서 그대로 돕니다. 텔레그램용으로 만든 봇을 옮길 때도 이것만 바꾸고, 토큰은 파발의 BotFather 에게서 새로 받으면 됩니다.

라이브러리바꿀 설정확인한 판
python-telegram-bot.base_url("https://pabal.me/bot"), .base_file_url("https://pabal.me/file/bot")22.8
aiogramAiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me"))3.31
라이브러리 없이 (HTTP)주소 앞부분 https://api.telegram.orghttps://pabal.me
# hello_bot.py — pip install python-telegram-bot
# 실행: BOT_TOKEN='100003:…' python3 hello_bot.py
import os

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler, ContextTypes,
                          MessageHandler, filters)

SERVER = "https://pabal.me"


def buttons():
    return InlineKeyboardMarkup([[InlineKeyboardButton("👍 좋아요", callback_data="like"),
                                  InlineKeyboardButton("🔢 숫자 올리기", callback_data="count")]])


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("안녕하세요! 버튼을 눌러 보세요.", reply_markup=buttons())


async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if query.data == "like":
        await query.answer("고마워요!")                  # 누른 사람 화면에 잠깐 뜨는 문구
    else:
        n = context.chat_data.get("n", 0) + 1
        context.chat_data["n"] = n
        await query.answer()                             # 먼저 답하고
        await query.edit_message_text(f"숫자: {n}", reply_markup=buttons())   # 메시지를 고친다


async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(f"'{update.message.text}' 라고 하셨네요.")


def main():
    app = (Application.builder().token(os.environ["BOT_TOKEN"])
           .base_url(f"{SERVER}/bot")                   # api.telegram.org 대신 파발
           .base_file_url(f"{SERVER}/file/bot")
           .build())
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CallbackQueryHandler(button))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    print("봇이 켜졌습니다. 멈추려면 Ctrl+C")
    app.run_polling()


if __name__ == "__main__":
    main()
# echo_aiogram.py — pip install aiogram
# 실행: BOT_TOKEN='100003:…' python3 echo_aiogram.py
import asyncio
import os

from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
from aiogram.filters import CommandStart

dp = Dispatcher()


@dp.message(CommandStart())
async def start(message):
    await message.answer("안녕하세요! 아무 말이나 보내 보세요.")


@dp.message()
async def echo(message):
    if message.text:
        await message.answer(message.text)


async def main():
    session = AiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me"))   # 파발
    bot = Bot(os.environ["BOT_TOKEN"], session=session)
    print("봇이 켜졌습니다. 멈추려면 Ctrl+C")
    await dp.start_polling(bot)


asyncio.run(main())

8. 버튼과 콜백

메시지에 인라인 버튼을 달려면 reply_markupinline_keyboard(줄의 배열, 줄은 버튼의 배열)를 줍니다. 버튼은 누르면 봇에게 알리는 callback_data 버튼과, 링크를 여는 url 버튼 두 가지입니다.

curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" -H 'Content-Type: application/json' -d '{
  "chat_id": 100001,
  "text": "골라 주세요",
  "reply_markup": {
    "inline_keyboard": [
      [ {"text": "👍 좋아요", "callback_data": "like"}, {"text": "👎 별로", "callback_data": "dislike"} ],
      [ {"text": "파발 문서 열기", "url": "https://pabal.me/docs/"} ]
    ]
  }
}'

사람이 callback_data 버튼을 누르면 봇은 callback_query 업데이트를 받습니다.

{
  "update_id": 7,
  "callback_query": {
    "id": "5812039457730125441",
    "from": { "id": 100001, "is_bot": false, "first_name": "하나" },
    "message": { "message_id": 4, "chat": { "id": 100001, "type": "private", "first_name": "하나" }, "text": "골라 주세요", … },
    "chat_instance": "8413962145072395171",
    "data": "like"
  }
}

봇은 10초 안에 answerCallbackQuery 로 답해야 합니다. 앱은 그동안 버튼에 시계 표시를 띄우고 기다립니다.

# 화면 위에 잠깐 뜨는 문구 (show_alert: true 면 확인 창)
curl -s "https://pabal.me/bot$BOT_TOKEN/answerCallbackQuery" -H 'Content-Type: application/json' \
  -d '{"callback_query_id": "5812039457730125441", "text": "고마워요!"}'

# 누른 메시지의 글과 버튼 바꾸기
curl -s "https://pabal.me/bot$BOT_TOKEN/editMessageText" -H 'Content-Type: application/json' \
  -d '{"chat_id": 100001, "message_id": 4, "text": "좋아요를 눌렀어요 👍"}'
  • callback_data 는 1~64바이트입니다. 버튼은 한 메시지에 최대 100개입니다.
  • 콜백에 답하지 않으면 앱은 10초 뒤 기다리기를 멈춥니다. 봇이 꺼져 있으면 서버가 바로 끝냅니다.
  • 답은 처음 한 번만 유효합니다. 라이브러리가 메시지를 고치면서 빈 답을 먼저 보내는 경우가 있으니, 문구가 있는 답을 먼저 보내세요.
  • 같은 글·같은 버튼으로 고치면 400 Bad Request: message is not modified 입니다(텔레그램과 같음).

입력창 아래 키보드

keyboard 를 주면 입력창 대신 버튼 판이 뜨고, 누르면 그 글자가 메시지로 보내집니다. remove_keyboard 로 숨기고, force_reply 로 답장 모드를 켭니다.

{
  "chat_id": 100001,
  "text": "어느 쪽인가요?",
  "reply_markup": {
    "keyboard": [ [ {"text": "예"}, {"text": "아니요"} ], [ {"text": "내 위치 보내기", "request_location": true} ] ],
    "resize_keyboard": true,
    "one_time_keyboard": true
  }
}

9. 명령어 메뉴

대화창에서 / 를 누르거나 메뉴 버튼을 누르면 뜨는 목록입니다. 코드로 정하거나 BotFather 의 /setcommands 로 정합니다.

curl -s "https://pabal.me/bot$BOT_TOKEN/setMyCommands" -H 'Content-Type: application/json' -d '{
  "commands": [
    {"command": "start", "description": "시작하기"},
    {"command": "help",  "description": "도움말"}
  ]
}'

명령어는 영소문자·숫자·밑줄 1~32자, 설명은 1~256자, 최대 100개입니다. language_code 를 주면 언어별 목록을 따로 저장하지만, 지금 앱에는 언어 코드 없이 정한 기본 목록만 보입니다. 사람이 보낸 /start 같은 명령은 메시지의 entitiesbot_command 로 표시되어 옵니다.

10. 사진 주고받기

보내기

파일을 multipart/form-data 로 올리거나, 전에 받은 사진의 file_id 를 다시 씁니다. 사진은 10MB 까지, 설명(caption)은 1,024자까지입니다. URL 로 보내기는 아직 지원하지 않습니다.

curl -s "https://pabal.me/bot$BOT_TOKEN/sendPhoto" \
  -F chat_id=100001 -F caption='오늘의 사진' -F photo=@sunset.jpg
with open("sunset.jpg", "rb") as f:
    requests.post(f"{API}/sendPhoto", data={"chat_id": 100001, "caption": "오늘의 사진"},
                  files={"photo": f}, timeout=60)

받기

사람이 보낸 사진은 메시지의 photo(크기별 목록, 파발은 원본 하나)로 옵니다. getFile 로 경로를 얻어 내려받습니다.

# 1) file_id → file_path
curl -s "https://pabal.me/bot$BOT_TOKEN/getFile?file_id=AQAAAAAAAAB7…"
# {"ok":true,"result":{"file_id":"AQAA…","file_unique_id":"AQAA…","file_size":48213,"file_path":"photos/AQAA….jpg"}}

# 2) 내려받기 — 주소에 /file/ 이 들어갑니다
curl -s -o photo.jpg "https://pabal.me/file/bot$BOT_TOKEN/photos/AQAA….jpg"

11. 그룹에서

  • 앱에서 그룹을 만들 때, 또는 그룹 정보 → 멤버 추가에서 봇의 사용자명을 검색해 넣습니다.
  • 그룹에 들어간 봇은 그룹의 모든 메시지를 받습니다(텔레그램의 "프라이버시 모드 꺼짐"과 같음). chat.type"group", chat.id 는 음수입니다.
  • chat.idsendMessage 하면 그룹에 보내집니다. 버튼·사진·수정도 1:1 과 똑같습니다.
  • 봇이 그룹에서 빠지면 그 그룹으로 보내기는 403 Forbidden: bot is not a member of the group chat 입니다.

12. 웹훅으로 바꾸기

봇이 공개된 HTTPS 주소를 가진 서버에서 돈다면, getUpdates 로 묻는 대신 파발 서버가 새 소식을 그 주소로 보내게 할 수 있습니다.

curl -s "https://pabal.me/bot$BOT_TOKEN/setWebhook" -H 'Content-Type: application/json' \
  -d '{"url": "https://bot.example.com/pabal-webhook", "secret_token": "긴-무작위-문자열"}'

설정·검증·재시도·응답으로 답하기까지는 웹훅 문서에 있습니다.

MTProto 로 붙는 봇 (Telethon)

봇은 앱과 같은 MTProto 로도 붙을 수 있습니다. 사람 계정용 도구(Telethon 등)를 이미 쓰고 있다면 편합니다. 같은 봇 계정이므로 HTTP 와 섞어 써도 됩니다.

# pip install telethon==1.42.0   ← 1.42 를 쓰세요 (아래 설명)
# 서버 공개키: https://pabal.me/docs/server-key.pem 을 받아 같은 폴더에
import asyncio
import os

from telethon import TelegramClient, events
from telethon.crypto import rsa
from telethon.sessions import StringSession

rsa.add_key(open("server-key.pem").read(), old=False)        # 파발 서버의 공개키
client = TelegramClient(StringSession(), api_id=1, api_hash="0" * 32)
client.session.set_dc(2, "122.34.175.215", 8443)


@client.on(events.NewMessage(incoming=True))
async def echo(event):
    await event.reply(event.raw_text)


async def main():
    await client.start(bot_token=os.environ["BOT_TOKEN"])      # auth.importBotAuthorization
    print("봇이 켜졌습니다. 멈추려면 Ctrl+C")
    await client.run_until_disconnected()


asyncio.run(main())
  • Telethon 1.42 를 쓰세요. 파발은 레이어 216 을 말하는데, 더 새 Telethon 은 더 높은 레이어로 응답을 읽으려 해서 로그인부터 실패합니다(TypeNotFoundError).
  • api_id·api_hash 는 파발에서 확인하지 않으므로 아무 값이나 됩니다.
  • MTProto 봇에는 서버가 새 메시지를 실시간으로 밀어 줍니다(getUpdates·웹훅이 필요 없음). 콜백 답은 event.answer("…")event.edit(…) 보다 먼저 부르세요.

규칙과 한도

항목
메시지 글자 수 / 사진 설명4,096자 / 1,024자
사진 크기 (sendPhoto 업로드)10MB
요청 본문 크기12MB
getUpdates timeout · limit0~50초 · 1~100개
가져가지 않은 업데이트 보관봇마다 최근 1,000개, 서버 메모리 (재시작 시 사라짐)
콜백 답 기다림10초
callback_data · 버튼 수1~64바이트 · 메시지당 100개
명령어영소문자·숫자·밑줄 1~32자, 설명 1~256자, 최대 100개
먼저 말 걸기안 됨 — 사람이 먼저 봇에게 보내야 함

문제 해결

증상원인과 해결
401 Unauthorized토큰이 틀렸거나 /revoke 로 바뀌었습니다. BotFather 에 /token 을 보내 확인하세요.
404 Not Found: method not found파발이 아직 지원하지 않는 메서드입니다. 메서드 목록을 확인하세요.
403 Forbidden: bot can't initiate conversation with a user그 사람이 아직 봇에게 말을 건 적이 없습니다. 앱에서 봇을 열고 시작을 누르게 하세요.
409 Conflict: can't use getUpdates method while webhook is active웹훅이 설정돼 있습니다. deleteWebhook 을 부르거나 웹훅으로 받으세요.
버튼을 눌러도 반응이 없다봇이 꺼져 있거나 answerCallbackQuery 를 부르지 않았습니다.
같은 메시지를 계속 받는다offset 을 올리지 않았습니다. 처리한 update_id + 1 을 다음 요청에 주세요.
굵게·링크 서식이 안 먹는다parse_mode 는 아직 지원하지 않아 글자 그대로 보냅니다. 명령·@멘션·URL·#태그 는 자동으로 눌리게 표시됩니다.
그룹에서 봇이 조용하다봇이 그룹 멤버가 아닙니다. 그룹 정보 → 멤버 추가로 넣으세요.