Skip to content

小游戏(Games)

小游戏让 Bot 在会话中发出一种特殊的「游戏卡片」。用户点击卡片上的按钮后,客户端会在内置 WebView 中打开一个 HTML5 游戏;游戏结束时通过 Bot API 回传分数,并把高分榜直接展示在卡片上。

小游戏基于 SafeW 的 HTML5 Games 协议,与 Mini App 是两套独立的能力:游戏运行在普通网页中,不依赖 Mini App 的全屏、安全区、主按钮等 WebApp 接口。

准备工作

  1. 通过 @BotFather 的 /newbot 创建一个 Bot 并获取 Token(详见快速开始)。
  2. 通过 @BotFather 的 /newgame 为该 Bot 注册一个游戏,得到游戏的 short_name(见下文)。
  3. 把游戏页面部署到一个可公网访问的 HTTPS 地址。

创建游戏(/newgame)

向 @BotFather 发送 /newgame,按提示依次录入:

步骤说明
选择 Bot选择由哪个 Bot 提供该游戏
标题 Title游戏名称
描述 Description游戏的简短介绍
封面图 Photo游戏卡片封面,建议 640×360
演示动图 GIF可选,发送 /empty 跳过
游戏地址 URL游戏的 HTTPS 地址
短名 short_name游戏的唯一标识,3–30 个字符,仅含 a-z A-Z 0-9 _

创建成功后,short_name 即可作为 sendGamegame_short_name 使用。同一个 Bot 下 short_name 必须唯一。

工作流程

1. 发送游戏卡片
   Bot ── sendGame{chat_id, game_short_name} ──▶ 会话中出现游戏卡片

2. 用户打开游戏
   用户点击卡片上的游戏按钮
   → Bot 收到一条 callback_query(携带 game_short_name,无 data)
   → Bot 用 answerCallbackQuery{url} 返回游戏地址
   → 客户端在 WebView 中打开该地址

3. 回传分数
   游戏内 ── setGameScore{user_id, score, …} ──▶ 服务端记录分数
   → 默认自动编辑原卡片,刷新高分榜

4. 查看高分榜
   Bot/客户端 ── getGameHighScores{user_id, …} ──▶ 返回高分榜

游戏按钮(callback_game)

游戏卡片上的按钮是一个特殊的内联键盘按钮:它带有 callback_game 字段,点击后客户端会发起游戏回调(而不是普通的 callback_data 回调)。

  • 调用 sendGame 时,如果不传 reply_markup,服务端会自动追加一个「播放 + 游戏标题」按钮。
  • 如果自定义 reply_markup第一行的第一个按钮必须callback_game 按钮,用于启动游戏。
json
{
  "inline_keyboard": [
    [{ "text": "▶️ 开始游戏", "callback_game": {} }],
    [{ "text": "玩法说明", "url": "https://example.com/how-to-play" }]
  ]
}

callback_game 当前是一个占位对象,无需填写任何字段。

打开游戏(处理游戏回调)

当用户点击游戏按钮时,Bot 会通过 getUpdates 或 Webhook 收到一条 callback_query。游戏回调的特征是 game_short_name 非空、且不含 data 字段,据此可与普通的 callback_data 回调区分。

CallbackQuery 的主要字段:

字段类型描述
idString回调查询的唯一标识符,应答时回传
fromUser触发回调的用户
messageMessage携带游戏按钮的消息(消息载体时存在)
inline_message_idString内联消息标识符(内联载体时存在)
dataString普通回调数据;游戏回调时不含此字段
game_short_nameString触发回调的游戏短名;用于识别游戏回调

Bot 收到后,应调用 answerCallbackQuery 并在 url 中返回游戏的打开地址:

bash
curl -X POST "https://api.safew.bot/<token>/answerCallbackQuery" \
  -H "Content-Type: application/json" \
  -d '{
    "callback_query_id": "<callback_query_id>",
    "url": "https://example.com/game?session=<signed_token>"
  }'

建议在 url 中附带一次性、带签名和有效期的会话参数(标识 user_id、所在会话/消息、过期时间),供游戏前端在调用 setGameScore 时定位实例并防止伪造分数。

平台也可由服务端直接下发已签名的游戏地址(无需 Bot 往返)。因此 answerCallbackQuery{url} 是标准且通用的接入方式,但并非唯一方式。

提交与展示分数

  • 游戏结束后,用 setGameScore 提交分数。默认情况下,新分数必须严格大于该用户的现有分数,否则会被拒绝;可用 force=true 强制覆盖。
  • 提交成功后默认会自动编辑原游戏卡片,刷新其上的高分榜;传 disable_edit_message=true 可关闭此行为。
  • getGameHighScores 拉取高分榜。

数据类型

Game

游戏卡片中携带的游戏对象。

字段类型描述
titleString游戏标题
descriptionString游戏描述
photoPhotoSize[]游戏封面图,不同尺寸的数组
textString可选,游戏卡片中的简介文本,0–4096 个字符
text_entitiesMessageEntity[]可选,简介文本中的特殊实体(如用户名、URL 等)
animationAnimation可选,展示在游戏卡片中的演示动图

GameHighScore

高分榜中的一条记录。

字段类型描述
positionInteger名次(从 1 开始)
userUser该名次对应的用户
scoreInteger该用户的分数

CallbackGame

游戏按钮的占位对象,当前不包含任何字段。

方法一览

方法描述HTTP 方法
sendGame发送游戏卡片POST
setGameScore提交/更新用户分数POST
getGameHighScores获取游戏高分榜POST

相关方法:answerCallbackQuery(处理游戏回调、返回游戏地址)。