koishi-plugin-tomori-ournotes
v1.2.0
Published
BanG Dream! Our Notes 查询插件,对接 Tomori 后端(tsugu 兼容协议)
Maintainers
Readme
koishi-plugin-tomori-ournotes
《BanG Dream! Our Notes》查询插件,对接 Tomori 后端。
Tomori 后端出图或出 JSON,响应协议与 tsugu 兼容;本插件是它的 Koishi 前端。
与 bestdori-tsugu-extra 的关系
指令形态与消息设置沿用参考项目,但定位完全不同:
| | bestdori-tsugu-extra | 本插件 |
| --- | --- | --- |
| 模糊搜索 | 客户端自己调 /fuzzySearch 再二次请求 | 后端 /searchSong 等内部已做,一次请求 |
| 谱面渲染 | 客户端 @napi-rs/canvas 自绘(BestdoriPreview.cjs) | 后端 /songChart 画好,插件只收图 |
| 素材 | 仓库自带 SongChart/note/*.png、nickname_song.xlsx | 无,全部来自后端 |
| 数据源 | 直连 bestdori.com | 只连 Tomori 后端 |
| 分数线 | /cutoff* | 后端未实现,相关指令是占位桩 |
所以本插件没有 canvas、没有素材目录、没有 BestdoriPreview.cjs,也不碰 Bestdori。
安装
npm install # 开发
npm run build # 产物在 lib/Koishi 里加载 koishi-plugin-tomori-ournotes(npm 上同名,https://www.npmjs.com/package/koishi-plugin-tomori-ournotes),
然后把 backendURL 指向你的 Tomori 后端。
版本与兼容(回退指引)
插件与后端是成对的:请求体字段变过一次,两边对不上时不会报错,只会查错服或查不到。
| 插件版本 | 对应 Tomori 后端 | 请求体契约 |
| --- | --- | --- |
| 1.2.x(本版) | ≥ 1.2.0,且含账号包端点(/playerCard、/b25、/deckBuilder、/playerBind/*) | 查询输入统一为 id,服务器统一为 displayedServerList |
| 1.1.x | ≥ 1.1.0 | 同上(还没有绑定 / 发名片 / b25 / 组卡) |
| 1.0.0(npm 上的旧版) | 1.0.x 及以前 | 查询输入用 text/songId/…,服务器用 server/mainServer |
1.2.0 起新增的绑定 / 发名片 / b25 / 组卡 打到没有这些端点的旧后端上会回 404 Not Found
(插件原样透出这一句),其它指令不受影响。
回退办法:后端还是 1.0.x(或更早)时,别用本版插件 —— 它发的 displayedServerList 会被旧后端
静默忽略,于是 公告列表 jp、ycx … jp 这类会查成港澳台服。装回 npm 上的 1.0.0 即可:
npm i [email protected]反过来,后端升到 1.2.0 之后再用本版;不确定时先敲「后端状态」,它会在后端契约版本不符时直接把上面 这句回退指引打出来。Koishi 配置页的「后端设置」里也写着同样一句。
服务器(区域)
后端有四个区域,短码 tw / jp / kr / en(别名也认:hk-tw-mo、台服、日服、国际服、韩服……)。
服务器词写在哪个位置都认:查曲 迷星叫 jp 与 查曲 jp 迷星叫 等价。
请求体里服务器只有一个字段 displayedServerList(单值、数组、缺省都收),差别在后端取谁:
| | 静态游戏数据(多服) | 动态用户数据(单服) |
| --- | --- | --- |
| 指令 | 查曲 / 查卡 / 查角色 / 查乐团 / 查活动 / 查卡池 / 查谱面 / 曲效率 / 随机曲 / 查贴纸 | 查排行 / 活动歌榜 / 活动榜线(ycx) / 活动推荐 / 抽卡模拟 / 查玩家 / 发名片 / b25 / 组卡 / 公告 |
| 出图 | 一图多服,每个实体一个区块、区块内每服一行 | 单服一图(后端取列表第一个,不回退) |
| 默认 | 全部四服(配置 defaultServers) | tw(配置 defaultServer) |
「查玩家 / 发名片 / b25 / 组卡」是例外:这四个命令的服务器只在写了玩家 ID 时有意义 ——
写了 ID 就按 ID 首位推断(2→港澳台、3→国际、4→韩;日服没有前缀规则,必须显式写 jp);
不写 ID 时用绑定的默认账号,服务器由绑定记录决定。
只写服务器不写 ID(查玩家 jp)会被插件直接拦下并说明原因 —— 那种情况下后端会静默忽略服务器,
凭空让人以为查的是日服。
配置
| 配置项 | 默认值 | 说明 |
| --- | --- | --- |
| backendURL | http://127.0.0.1:3002 | Tomori 后端地址。后端 .env.example 写的是 3000,实际部署常在 3002,以 .env 里的 PORT 为准 |
| backupURLs | [] | 备用地址,仅在主地址连不上时按顺序重试;后端返回 4xx/5xx 不会切换 |
| compress | true | 出 JPEG(快、体积小);关闭出 PNG(更清晰) |
| reply / at | true | 是否引用回复 / @ 用户 |
| defaultServers | 全部四服 | 静态实体查询默认展示哪些服 |
| defaultServer | tw | 单服指令(排行/玩家/抽卡/公告)的默认服 |
| stubCommands | true | 是否注册后端未实现功能的占位桩指令 |
| announcementStream | true | 是否连接公告推流 |
| subscriptionFile | 空 | 订阅落盘路径;空则用 <Koishi 目录>/data/tomori-ournotes/subscriptions.json |
| requestTimeout | 120000 | 单次请求超时(毫秒)。谱面要现画,别设太小 |
指令
查询输入:所有「查什么」的参数都支持数字 ID 或名字。后端统一按
id处理 —— 纯数字(或纯数字串)按 ID 直查,其余文本走模糊搜索(可拼词条);名字命中多个时出该种类的列表图。 所以插件不需要客户端模糊搜索,谱面也是一次请求直达。玩家类指令(查玩家 / 发名片 / b25 / 组卡)是例外:它们只认裸数字的玩家 ID(可省略 = 用绑定账号), 没有名字搜索,多写一个数字会被明确拒绝而不是替你挑一个。
| 指令 | 说明 |
| --- | --- |
| 查曲 <ID或名字> | 命中单首出详情图,多首出列表图 |
| 随机曲 [筛选词] | 随机一首,可限定范围(lv25 这类筛选词后端认);单服取服务器列表首个 |
| 曲效率 [服务器…](别名 曲表、分数表、查询分数表) | 歌曲 meta 效率排行:一张图两榜,击奏live 与 自由live 各自效率最高的前 15 张谱面,四难度混排 |
| 查谱面 <ID或名字>(别名 查谱) | 24 轨谱面预览图。难度 ez/nm/hd/ex(默认 ex),末尾加 镜像 或 -m 出镜像谱,速度7.5 调流速(1~12)。歌名命中多首时出列表图并提示用 ID |
| 谱面数据 <歌名或ID> [难度] [镜像] | 谱面摘要(BPM / 物量 / Fever / 滑条);歌名多命中时后端会回一句「请用 songId 精确指定」。要原始 JSON 请直接调后端 /songChartData |
| 查卡 <关键词> | 角色卡 + 支援卡一起查;可写卡种 角色卡/支援卡 |
| 查角色卡 / 查支援卡 <关键词> | 只查其中一类 |
| 查卡面 <卡面ID>(别名 卡面) | 卡面原图;种类 member/support,也认 角色卡/支援卡/留影 |
| 查卡池 <卡池ID或名字> | 卡池详情(名字多命中出卡池列表图) |
| 抽卡模拟 [次数] [卡池ID或名字](别名 抽卡、卡池模拟) | 默认 10 连;不指定卡池则用当前开放卡池;上限 10000。卡池写名字时只在该服索引里搜,多命中会回一句「请用 gachaId 精确指定」 |
| 查贴纸 <ID> | 官方贴纸原图 |
| 查贴纸 <关键词> [角色\|团体] | 搜索贴纸,出列表图(带 ID 与名称) |
| 查贴纸 全部 | 列出该服全部贴纸(每 30 张一页) |
| 查角色 / 查乐团 <ID或名字> | 角色搜索 / 乐团搜索(乐团也能挂自定义关键词) |
| 查活动 <关键词> | 活动搜索:只写一个服且该服有收录时出丰富详情图,否则出多服组合表;命中多个出活动列表图 |
| 活动歌榜 [名次] [活动ID或名字] [服务器](别名 活动榜线) | 该活动各乐曲榜(名次/玩家名/综合力/出分)。名次挑一段十名:给 100 就是第 91~100 名,省略则前十。活动省略时取当前活动 |
| ycx [档位] [活动ID或名字] [服务器] | 活动榜线:各档分数线随时间的折线图(单档位)。档位 10/100/1000/5000/10000,省略用 100 |
| ycxall [活动ID或名字] [服务器] | 同上,但画数据支持的全部档位 |
| 活动推荐 [活动ID或名字] [服务器](别名 推荐曲) | 推荐曲:击奏live / 自由live 各按目标评级 SS/S/A/B,加一截挑战live;附局/小时与活动报酬估算 |
| 查排行 <歌名或ID> | 该曲前十用户与出分(单服)。歌名只在该服索引里搜,多命中出歌曲列表图 |
| 查玩家 [玩家ID](别名 查账号) | 玩家档案图(最爱卡面/名称/等级/应援数);有网页账号包时再附一张账号包图。ID 省略 = 用绑定的默认账号 |
| 发名片 [玩家ID] [页N] [服务器] | 玩家自己设计的那套名片的原图(最多 3 页)。页码写成 页2 / 第2页(不能写裸数字,裸数字是玩家 ID);省略则按页依次发全部 |
| b25 [玩家ID] [服务器](别名 查b25;指令名不分大小写,B25 也认) | B25 计分榜:账号最强 25 张谱面(计入值 / 分数 / 总计值)。需要账号包的歌曲公开 |
| 组卡 [玩家ID] [常规\|活动[ID]\|自动] [服务器](别名 组队、配队) | 组卡工具:推荐队伍与收益/效率曲。活动 ID 写在「活动」后面(活动 123 / 活动123);需要账号包的卡片与道具公开 |
| 绑定玩家 <绑定码> | 用网页生成的绑定码绑定一个游戏账号(一个 QQ 可绑多个,首个自动设默认) |
| 玩家绑定(别名 玩家状态、玩家状态列表) | 列出已绑定的账号(★ = 默认),给出解绑/切换默认的写法 |
| 默认玩家 [ID或序号] | 切换默认账号(不传 ID 的查玩家 / 发名片 / b25 / 组卡 都用它) |
| 解除绑定 [ID或序号] | 解绑一个账号。序号看「玩家绑定」列表,1~3 位数字按序号、更长的按玩家 ID |
| 公告列表 [服务器] | 该服公告列表图 |
| 公告 <公告ID> [服务器] | 该条公告详情图(正文过长会分页) |
| 公告 最新 [服务器] | 该服最新一条公告的详情图(后端的 -1 哨兵值;写成 公告 "-1" 也认,但裸 -1 会被 Koishi 当选项吞掉) |
| 公告订阅 <服务器…> / 公告退订 [服务器…] | 见下方「公告推流」 |
| 公告 <服务器> / 公告 关闭 [服务器] | 上面两条的简写,与参考项目习惯一致 |
| 新增关键词 <类型> <实体ID> <关键词> | 给角色/角色卡/支援卡/歌曲/乐团挂一个检索别名,该别名参与模糊搜索(依赖后端数据库)。类型写 角色/角色卡/支援卡/歌曲/乐团 |
| 删除关键词 <类型> <实体ID> <关键词> | 只能删自己上传的 |
| 车来(别名 ycm、有车吗) | 车站列表图 |
| 上传车牌 <房间号> [备注] | 房间号 5-7 位数字;同房号重复提交 = 刷新有效期 |
| 交友登记 <游戏ID> [服务器] | 一人一条,重复登记覆盖 |
| 解除交友(别名 删除交友)/ 交友列表 | 删除自己的名片 / 列表图 |
| 后端状态 | 后端连通性、四服 master 版本、玩家数据源、运行时长。排查问题先用它 |
曲效率改过语义。 它对应的/songMeta早期是「全歌曲表」(ID/标题/乐队/等级/时长,每 20 首一页), 现在是效率排行(一张图两榜,各前 15 张谱面)。请求体没变,所以旧名曲表/分数表/查询分数表全部保留为别名, 但看到的东西已经完全是另一张图了。
占位桩指令(stubCommands: false 可关掉):lsycx、查试炼、查自制谱、逮捕。
这些在 tsugu 系插件里存在,但 Tomori 后端没有对应端点(/cutoffDetail、/cutoffListOfRecentEvent、tsugu 的 /user 都是 404 占位)。
注册它们是为了从旧配置迁移过来时能得到「暂不支持」而不是「未知指令」。
「查玩家」不在占位之列 —— 后端的
/searchPlayer早就实现了。ycx/ycxall也已经是真指令了:后端的/cutoffAll现在是各档分数线随时间的折线图。 绑定管理(绑定玩家/玩家绑定/默认玩家/解除绑定)曾经是桩,1.2.0 起接上了后端的/playerBind/*,也是真指令了。 只有lsycx(最近活动榜线)还挡着,它对应的/cutoffListOfRecentEvent仍是 404。
玩家绑定与网页账号包
后端的账号数据分两路:上游玩家档案(名字/等级/最爱卡面,看 ID 就能查)与 网页账号包(玩家自己在网页平台导入的游戏数据 —— 歌曲完成度、乐队道具、卡片)。 绑定只是把 QQ 与游戏账号对上号,让下面这些指令不用每次输玩家 ID:
- 到网页「我的账号」里给账号包填好玩家 ID,生成绑定码(8 位、一次性、15 分钟);
- 在群里发
绑定玩家 <绑定码>; - 之后
查玩家/发名片/b25/组卡(都不写 ID)就用这个账号。
- 一个 QQ 可绑多个账号:
玩家绑定看列表,默认玩家 2切换默认(序号或玩家 ID 都行),解除绑定 2解绑。 - 绑定只做两件事:免输玩家 ID + 证明账号归属(防冒绑)。它不解锁任何隐藏数据 —— 群里的展示一律按网页上的公开开关(卡片 / 道具 / 歌曲各自是否公开),绑定本人也一样,隐藏即不显示。
- 因此:
查玩家的账号包图需要对应类别公开;b25需要歌曲公开;组卡需要卡片与道具公开。隐藏时后端会提示去网页调整。 - 账号包数据不是实时的(导入时算好),且
b25/组卡还需要网页平台在线。 - 绑定管理依赖后端数据库(
ENABLE_DB);没开时这几条会回「错误: 服务器未启用数据库」。 - 绑定管理的回复是文字(列表/成功提示),不是图 —— 这是后端定的,插件原样透出。
公告推流
后端只在公告新增或修改时推送该条的详情图,所以:
- 连接时不发快照,订阅之后只有新公告会推过来;想看全量列表用「公告列表」
- 下架不推(需要的话定期用「公告列表」对账)
- 更新来自后端轮询上游(默认 300s),所以有最多约 5 分钟的延迟
- 场景侧默认没有订阅,要用
公告订阅 tw(可累积多个服)注册;公告退订 [服务器]取消 - 机器人侧总开关是配置里的
announcementStream;关掉时仍可登记,但会回复一句「推流未启用」
插件只对有订阅者的服建连接,最后一个订阅者退订就断开 —— 后端也只在有订阅者时才轮询上游。
关于后端的一些事实(踩过的坑)
- 1.1.0 起请求体只有一套叫法:查询输入一律
id(后端按id> 专属 ID 字段 >text>fuzzySearchResult取第一个有值的),服务器一律displayedServerList(单值 / 数组 / 缺省都收)。 旧的server/mainServer已被删除 —— 传了不报错,但会被静默忽略,于是「查 jp」实际查的是缺省服。 改版时别再写回去。 - 区域命名有三套并存:
hk-tw-mo只是current_version.json的清单键,master 表路径段是tw; 拿hk-tw-mo去拼路径会 404。对本插件而言它只是tw的别名。 日服素材只有ja目录,且日服的模糊索引里只有日文曲名 —— 拿中文曲名搜jp搜不到是正常的。 - 难度只有 4 个:
difficultyId0-3 = easy/normal/hard/expert,没有 special。sp会被插件本地拦下(发出去必然 400)。 - 域内错误的形状不止一种:多数是 HTTP 200 +
['错误: …'],但占位路由是 404 +{status:'fail', data:'错误: 服务器未启用数据库'}, 参数校验失败是 400 +{status:'failed', data:'参数错误', error:[…]}(带字段级原因), 「一个查询输入都没给」是 422 +{status:'failed', data:'需要提供查询输入: …'}(没有error数组), 兜底 404 干脆是纯文本404 Not Found。 所以插件不用ctx.http.post()(那个只回response.data,会丢状态码),而是把 HTTP 服务当函数调用来拿完整响应。 - 图片 MIME 靠嗅探:
compress=true出 JPEG,/getStampImage原来只出 webp、现在原图仍可能是 webp,/getCardIllustration出 PNG。写死image/png会让适配器拿到错的类型,所以按魔术字节判断。 - 分页端点会连发多条 base64(贴纸列表 30/页、车站与交友 30/条、抽卡 >10 连、公告详情 42 行/页),
中间不夹文本。插件原样渲染成多张图。
(
/songMeta改成语义之后不再分页,一张图两榜。) /songChart的流速默认 7.5、范围 1~12;插件不写这个字段时由后端用它自己的默认值。/station/submitRoomNumber的time是毫秒时间戳,后端视图拿它算「x 秒前」。 (参考项目传的是120,那是个 bug,别照抄。)- 贪心的
text参数会吞掉 Koishi 选项:查谱面 100008 ex -m的-m会进到文本里,.option('mirror')拿不到值。 所以谱面类指令的-m是插件自己从 token 里解析的。 - 交友 / 车站 / 关键词依赖后端的两件事:
.env里ENABLE_DB=true且 本机 MongoDB 在跑。 两者缺一,后端就回「错误: 服务器未启用数据库」,插件原样透出。 - 关键词有两道查重:同一实体上不能重复,且不得与任何现有实体名/别名重合(乐团、角色、歌曲、卡池、活动……的全部语言别名与去标点变体)。 命中就拒绝,所以「挂不上去」多半是撞了官方名,不是插件的问题。 关键词参与模糊搜索(所以外号能搜到),但模糊索引按区域分片,某个服挂的别名在别的服搜不到。
- 活动:上游活动数据由各服独立上传,所以「某服暂时没有」不等于活动不存在 ——
/searchEvent对这种情况出多服组合表 + 未收录占位,只有四个服都没有才当作 ID 不存在。活动歌榜取的是活动内的挑战曲榜(/events/{id}/challenges/{挑战曲id}/ranking), 不是/music/{id}/ranking那个曲子历史最高分榜 —— 活动曲在后者常常查不到数据。 - 「活动歌榜」与「活动榜线(ycx)」是两张不同的图,别被名字带偏:
歌榜(
/eventSongRanking)是当前排行榜;榜线(/cutoffAll)是各档分数线随时间的折线。 榜线数据上游没有历史接口,由后端按小时自己采样攒 —— 所以刚接入的活动可能一个点都没有, 后端会在图上附一句说明,那不是插件的问题。 - 名次/档位与活动 ID 都是纯数字,插件严格按位置认,不去猜:
活动歌榜 100 1与ycx 100 1都是「第一个数字是名次/档位、第二个是活动 ID」(ycxall只有活动 ID); 多出来的数字一律算参数错误。 早先的写法是「落在档位表10/100/1000/5000/10000里就算档位」,那样ycx 100 1与ycx 1 100里 同样两个数字的含义会随取值对调 —— 用户没法从写法上判断哪个是哪个。现在只有一条规则。 代价是ycx 1会被当成「档位 1」而不是「活动 1」;不过后端对不适配的档位会回一句 「当前可用档位: …」,照着改即可,比含义漂移好。 - 查谱面一步直达:
/songChart自己收文本(唯一命中出谱面图、多命中出歌曲列表图), 所以插件不再先调/fuzzySearch拿 ID —— 难度/镜像/流速一次请求带齐,不会在中转那一跳丢掉。 (后端还给/searchSong留了个singleDraw: 'chart'开关,1.1.0 起对 ID 直查也生效;本插件用不到它。) 公告 最新走id = -1这个哨兵值:后端/announcements收到-1时先拉列表挑出该服 发布时间最新的一条,再出它的详情图(读列表时跳过 TTL —— 问「最新」却拿缓存里的旧列表会答错)。 插件侧最新/-1/new都映射到它;公告 23这类真实 ID 原样透传。 坑:裸-1在 Koishi 里会被当成选项吃掉,根本轮不到指令参数(公告 -1只会回用法提示)—— 所以对外只宣传最新;真要用数字就写带引号的公告 "-1"。- 查玩家默认只能查到「在 StarMoe 已验证且设为公开」的账号,其余回「该账号未公开或不存在」;
想让任意 ID 可查要在后端配
MOENOTES_API_BASE。 - 玩家类端点的服务器解析有差异:
/searchPlayer收 ID 后自己按首位推断服务器;/playerCard、/b25、/deckBuilder则是「有displayedServerList取首个、没有用缺省服」—— 所以插件替后三条补了同一套前缀推断(2→tw /3→en /4→kr)。 四条都是不写 ID 时服务器由绑定记录决定,这时显式传的服务器会被静默忽略; 插件对这种写法(查玩家 jp)直接报错,而不是发一个必然被忽略的字段。 /searchPlayer的输出是组合的:档案图与账号包图是两个独立的块; 上游档案查不到而账号包存在时只出账号包图(页脚注明),两边都没有才回原错误文案。 档案图只带当前使用的那一张名片,其余页要用「发名片」单独取。- 绑定码兑换走网页平台:
/playerBind/bind内部调网页的/api/bot/bind(需要 bot 侧WEB_PLATFORM_TOKEN与网页侧配对好),换来账号包的身份(账号 ID / 玩家 ID / 服); 绑定码一次性、15 分钟,只能证明归属,不解除任何隐藏。 - 名片原图直出:
/playerCard不加框不加字,逐页取上游图床的原图(走外链白名单); 上游只给当前使用的那套的 1~3 页。个别页取不到时跳过,全取不到才报错。 - 组卡与网页组卡器是同一套算法(移植 + 数值对照脚本):活动模式以实际收益为目标 (点数吃活动点加成、道具吃交换所加成;同评级下加成高者胜),出三块榜——自由live 混池 3 首、 自由live 活动加成乐队 1 首、挑战live 活动挑战曲 1 首,每行都自带它自己的编队(图上用编号引用)。 谱面效率数据回退到降级源(缺技能权重)时它拒绝出图,而不是给一个算不准的推荐。
测试
npm test # 单元测试 + 集成测试(后端不可达时集成部分自动跳过)
npm run typecheck
npm run typecheck:loose # 用非 strict 配置再查一遍(见下)为什么有
typecheck:loose:Koishi 实例(yukamo 等模板)的 tsconfig 常常没开strict, 而strictNullChecks关掉时真值收窄(if (!x.ok))对可辨识联合不生效, 只有等值收窄(== false/=== false)生效 —— 本仓库开着 strict 测不出来, 装进实例才会报TS2339: Property 'error' does not exist。 新代码里判断parseArgs/parsePlayerArgs/parseDeckArgs的结果一律写== false。
测试分两层:
test/unit/—— 模拟输入 / 模拟输出。跑的是真实 Koishi 运行时(指令注册、参数解析、before-send钩子都是 Koishi 自己的实现), 只把ctx.http换成记录型假实现:既能断言插件到底发了什么请求体,又能完全控制后端回什么。 输入用官方的@koishijs/plugin-mock发消息。parse.test.ts—— 参数解析(服务器抽取、难度/镜像/速度、玩家/组卡/绑定的入参、keepQuery 边界)commands.test.ts—— 每个指令的请求体与各种成功/失败/畸形响应backend.test.ts—— HTTP 层与响应渲染的畸形形状sse.test.ts/subscription.test.ts/manager.test.ts—— 公告推流的解析、订阅表、连接生命周期
test/integration/—— 打真实后端,验证请求体字段是否真被接受、大图 base64 能否变成可发送的图片元素、 以及ctx.http的流式响应能不能读到真实的 SSE 握手。默认http://127.0.0.1:3002,可用环境变量指向别处:TOMORI_TEST_BACKEND=http://127.0.0.1:3102 npm test跑之前会先探一次
/health:连不上跳过,连上了但不是这一版契约的后端(没有四区域列表) 也跳过,并打印一句说明。这样「端口上蹲着另一个旧实例」和「插件改坏了」不会混为一谈。集成断言刻意宽松(只验「有图」或用正则匹配若干可能文案),后端改版式不该让插件测试变红。 对「后端有没有启用数据库」也宽容:交友/车站那几条只要求插件把后端的答复清楚地转达给用户。
两个测试环境上的注意点
vitest.config.ts把koishi别名到了@koishijs/core。因为koishi主入口 =@koishijs/core+@koishijs/loader,而 loader 的产物里有import Loader from './shared.js'这种「ESM 引 CJS」的相对导入,在 Vite 的转换管线里会解析成Class extends value #<Object> is not a constructor直接炸掉。 插件用到的Context/Schema/h与全部类型都来自 core,所以测试环境指向 core 即可。- 夹具里
announcementStream默认是 false:推流是后台长连接,而测试夹具故意不调app.stop(), 开着会把 vitest 挂住。推流的测试单独构造 manager 并显式stop()。
许可
MIT。本项目与 Bushiroad、Craft Egg 及《BanG Dream!》官方无任何关联。
