@tencent-connect/openclaw-qqbot 1.6.5-alpha.0 → 1.6.5-alpha.2

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.
@@ -129,26 +129,24 @@ export function sendStartupGreetings(ctx, trigger) {
129
129
  return;
130
130
  }
131
131
  const upgradeTargetOpenId = loadUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId, ctx.log);
132
- const targetOpenId = upgradeTargetOpenId || resolveAdminOpenId(ctx);
133
- if (!targetOpenId) {
134
- markStartupGreetingFailed(ctx.accountId, ctx.appId, plan.version, "no-admin");
135
- ctx.log?.info(`[qqbot:${ctx.accountId}] Skipping startup greeting (no admin or known user)`);
132
+ // 没有 upgrade-greeting-target 文件 不是通过 /bot-upgrade 触发的升级
133
+ // (console 手动重启、脚本升级等场景),静默更新 marker 不发消息
134
+ if (!upgradeTargetOpenId) {
135
+ markStartupGreetingSent(ctx.accountId, ctx.appId, plan.version);
136
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Version changed but no upgrade-greeting-target, silently updating marker (trigger=${trigger})`);
136
137
  return;
137
138
  }
138
139
  try {
139
- const receiverType = upgradeTargetOpenId ? "upgrade-requester" : "admin";
140
- ctx.log?.info(`[qqbot:${ctx.accountId}] Sending startup greeting to ${receiverType} (trigger=${trigger}): "${plan.greeting}"`);
140
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Sending startup greeting to upgrade-requester (trigger=${trigger}): "${plan.greeting}"`);
141
141
  const token = await getAccessToken(ctx.appId, ctx.clientSecret);
142
142
  const GREETING_TIMEOUT_MS = 10_000;
143
143
  await Promise.race([
144
- sendProactiveC2CMessage(token, targetOpenId, plan.greeting),
144
+ sendProactiveC2CMessage(token, upgradeTargetOpenId, plan.greeting),
145
145
  new Promise((_, reject) => setTimeout(() => reject(new Error("Startup greeting send timeout (10s)")), GREETING_TIMEOUT_MS)),
146
146
  ]);
147
147
  markStartupGreetingSent(ctx.accountId, ctx.appId, plan.version);
148
- if (upgradeTargetOpenId) {
149
- clearUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId);
150
- }
151
- ctx.log?.info(`[qqbot:${ctx.accountId}] Sent startup greeting to ${receiverType}: ${targetOpenId}`);
148
+ clearUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId);
149
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Sent startup greeting to upgrade-requester: ${upgradeTargetOpenId}`);
152
150
  }
153
151
  catch (err) {
154
152
  const message = err instanceof Error ? err.message : String(err);
@@ -3,6 +3,7 @@
3
3
  "name": "OpenClaw QQ Bot",
4
4
  "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
5
5
  "channels": ["qqbot"],
6
+ "extensions": ["./dist/index.js"],
6
7
  "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
7
8
  "capabilities": {
8
9
  "proactiveMessaging": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.5-alpha.0",
3
+ "version": "1.6.5-alpha.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,32 +16,19 @@
16
16
  "scripts",
17
17
  "index.ts",
18
18
  "tsconfig.json",
19
- "openclaw.plugin.json",
20
- "clawdbot.plugin.json",
21
- "moltbot.plugin.json"
19
+ "openclaw.plugin.json"
22
20
  ],
23
- "clawdbot": {
24
- "id": "openclaw-qqbot",
25
- "extensions": [
26
- "./index.ts"
27
- ]
28
- },
29
- "moltbot": {
30
- "id": "openclaw-qqbot",
31
- "extensions": [
32
- "./index.ts"
33
- ]
34
- },
35
21
  "openclaw": {
36
22
  "id": "openclaw-qqbot",
37
23
  "extensions": [
38
- "./index.ts"
24
+ "./dist/index.js"
39
25
  ]
40
26
  },
41
27
  "scripts": {
42
28
  "build": "tsc || true",
43
29
  "dev": "tsc --watch",
44
- "prepack": "npm install --omit=dev"
30
+ "prepack": "npm install --omit=dev",
31
+ "postinstall": "node scripts/postinstall-link-sdk.js 2>/dev/null || true"
45
32
  },
46
33
  "dependencies": {
47
34
  "mpg123-decoder": "^1.0.3",
@@ -59,8 +46,6 @@
59
46
  "typescript": "^5.9.3"
60
47
  },
61
48
  "peerDependencies": {
62
- "clawdbot": "*",
63
- "moltbot": "*",
64
49
  "openclaw": "*"
65
50
  },
66
51
  "homepage": "https://github.com/tencent-connect/openclaw-qqbot",
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+
3
+ // When installed as an openclaw extension under ~/.openclaw/extensions/,
4
+ // the plugin needs access to `openclaw/plugin-sdk` at runtime.
5
+ // openclaw's jiti loader resolves this via alias by walking up from the plugin
6
+ // path to find the openclaw package root — but ~/.openclaw/extensions/ is not
7
+ // under the openclaw package tree, so the alias lookup fails.
8
+ //
9
+ // This script creates a symlink from the plugin's node_modules/openclaw to the
10
+ // globally installed openclaw package, allowing Node's native ESM resolver
11
+ // (used by jiti with tryNative:true for .js files) to find `openclaw/plugin-sdk`.
12
+
13
+ import { existsSync, symlinkSync, mkdirSync, realpathSync } from "node:fs";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { execSync } from "node:child_process";
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+ const pluginRoot = resolve(__dirname, "..");
20
+
21
+ // Only run when installed under an openclaw-like extensions directory
22
+ // (supports openclaw, clawdbot, moltbot, etc.)
23
+ if (!pluginRoot.includes("extensions")) {
24
+ process.exit(0);
25
+ }
26
+
27
+ const linkTarget = join(pluginRoot, "node_modules", "openclaw");
28
+
29
+ // Already linked or exists
30
+ if (existsSync(linkTarget)) {
31
+ process.exit(0);
32
+ }
33
+
34
+ // CLI names to try (openclaw and its aliases)
35
+ const CLI_NAMES = ["openclaw", "clawdbot", "moltbot"];
36
+
37
+ // Find the global openclaw installation
38
+ let openclawRoot = null;
39
+
40
+ // Strategy 1: npm root -g → look for any known CLI package name
41
+ if (!openclawRoot) {
42
+ try {
43
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
44
+ for (const name of CLI_NAMES) {
45
+ const candidate = join(globalRoot, name);
46
+ if (existsSync(join(candidate, "package.json"))) {
47
+ openclawRoot = candidate;
48
+ break;
49
+ }
50
+ }
51
+ } catch {}
52
+ }
53
+
54
+ // Strategy 2: resolve from the CLI binary (which openclaw / clawdbot / moltbot)
55
+ if (!openclawRoot) {
56
+ const whichCmd = process.platform === "win32" ? "where" : "which";
57
+ for (const name of CLI_NAMES) {
58
+ try {
59
+ const bin = execSync(`${whichCmd} ${name}`, { encoding: "utf-8" }).trim().split("\n")[0];
60
+ if (!bin) continue;
61
+ // Resolve symlinks to get actual binary location
62
+ const realBin = realpathSync(bin);
63
+ // bin is typically <prefix>/bin/<name> -> ../lib/node_modules/<name>/...
64
+ const candidate = resolve(dirname(realBin), "..", "lib", "node_modules", name);
65
+ if (existsSync(join(candidate, "package.json"))) {
66
+ openclawRoot = candidate;
67
+ break;
68
+ }
69
+ // Also try: binary might be inside the package itself (e.g. .../node_modules/<name>/bin/<name>)
70
+ const candidate2 = resolve(dirname(realBin), "..");
71
+ if (existsSync(join(candidate2, "package.json")) && existsSync(join(candidate2, "plugin-sdk"))) {
72
+ openclawRoot = candidate2;
73
+ break;
74
+ }
75
+ } catch {}
76
+ }
77
+ }
78
+
79
+ // Strategy 3: walk up from the extensions directory to find the CLI's data root,
80
+ // then look for a global node_modules sibling
81
+ if (!openclawRoot) {
82
+ // pluginRoot is like /home/user/.openclaw/extensions/openclaw-qqbot
83
+ // The CLI data dir is /home/user/.openclaw (or .clawdbot, .moltbot)
84
+ const extensionsDir = dirname(pluginRoot);
85
+ const dataDir = dirname(extensionsDir);
86
+ const dataDirName = dataDir.split("/").pop() || dataDir.split("\\").pop() || "";
87
+ // dataDirName is like ".openclaw" → strip the dot to get "openclaw"
88
+ const cliName = dataDirName.replace(/^\./, "");
89
+ if (cliName) {
90
+ try {
91
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
92
+ const candidate = join(globalRoot, cliName);
93
+ if (existsSync(join(candidate, "package.json"))) {
94
+ openclawRoot = candidate;
95
+ }
96
+ } catch {}
97
+ }
98
+ }
99
+
100
+ if (!openclawRoot) {
101
+ // Not fatal — plugin may work if openclaw loads it with proper alias resolution
102
+ // But log a warning so upgrade scripts can detect the failure
103
+ console.error("[postinstall-link-sdk] WARNING: could not find openclaw/clawdbot/moltbot global installation, symlink not created");
104
+ process.exit(0);
105
+ }
106
+
107
+ try {
108
+ mkdirSync(join(pluginRoot, "node_modules"), { recursive: true });
109
+ symlinkSync(openclawRoot, linkTarget, "junction");
110
+ console.log(`[postinstall-link-sdk] symlink created: node_modules/openclaw -> ${openclawRoot}`);
111
+ } catch (e) {
112
+ console.error(`[postinstall-link-sdk] WARNING: symlink creation failed: ${e.message}`);
113
+ }
@@ -315,6 +315,47 @@ foreach ($legacyName in @("qqbot", "openclaw-qq")) {
315
315
  }
316
316
  Write-Host " Installed to: $TARGET_DIR"
317
317
 
318
+ # Execute postinstall script to create openclaw SDK symlink
319
+ # (upgrade-via-npm is pure file operation, npm install is not run, so postinstall won't trigger automatically)
320
+ $PostinstallScript = Join-Path $TARGET_DIR "scripts" "postinstall-link-sdk.js"
321
+ if (Test-Path $PostinstallScript) {
322
+ Write-Host " Running postinstall: creating openclaw SDK symlink..."
323
+ try {
324
+ Push-Location $TARGET_DIR
325
+ $postOutput = & node $PostinstallScript 2>&1
326
+ Pop-Location
327
+ if ($postOutput) { Write-Host " $postOutput" }
328
+ } catch {
329
+ Write-Host " [WARN] postinstall script failed (non-fatal)" -ForegroundColor Yellow
330
+ try { Pop-Location } catch {}
331
+ }
332
+ # Verify symlink creation
333
+ $symlinkPath = Join-Path $TARGET_DIR "node_modules" "openclaw"
334
+ if (Test-Path $symlinkPath) {
335
+ Write-Host " [OK] openclaw SDK symlink ready"
336
+ } else {
337
+ Write-Host " [WARN] openclaw SDK symlink not created, attempting manual fallback..." -ForegroundColor Yellow
338
+ $cliDataDir = Split-Path $EXTENSIONS_DIR -Parent
339
+ $cliName = (Split-Path $cliDataDir -Leaf) -replace '^\.',''
340
+ try {
341
+ $globalRoot = (& npm root -g 2>$null).Trim()
342
+ $globalPkg = Join-Path $globalRoot $cliName
343
+ if ($globalRoot -and (Test-Path $globalPkg)) {
344
+ $nmDir = Join-Path $TARGET_DIR "node_modules"
345
+ if (-not (Test-Path $nmDir)) { New-Item -ItemType Directory -Path $nmDir -Force | Out-Null }
346
+ New-Item -ItemType Junction -Path $symlinkPath -Target $globalPkg -Force | Out-Null
347
+ Write-Host " [OK] Manual symlink created: -> $globalPkg"
348
+ } else {
349
+ Write-Host " [ERROR] Cannot locate global $cliName installation (npm root -g: $globalRoot)" -ForegroundColor Red
350
+ }
351
+ } catch {
352
+ Write-Host " [ERROR] Manual symlink creation also failed: $($_.Exception.Message)" -ForegroundColor Red
353
+ }
354
+ }
355
+ } else {
356
+ Write-Host " [WARN] postinstall script not found, skipping symlink creation" -ForegroundColor Yellow
357
+ }
358
+
318
359
  # [4/5] Verify installation
319
360
  Write-Host ""
320
361
  Write-Host "[4/5] Verifying installation..."
@@ -297,6 +297,36 @@ for dir_name in qqbot openclaw-qq; do
297
297
  done
298
298
  echo " 已安装到: $TARGET_DIR"
299
299
 
300
+ # 执行 postinstall 脚本创建 openclaw SDK symlink
301
+ # (upgrade-via-npm 是纯文件操作,不走 npm install,所以 postinstall 不会自动触发)
302
+ POSTINSTALL_SCRIPT="$TARGET_DIR/scripts/postinstall-link-sdk.js"
303
+ if [ -f "$POSTINSTALL_SCRIPT" ]; then
304
+ echo " 执行 postinstall: 创建 openclaw SDK symlink..."
305
+ POSTINSTALL_OUTPUT="$(cd "$TARGET_DIR" && node "$POSTINSTALL_SCRIPT" 2>&1)" || true
306
+ [ -n "$POSTINSTALL_OUTPUT" ] && echo " $POSTINSTALL_OUTPUT"
307
+ # 验证 symlink 是否创建成功
308
+ if [ -d "$TARGET_DIR/node_modules/openclaw" ]; then
309
+ echo " ✅ openclaw SDK symlink 已就绪"
310
+ else
311
+ echo " ⚠️ openclaw SDK symlink 未创建,插件可能无法加载"
312
+ echo " 尝试手动创建 symlink..."
313
+ # 手动 fallback:尝试从 CLI 数据目录名推断全局包名
314
+ _CLI_DATA_DIR="$(dirname "$EXTENSIONS_DIR")"
315
+ _CLI_NAME="$(basename "$_CLI_DATA_DIR" | sed 's/^\.//')"
316
+ _GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"
317
+ if [ -n "$_GLOBAL_ROOT" ] && [ -n "$_CLI_NAME" ] && [ -d "$_GLOBAL_ROOT/$_CLI_NAME" ]; then
318
+ mkdir -p "$TARGET_DIR/node_modules"
319
+ ln -sf "$_GLOBAL_ROOT/$_CLI_NAME" "$TARGET_DIR/node_modules/openclaw" 2>/dev/null && \
320
+ echo " ✅ 手动 symlink 创建成功: -> $_GLOBAL_ROOT/$_CLI_NAME" || \
321
+ echo " ❌ 手动 symlink 创建也失败了"
322
+ else
323
+ echo " ❌ 无法定位全局 $_CLI_NAME 安装路径(npm root -g: $_GLOBAL_ROOT)"
324
+ fi
325
+ fi
326
+ else
327
+ echo " ⚠️ 未找到 postinstall 脚本,跳过 symlink 创建"
328
+ fi
329
+
300
330
  # [4/5] 输出新版本号和升级报告(供调用方解析)
301
331
  echo ""
302
332
  echo "[4/5] 验证安装..."
@@ -152,27 +152,26 @@ export function sendStartupGreetings(ctx: AdminResolverContext, trigger: "READY"
152
152
  }
153
153
 
154
154
  const upgradeTargetOpenId = loadUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId, ctx.log);
155
- const targetOpenId = upgradeTargetOpenId || resolveAdminOpenId(ctx);
156
- if (!targetOpenId) {
157
- markStartupGreetingFailed(ctx.accountId, ctx.appId, plan.version, "no-admin");
158
- ctx.log?.info(`[qqbot:${ctx.accountId}] Skipping startup greeting (no admin or known user)`);
155
+
156
+ // 没有 upgrade-greeting-target 文件 → 不是通过 /bot-upgrade 触发的升级
157
+ // (console 手动重启、脚本升级等场景),静默更新 marker 不发消息
158
+ if (!upgradeTargetOpenId) {
159
+ markStartupGreetingSent(ctx.accountId, ctx.appId, plan.version);
160
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Version changed but no upgrade-greeting-target, silently updating marker (trigger=${trigger})`);
159
161
  return;
160
162
  }
161
163
 
162
164
  try {
163
- const receiverType = upgradeTargetOpenId ? "upgrade-requester" : "admin";
164
- ctx.log?.info(`[qqbot:${ctx.accountId}] Sending startup greeting to ${receiverType} (trigger=${trigger}): "${plan.greeting}"`);
165
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Sending startup greeting to upgrade-requester (trigger=${trigger}): "${plan.greeting}"`);
165
166
  const token = await getAccessToken(ctx.appId, ctx.clientSecret);
166
167
  const GREETING_TIMEOUT_MS = 10_000;
167
168
  await Promise.race([
168
- sendProactiveC2CMessage(token, targetOpenId, plan.greeting),
169
+ sendProactiveC2CMessage(token, upgradeTargetOpenId, plan.greeting),
169
170
  new Promise((_, reject) => setTimeout(() => reject(new Error("Startup greeting send timeout (10s)")), GREETING_TIMEOUT_MS)),
170
171
  ]);
171
172
  markStartupGreetingSent(ctx.accountId, ctx.appId, plan.version);
172
- if (upgradeTargetOpenId) {
173
- clearUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId);
174
- }
175
- ctx.log?.info(`[qqbot:${ctx.accountId}] Sent startup greeting to ${receiverType}: ${targetOpenId}`);
173
+ clearUpgradeGreetingTargetOpenId(ctx.accountId, ctx.appId);
174
+ ctx.log?.info(`[qqbot:${ctx.accountId}] Sent startup greeting to upgrade-requester: ${upgradeTargetOpenId}`);
176
175
  } catch (err) {
177
176
  const message = err instanceof Error ? err.message : String(err);
178
177
  markStartupGreetingFailed(ctx.accountId, ctx.appId, plan.version, message);
@@ -1,16 +0,0 @@
1
- {
2
- "id": "openclaw-qqbot",
3
- "name": "OpenClaw QQ Bot",
4
- "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
5
- "channels": ["qqbot"],
6
- "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
7
- "capabilities": {
8
- "proactiveMessaging": true,
9
- "cronJobs": true
10
- },
11
- "configSchema": {
12
- "type": "object",
13
- "additionalProperties": false,
14
- "properties": {}
15
- }
16
- }
@@ -1,16 +0,0 @@
1
- {
2
- "id": "openclaw-qqbot",
3
- "name": "OpenClaw QQ Bot",
4
- "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
5
- "channels": ["qqbot"],
6
- "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
7
- "capabilities": {
8
- "proactiveMessaging": true,
9
- "cronJobs": true
10
- },
11
- "configSchema": {
12
- "type": "object",
13
- "additionalProperties": false,
14
- "properties": {}
15
- }
16
- }