websocket events - yumi-furry/AstralGame-cli GitHub Wiki

WebSocket 事件推送

第三方软件可通过 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 字段)

audit 事件(1.5.1_1 起)

面板端与第三方服务端点都会收到。格式:

{
  "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

第三方服务指令协议(1.4.2_4 起)

连接成功后除被动接收事件外,可发送指令并收到应答。控制类指令(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":"第三方服务控制未开启(请在面板设置 → 第三方服务 中启用)"}

get_room_info 应答 data 结构

{
  "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

相关页面

⚠️ **GitHub.com Fallback** ⚠️