mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
fix(qq): pin @sansenjian/qq-music-api to ~2.4.0 + guard startup failures
A loose `^2.2.10` range let npm pull a newer build of the QQ Music API. The library went ESM in 2.3.x: an ESM-only 2.3.0/2.3.1 throws ERR_REQUIRE_ESM on load, so the embedded server never binds port 3200 and every QQ request (including /getQQLoginQr) fails downstream with ECONNREFUSED — the QR code never appears and cookies look broken. - Pin to `~2.4.0` (verified: loads via the bot's native ESM import, exposes the Koa app, and every endpoint qq.ts calls returns the exact shapes it parses — QR, recommend, lyric, play, playlist detail). Blocks the broken 2.3.0/2.3.1 and any future 2.5 migration. NOTE: 2.4.x requires Node >=20.17 (or >=22.9). - Add describeQqApiStartupError(): on startup failure, log an actionable error (ERR_REQUIRE_ESM → version-pin hint; engine mismatch → Node-upgrade hint) instead of a generic warning, so this is obvious from the logs next time. - README: troubleshooting entry for "QQ 二维码不弹 / 登录失败 / cookie 无法使用" and the version/Node note in the dependency table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
de4343cfbb
commit
08260182a9
5 files changed
+101
-47
No files matched your search
@@ -461,7 +461,7 @@ teamspeak-music-bot/
|
|||||||
| **音频处理** | FFmpeg (ffmpeg-static 内置), @discordjs/opus |
|
| **音频处理** | FFmpeg (ffmpeg-static 内置), @discordjs/opus |
|
||||||
| **TS 协议** | @honeybbq/teamspeak-client(完整客户端协议)+ 自研 TS6 协议适配层 |
|
| **TS 协议** | @honeybbq/teamspeak-client(完整客户端协议)+ 自研 TS6 协议适配层 |
|
||||||
| **网易云 API** | NeteaseCloudMusicApi |
|
| **网易云 API** | NeteaseCloudMusicApi |
|
||||||
| **QQ 音乐 API** | @sansenjian/qq-music-api |
|
| **QQ 音乐 API** | @sansenjian/qq-music-api(锁定 `~2.4.0`,需 Node ≥ 20.17) |
|
||||||
| **哔哩哔哩** | BiliBili Web API(搜索、DASH 音频流、QR 登录) |
|
| **哔哩哔哩** | BiliBili Web API(搜索、DASH 音频流、QR 登录) |
|
||||||
| **酷狗音乐** | 酷狗公开 API(直连,无 npm 依赖 / 无内嵌服务;请求签名 / KRC 歌词解码 / 设备注册移植自 MIT 的 MakcRe/KuGouMusicApi,改用 Node 内置 crypto + zlib) |
|
| **酷狗音乐** | 酷狗公开 API(直连,无 npm 依赖 / 无内嵌服务;请求签名 / KRC 歌词解码 / 设备注册移植自 MIT 的 MakcRe/KuGouMusicApi,改用 Node 内置 crypto + zlib) |
|
||||||
| **前端框架** | Vue 3, Vite 5, Pinia, Vue Router 4 |
|
| **前端框架** | Vue 3, Vite 5, Pinia, Vue Router 4 |
|
||||||
@@ -578,6 +578,12 @@ A:可以。在设置页面创建多个实例,分别连接不同的 TS 服务
|
|||||||
**Q:端口 3200 被占用?**
|
**Q:端口 3200 被占用?**
|
||||||
A:QQ 音乐 API 启动时自动监听 3200 端口。如果之前的进程还在运行,程序会自动复用。如需重启可手动结束 `node` 进程。
|
A:QQ 音乐 API 启动时自动监听 3200 端口。如果之前的进程还在运行,程序会自动复用。如需重启可手动结束 `node` 进程。
|
||||||
|
|
||||||
|
**Q:QQ 音乐二维码不弹 / 扫码登录失败 / cookie 无法使用?**
|
||||||
|
A:通常是内置的 QQ 音乐 API 服务没起来——它一旦没监听 3200 端口,机器人去取二维码就会拿到 `ECONNREFUSED 127.0.0.1:3200`,于是二维码不显示,登录和 cookie 也全失效。先看日志里 QQ API 的启动报错:
|
||||||
|
- 报 `ERR_REQUIRE_ESM`:装到了不兼容的 `@sansenjian/qq-music-api` 版本。本项目把它锁在 **`~2.4.0`**(需要 **Node ≥ 20.17 / 22.9**);务必用 `npm ci` 或 `npm install` 让版本与锁文件一致,**不要**手动 `npm update` 把它升级或降级到不兼容的中间版本(2.3.0/2.3.1 是纯 ESM、会触发此错)。
|
||||||
|
- 报 Node 版本不满足:升级 Node 到 ≥ 20.17,或将该依赖降到 `~2.2.10`(无此 Node 要求)后重装。
|
||||||
|
修好版本后重新 `npm install && npm run build` 并重启即可。
|
||||||
|
|
||||||
**Q:播放歌曲时报 FFmpeg EACCES 错误?**
|
**Q:播放歌曲时报 FFmpeg EACCES 错误?**
|
||||||
A:`ffmpeg-static` 内置的 FFmpeg 二进制文件缺少执行权限。程序已自动尝试修复,如果仍然失败,请手动执行:
|
A:`ffmpeg-static` 内置的 FFmpeg 二进制文件缺少执行权限。程序已自动尝试修复,如果仍然失败,请手动执行:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Generated
+25
-41
@@ -12,7 +12,7 @@
|
|||||||
"@discordjs/opus": "^0.10.0",
|
"@discordjs/opus": "^0.10.0",
|
||||||
"@honeybbq/teamspeak-client": "^0.2.1",
|
"@honeybbq/teamspeak-client": "^0.2.1",
|
||||||
"@koa/router": "^15.4.0",
|
"@koa/router": "^15.4.0",
|
||||||
"@sansenjian/qq-music-api": "^2.2.10",
|
"@sansenjian/qq-music-api": "~2.4.0",
|
||||||
"axios": "^1.14.0",
|
"axios": "^1.14.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"better-sqlite3": "^12.8.0",
|
"better-sqlite3": "^12.8.0",
|
||||||
@@ -595,15 +595,15 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@koa/router": {
|
"node_modules/@koa/router": {
|
||||||
"version": "15.4.0",
|
"version": "15.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@koa/router/-/router-15.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@koa/router/-/router-15.6.0.tgz",
|
||||||
"integrity": "sha512-vKYlXtoCfcAN8z4dHiveYX55rTYOgHEYJNumK1WM9ZAwaArhreGVkyC1LTMGfUQUJyIO/SbwRFBOHeOCY8/MaQ==",
|
"integrity": "sha512-iEOXlvGIBqSNkGXrg0XtMARAOm5zA24oedXxiTGEkrD4JgwVjfRDddCQvW1s4WEcwDYvyecRbf8BikXsuEEj8w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"http-errors": "^2.0.1",
|
"http-errors": "^2.0.1",
|
||||||
"koa-compose": "^4.1.0",
|
"koa-compose": "^4.1.0",
|
||||||
"path-to-regexp": "^8.3.0"
|
"path-to-regexp": "^8.4.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 20"
|
"node": ">= 20"
|
||||||
@@ -967,25 +967,24 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sansenjian/qq-music-api": {
|
"node_modules/@sansenjian/qq-music-api": {
|
||||||
"version": "2.2.10",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sansenjian/qq-music-api/-/qq-music-api-2.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/@sansenjian/qq-music-api/-/qq-music-api-2.4.0.tgz",
|
||||||
"integrity": "sha512-EjhZ3s+ZErSrO6jt5BkThIq6hmMFfvr6RNg945m8SrBOHfuih0cC2kbpoz7WgwnOXoq/psN8iVG/jXrYRUFyQA==",
|
"integrity": "sha512-yPV5h/Qd+oVOKVmDrG0C/r+Xs1QUXcE1Eo16aJYwY9Fugg3CoEaAYFYtitwSIQD6PpnuXqVqWIIot+gtLeAXOQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"workspaces": [
|
||||||
|
"packages/mcp"
|
||||||
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/router": "^15.3.1",
|
"@koa/router": "^15.5.0",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.16.1",
|
||||||
"date-fns": "^4.1.0",
|
"koa": "^2.16.4"
|
||||||
"is-generator-function": "1.0.10",
|
|
||||||
"koa": "^2.16.1",
|
|
||||||
"koa-bodyparser": "^4.4.1",
|
|
||||||
"koa-static": "^5.0.0",
|
|
||||||
"reflect-metadata": "^0.2.2"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"qq-music-api": "dist/app.js"
|
"qq-music-api": "dist/cli.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "22.x"
|
"node": "^20.17.0 || >=22.9.0",
|
||||||
|
"npm": "^11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sansenjian/qq-music-api/node_modules/accepts": {
|
"node_modules/@sansenjian/qq-music-api/node_modules/accepts": {
|
||||||
@@ -1639,13 +1638,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.15.0",
|
"version": "1.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
|
||||||
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
|
"integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.11",
|
"follow-redirects": "^1.16.0",
|
||||||
"form-data": "^4.0.5",
|
"form-data": "^4.0.5",
|
||||||
|
"https-proxy-agent": "^5.0.1",
|
||||||
"proxy-from-env": "^2.1.0"
|
"proxy-from-env": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2275,16 +2275,6 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/date-fns": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/kossnocorp"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -2839,9 +2829,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.11",
|
"version": "1.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -5242,12 +5232,6 @@
|
|||||||
"node": ">= 12.13.0"
|
"node": ">= 12.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/reflect-metadata": {
|
|
||||||
"version": "0.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
|
||||||
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
"@discordjs/opus": "^0.10.0",
|
"@discordjs/opus": "^0.10.0",
|
||||||
"@honeybbq/teamspeak-client": "^0.2.1",
|
"@honeybbq/teamspeak-client": "^0.2.1",
|
||||||
"@koa/router": "^15.4.0",
|
"@koa/router": "^15.4.0",
|
||||||
"@sansenjian/qq-music-api": "^2.2.10",
|
"@sansenjian/qq-music-api": "~2.4.0",
|
||||||
"axios": "^1.14.0",
|
"axios": "^1.14.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"better-sqlite3": "^12.8.0",
|
"better-sqlite3": "^12.8.0",
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { describeQqApiStartupError } from "./api-server.js";
|
||||||
|
|
||||||
|
describe("describeQqApiStartupError", () => {
|
||||||
|
it("flags ERR_REQUIRE_ESM by error code with version-pin guidance", () => {
|
||||||
|
const hint = describeQqApiStartupError({ code: "ERR_REQUIRE_ESM", message: "..." });
|
||||||
|
expect(hint).toMatch(/ERR_REQUIRE_ESM/);
|
||||||
|
expect(hint).toMatch(/~2\.4\.0/);
|
||||||
|
expect(hint).toMatch(/~2\.2\.10/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("flags ERR_REQUIRE_ESM by message when the code is absent", () => {
|
||||||
|
const hint = describeQqApiStartupError(
|
||||||
|
new Error("require() of ES Module .../@sansenjian/qq-music-api/dist/index.js not supported")
|
||||||
|
);
|
||||||
|
expect(hint).toMatch(/incompatible @sansenjian\/qq-music-api/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("flags a Node engine mismatch with a Node-upgrade hint", () => {
|
||||||
|
const hint = describeQqApiStartupError(new Error("Unsupported engine: requires Node >=20.17"));
|
||||||
|
expect(hint).toMatch(/Node >=20\.17/);
|
||||||
|
expect(hint).toMatch(/~2\.2\.10/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null for an unrelated startup error (falls back to the generic warning)", () => {
|
||||||
|
expect(describeQqApiStartupError(new Error("EADDRINUSE: port in use"))).toBeNull();
|
||||||
|
expect(describeQqApiStartupError(undefined)).toBeNull();
|
||||||
|
expect(describeQqApiStartupError(null)).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
+38
-4
@@ -14,6 +14,32 @@ export interface ApiServerManager {
|
|||||||
getQQMusicBaseUrl(): string;
|
getQQMusicBaseUrl(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classify a QQ Music API (@sansenjian/qq-music-api) startup failure into
|
||||||
|
* actionable operator guidance, or null when it isn't a recognised
|
||||||
|
* dependency/runtime mismatch. Exported for testing.
|
||||||
|
*
|
||||||
|
* Background: the package became ESM in 2.3.x. A loose `^` range could pull an
|
||||||
|
* ESM-only build (2.3.0/2.3.1) that throws ERR_REQUIRE_ESM, or a 2.4.x build
|
||||||
|
* that needs Node >=20.17 — either way the embedded server never binds, so
|
||||||
|
* every QQ request fails downstream with ECONNREFUSED on the API port.
|
||||||
|
*/
|
||||||
|
export function describeQqApiStartupError(err: unknown): string | null {
|
||||||
|
const e = (err ?? {}) as { code?: string; message?: string };
|
||||||
|
const code = String(e.code ?? "");
|
||||||
|
const msg = String(e.message ?? "");
|
||||||
|
if (code === "ERR_REQUIRE_ESM" || /ERR_REQUIRE_ESM|require\(\) of ES ?Module/i.test(msg)) {
|
||||||
|
return (
|
||||||
|
"an incompatible @sansenjian/qq-music-api build is installed (ERR_REQUIRE_ESM). " +
|
||||||
|
"Pin it to ~2.4.0 (needs Node >=20.17) or ~2.2.10 in package.json, then reinstall"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (/Unsupported engine|EBADENGINE|requires Node|Node\.js version/i.test(msg)) {
|
||||||
|
return "@sansenjian/qq-music-api 2.4.x requires Node >=20.17 (or >=22.9) — upgrade Node, or pin the package to ~2.2.10";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function isPortFree(port: number): Promise<boolean> {
|
function isPortFree(port: number): Promise<boolean> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const server = net.createServer();
|
const server = net.createServer();
|
||||||
@@ -100,10 +126,18 @@ export function createApiServerManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn(
|
const hint = describeQqApiStartupError(err);
|
||||||
{ err },
|
if (hint) {
|
||||||
"QQ Music API not available — QQ Music features may be limited"
|
logger.error(
|
||||||
);
|
{ err },
|
||||||
|
`QQ Music API failed to start — ${hint}. QQ features (search/play/login) will be unavailable until fixed; port ${options.qqMusicPort} is down.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
{ err },
|
||||||
|
"QQ Music API not available — QQ Music features may be limited"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user