mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
feat(bot): !play id <id> 与其他命令语法保持一致
按 id 精确播放原本要写 `!play id:<id>`,冒号在一堆 `!<命令> <子命令> <参数>` 的命令里显得很突兀。现在空格写法 `!play id <id>` 也可以,`!add` / `!playnext` 共用同一个解析器,一起生效。 `id:<id>` 继续支持,不做废弃:用户的聊天记录、旧文档和 !search 输出里都是 这个写法。 冒号是个明确的标记,所以 `id:<任意内容>` 一律当 id。空格不是——「ID 4」和 「ID Bruno」都是真实存在的歌名,而且 `id <链接>` 原本会落到 URL 分支正常解析。 所以空格写法只认「长得像 id」的 token(纯数字 / BV 号 / 11 位以上的 id 字符), 其余照旧继续走 URL 识别,最后落到普通搜索,不会把搜索词误当成 id。 同步更新 !search 输出的提示、三条 Usage、!help 和 README。 Closes #139 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4e4354282d
commit
74ea8d26d4
4 files changed
+100
-20
No files matched your search
@@ -21,6 +21,59 @@ describe("parseSongRef (#90 exact-song selection)", () => {
|
||||
expect(parseSongRef("id:185868,")).toEqual({ id: "185868", platform: null });
|
||||
});
|
||||
|
||||
// Issue #139: `!play id <id>` matches the "<command> <subcommand> <arg>"
|
||||
// shape of every other command. The colon form stays supported — users have
|
||||
// it in their chat scrollback and in older docs.
|
||||
it("parses the space-separated id form", () => {
|
||||
expect(parseSongRef("id 185868")).toEqual({ id: "185868", platform: null });
|
||||
expect(parseSongRef("ID 004Z8Ihr0JIu5s")).toEqual({ id: "004Z8Ihr0JIu5s", platform: null });
|
||||
expect(parseSongRef("id 185868")).toEqual({ id: "185868", platform: null });
|
||||
expect(parseSongRef("id 185868.")).toEqual({ id: "185868", platform: null });
|
||||
});
|
||||
|
||||
it("does not mistake a word merely starting with 'id' for an id reference", () => {
|
||||
expect(parseSongRef("idol")).toBeNull();
|
||||
expect(parseSongRef("identity 185868")).toBeNull();
|
||||
expect(parseSongRef("id")).toBeNull();
|
||||
expect(parseSongRef("id:")).toBeNull();
|
||||
// Two remaining tokens are a search phrase, not an id.
|
||||
expect(parseSongRef("id die for you")).toBeNull();
|
||||
});
|
||||
|
||||
// Without a colon, "id" is just a word — "ID 4" and "ID Bruno" are real track
|
||||
// titles. The space form therefore only claims tokens that could actually be
|
||||
// an id; everything else stays a search term.
|
||||
it("only treats the space form as an id when the token looks like one", () => {
|
||||
expect(parseSongRef("id Bruno")).toBeNull();
|
||||
expect(parseSongRef("id Marshmello")).toBeNull();
|
||||
expect(parseSongRef("id 4ever")).toBeNull();
|
||||
// …while every real id shape is still accepted.
|
||||
expect(parseSongRef("id 4")).toEqual({ id: "4", platform: null }); // numeric
|
||||
expect(parseSongRef("id BV1yxHQeYEuE")).toEqual({ id: "BV1yxHQeYEuE", platform: null });
|
||||
expect(parseSongRef("id 004Z8Ihr0JIu5s")).toEqual({ id: "004Z8Ihr0JIu5s", platform: null }); // QQ mid
|
||||
expect(parseSongRef("id a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6")).toEqual({
|
||||
id: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
|
||||
platform: null,
|
||||
}); // Jellyfin GUID / Kugou hash
|
||||
});
|
||||
|
||||
it("keeps the colon form unrestricted, so a short or odd id still works", () => {
|
||||
expect(parseSongRef("id:Bruno")).toEqual({ id: "Bruno", platform: null });
|
||||
expect(parseSongRef("id: 4ever")).toEqual({ id: "4ever", platform: null });
|
||||
});
|
||||
|
||||
it("does not let the space form swallow a pasted URL", () => {
|
||||
// `id <url>` used to fall through to the URL branches; it still must.
|
||||
expect(parseSongRef("id https://music.163.com/song?id=185868")).toEqual({
|
||||
id: "185868",
|
||||
platform: "netease",
|
||||
});
|
||||
expect(parseSongRef("id https://y.qq.com/n/ryqq/songDetail/004Z8Ihr0JIu5s")).toEqual({
|
||||
id: "004Z8Ihr0JIu5s",
|
||||
platform: "qq",
|
||||
});
|
||||
});
|
||||
|
||||
it("does NOT treat NetEase collection (playlist/album/artist) URLs as a song id", () => {
|
||||
// These reuse ?id= but are not songs — they should fall through to search,
|
||||
// not misresolve to getSongDetail(collectionId) and error "no song".
|
||||
|
||||
Reference in new issue
Block a user