EzEzLife Developers
Tiếng Việt
Bot API
EZLIFE / BOT PLATFORM

Tài liệu Bot API

Từ tạo bot đến tích hợp ứng dụng

Hướng dẫn đầy đủ về BotMother, tin nhắn, sao chép mã xác minh và bàn phím trả lời. Ví dụ dùng giá trị mẫu.

Cập nhật HTTPS · JSON · UTF-8

Tổng quan và tính năng

#

API công khai có getMe, sendMessage, getUpdates. Có thể gửi văn bản riêng hoặc trong nhóm và nhận sự kiện chứa cả nội dung phi văn bản.

Dùng copy_text để hiện biểu tượng sao chép mã; dùng reply_markup để hiển thị nút văn bản dưới ô soạn tin riêng.

Tên miền này lưu tài liệu. API vẫn tại im.ez7.life dưới /bot-api/. Sao chép ID người nhận ở Cài đặt → Chi tiết tài khoản → ID. Dùng message.chat.id nhận được để trả lời; sự kiện nhóm chứa ID nhóm.

Địa chỉ API
https://im.ez7.life/bot-api/<BOT_TOKEN>/<METHOD>

Bắt đầu nhanh

#
  1. Tìm và thêm @BotMother, rồi mở quản lý bot ở đầu cuộc trò chuyện.
  2. Tạo bot, sao chép Token và lưu vào biến môi trường phía máy chủ EZLIFE_BOT_TOKEN.
  3. Gọi getMe để kiểm tra danh tính, sau đó gửi sendMessage với ID người nhận.
  4. Thăm dò getUpdates; chỉ lưu offset tiếp theo sau khi xử lý thành công.
bash
python3 bot.py

Quản lý bằng BotMother

#

Tên hiển thị dài 1–64 ký tự. Tiền tố tên tài khoản trong biểu mẫu dài 3–28 ký tự, bắt đầu bằng chữ cái, chỉ gồm chữ cái, số và gạch dưới; biểu mẫu thêm _bot. Hãy kiểm tra tên còn trống.

Trang quản lý cho phép tìm bot, xem ID, trạng thái, ngày tạo và **** kèm 6 ký tự cuối Token; cấp lại hoặc thu hồi Token. Quản trị viên đặt hạn mức số bot.

LệnhChức năng
/startHiện hướng dẫn quản lý
/newbotBắt đầu tạo bot
/mybotsLiệt kê bot của bạn
/token @usernameCấp lại Token, vô hiệu Token cũ
/revoke @usernameThu hồi Token hiện tại
/cancelHủy quá trình tạo
/helpHiện trợ giúp

Token và bảo mật

#

Token đầy đủ chỉ hiện khi tạo hoặc cấp lại. Đây là mật khẩu của bot: lưu ở máy chủ, không đưa vào trang web, gói ứng dụng, kho công khai hay nhật ký.

Cấp lại làm Token cũ mất hiệu lực ngay; thu hồi chặn truy cập bên ngoài. Phần đuôi che giấu không thể khôi phục Token. Nếu mất, cấp lại và cập nhật chương trình.

Phần đuôi Token cũ được ghi lại khi gọi Bot API hợp lệ tiếp theo. Token API tài khoản trong chi tiết tài khoản dùng cho API tài khoản; Bot API này dùng Token do BotMother cấp.

getMe · Danh tính bot

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

Không cần tham số nghiệp vụ. Trả về ID bot, tên hiển thị, tên tài khoản và cờ hỗ trợ nhóm. Dùng để kiểm tra Token.

Yêu cầu
curl --fail-with-body --request POST \
  "https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getMe" \
  --header 'Content-Type: application/json' --data '{}'
Phản hồi
{
  "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 · Gửi tin nhắn

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

Dùng POST application/json. Luôn gửi chat_id dạng chuỗi: ID người nhận cho tin riêng, ID nhóm và chat_type: "group" cho nhóm.

Văn bản dài 1–5000 ký tự, bỏ khoảng trắng hai đầu. Lưu message_id trả về. Chưa có khóa chống gửi lặp; khi hết thời gian chờ hãy kiểm tra kết quả trước khi gửi lại.

Tham sốKiểuBắt buộcMô tả
chat_idstringCóID người nhận hoặc ID nhóm, luôn là chuỗi.
textstringCóVăn bản 1–5000 ký tự.
chat_typestringKhôngMặc định private; nhóm dùng group, cũng chấp nhận supergroup.
copy_textstringKhôngChuỗi 4–12 chữ số xuất hiện nguyên vẹn trong text.
reply_markupobject / JSON stringKhôngĐối tượng bàn phím hoặc chuỗi JSON, không bắt buộc.
Yêu cầu
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": "Đơn hàng của bạn đã được xử lý."
}
JSON
sendMessage · group
{
  "chat_id": "GROUP_ID",
  "chat_type": "group",
  "text": "Đơn hàng của bạn đã được xử lý."
}
Phản hồi
{
  "ok": true,
  "result": {
    "message_id": "EXAMPLE_MESSAGE_ID",
    "date": 1794297600,
    "chat": {
      "id": "USER_ID",
      "type": "private"
    },
    "text": "Đơn hàng của bạn đã được xử lý."
  }
}

Gửi thành công trả ok: true và result, thêm copy_text cho mã; reply_markup và reply_keyboard_revision cho menu. Nhận trả mảng result, không có sự kiện là result: [].

Sao chép mã xác minh

#

Giữ tên hệ thống, mục đích và thời hạn trong tin nhắn rồi thêm copy_text: chuỗi 4–12 chữ số ASCII xuất hiện nguyên vẹn trong text. Dùng chuỗi để giữ số 0 đầu.

Biểu tượng chỉ sao chép mã. Ứng dụng của bạn tạo mã, kiểm tra và áp dụng thời hạn; trường này chỉ điều khiển hiển thị và sao chép. Ứng dụng cũ vẫn đọc được tin.

sendMessage · copy_text
{
  "chat_id": "USER_ID",
  "text": "Mã đăng nhập mẫu: 003812. Hiệu lực 5 phút.",
  "copy_text": "003812"
}

Bàn phím trả lời

#

Đây là bàn phím văn bản nhiều hàng. Nhấn nút gửi nhãn như tin nhắn thường; xử lý qua message.text của getUpdates.

Có 1–8 hàng, 1–4 nút mỗi hàng, tổng tối đa 24 nút. Nhãn 1–64 ký tự, không rỗng, xuống dòng hay ký tự điều khiển. Nút là chuỗi hoặc đối tượng chỉ chứa text.

Bỏ reply_markup giữ bàn phím cũ; bàn phím mới thay bàn phím cũ. Gửi riêng remove_keyboard: true để xóa bàn phím và nút mở.

Người dùng có thể thu gọn hoặc mở. Bàn phím một lần chỉ ẩn sau khi gửi nút thành công; phiên bản mới tự mở. Trạng thái riêng cho từng bot/người dùng, kiểm tra khoảng 5 giây khi trò chuyện đang hoạt động.

Bàn phím chỉ cho tin riêng; gửi reply_markup vào nhóm trả 422. text vẫn bắt buộc. Dùng true/false JSON, không dùng chuỗi. Thuộc tính bàn phím/nút lạ bị từ chối. reply_markup dạng chuỗi JSON tối đa 16 KiB.

Tham sốKiểuBắt buộcMô tả
reply_markupobject / JSON stringKhôngĐối tượng bàn phím hoặc chuỗi JSON, không bắt buộc.
keyboardarray[]CóMảng hai chiều, mỗi mảng con là một hàng.
resize_keyboardbooleanKhôngMặc định true cho nút gọn; false cho nút cao hơn.
one_time_keyboardbooleanKhôngMặc định false; true ẩn sau khi gửi nút thành công.
remove_keyboardbooleanKhôngtrue xóa bàn phím, không kết hợp tùy chọn khác.
sendMessage · reply_markup
{
  "chat_id": "USER_ID",
  "text": "Chọn dịch vụ:",
  "reply_markup": {
    "keyboard": [
      [
        {
          "text": "Ngôn ngữ"
        },
        {
          "text": "Theo dõi bưu kiện"
        }
      ],
      [
        {
          "text": "Thông báo"
        },
        {
          "text": "Nhập mã vận đơn"
        }
      ]
    ],
    "resize_keyboard": true,
    "one_time_keyboard": false
  }
}
sendMessage · one_time_keyboard
{
  "chat_id": "USER_ID",
  "text": "Chọn dịch vụ:",
  "reply_markup": {
    "keyboard": [
      [
        "Ngôn ngữ",
        "Theo dõi bưu kiện"
      ]
    ],
    "one_time_keyboard": true
  }
}
sendMessage · remove_keyboard
{
  "chat_id": "USER_ID",
  "text": "Thao tác đã hoàn tất.",
  "reply_markup": {
    "remove_keyboard": true
  }
}

getUpdates · Nhận tin nhắn

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

Đây là thăm dò ngắn: không có tin sẽ trả mảng rỗng ngay. Gọi mỗi 1–3 giây, tăng thời gian chờ khi lỗi. Chưa hỗ trợ webhook công khai hoặc thăm dò dài.

Sau xử lý thành công, đặt offset = update_id cuối + 1; các sự kiện trước đó được xác nhận. Nếu lỗi giữ offset cũ, chống lặp bằng update_id hoặc khóa nghiệp vụ.

Phối hợp các tiến trình dùng chung bot để tránh tăng offset của nhau. Xem content_type và raw_content cho sự kiện phi văn bản.

Tham sốKiểuBắt buộcMô tả
offsetintegerKhôngID sự kiện tiếp theo không âm, mặc định 0.
limitintegerKhông1–100 sự kiện mỗi yêu cầu, mặc định 100.
Yêu cầu
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}'
Phản hồi
{
  "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": "Ngôn ngữ",
        "content_type": 101,
        "raw_content": "{\n  \"content\": \"Ngôn ngữ\"\n}"
      }
    }
  ]
}
Tham sốMô tả
update_idID tăng dần để xác nhận xử lý.
message_idID tin nhắn trên nền tảng.
dateThời gian Unix tính bằng giây.
chatID người gửi khi tin riêng hoặc ID nhóm, kèm loại private/group.
fromID và tên hiển thị của người gửi thực tế.
textNội dung văn bản; có thể rỗng khi phi văn bản.
content_typeLoại nội dung; 101 là văn bản.
raw_contentNội dung gốc của sự kiện ảnh, tệp và loại khác.

Chương trình Python đầy đủ

#

Ví dụ đầy đủ chỉ dùng thư viện chuẩn Python, xử lý /start, /ping và nhãn nút, lưu offset. Chạy trong thư mục riêng.

Thay xử lý nút mẫu bằng nghiệp vụ thực tế. Đảm bảo đơn hàng, thanh toán có khả năng chống lặp trước khi lưu tiến độ.

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 = [
  [
    "Ngôn ngữ",
    "Theo dõi bưu kiện"
  ],
  [
    "Thông báo",
    "Nhập mã vận đơn"
  ]
]

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="Chọn dịch vụ:", 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"] = "Thao tác đã hoàn tất."
    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

Nhóm và phạm vi tin nhắn

#

Bot phải tham gia nhóm trước. Mặc định cần chủ sở hữu duyệt; vào cuộc trò chuyện của bot → Thêm → Cài đặt bot → Nhóm đã tham gia để duyệt.

Mặc định chế độ riêng tư nhóm tắt: nhóm bật nhận tin không cần @bot. Tin do chính bot gửi không vào hàng đợi của nó; thông báo vòng đời nhóm không phải tin thường.

Công tắc nhận của từng nhóm kiểm soát sự kiện, chuyển tiếp và tự trả lời. Bot thuộc người dùng chuyển tin nhóm đã bật tới tin riêng của chủ kèm tên nhóm. Quyền truy cập bên ngoài vẫn kiểm soát API và nhận sự kiện.

Cài đặt và trả lời tự động

#

Chỉ chủ sở hữu quản lý duyệt vào nhóm, truy cập bên ngoài, nhận theo nhóm và quy tắc từ khóa. Tắt truy cập bên ngoài giữ Token nhưng từ chối gọi API.

Tối đa 50 quy tắc; từ khóa 1–100 ký tự, câu trả lời 1–2000. So khớp chứa, không phân biệt hoa thường, theo thứ tự; quy tắc bật đầu tiên khớp sẽ trả lời.

Tự trả lời hỗ trợ tin riêng và nhóm bật nhận, tránh chuỗi bot trả lời nhau. Tích hợp AI API đang được dự kiến, chưa có phương thức công khai.

Xử lý lỗi

#

Lỗi đầu vào sớm như JSON sai hoặc nội dung quá lớn dùng errorCode và message. Lỗi API khác dùng error_code và description. Luôn kiểm tra HTTP và ok trước.

Phản hồi
{
  "ok": false,
  "error_code": 422,
  "description": "chat_id 与 1–5000 字的 text 为必填"
}
Trạng tháiCách xử lý
400JSON không hợp lệ. Kiểm tra mã hóa và cú pháp.
401Token sai, đã thu hồi/cấp lại hoặc bot bị khóa. Cập nhật thông tin xác thực.
403Chủ sở hữu tắt truy cập bên ngoài. Bật trong cài đặt bot.
404Kiểm tra đường dẫn /bot-api/, định dạng Token và tên phương thức.
409Bàn phím bot/người dùng đang được cập nhật. Thử lại sau ít giây.
413Nội dung yêu cầu quá 1 MiB. Giảm kích thước.
422Sửa tham số, định dạng mã hoặc cấu trúc bàn phím.
500Lỗi tạm thời. Giữ offset và lùi thời gian gọi; kiểm tra kết quả gửi trước khi thử lại.

Giới hạn và hỗ trợ ứng dụng

#

Hướng dẫn theo API hiện tại. Chưa có API gửi ảnh/tệp, nút callback inline, yêu cầu danh bạ/vị trí hoặc webhook công khai.

Bàn phím cần Web/H5 hiện hành; ứng dụng chứa H5 cũ cần cập nhật để hiển thị điều khiển mới.

Toàn bộ yêu cầu tối đa 1 MiB. Điều chỉnh tần suất theo tải thực tế; hướng dẫn không cam kết thông lượng cố định. Cờ khả năng nhóm không thay thế tư cách thành viên, phê duyệt và quyền nhận.