- genSecret: drop SeaReport- prefix, plain 32-char random hex - users.source: no default anywhere (register/admin/external), ADD COLUMN migration now VARCHAR DEFAULT '' (was ENUM netease default) - legacy upgrade path kept: ENUM->VARCHAR MODIFY + user_identities MODIFY + sources seeded netease/skin idempotently - docs updated (credential format, no prefix)
9.6 KiB
9.6 KiB
外部 API 文档(机器人 / 第三方系统)
适用对象:QQ 官方机器人、游戏服务器插件、统计面板等无法做网页登录的外部系统。
- Base URL:
https://report.sea-studio.top/api/external - 数据格式:JSON(
Content-Type: application/json) - 本文档所有接口都需要客户端凭据(见下)
一、鉴权流程(ID + Secret 换取 SESSION)
在站点后台 →「外部API」页面创建客户端,获得一对凭据:
Client ID: 1829473056482917 # 16 位纯数字, 随机生成
Secret: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 # 32 位全随机字符串
⚠️ Secret 只在创建时显示一次,请立即保存。支持创建多个客户端、单独停用/删除,互不影响。
第一步:换取 SESSION(唯一使用 ID+Secret 的接口)
POST /api/external/auth/session
请求头:
| 请求头 | 值 |
|---|---|
x-api-client-id |
你的 Client ID |
x-api-secret |
你的 Secret |
成功响应 200:
{
"session_token": "5f8a2b3c...96位十六进制",
"expires_in": 86400,
"client_id": "1829473056482917",
"message": "SESSION 有效期 24 小时, 请用 Authorization: Bearer <session> 访问其余接口"
}
第二步:后续请求携带 SESSION
所有其余接口(工单/封禁/统计等)使用:
| 请求头 | 值 |
|---|---|
Authorization |
Bearer <session_token> |
- SESSION 有效期 24 小时;过期后重新调用第一步换取。
- 同一客户端重新换取时,旧 SESSION 立即失效(单会话)。
- 客户端被停用 → 已发 SESSION 立即失效(401)。
- 无 SESSION / SESSION 无效或过期 → 一律
401。
二、快速开始(Python / Node 示例)
Python
import requests
BASE = "https://你的域名/api/external"
# 1) 用 ID + Secret 换取 SESSION
r = requests.post(f"{BASE}/auth/session", headers={
"x-api-client-id": "1829473056482917",
"x-api-secret": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
})
session = r.json()["session_token"]
HEADERS = {"Authorization": f"Bearer {session}", "Content-Type": "application/json"}
# 2) 提交举报工单(带 SESSION)
r = requests.post(f"{BASE}/tickets", headers=HEADERS, json={
"type": "report",
"title": "恶意破坏",
"reporter_game_name": "Steve",
"target_game_name": "Alex",
"reason": "刷屏+破坏他人建筑",
"description": "多次警告无效",
"server": "survival", # 可选: 子服别名
})
print(r.json()) # {"id": 123, "tracking_token": "..."}
Node.js
const BASE = 'https://你的域名/api/external';
// 1) 换取 SESSION
const authRes = await fetch(`${BASE}/auth/session`, {
method: 'POST',
headers: { 'x-api-client-id': '1829473056482917', 'x-api-secret': 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6' },
});
const { session_token } = await authRes.json();
const H = { Authorization: `Bearer ${session_token}`, 'Content-Type': 'application/json' };
// 2) 查询工单进度(从提交到结束全程可查)
const res = await fetch(`${BASE}/tickets/track?token=你的tracking_token`, { headers: H });
console.log(await res.json());
三、子服务器定位(server 参数)
子服可在后台「服务器管理」配置外部别名(alias)。所有带 server 的接口支持三种写法:
| 写法 | 示例 | 说明 |
|---|---|---|
| 别名(推荐) | survival |
服务器改名不影响 |
| 分组/子服 | 网易服务器/生存服 |
URL 编码或 JSON 直接传 |
| 子服名 | 生存服 |
同名时取第一个 |
查询服务器列表(含别名):GET /api/external/servers
四、接口清单
0. 换取 SESSION(见第一节, 其余接口均需 Bearer SESSION)
POST /api/external/auth/session
1. 提交工单
POST /api/external/tickets
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | string | ✅ | report / suggestion / appeal |
| title | string | ✅ | 标题 |
| reporter_game_name | string | ✅ | 提交人游戏名 |
| reporter_game_uid | string | 否 | 提交人 UID / UUID(选填) |
| target_game_name | string | 条件 | 被举报人游戏名(report 建议填) |
| target_game_uid | string | 条件 | 被举报人 UID |
| reason | string | 条件 | 举报原因(report/appeal 必填) |
| description | string | 条件 | 详情(suggestion/appeal 必填) |
| server | string | 否 | 子服(别名/分组/子服名) |
限制:同一提交人待处理工单最多 5 个。
成功响应 201:
{
"id": 123,
"tracking_token": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"server": "生存服"
}
tracking_token是查询进度的唯一凭证,请妥善保存。
2. 查询工单进度(提交 → 处理 → 结束)
GET /api/external/tickets/track?token=<tracking_token>
成功响应:
{
"id": 123,
"type": "report",
"title": "恶意破坏",
"status": "processing",
"priority": "medium",
"reporter_game_name": "Steve",
"target_game_name": "Alex",
"reason": "刷屏+破坏他人建筑",
"assigned_to": "管理员",
"claim_note": "已受理",
"server_name": "生存服",
"created_at": "2026-08-19T10:00:00.000Z",
"updated_at": "2026-08-19T11:30:00.000Z",
"responses": [
{ "content": "游戏内提交...", "is_staff": 0, "created_at": "..." },
{ "content": "正在核实,请耐心等待", "is_staff": 1, "created_at": "..." }
]
}
工单状态流转:
pending(待处理) → processing(处理中) → resolved(已解决)
↘ awaiting_info(待补充信息) → processing / rejected(驳回)
↘ closed(关闭) appealed(申诉中) → resolved / rejected
3. 工单列表
GET /api/external/all-tickets?type=&status=&server=&page=&limit=
| 参数 | 说明 |
|---|---|
| type | report / suggestion / appeal |
| status | pending / processing / awaiting_info / appealing / resolved / rejected / closed |
| server | 子服(别名/分组/子服名) |
| page / limit | 分页,limit ≤ 200,默认 page=1 limit=50 |
4. 工单详情(含回复与附件)
GET /api/external/all-tickets/:id
5. 拉取封禁列表
GET /api/external/bans?server=&status=&player=&page=&limit=
| 参数 | 说明 |
|---|---|
| server | 子服过滤(可选) |
| status | active / expired / appealed / lifted(默认不滤) |
| player | 按玩家名或 UID 精确匹配 |
| page / limit | 分页,limit ≤ 500 |
成功响应:
[
{
"id": 7,
"player_name": "Alex",
"player_uid": "SKIN_UUID_002",
"source": "netease",
"reason": "使用外挂",
"type": "ban",
"duration": "7d",
"server_name": "生存服",
"status": "active",
"created_at": "2026-08-19T10:00:00.000Z",
"expires_at": "2026-08-26T10:00:00.000Z"
}
]
6. 新增封禁
POST /api/external/bans
{
"player_name": "Alex",
"player_uid": "SKIN_UUID_002",
"source": "netease",
"reason": "使用外挂",
"type": "ban",
"duration": "7d",
"expires_at": "2026-08-26T10:00:00.000Z",
"server": "survival",
"status": "active"
}
| 字段 | 必填 | 说明 |
|---|---|---|
| player_name | ✅ | 玩家名 |
| type | 否 | ban / mute / warn / other,默认 ban |
| status | 否 | active / expired / appealed / lifted,默认 active |
| expires_at | 否 | ISO 时间或 null(永久) |
响应 201: { "id": 7 }
7. 被举报人统计
GET /api/external/reported-players?server=
响应:
[
{
"target_game_name": "Alex",
"target_game_uid": "SKIN_UUID_002",
"total": 5,
"active": 2,
"resolved": 3,
"last_report": "2026-08-19T10:00:00.000Z"
}
]
8. 服务器列表(含别名)
GET /api/external/servers
响应:
[
{ "group_name": "网易服务器", "server_name": "生存服", "alias": "survival" },
{ "group_name": "网易服务器", "server_name": "空岛服", "alias": "skyblock" }
]
9. 工单统计
GET /api/external/stats?server=
响应:
{
"total": 128,
"byType": [ { "type": "report", "c": 95 }, { "type": "suggestion", "c": 20 } ],
"byStatus": [ { "status": "pending", "c": 12 }, { "status": "resolved", "c": 100 } ]
}
五、错误码
| HTTP | 说明 |
|---|---|
| 400 | 参数错误(缺少必填字段 / 类型不正确 / 待处理工单超上限) |
| 401 | 鉴权失败(凭据错误或客户端已停用) |
| 404 | 工单 / 封禁不存在 |
| 500 | 服务器内部错误 |
错误响应统一为 { "error": "描述信息" }。
六、鉴权失败排查
- 401 缺少凭据:请求必须携带
x-api-client-id与x-api-secret两个请求头。 - 401 客户端鉴权失败:检查 Client ID / Secret 是否与创建时一致;secret 是否完整无换行空格;客户端是否被停用。
- 客户端被停用:后台「外部API」→ 启用。
- 需要多个客户端:后台可创建多个,分别用于机器人 / 插件 / 统计面板,互不影响;删除即立即失效。
七、典型场景
QQ 机器人:玩家提交举报
- 玩家在 QQ 群发送举报信息 → 机器人调
POST /tickets提交(reporter 为玩家 QQ 绑定的游戏名/UID) - 机器人把返回的
tracking_token发给玩家 → 玩家随时发「查进度」→ 机器人调GET /tickets/track?token=... - 处理结果(状态变化)可通过工单详情轮询,或后台「通知配置」设置 Webhook 推送
服务器插件:同步封禁
- 插件启动时调
GET /bans?status=active&server=生存服拉取本服封禁 - 服内封禁新玩家时调
POST /bans同步到系统 - 定期轮询
GET /bans?status=active保持同步
协议版本 v1 · 服务端接口以部署版本为准