平安志 · 玩家开发文档 · API v1
把你的平安京,放进自己的展柜
这份文档面向首页组件作者与本机工具开发者。个人游戏数据来自读取快照,保存在玩家电脑;NAS 仅分发公共公告和活动资料。
本机 HTTP 接口
平安志运行时自动监听
127.0.0.1,端口可能随启动变化。打开「帮助/设置 → 设置 →
本机数据接口」复制地址和令牌。请求使用
Authorization: Bearer <token>;重置令牌会立即使旧令牌失效。首版不开放局域网或互联网访问。只读接口不提供会员购买、兑换、密钥、游戏登录凭据或修改游戏的能力。
curl -H "Authorization: Bearer YOUR_LOCAL_TOKEN" \ "http://127.0.0.1:YOUR_PORT/v1/guild"
| 接口 | 内容 | 权限 |
|---|---|---|
GET /v1/context |
档案与权限 | 基础访问 |
GET /v1/tasks |
原始任务与手动记录 | 任务收费字段过滤 |
GET /v1/realm-cards |
结界卡状态 | realm_cards |
GET /v1/guild |
寮概况与成员 | 基础访问 |
GET /v1/profile-summary |
档案摘要 | 基础访问 |
GET /v1/activities |
本机活动查询 | 基础访问 |
GET /v1/announcements |
本机公告查询 | 基础访问 |
免费用户可调用基础接口;每周任务和结界卡继续使用已有会员权限。HTTP 令牌不替代会员授权。每令牌每分钟 120 次;超限返回 429 和 Retry-After。只有受信任的应用来源允许浏览器跨域访问;其他本机程序可使用 HTTP 客户端。不要把本机令牌放进插件包或分享出去。
{"apiVersion":1,"profileId":null,"capturedAt":null,"fetchedAt":"2026-10-10T00:00:00Z","data":null}
capturedAt 是数据快照时间;fetchedAt
是本次查询时间。空值表示未知,不是零。tasks 返回
records(采集事实)与
manual(手动勾选);二者不能混为游戏已完成状态。manual
按档案、每日日期、周一日期隔离,过期勾选不沿用。realm-cards 返回
own/foster;startedAt、endsAt 是 Unix 秒,预计收益可能为
null。到期仅表示“预计已到期,待读取确认”。寮成员 offlineAt、joinedAt、createTime
也是 Unix 秒;offlineAt=0 表示快照采集时在线,不表示现在在线。
activities/announcements 支持 server=live|test、q、offset、limit(默认 50,最大
100),返回 version、total、items。HTTP 错误格式为
{"error":{"code":"..."}}:UNAUTHORIZED、MEMBERSHIP_REQUIRED、SCOPE_DENIED(SDK)、CONTEXT_CHANGED、RATE_LIMITED、DATA_UNAVAILABLE。409
时清空旧档案结果再重试;429 按 Retry-After 等待。
响应中的
snapshotId 标识当前档案版本;切换档案或版本后丢弃旧响应。tasks 的
states.daily/weekly 分别为
missing(缺少读取)、unknown(时间未知)、current(当前周期)、stale(过期),收费字段不可读时为
restricted。realm-cards 的 own/foster 各自增加
status:unknown、inactive、active、expired;expired
不继续返回旧收益。接口不把手动勾选变成读取事实。每日与每周手动记录沿用软件的本地日历周期。
组件 JavaScript SDK
SDK 由平安志自动注入,无需依赖、构建工具或令牌。data.get 返回与本机 HTTP 相同的响应;宿主检查组件声明权限并代理 HTTP 查询。每组件每分钟最多 30 次宿主调用;倒计时应使用本地时钟,避免每秒取数。
async function render() {
const response = await yys.data.get('guild');
document.getElementById('title').textContent =
response.data?.name ?? '暂无寮信息';
}
const unsubscribe = yys.data.subscribe(render);
render();
// 配置:await yys.config.get()
// 首页皮肤:await yys.theme.get() → { skin: 'scroll' | 'moon' | 'barrier' }
// 查询:await yys.data.get('activities', {server:'live', q:'关键词', limit:50})
subscribe 在游戏档案、数据、配置、皮肤变化时触发,返回取消订阅函数。切换档案过程中旧响应会被拒绝,调用方应处理 CONTEXT_CHANGED。会员失效后停止自定义组件,会员皮肤回退至默认亮色,保存的卡片装修仍生效。组件使用沙箱 iframe;不直接调用 Tauri,不访问主窗口 DOM,不获取主令牌。样式局限于卡片内,禁止外部依赖下载与直接网络请求。用户数据请通过 textContent 绘制。
导入代码组件
会员进入「工作台 → 装修我的庭院 → 导入代码组件」,选择 .yys-widget.json,查看声明的数据范围后导入。可启用、禁用、导出和删除。组件包最多 4 MiB;HTML/CSS/JS 每字段最多 1 MiB。HTML 填写 body 片段,JS 可直接使用 yys SDK,CSS 背景建议透明。
{
"id": "guild-ranking",
"name": "寮贡献排行",
"permissions": [
"guild"
],
"configFields": [
{
"key": "limit",
"label": "显示人数",
"type": "number",
"default": 5
}
],
"html": "<h3 id=\"title\">寮贡献排行</h3><ul id=\"ranking\"></ul><small id=\"stamp\"></small>",
"js": "const root=document.getElementById('ranking');async function render(){try{const config=await yys.config.get();const result=await yys.data.get('guild');root.replaceChildren();if(!result.data){document.getElementById('title').textContent='暂无寮信息';return;}document.getElementById('title').textContent=result.data.name;const count=Math.max(1,Math.min(20,Number(config.limit)||5));for(const [index,member] of [...result.data.members].sort((a,b)=>b.weeklyHonor-a.weeklyHonor).slice(0,count).entries()){const row=document.createElement('li');row.textContent=`${index+1}. ${member.name} · ${member.weeklyHonor} 寮勋`;root.append(row);}document.getElementById('stamp').textContent=`快照 ${result.capturedAt??'未知'},非实时状态`;}catch(error){root.textContent=error.message;}}yys.data.subscribe(render);render();",
"apiVersion": 1,
"version": "1.0.0",
"css": "body{margin:0;padding:18px;background:transparent;color:#8d9ea6;font:12px/1.7 sans-serif}h3{color:#a98b67;margin:0 0 12px}ul{padding:0;list-style:none}li{padding:7px 0;border-bottom:1px solid #8d9ea633}small{opacity:.7}strong{color:#a98b67}"
}
id 仅允许英文字母、数字、下划线和连字符,长度 1–100;API 版本为 1。permissions 只能是表中接口的资源名。configFields 支持 text、number、boolean,用户值通过 config.get 读取。相同 id 导入会更新组件;不同布局可保存为 .yys-layout.json。
字段与类型参考
档案摘要沿用 snake_case,其余为 camelCase。不存在、null 或 available=false 表示未知。结界卡和寮时间为 Unix 秒;活动时间为带时区的 ISO 8601。枚举及收费字段见下表。
Error
{
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
}
},
"required": [
"code"
]
}
},
"required": [
"error"
]
}
Envelope
{
"type": "object",
"properties": {
"apiVersion": {
"const": 1
},
"profileId": {
"type": [
"string",
"null"
]
},
"capturedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"fetchedAt": {
"type": "string",
"format": "date-time"
},
"data": {},
"snapshotId": {
"type": [
"string",
"null"
]
}
},
"required": [
"apiVersion",
"profileId",
"capturedAt",
"fetchedAt",
"data",
"snapshotId"
]
}
Context
{
"type": "object",
"properties": {
"profileId": {
"type": [
"string",
"null"
]
},
"member": {
"type": "boolean"
},
"permissions": {
"type": "object",
"properties": {
"weekly_tasks": {
"type": "boolean"
},
"realm_cards": {
"type": "boolean"
}
}
},
"activityVersion": {
"type": [
"string",
"null"
]
},
"snapshotId": {
"type": [
"string",
"null"
]
}
}
}
ManualPeriod
{
"type": "object",
"properties": {
"period": {
"type": "string",
"format": "date"
},
"ids": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"period",
"ids"
]
}
RealmStatus
{
"type": "object",
"properties": {
"available": {
"type": "boolean"
},
"active": {
"type": "boolean"
},
"itemId": {
"type": [
"integer",
"null"
]
},
"startedAt": {
"type": [
"number",
"null"
],
"description": "Unix 秒"
},
"endsAt": {
"type": [
"number",
"null"
],
"description": "Unix 秒;到期仅表示快照推算"
},
"expectedJade": {
"type": [
"number",
"null"
]
},
"expectedStamina": {
"type": [
"number",
"null"
]
},
"status": {
"type": "string",
"enum": [
"unknown",
"inactive",
"active",
"expired"
]
}
},
"required": [
"available"
]
}
RealmCards
{
"type": [
"object",
"null"
],
"properties": {
"own": {
"$ref": "#/components/schemas/RealmStatus"
},
"foster": {
"$ref": "#/components/schemas/RealmStatus"
}
}
}
Tasks
{
"type": "object",
"properties": {
"records": {
"type": [
"object",
"null"
],
"properties": {
"schemaVersion": {
"type": "integer"
},
"capturedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"daily": {
"type": "object",
"additionalProperties": true
},
"weekly": {
"type": "object",
"additionalProperties": true
},
"realmCardStatus": {
"$ref": "#/components/schemas/RealmCards"
}
}
},
"manual": {
"type": [
"object",
"null"
],
"properties": {
"daily": {
"$ref": "#/components/schemas/ManualPeriod"
},
"weekly": {
"$ref": "#/components/schemas/ManualPeriod"
}
}
},
"states": {
"type": "object",
"required": [
"daily",
"weekly"
],
"properties": {
"daily": {
"type": "string",
"enum": [
"missing",
"unknown",
"current",
"stale",
"restricted"
]
},
"weekly": {
"type": "string",
"enum": [
"missing",
"unknown",
"current",
"stale",
"restricted"
]
}
}
}
},
"required": [
"records",
"manual",
"states"
]
}
Stage
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"startsAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"endsAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"required": [
"title"
]
}
Announcement
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"server": {
"enum": [
"live",
"test"
]
},
"title": {
"type": "string"
},
"publishedAt": {
"type": "string",
"format": "date-time"
},
"body": {
"type": "string"
},
"sourceUrl": {
"type": "string",
"format": "uri"
}
},
"required": [
"id",
"server",
"title",
"publishedAt",
"body"
]
}
Activity
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"server": {
"enum": [
"live",
"test"
]
},
"title": {
"type": "string"
},
"summary": {
"type": "string"
},
"startsAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"endsAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"exchangeEndsAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"announcementIds": {
"type": "array",
"items": {
"type": "string"
}
},
"stages": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Stage"
}
}
},
"required": [
"id",
"server",
"title",
"announcementIds"
]
}
Manifest
{
"type": "object",
"properties": {
"schemaVersion": {
"const": 1
},
"version": {
"type": "string"
},
"sha256": {
"type": "string"
},
"bytes": {
"type": "integer"
},
"publishedAt": {
"type": "string",
"format": "date-time"
},
"path": {
"type": "string"
}
},
"required": [
"schemaVersion",
"version",
"sha256",
"bytes",
"publishedAt",
"path"
]
}
Packet
{
"type": "object",
"properties": {
"schemaVersion": {
"const": 1
},
"version": {
"type": "string"
},
"activities": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Activity"
}
},
"announcements": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Announcement"
}
}
},
"required": [
"schemaVersion",
"version",
"activities",
"announcements"
]
}
GuildMember
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"dutyCode": {
"type": "number"
},
"dutyLabel": {
"type": "string"
},
"level": {
"type": "number"
},
"joinedAt": {
"type": "number"
},
"joinedDate": {
"type": "string"
},
"weeklyHonor": {
"type": "number"
},
"weeklyFeats": {
"type": "number"
},
"lastWeeklyFeats": {
"type": [
"number",
"null"
]
},
"totalFeats": {
"type": "number"
},
"historyDonate": {
"type": "number"
},
"offlineAt": {
"type": "number"
},
"offlineAtLabel": {
"type": "string"
},
"pvpScore": {
"type": "number"
},
"taskDayFinished": {
"type": [
"number",
"null"
]
},
"taskWeekFinished": {
"type": [
"number",
"null"
]
}
}
}
Guild
{
"type": [
"object",
"null"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"level": {
"type": "number"
},
"funds": {
"type": "number"
},
"activeMemberCount": {
"type": "number"
},
"memberCount": {
"type": "number"
},
"serverId": {
"type": "number"
},
"createTime": {
"type": "number"
},
"createDate": {
"type": "string"
},
"badge": {
"type": "number"
},
"pvpScore": {
"type": "number"
},
"leaderName": {
"type": "string"
},
"declare": {
"type": "string"
},
"activeRank": {
"type": "number"
},
"pvpRank": {
"type": "number"
},
"activeScore": {
"type": "number"
},
"construction": {
"type": "number"
},
"insigniaCount": {
"type": "number"
},
"pvpSeason": {
"type": "number"
},
"maxGveDifficulty": {
"type": "number"
},
"creviceDifficulty": {
"type": "number"
},
"creviceDefeatedDifficulty": {
"type": "number"
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GuildMember"
}
}
}
}
ProfileSummary
{
"type": "object",
"properties": {
"file_name": {
"type": [
"string",
"null"
]
},
"source_kind": {
"type": [
"string",
"null"
]
},
"completeness": {
"type": [
"string",
"null"
]
},
"imported_at": {
"type": [
"string",
"null"
]
},
"guild_name": {
"type": [
"string",
"null"
]
},
"account_name": {
"type": [
"string",
"null"
]
},
"server_name": {
"type": [
"string",
"null"
]
},
"short_id": {
"type": [
"string",
"null"
]
},
"platform": {
"type": [
"string",
"null"
]
},
"soul_count": {
"type": "integer"
},
"shikigami_count": {
"type": "integer"
},
"item_count": {
"type": "integer"
},
"realm_card_count": {
"type": "integer"
},
"guild_member_count": {
"type": "integer"
},
"exists": {
"type": "boolean"
}
}
}
独立活动服务与缓存
NAS 的 GET /v1/manifest 返回版本、SHA-256、字节数和包路径;GET /v1/packages/<version>.json
返回不可变数据包。客户端首次获取后保存本机缓存,启动时最多每 24
小时检查一次,也可手动更新。同一版本只下载一次,多个组件共享本机结果。请求支持
ETag/If-None-Match;网络故障和校验失败保留旧缓存。
数据服务与会员服务分开部署。每来源 IP 每分钟 60 次、突发 10 次;全服务每分钟 600 次、并发 8。外部开发者也应缓存资料,不高频轮询。数据包没有会员后台接口或个人游戏数据。
维护者提供原公告,经核对整理为 schemaVersion=1、announcements[]、activities[];公告包含 id、server、title、publishedAt、body 和可选 sourceUrl,活动包含来源公告 ID 和可选时间、阶段。未知时间留空;重复 id 更新当前记录,历史导入保留在 revisions。下载空白导入模板。维护脚本只在管理环境运行:
node server/activity/import.mjs /独立活动数据目录 公告.json --dry-run node server/activity/import.mjs /独立活动数据目录 公告.json
示例响应与组件代码仅用于说明接口,不代表当前游戏活动或玩家状态。