EzEzLife Developers
简体中文
Bot API
EZLIFE / BOT PLATFORM

Bot API 使用文档

从创建机器人到程序接入

完整说明 BotMother、消息接口、验证码复制与底部按钮。所有示例使用占位值。

更新于 HTTPS · JSON · UTF-8

概览与能力

#

当前公开方法是 getMe、sendMessage、getUpdates。发送支持私聊/群聊文字,接收事件可包含非文字内容。

通过 sendMessage 的 copy_text 显示验证码复制图标,通过 reply_markup 下发私聊底部文字按钮。

该域名提供文档;实际 API 仍在 im.ez7.life 的 /bot-api/ 路径。接收者 ID 可在设置 → 账号详情 → ID 复制;收到消息的 message.chat.id 可用于回复,群聊时它是群 ID。

API 基础地址
https://im.ez7.life/bot-api/<BOT_TOKEN>/<METHOD>

快速开始

#
  1. 搜索并添加 @BotMother,打开对话顶部的机器人管理。
  2. 创建机器人、复制 Token,并把它放入后端环境变量 EZLIFE_BOT_TOKEN。
  3. 先调用 getMe 确认身份,再用用户 ID 调用 sendMessage。
  4. 启动 getUpdates 轮询;处理成功后持久化下一次 offset。
bash
python3 bot.py

BotMother 管理

#

创建时填写 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 · 机器人身份

#
POST/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 · 发送消息

#
POST/bot-api/<BOT_TOKEN>/sendMessage

推荐使用 POST application/json。chat_id 始终传字符串:私聊是接收用户ID;群聊是群ID,并传 chat_type: "group"。

消息正文 1–5000 字,前后空白会被去除。响应中的 message_id 可供业务记录;发送接口没有幂等键,超时后先核对结果再决定是否重发。

参数类型必填说明
chat_idstring是接收用户ID;群聊时为群ID,始终用字符串。
textstring是1–5000字正文。
chat_typestring否默认private;群消息用group,也接受supergroup。
copy_textstring否正文中完整出现的4–12位数字字符串。
reply_markupobject / 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
sendMessage · group
{
  "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 数字字符串;使用字符串保留前导零。

点击图标只复制验证码。此字段负责显示和复制,验证码生成、有效期与验证由你的业务程序处理;旧客户端仍能阅读正文。

sendMessage · copy_text
{
  "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_markupobject / JSON string否可选的底部按钮对象,也可传JSON字符串。
keyboardarray[]是二维数组,每个内部数组是一行按钮。
resize_keyboardboolean否默认true,紧凑按钮;false使用更大高度。
one_time_keyboardboolean否默认false;true时成功点击发送后收起。
remove_keyboardboolean否true清除菜单,不能与其他键盘选项组合。
sendMessage · reply_markup
{
  "chat_id": "USER_ID",
  "text": "请选择需要的服务:",
  "reply_markup": {
    "keyboard": [
      [
        {
          "text": "语言设置"
        },
        {
          "text": "快递追踪"
        }
      ],
      [
        {
          "text": "通知设置"
        },
        {
          "text": "录入单号"
        }
      ]
    ],
    "resize_keyboard": true,
    "one_time_keyboard": false
  }
}
sendMessage · one_time_keyboard
{
  "chat_id": "USER_ID",
  "text": "请选择需要的服务:",
  "reply_markup": {
    "keyboard": [
      [
        "语言设置",
        "快递追踪"
      ]
    ],
    "one_time_keyboard": true
  }
}
sendMessage · remove_keyboard
{
  "chat_id": "USER_ID",
  "text": "本次操作已结束。",
  "reply_markup": {
    "remove_keyboard": true
  }
}

getUpdates · 接收消息

#
POST/bot-api/<BOT_TOKEN>/getUpdates

这是短轮询:没有消息时立即返回空数组。建议每 1–3 秒请求,网络失败逐步退避;当前没有对外 Webhook 或长轮询。

业务处理成功后,将下一次 offset 设为最后一条 update_id + 1。服务器会确认更早更新;处理失败保留原 offset,并按 update_id 或业务编号防重。

同一机器人的程序应协调消费队列,避免多个进程相互推进 offset。非文字事件可以查看 content_type 和 raw_content。

参数类型必填说明
offsetinteger否非负整数,下一笔希望读取的更新ID,默认0。
limitinteger否每次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。
dateUnix秒时间戳。
chat私聊发信人ID或群ID,以及private/group类型。
from实际发信人ID与显示名称。
text文字内容;非文字事件可能为空。
content_type消息内容类型,101表示文字。
raw_content原始消息内容,供处理图片、文件等接收事件。

完整 Python 程序

#

下方完整示例只用 Python 标准库,支持 /start 显示按钮、/ping 回复及按钮文字处理,并持久化 offset。请从程序的私有运行目录启动。

把示例里的按钮分支替换为自己的业务逻辑。涉及订单、付款等操作时,先完成业务幂等处理,再保存消费进度。

bot.py
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 为必填"
}
状态处理方式
400JSON 格式无效,请检查编码与语法。
401Token无效、已撤销/重新签发或机器人停权;更新密钥后再试。
403拥有者关闭外部访问;到机器人设置重新开启。
404检查 /bot-api/ 路径、Token格式和方法名称。
409同一Bot与用户的菜单正在更新,稍后重试。
413请求体超过1 MiB,缩小请求内容。
422修正参数、验证码格式或键盘结构。
500暂时故障;接收保持offset并退避,发送先核对是否已成功。

限制与客户端兼容

#

本页覆盖当前已实现的公开能力。媒体发送、内联 callback 按钮、联系人/位置请求按钮和对外Webhook尚无公开方法。

底部键盘适用于新版 Web/H5;内嵌旧版 H5 资源的客户端需更新才能显示新控件。

完整请求正文不能超过 1 MiB。按实际负载控制发送与轮询频率;本指南没有承诺固定吞吐量。群组能力标记不替代实际入群、审核与接收权限。