dsh-qq-moony 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.6.0 — 2026-10-25
4
+
5
+ 有人报告"装上了但一直连不上、还反复自己重启",根因是两个 bug 叠在一起:
6
+
7
+ - **修:配置优先级反了。** README 写的是"本地 config.json 优先",代码里却是包的 patch 排在最后、压掉了本地文件——用户改的端口和 token 从来没生效过。现在顺序是 `默认 < patch < 本地文件`。
8
+ - **修:端口被别的服务占着时白折腾。** 原来只看"请求通不通",有人机器上 3000 早被别的网页服务占了(回的是 `{"code":90004,"message":"资源不存在"}`),插件误判成 NapCat 挂了,于是反复拉起、每次白等一分钟。现在探活区分三种:`ok` / `foreign`(端口被占)/ `down`,被占时不自动拉起,设置页直接说"3000 被别的服务占了,换个端口或者把它挪开"。
9
+
10
+ **给改装的人**:如果 3000 上有别的东西,改 `baseUrl`(比如 `http://127.0.0.1:3010`),同时把 NapCat 的 OneBot HTTP 端口也改成一�样。
11
+ ## 0.5.2 — 2026-10-25
12
+
13
+ - 拉起 NapCat 时不再弹黑窗口:改成生成 VBS、用 `wscript` 静默执行(`windowsHide` 对批处理无效)
14
+ - README 补一句:自启是静默的,不用留一个控制台窗口在那儿
15
+
16
+ ## 0.5.1 — 2026-10-25
17
+
18
+ - 修:点「保存更改」之后设置页会空白——草稿被清空、新值还没回来那一瞬没有可显示的值
19
+ - 现在每个字段都有兜底:草稿为空就用服务端返回的值
20
+
3
21
  ## 0.5.0 — 2026-10-25
4
22
 
5
23
  装完之后不再一片红:缺什么会自己说出来。
package/README.md CHANGED
@@ -40,7 +40,7 @@ dsh plugin add dsh-qq-moony
40
40
  - *接消息*:白名单里的群,被 @ 才应。
41
41
  - *参与聊天*:每群记着最近三十条,隔 8–20 分钟给一次"要不要插一句"的机会,把这现场交给它判断,接不住就什么都不发。冷场(五分钟没人说话)不开口;同群冷却十分钟;群里一天最多二十条。
42
42
 
43
- **打开 DSH 就自带 NapCat。** 启动时探一下 3000 端口,没通就用 `-q` 快速登录把 NapCat 拉起来——免扫码,几秒上线。
43
+ **打开 DSH 就自带 NapCat。** 启动时探一下 3000 端口,没通就用 `-q` 快速登录把 NapCat 拉起来——免扫码,几秒上线。**而且是静默的**:插件生成一个 VBS 用 `wscript` 起,不会弹黑窗口(`windowsHide` 对批处理不管用,这是唯一可靠的办法)。
44
44
 
45
45
  ---
46
46
 
package/client.js CHANGED
@@ -159,7 +159,9 @@ window.__ModuleLoader__.load({
159
159
  const 缺口 = [];
160
160
  if (!状态.qqnt) 缺口.push('没找到 QQ NT —— 去 im.qq.com 装新版(9.9.x)。老版 9.7 注不进去,这是死条件。');
161
161
  if (!状态.napcat) 缺口.push('没找到 NapCat —— 去 github.com/NapNeko/NapCatQQ 下 Shell 版,解压后把 napCatDir 指过去。');
162
- if (状态.qqnt && 状态.napcat && !状态.online)
162
+ if (状态.portState === 'foreign')
163
+ 缺口.push('端口上坐着的不是 NapCat —— 3000 被别的服务占了。换个端口(改 baseUrl),或者把那个服务挪开。');
164
+ else if (状态.qqnt && 状态.napcat && !状态.online)
163
165
  缺口.push('QQ NT 和 NapCat 都在,但连不上 —— 检查 NapCat 启没启、3000 端口、token 三样。');
164
166
 
165
167
  return h(
package/index.js CHANGED
@@ -114,7 +114,7 @@ const 工具输出 = (字段, 渲染) => ({
114
114
  });
115
115
 
116
116
  export function apply(ctx, 配置) {
117
- const c = { ...默认配置, ...读取本地配置(), ...(配置 ?? {}) };
117
+ const c = { ...默认配置, ...(配置 ?? {}), ...读取本地配置() };
118
118
  const 账本 = join(家目录(), 'sent.log');
119
119
  const 收件箱 = join(家目录(), 'inbox.jsonl');
120
120
 
@@ -133,22 +133,40 @@ export function apply(ctx, 配置) {
133
133
  return { qqnt, napcat };
134
134
  }
135
135
 
136
- /** DSH 起来时顺手看一眼 NapCat:没跑就自己拉起来(免扫码,用 -q 快速登录)。 */
137
- async function 确认NapCat在跑() {
138
- if (c.autoStartNapCat === false) return;
139
- const 探活 = async () => {
136
+ /** 探活:区分「NapCat 在」「端口被别的服务占了」「根本没起来」三种情况。 */
137
+ async function 探NapCat() {
138
+ try {
139
+ const 头 = c.token ? { Authorization: `Bearer ${c.token}` } : {};
140
+ const r = await fetch(`${c.baseUrl}/get_login_info`, { headers: 头, signal: AbortSignal.timeout(4000) });
141
+ const 文本 = await r.text();
142
+ let 体 = null;
140
143
  try {
141
- const 头 = c.token ? { Authorization: `Bearer ${c.token}` } : {};
142
- const r = await fetch(`${c.baseUrl}/get_login_info`, { headers: 头, signal: AbortSignal.timeout(4000) });
143
- return r.ok;
144
+ 体 = JSON.parse(文本);
144
145
  } catch {
145
- return false;
146
+ /* 不是 JSON,多半是别的服务 */
146
147
  }
147
- };
148
- if (await 探活()) {
148
+ if (r.ok && 体?.status === 'ok') return 'ok';
149
+ // 有东西在应答,但不是 OneBot——端口被别的程序占了
150
+ return 体 ? 'foreign' : 'foreign';
151
+ } catch {
152
+ return 'down';
153
+ }
154
+ }
155
+
156
+ /** DSH 起来时顺手看一眼 NapCat:没跑就自己拉起来(免扫码,用 -q 快速登录)。 */
157
+ async function 确认NapCat在跑() {
158
+ if (c.autoStartNapCat === false) return;
159
+
160
+ const 状态 = await 探NapCat();
161
+ if (状态 === 'ok') {
149
162
  记调试('NapCat 已经在跑,不用拉');
150
163
  return;
151
164
  }
165
+ if (状态 === 'foreign') {
166
+ // 端口上坐着别的程序:这时候去拉起 NapCat 只会白折腾
167
+ 记调试(`${c.baseUrl} 上应答的不是 NapCat(多半被别的服务占了),不自动拉起`);
168
+ return;
169
+ }
152
170
  const 目录 = c.napCatDir || '';
153
171
  const 号码 = String(c.qqUin || c.uin || '');
154
172
  if (!目录 || !号码) {
@@ -156,14 +174,22 @@ export function apply(ctx, 配置) {
156
174
  return;
157
175
  }
158
176
  try {
159
- const 子 = spawn('cmd.exe', ['/c', 'launcher-user.bat', '-q', 号码], {
160
- cwd: 目录,
177
+ // 用 VBS 起,窗口不弹出来(cmd 的隐藏参数对批处理不管用)
178
+ const 启动脚本 = join(家目录(), 'start-napcat.vbs');
179
+ mkdirSync(家目录(), { recursive: true });
180
+ const 批处理 = join(目录, 'launcher-user.bat');
181
+ const vbs =
182
+ `Set sh = CreateObject("WScript.Shell")\r\n` +
183
+ `sh.Run "cmd /c ""${批处理}"" -q ${号码}", 0, False\r\n`;
184
+ // .vbs 用 UTF-16LE + BOM,中文路径才认
185
+ writeFileSync(启动脚本, Buffer.concat([Buffer.from([0xff, 0xfe]), Buffer.from(vbs, 'utf16le')]));
186
+ const 子 = spawn('wscript.exe', [启动脚本], {
161
187
  detached: true,
162
188
  stdio: 'ignore',
163
189
  windowsHide: true,
164
190
  });
165
191
  子.unref();
166
- 记调试(`NapCat 没在跑,已拉起(${目录},-q ${号码})`);
192
+ 记调试(`NapCat 没在跑,已静默拉起(${目录},-q ${号码})`);
167
193
  } catch (e) {
168
194
  记调试(`拉 NapCat 失败:${e?.message ?? e}`);
169
195
  return;
@@ -171,7 +197,7 @@ export function apply(ctx, 配置) {
171
197
  // 给它一点时间登录,最多等一分钟
172
198
  for (let i = 0; i < 30; i += 1) {
173
199
  await new Promise((r) => setTimeout(r, 2000));
174
- if (await 探活()) {
200
+ if ((await 探NapCat()) === 'ok') {
175
201
  记调试(`NapCat 起来了(等了约 ${(i + 1) * 2} 秒)`);
176
202
  return;
177
203
  }
@@ -660,16 +686,20 @@ export function apply(ctx, 配置) {
660
686
  const 记录 = 读账(账本);
661
687
  let 在线 = false;
662
688
  let 昵称 = '';
663
- try {
664
- const r = await 调('get_login_info', {});
665
- 在线 = true;
666
- 昵称 = String(r.data?.nickname ?? '');
667
- } catch {
668
- /* 离线就报离线 */
689
+ 在线 = (await 探NapCat()) === 'ok';
690
+ if (在线) {
691
+ try {
692
+ const r = await 调('get_login_info', {});
693
+ 昵称 = String(r.data?.nickname ?? '');
694
+ } catch {
695
+ /* 探到了但取名字失败,别当离线 */
696
+ }
669
697
  }
698
+ const 端口状态 = 在线 ? 'ok' : await 探NapCat();
670
699
  发json(res, 200, {
671
700
  ok: true,
672
701
  online: 在线,
702
+ portState: 端口状态,
673
703
  nickname: 昵称,
674
704
  qqnt: 体检().qqnt,
675
705
  napcat: 体检().napcat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-qq-moony",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "把 QQ 小号接进 DeepSeek Harness:收发消息、发表情包、发 QQ 空间说说、参与群聊,能自己找话题,自带启动与限速防封。",
5
5
  "keywords": [
6
6
  "dsh",