# EzLife · Bot API 使用文件

https://im-api.ez7.life/zh-hant/

更新於: 2026-11-10

完整介紹 BotMother、訊息介面、驗證碼複製及底部按鈕。範例均使用佔位值。

## 概覽與功能

目前公開方法為 `getMe`、`sendMessage`、`getUpdates`。可傳送私聊、群聊文字，接收事件可包含非文字內容。

使用 `copy_text` 顯示驗證碼複製圖示；使用 `reply_markup` 下發私聊輸入區下方的文字按鈕。

此網域提供文件；實際 API 仍位於 im.ez7.life 的 /bot-api/ 路徑。收件者可從設定 → 帳號詳情 → ID 複製；收到訊息的 message.chat.id 可直接用於回覆，群聊時是群組 ID。

```text
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、狀態、建立時間與 `****` 加尾部六碼，並重新簽發或撤銷 Token。機器人數量上限由管理員設定。

| 指令 | 用途 |
| --- | --- |
| `/start` | 顯示管理說明 |
| `/newbot` | 開始建立機器人 |
| `/mybots` | 列出自己的機器人 |
| `/token @username` | 重新簽發 Token，舊值失效 |
| `/revoke @username` | 撤銷目前 Token |
| `/cancel` | 取消建立流程 |
| `/help` | 顯示說明 |

## Token 與安全

完整 Token 僅於建立或重新簽發時顯示。請當作機器人密碼，保存在後端，勿放在網站、APK、公開儲存庫或日誌。

重新簽發立即使舊 Token 失效；撤銷後停用外部呼叫。遮罩尾碼不能還原 Token，遺失時請重新簽發並更新程式。

舊 Token 的尾碼會在下次合法 Bot API 呼叫時記錄。帳號詳情中的一般 API Token 用於帳號介面；本 Bot API 使用 BotMother 發出的機器人 Token。

## getMe · 機器人身分

`POST https://im.ez7.life/bot-api/<BOT_TOKEN>/getMe`

不需要業務參數。回傳機器人 ID、顯示名稱、帳號與群組能力標記，可用來確認 Token。

```bash
curl --fail-with-body --request POST \
  "https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getMe" \
  --header 'Content-Type: application/json' --data '{}'
```

```json
{
  "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 https://im.ez7.life/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 字串。 |

```bash
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
```

```json
{
  "chat_id": "GROUP_ID",
  "chat_type": "group",
  "text": "你的訂單已處理。"
}
```

```json
{
  "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 數字字串，字串可保留開頭的零。

圖示只複製驗證碼。產生、有效期限與驗證由你的系統負責；此欄位只控制顯示與複製。舊用戶端仍可閱讀文字。

```json
{
  "chat_id": "USER_ID",
  "text": "範例登入驗證碼：003812，5 分鐘內有效。",
  "copy_text": "003812"
}
```

## 底部按鈕鍵盤

這是多列文字回覆鍵盤。點擊按鈕會傳送其文字，從 getUpdates 的 message.text 處理業務。

1–8 列、每列 1–4 個、總計最多 24 個按鈕。文字長度 1–64 字元，不可空白、換行或含控制字元。按鈕可為字串或只有 text 的物件。

省略 reply_markup 會保留原鍵盤；新鍵盤會取代舊鍵盤。移除請單獨傳入 remove_keyboard: true。

使用者可收合及展開；一次性鍵盤在按鈕文字傳送成功後收合。新版鍵盤會再展開。狀態按機器人與使用者獨立保存，開啟對話時約每五秒檢查。

鍵盤僅支援機器人私聊，群聊帶 reply_markup 會回傳 422。正文 text 仍必填。布林值須為 JSON true/false，不可傳字串；未知鍵盤或按鈕屬性會拒絕。字串形式的 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 清除鍵盤，不可與其他鍵盤欄位並用。 |

```json
{
  "chat_id": "USER_ID",
  "text": "請選擇服務：",
  "reply_markup": {
    "keyboard": [
      [
        {
          "text": "語言設定"
        },
        {
          "text": "快遞追蹤"
        }
      ],
      [
        {
          "text": "通知設定"
        },
        {
          "text": "輸入單號"
        }
      ]
    ],
    "resize_keyboard": true,
    "one_time_keyboard": false
  }
}
```

```json
{
  "chat_id": "USER_ID",
  "text": "請選擇服務：",
  "reply_markup": {
    "keyboard": [
      [
        "語言設定",
        "快遞追蹤"
      ]
    ],
    "one_time_keyboard": true
  }
}
```

```json
{
  "chat_id": "USER_ID",
  "text": "操作已完成。",
  "reply_markup": {
    "remove_keyboard": true
  }
}
```

## getUpdates · 接收訊息

`POST https://im.ez7.life/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。 |

```bash
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}'
```

```json
{
  "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` | 遞增事件 ID，用於確認消費。 |
| `message_id` | 平台訊息 ID。 |
| `date` | Unix 時間戳記，單位為秒。 |
| `chat` | 私聊為發送者 ID；群聊為群組 ID，含 private/group 類型。 |
| `from` | 實際發送者 ID 與顯示名稱。 |
| `text` | 文字內容；非文字事件可能為空。 |
| `content_type` | 內容類型；101 為文字。 |
| `raw_content` | 接收圖片、檔案等事件的原始內容。 |

## 完整 Python 程式

完整範例僅用 Python 標準函式庫，處理 /start、/ping、按鈕文字並保存 offset。請在私有工作目錄執行。

請將範例按鈕處理替換成實際業務；訂單、付款等操作應可去重，再保存消費進度。

```python
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
```

## 群組與訊息範圍

機器人須先加入目標群組。預設需擁有人核准，於機器人對話的更多 → 機器人設定 → 已加入群組檢查邀請。

預設關閉群組隱私模式，啟用接收的群組不必 @ 機器人。自身訊息不進自己的接收佇列；群組生命週期通知不視為一般訊息。

各群接收開關控制事件、自動回覆及轉發。使用者持有的機器人會將啟用群組訊息附群名轉到擁有人私聊；外部存取開關仍控制 API 與事件交付。

## 機器人設定與自動回覆

僅擁有人可設定入群核准、外部存取、各群接收及關鍵字自動回覆。關閉外部存取保留 Token，但會拒絕程式呼叫。

最多 50 條規則，關鍵字 1–100 字元、回覆 1–2000 字元。依序比對、不分大小寫的包含關係，第一條啟用且符合的規則回覆。

自動回覆適用私聊與啟用接收的群組，會避免機器人互相觸發。AI API 串接目前屬規劃功能，尚無公開方法。

## 錯誤處理

JSON 解析失敗或本文過大等前期錯誤使用 errorCode 與 message；其他 API 錯誤使用 error_code 與 description。先檢查 HTTP 狀態與 ok。

```json
{
  "ok": false,
  "error_code": 422,
  "description": "chat_id 与 1–5000 字的 text 为必填"
}
```

| 狀態 | 處理方式 |
| --- | --- |
| 400 | JSON 格式無效，請檢查編碼與語法。 |
| 401 | Token 無效、撤銷、重新簽發或機器人停權，請更新憑證。 |
| 403 | 擁有人停用外部存取，請至機器人設定開啟。 |
| 404 | 檢查 /bot-api/ 路徑、Token 格式與方法名稱。 |
| 409 | 同一機器人及使用者的鍵盤正在更新，稍後重試。 |
| 413 | 請求本文超過 1 MiB，請縮小。 |
| 422 | 修正參數、驗證碼格式或鍵盤結構。 |
| 500 | 暫時故障，保留接收 offset 並退避重試；傳送先確認結果。 |

## 限制與用戶端相容性

文件以目前公開介面為準。尚未公開傳送媒體、inline callback 按鈕、聯絡人或位置請求按鈕，以及 webhook。

底部鍵盤適用新版 Web/H5；內嵌舊 H5 資源的用戶端需更新才能顯示新控制項。

完整請求本文不能超過 1 MiB。依負載控制傳送及輪詢頻率；本指南不承諾固定吞吐量。群組能力標記不取代實際入群、審核與接收權限。

