Bot API 使用文档
从创建机器人到程序接入
完整说明 BotMother、消息接口、验证码复制与底部按钮。所有示例使用占位值。
概览与能力
#当前公开方法是 getMe、sendMessage、getUpdates。发送支持私聊/群聊文字,接收事件可包含非文字内容。
通过 sendMessage 的 copy_text 显示验证码复制图标,通过 reply_markup 下发私聊底部文字按钮。
该域名提供文档;实际 API 仍在 im.ez7.life 的 /bot-api/ 路径。接收者 ID 可在设置 → 账号详情 → ID 复制;收到消息的 message.chat.id 可用于回复,群聊时它是群 ID。
https://im.ez7.life/bot-api/<BOT_TOKEN>/<METHOD>快速开始
#- 搜索并添加
@BotMother,打开对话顶部的机器人管理。 - 创建机器人、复制 Token,并把它放入后端环境变量
EZLIFE_BOT_TOKEN。 - 先调用
getMe确认身份,再用用户 ID 调用sendMessage。 - 启动
getUpdates轮询;处理成功后持久化下一次offset。
python3 bot.pyBotMother 管理
#创建时填写 1–64 字显示名称。用户名界面输入 3–28 字前缀,以英文字母开头,仅用字母、数字、下划线;界面补上 _bot,先检查可用再创建。
管理页可搜索机器人、查看机器人ID、状态、创建时间与 **** 加尾6位提示,并重新签发或撤销 Token。数量上限由后台配置。
| 命令 | 作用 |
|---|---|
/start | 显示管理说明 |
/newbot | 开始创建机器人 |
/mybots | 列出本人机器人 |
/token @username | 重新签发,旧Token立即失效 |
/revoke @username | 撤销当前Token |
/cancel | 取消创建流程 |
/help | 显示帮助 |
Token 与安全
#完整 Token 仅在创建或重新签发时显示一次;它是机器人密码,只应保存在后端配置。不要放入网页、App、公开仓库或日志。
重新签发会立即使旧 Token 失效;撤销会阻止外部调用。显示尾码不等于可找回完整值,遗失时应重新签发并更新程序。
旧记录若没有尾码,会在下一次合法 Bot API 调用后补记。账号详情里的通用 API Token 用于账号接口;此处 Bot API 使用 BotMother 签发的机器人 Token。
getMe · 机器人身份
#/bot-api/<BOT_TOKEN>/getMe不需要业务参数,返回机器人ID、显示名称、用户名及群能力标记。请先用此方法检查环境变量和 Token 是否正确。
curl --fail-with-body --request POST \
"https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getMe" \
--header 'Content-Type: application/json' --data '{}'{
"ok": true,
"result": {
"id": "1000000000",
"is_bot": true,
"display_name": "Example Bot",
"username": "example_bot",
"can_join_groups": true,
"can_read_all_group_messages": true
}
}sendMessage · 发送消息
#/bot-api/<BOT_TOKEN>/sendMessage推荐使用 POST application/json。chat_id 始终传字符串:私聊是接收用户ID;群聊是群ID,并传 chat_type: "group"。
消息正文 1–5000 字,前后空白会被去除。响应中的 message_id 可供业务记录;发送接口没有幂等键,超时后先核对结果再决定是否重发。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
chat_id | string | 是 | 接收用户ID;群聊时为群ID,始终用字符串。 |
text | string | 是 | 1–5000字正文。 |
chat_type | string | 否 | 默认private;群消息用group,也接受supergroup。 |
copy_text | string | 否 | 正文中完整出现的4–12位数字字符串。 |
reply_markup | object / JSON string | 否 | 可选的底部按钮对象,也可传JSON字符串。 |
curl --fail-with-body --request POST \
"https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/sendMessage" \
--header 'Content-Type: application/json' --data-binary @- <<'JSON'
{
"chat_id": "USER_ID",
"text": "订单已处理完成。"
}
JSON{
"chat_id": "GROUP_ID",
"chat_type": "group",
"text": "订单已处理完成。"
}{
"ok": true,
"result": {
"message_id": "EXAMPLE_MESSAGE_ID",
"date": 1794297600,
"chat": {
"id": "USER_ID",
"type": "private"
},
"text": "订单已处理完成。"
}
}发送成功返回 ok: true 与 result;验证码附加 copy_text,菜单附加 reply_markup 和 reply_keyboard_revision。收消息返回 result 数组。没有消息时 result: []。
验证码复制
#保留正文里的系统名、用途和有效期,再传 copy_text。它必须是正文中完整出现的 4–12 位 ASCII 数字字符串;使用字符串保留前导零。
点击图标只复制验证码。此字段负责显示和复制,验证码生成、有效期与验证由你的业务程序处理;旧客户端仍能阅读正文。
{
"chat_id": "USER_ID",
"text": "示例系统登录验证码:003812,5分钟内有效。",
"copy_text": "003812"
}底部按钮键盘
#按钮是多行文字回复键盘。点击后,用户以普通消息发送按钮文字;程序在 getUpdates 的 message.text 中处理对应业务。
每行 1–4 个按钮,1–8 行,总计最多24个;每个文字 1–64 字,不允许空白、换行或控制字符。按钮可用字符串或仅含 text 的对象。
省略 reply_markup 保留旧菜单;发送新 keyboard 替换菜单。remove_keyboard: true 必须单独使用,移除菜单及展开入口。
用户可收起/展开。一次性键盘仅在按钮消息发送成功后收起,新版本菜单重新展开。菜单按用户和Bot隔离保存;活跃私聊约每5秒核对状态。
键盘仅支持机器人私聊,群聊携带 reply_markup 会返回 422。正文 text 仍必填。布尔值须用 JSON true/false,不能传字符串;未知键盘或按钮属性会拒绝。JSON 字符串形式的 reply_markup 最多 16 KiB。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
reply_markup | object / JSON string | 否 | 可选的底部按钮对象,也可传JSON字符串。 |
keyboard | array[] | 是 | 二维数组,每个内部数组是一行按钮。 |
resize_keyboard | boolean | 否 | 默认true,紧凑按钮;false使用更大高度。 |
one_time_keyboard | boolean | 否 | 默认false;true时成功点击发送后收起。 |
remove_keyboard | boolean | 否 | true清除菜单,不能与其他键盘选项组合。 |
{
"chat_id": "USER_ID",
"text": "请选择需要的服务:",
"reply_markup": {
"keyboard": [
[
{
"text": "语言设置"
},
{
"text": "快递追踪"
}
],
[
{
"text": "通知设置"
},
{
"text": "录入单号"
}
]
],
"resize_keyboard": true,
"one_time_keyboard": false
}
}{
"chat_id": "USER_ID",
"text": "请选择需要的服务:",
"reply_markup": {
"keyboard": [
[
"语言设置",
"快递追踪"
]
],
"one_time_keyboard": true
}
}{
"chat_id": "USER_ID",
"text": "本次操作已结束。",
"reply_markup": {
"remove_keyboard": true
}
}getUpdates · 接收消息
#/bot-api/<BOT_TOKEN>/getUpdates这是短轮询:没有消息时立即返回空数组。建议每 1–3 秒请求,网络失败逐步退避;当前没有对外 Webhook 或长轮询。
业务处理成功后,将下一次 offset 设为最后一条 update_id + 1。服务器会确认更早更新;处理失败保留原 offset,并按 update_id 或业务编号防重。
同一机器人的程序应协调消费队列,避免多个进程相互推进 offset。非文字事件可以查看 content_type 和 raw_content。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
offset | integer | 否 | 非负整数,下一笔希望读取的更新ID,默认0。 |
limit | integer | 否 | 每次1–100条,默认100。 |
curl --fail-with-body --request POST \
"https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getUpdates" \
--header 'Content-Type: application/json' \
--data '{"offset": 0, "limit": 100}'{
"ok": true,
"result": [
{
"update_id": 120,
"message": {
"message_id": "EXAMPLE_MESSAGE_ID",
"date": 1794297600,
"chat": {
"id": "1000000001",
"type": "private"
},
"from": {
"id": "1000000001",
"display_name": "Example User"
},
"text": "语言设置",
"content_type": 101,
"raw_content": "{\n \"content\": \"语言设置\"\n}"
}
}
]
}| 参数 | 说明 |
|---|---|
update_id | 递增更新编号,用于确认消费进度。 |
message_id | 平台消息ID。 |
date | Unix秒时间戳。 |
chat | 私聊发信人ID或群ID,以及private/group类型。 |
from | 实际发信人ID与显示名称。 |
text | 文字内容;非文字事件可能为空。 |
content_type | 消息内容类型,101表示文字。 |
raw_content | 原始消息内容,供处理图片、文件等接收事件。 |
完整 Python 程序
#下方完整示例只用 Python 标准库,支持 /start 显示按钮、/ping 回复及按钮文字处理,并持久化 offset。请从程序的私有运行目录启动。
把示例里的按钮分支替换为自己的业务逻辑。涉及订单、付款等操作时,先完成业务幂等处理,再保存消费进度。
import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path
TOKEN = os.environ["EZLIFE_BOT_TOKEN"]
BASE = "https://im.ez7.life/bot-api/" + TOKEN
OFFSET_FILE = Path("bot-offset.json")
MENU = [
[
"语言设置",
"快递追踪"
],
[
"通知设置",
"录入单号"
]
]
class ApiFailure(Exception):
pass
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args):
return None
HTTP = urllib.request.build_opener(NoRedirect())
def api(method, payload):
req = urllib.request.Request(
BASE + "/" + method,
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with HTTP.open(req, timeout=20) as response:
body = json.load(response)
except urllib.error.HTTPError as error:
raise ApiFailure("HTTP " + str(error.code)) from None
except (urllib.error.URLError, TimeoutError, OSError, ValueError):
raise ApiFailure("Network or response failure") from None
if not body.get("ok"):
raise ApiFailure("API error " + str(body.get("error_code", "unknown")))
return body["result"]
def handle(update):
message = update.get("message", {})
chat = message.get("chat", {})
text = message.get("text", "")
if not chat.get("id"):
return
payload = {"chat_id": str(chat["id"]),
"chat_type": chat.get("type", "private")}
if text == "/start" and payload["chat_type"] == "private":
payload.update(text="请选择需要的服务:", reply_markup={
"keyboard": MENU, "resize_keyboard": True,
"one_time_keyboard": False,
})
elif text == "/ping":
payload["text"] = "pong"
elif text in [label for row in MENU for label in row]:
payload["text"] = "本次操作已结束。"
else:
return
api("sendMessage", payload)
def save_offset(value):
temp = OFFSET_FILE.with_suffix(".tmp")
fd = os.open(temp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as stream:
json.dump(value, stream)
stream.flush()
os.fsync(stream.fileno())
os.replace(temp, OFFSET_FILE)
def main():
offset = json.loads(OFFSET_FILE.read_text()) if OFFSET_FILE.exists() else 0
if isinstance(offset, bool) or not isinstance(offset, int) or offset < 0:
raise ValueError("Invalid offset file")
delay = 1
while True:
try:
updates = api("getUpdates", {"offset": offset, "limit": 100})
for update in updates:
handle(update)
next_offset = int(update["update_id"]) + 1
save_offset(next_offset)
offset = next_offset
delay = 1
time.sleep(2)
except (ApiFailure, OSError, ValueError):
print("Processing failed; offset retained. Check configuration.", flush=True)
time.sleep(delay)
delay = min(delay * 2, 30)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass群组与消息范围
#机器人必须先加入目标群。默认需要机器人拥有者同意;在与该Bot的对话右上角更多→机器人设置→加入群组列表中处理待确认邀请。
群隐私模式默认关闭,启用接收后不需要 @Bot。它自己的消息不会回到自己的队列;群系统通知不作为普通消息处理。
每群接收开关影响该群的外部更新、转发和自动回复。用户自建Bot会把启用接收的群消息标注群名转到拥有者私聊;外部访问开关仍控制 API 与更新队列。
机器人设置与自动回复
#机器人设置仅拥有者可管理:加入审批、外部程序访问、每群接收和关键词自动回复。关闭外部访问后,现有 Token 保留,但程序调用返回拒绝。
自动回复最多50条,关键词1–100字,回复1–2000字;按顺序匹配,不区分大小写的“包含”规则,命中首条已启用规则后回复。
自动回复可用于私聊及启用接收的群;会避免对Bot消息自动连锁回复。AI API接入目前是规划说明,尚无公开调用方法。
错误处理
#JSON 解析失败或正文过大等早期错误使用 errorCode 与 message;其他 API 错误使用 error_code 与 description。始终先检查 HTTP 状态与 ok。
{
"ok": false,
"error_code": 422,
"description": "chat_id 与 1–5000 字的 text 为必填"
}| 状态 | 处理方式 |
|---|---|
| 400 | JSON 格式无效,请检查编码与语法。 |
| 401 | Token无效、已撤销/重新签发或机器人停权;更新密钥后再试。 |
| 403 | 拥有者关闭外部访问;到机器人设置重新开启。 |
| 404 | 检查 /bot-api/ 路径、Token格式和方法名称。 |
| 409 | 同一Bot与用户的菜单正在更新,稍后重试。 |
| 413 | 请求体超过1 MiB,缩小请求内容。 |
| 422 | 修正参数、验证码格式或键盘结构。 |
| 500 | 暂时故障;接收保持offset并退避,发送先核对是否已成功。 |
限制与客户端兼容
#本页覆盖当前已实现的公开能力。媒体发送、内联 callback 按钮、联系人/位置请求按钮和对外Webhook尚无公开方法。
底部键盘适用于新版 Web/H5;内嵌旧版 H5 资源的客户端需更新才能显示新控件。
完整请求正文不能超过 1 MiB。按实际负载控制发送与轮询频率;本指南没有承诺固定吞吐量。群组能力标记不替代实际入群、审核与接收权限。