websocket events - yumi-furry/AstralGame-cli GitHub Wiki
第三方软件可通过 WebSocket 实时获取房间内容、房间码与运行状态,无需轮询。1.4.2_4 起还支持第三方服务发送指令(查询 / 重启 / 关闭 / 开启 / 加入房间)。
| 端点 | 面向对象 | 鉴权方式 |
|---|---|---|
ws://<服务器地址>:<端口>/api/ws |
浏览器面板 | 与 REST API 一致:浏览器自动携带登录 Cookie;程序内调用可使用 Basic Auth(ws://admin:password@host:port/api/ws,或使用带鉴权头的 WebSocket 客户端) |
ws://<服务器地址>:<端口>/api/ws/service?key=<密钥>&name=<服务名称>&type=<服务类型> |
AstrBOT 等第三方插件 / 服务 | 系统随机密钥 ws_key,不走面板账号鉴权 |
连接建立后立即收到一条当前快照(event: "snapshot");之后每次状态变动都会推送。
- 密钥在服务器启动时自动生成(也可在
config.toml的[astral_server]中手动指定ws_key),可在面板「设置 → 第三方服务」查看与复制。 - 手动刷新密钥后,旧密钥立即失效,所有已连接服务会被断开(面板可实时看到在线服务列表)。
-
name/type由连接方上报(如name=MyBot&type=AstrBOT),服务器记录初次连接时间与远端地址。 - 连接成功后先收到一条
event: "hello"(含连接 ID、名称、类型、初次连接时间),随后收到当前快照,之后与面板端一样实时接收事件。
| event | 触发时机 | room 字段 |
|---|---|---|
snapshot |
连接建立后立即推送 | 当前房间或 null
|
server.started |
服务启动完成(重启后自动恢复房间) | 当前房间或 null
|
room.created |
服务器创建新房间 | 新房间 |
room.joined |
服务器加入目标房间 | 目标房间 |
room.left |
服务器离开房间 | null |
audit |
产生操作记录(登录 / 登出 / 修改操作)时推送(1.5.1_1 起) | 无(见下方 entry 字段) |
面板端与第三方服务端点都会收到。格式:
{
"event": "audit",
"timestamp": 1792059565,
"entry": {"ts": 1792059565, "kind": "login", "action": "登录成功", "ip": "192.168.1.10"}
}-
entry.kind:login/login_failed/logout/modify/modify_denied(未授权的非 GET 请求尝试)。 -
entry.ip:操作者 IP(反向代理X-Forwarded-For/X-Real-IP头优先,否则取 TCP 源地址)。 - 第三方 WS 控制指令的
action带「第三方服务指令:」前缀。
{
"event": "room.created",
"timestamp": 1728038400,
"running": true,
"room": {
"is_host": true,
"game_id": "minecraft",
"game_name": "我的世界",
"network_name": "ag_ab12cd34ef",
"network_secret": "xxxxxxxxxxxxxxxx",
"display_name": "我的世界",
"short_code": "ABC123",
"offline_invite": "AG1.xxxxx…",
"peers": [{ "uri": "tcp://game.example.com:11010" }]
},
"share_url": "https://next.astral.fan/j?c=ABC123",
"node": {
"my_ipv4": "10.126.0.2",
"total_nodes": 3,
"error": null,
"server_version": "1.5.1_1",
"easytier_version": "1.x.x"
}
}字段说明:
-
event:事件类型(见上表)。 -
timestamp:事件发生时间(Unix 秒)。 -
running:节点实例是否运行中。 -
room:当前房间对象,离开房间后为null。包含is_host、game_id、game_name、network_name、network_secret、display_name、short_code、offline_invite(AG1.离线邀请串)、peers(中继服务器列表)。 -
share_url:房间邀请链接。 -
node:节点运行信息,含my_ipv4(本机虚拟 IP)、total_nodes(在线节点数)、error、server_version、easytier_version。
浏览器 / 原生 JavaScript
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
const ws = new WebSocket(`${proto}://${location.host}/api/ws`);
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.room) {
console.log('房间码:', msg.room.short_code);
console.log('邀请链接:', msg.share_url);
}
};Node.js(使用 ws 库,Basic Auth)
const WebSocket = require('ws');
const ws = new WebSocket('ws://192.168.1.10:1786/api/ws', {
headers: { Authorization: 'Basic ' + Buffer.from('admin:password').toString('base64') }
});
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
console.log(`[${msg.event}]`, msg.room?.short_code ?? '无房间');
});Python(websockets 库)
import asyncio, json, base64
import websockets
async def listen():
auth = base64.b64encode(b"admin:password").decode()
async with websockets.connect(
"ws://192.168.1.10:1786/api/ws",
additional_headers={"Authorization": f"Basic {auth}"}
) as ws:
async for raw in ws:
msg = json.loads(raw)
room = msg.get("room")
if room:
print(msg["event"], room.get("short_code"), msg.get("share_url"))
asyncio.run(listen())如果只需要获取一次当前房间,也可以直接调用 REST:
curl -u admin:password http://<服务器>:1786/api/room/current连接成功后除被动接收事件外,可发送指令并收到应答。控制类指令(restart / close / open / join)需服务端开启 service_control_enabled(面板「设置 → 第三方服务 → 允许服务端控制」,默认关闭);只读指令 get_room_info 不受开关限制。
发送 JSON 文本帧,id 为可选关联号(应答会原样回显):
| action | 说明 | 额外字段 |
|---|---|---|
get_room_info |
查询房间类型、成员列表(含延迟)、运行时长 | 无 |
restart |
用最近一次配置重启实例 | 无 |
close |
关闭房间(停止实例并清空当前房间) | 无 |
open |
开启房间:带 game_id / game_name 走建房流程;不带则仅重放上次配置启动 |
game_id、game_name、display_name(可选) |
join |
加入房间 |
input(短码 / 完整链接 / AG1. 离线串) |
{"id":"r1","action":"get_room_info"}
{"id":"r4","action":"open","game_id":"minecraft","game_name":"我的世界"}
{"id":"r6","action":"join","input":"ABC123"}所有指令统一返回 event: "result",含 id(回显请求关联号)、ok(成功与否)、error(失败原因,成功为空)与可选 data:
{"event":"result","id":"r4","ok":true,"data":{"instance_id":"...","room":{...},"share_url":"..."}}
{"event":"result","id":"r6","ok":false,"error":"第三方服务控制未开启(请在面板设置 → 第三方服务 中启用)"}{
"running": true,
"uptime_secs": 3600,
"room_type": {"game_id":"minecraft","game_name":"我的世界","display_name":"...","is_host":true},
"room": { "...同事件消息格式..." },
"share_url": "https://next.astral.fan/j?c=ABC123",
"node": {"my_ipv4":"10.126.0.2","total_nodes":3,"error":null},
"members": [{"peer_id":0,"hostname":"...","ipv4":"...","latency_ms":0,"loss_rate":0,"conn_type":"Local",...}]
}-
uptime_secs:房间运行时长(实例已运行秒数;未运行 / 从未启动为null)。 -
room_type:房间类型(游戏 ID / 名称 / 展示名 / 是否房主)。 -
members:完整节点列表,含 EasyTier 实测延迟latency_ms与丢包率loss_rate。
Node.js 示例
const WebSocket = require('ws');
const ws = new WebSocket('ws://192.168.1.10:1786/api/ws/service?key=你的密钥&name=MyBot&type=AstrBOT');
ws.on('open', () => ws.send(JSON.stringify({ id: 'r1', action: 'get_room_info' })));
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg.event === 'result') {
console.log(msg.id, msg.ok ? JSON.stringify(msg.data) : msg.error);
}
});- 控制类指令(
restart/close/open/join)在执行前统一校验service_control_enabled,未开启时返回ok: false与提示错误。 - 密钥刷新(
rotate)会使所有已连接服务被踢下线,旧密钥立即失效。
以下管理接口需面板 Cookie / Basic Auth:
# 查看当前密钥
curl -u admin:password http://<服务器>:1786/api/ws/key
# 刷新密钥(旧密钥立即失效,已连接服务被断开)
curl -u admin:password -X POST http://<服务器>:1786/api/ws/key/rotate
# 查看在线服务列表
curl -u admin:password http://<服务器>:1786/api/ws/services