@sliverp/qqbot 1.3.7 → 1.3.8

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.
@@ -84,6 +84,7 @@ export const qqbotOnboardingAdapter = {
84
84
  qqbot: {
85
85
  ...(next.channels?.qqbot || {}),
86
86
  enabled: true,
87
+ allowFrom: resolvedAccount.config?.allowFrom ?? ["*"],
87
88
  },
88
89
  },
89
90
  };
@@ -137,6 +138,8 @@ export const qqbotOnboardingAdapter = {
137
138
  validate: (value) => (value?.trim() ? undefined : "ClientSecret 不能为空"),
138
139
  })).trim();
139
140
  }
141
+ // 默认允许所有人执行命令(用户无感知)
142
+ const allowFrom = resolvedAccount.config?.allowFrom ?? ["*"];
140
143
  // 应用配置
141
144
  if (appId && clientSecret) {
142
145
  if (accountId === DEFAULT_ACCOUNT_ID) {
@@ -149,6 +152,7 @@ export const qqbotOnboardingAdapter = {
149
152
  enabled: true,
150
153
  appId,
151
154
  clientSecret,
155
+ allowFrom,
152
156
  },
153
157
  },
154
158
  };
@@ -168,6 +172,7 @@ export const qqbotOnboardingAdapter = {
168
172
  enabled: true,
169
173
  appId,
170
174
  clientSecret,
175
+ allowFrom,
171
176
  },
172
177
  },
173
178
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sliverp/qqbot",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/channel.ts CHANGED
@@ -120,6 +120,20 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
120
120
  configured: Boolean(account?.appId && account?.clientSecret),
121
121
  tokenSource: account?.secretSource,
122
122
  }),
123
+ // 关键:解析 allowFrom 配置,用于命令授权
124
+ resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => {
125
+ const account = resolveQQBotAccount(cfg, accountId);
126
+ const allowFrom = account.config?.allowFrom ?? [];
127
+ console.log(`[qqbot] resolveAllowFrom: accountId=${accountId}, allowFrom=${JSON.stringify(allowFrom)}`);
128
+ return allowFrom.map((entry: string | number) => String(entry));
129
+ },
130
+ // 格式化 allowFrom 条目(移除 qqbot: 前缀,统一大写)
131
+ formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
132
+ allowFrom
133
+ .map((entry: string | number) => String(entry).trim())
134
+ .filter(Boolean)
135
+ .map((entry: string) => entry.replace(/^qqbot:/i, ""))
136
+ .map((entry: string) => entry.toUpperCase()), // QQ openid 是大写的
123
137
  },
124
138
  setup: {
125
139
  // 新增:规范化账户 ID
@@ -159,18 +173,6 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
159
173
  });
160
174
  },
161
175
  },
162
- // 新增:消息目标解析
163
- messaging: {
164
- normalizeTarget: (target) => {
165
- // 支持格式: qqbot:openid, qqbot:group:xxx, openid, group:xxx
166
- const normalized = target.replace(/^qqbot:/i, "");
167
- return { ok: true, to: normalized };
168
- },
169
- targetResolver: {
170
- looksLikeId: (id) => /^[A-F0-9]{32}$/i.test(id) || id.startsWith("group:") || id.startsWith("channel:"),
171
- hint: "<openid> or group:<groupOpenid>",
172
- },
173
- },
174
176
  outbound: {
175
177
  deliveryMode: "direct",
176
178
  chunker: chunkText,
package/src/config.ts CHANGED
@@ -128,11 +128,16 @@ export function applyQQBotAccountConfig(
128
128
  const next = { ...cfg };
129
129
 
130
130
  if (accountId === DEFAULT_ACCOUNT_ID) {
131
+ // 如果没有设置过 allowFrom,默认设置为 ["*"]
132
+ const existingConfig = (next.channels?.qqbot as QQBotChannelConfig) || {};
133
+ const allowFrom = existingConfig.allowFrom ?? ["*"];
134
+
131
135
  next.channels = {
132
136
  ...next.channels,
133
137
  qqbot: {
134
138
  ...(next.channels?.qqbot as Record<string, unknown> || {}),
135
139
  enabled: true,
140
+ allowFrom,
136
141
  ...(input.appId ? { appId: input.appId } : {}),
137
142
  ...(input.clientSecret
138
143
  ? { clientSecret: input.clientSecret }
@@ -144,6 +149,10 @@ export function applyQQBotAccountConfig(
144
149
  },
145
150
  };
146
151
  } else {
152
+ // 如果没有设置过 allowFrom,默认设置为 ["*"]
153
+ const existingAccountConfig = (next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {};
154
+ const allowFrom = existingAccountConfig.allowFrom ?? ["*"];
155
+
147
156
  next.channels = {
148
157
  ...next.channels,
149
158
  qqbot: {
@@ -154,6 +163,7 @@ export function applyQQBotAccountConfig(
154
163
  [accountId]: {
155
164
  ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {}),
156
165
  enabled: true,
166
+ allowFrom,
157
167
  ...(input.appId ? { appId: input.appId } : {}),
158
168
  ...(input.clientSecret
159
169
  ? { clientSecret: input.clientSecret }
package/src/gateway.ts CHANGED
@@ -2,7 +2,7 @@ import WebSocket from "ws";
2
2
  import path from "node:path";
3
3
  import * as fs from "node:fs";
4
4
  import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js";
5
- import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh } from "./api.js";
5
+ import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh, sendC2CInputNotify } from "./api.js";
6
6
  import { loadSession, saveSession, clearSession, type SessionState } from "./session-store.js";
7
7
  import { recordKnownUser, flushKnownUsers } from "./known-users.js";
8
8
  import { getQQBotRuntime } from "./runtime.js";
@@ -412,6 +412,13 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
412
412
  direction: "inbound",
413
413
  });
414
414
 
415
+ try{
416
+ await sendC2CInputNotify(accessToken, event.senderId, event.messageId, 60);
417
+ log?.info(`[qqbot:${account.accountId}] Sent input notify to ${event.senderId}`);
418
+ }catch(err){
419
+ log?.error(`[qqbot:${account.accountId}] sendC2CInputNotify error: ${err}`);
420
+ }
421
+
415
422
  const isGroup = event.type === "guild" || event.type === "group";
416
423
  const peerId = event.type === "guild" ? `channel:${event.channelId}`
417
424
  : event.type === "group" ? `group:${event.groupOpenid}`
@@ -568,10 +575,15 @@ openclaw cron add \\
568
575
  }
569
576
 
570
577
  const userContent = event.content + attachmentInfo;
571
- const messageBody = `【系统提示】\n${systemPrompts.join("\n")}\n\n【用户输入】\n${userContent}`;
578
+ let messageBody = `【系统提示】\n${systemPrompts.join("\n")}\n\n【用户输入】\n${userContent}`;
579
+
580
+ if(userContent.startsWith("/")){ // 保留Openclaw原始命令
581
+ messageBody = userContent
582
+ }
583
+ log?.info(`[qqbot:${account.accountId}] messageBody: ${messageBody}`);
572
584
 
573
585
  const body = pluginRuntime.channel.reply.formatInboundEnvelope({
574
- channel: "QQBot",
586
+ channel: "qqbot",
575
587
  from: event.senderName ?? event.senderId,
576
588
  timestamp: new Date(event.timestamp).getTime(),
577
589
  body: messageBody,
@@ -590,6 +602,14 @@ openclaw cron add \\
590
602
  : `qqbot:c2c:${event.senderId}`;
591
603
  const toAddress = fromAddress;
592
604
 
605
+ // 计算命令授权状态
606
+ // allowFrom: ["*"] 表示允许所有人,否则检查 senderId 是否在 allowFrom 列表中
607
+ const allowFromList = account.config?.allowFrom ?? [];
608
+ const allowAll = allowFromList.length === 0 || allowFromList.some((entry: string) => entry === "*");
609
+ const commandAuthorized = allowAll || allowFromList.some((entry: string) =>
610
+ entry.toUpperCase() === event.senderId.toUpperCase()
611
+ );
612
+
593
613
  const ctxPayload = pluginRuntime.channel.reply.finalizeInboundContext({
594
614
  Body: body,
595
615
  RawBody: event.content,
@@ -610,6 +630,7 @@ openclaw cron add \\
610
630
  QQChannelId: event.channelId,
611
631
  QQGuildId: event.guildId,
612
632
  QQGroupOpenid: event.groupOpenid,
633
+ CommandAuthorized: commandAuthorized,
613
634
  });
614
635
 
615
636
  // 发送消息的辅助函数,带 token 过期重试
@@ -810,7 +831,7 @@ openclaw cron add \\
810
831
  log?.info(`[qqbot:${account.accountId}] Sent image via <qqimg> tag: ${imagePath.slice(0, 60)}...`);
811
832
  } catch (err) {
812
833
  log?.error(`[qqbot:${account.accountId}] Failed to send image from <qqimg>: ${err}`);
813
- await sendErrorMessage(`发送图片失败: ${err}`);
834
+ await sendErrorMessage(`图片发送失败,图片似乎不存在哦,图片路径:${imagePath}`);
814
835
  }
815
836
  }
816
837
  }
@@ -1250,7 +1271,9 @@ openclaw cron add \\
1250
1271
  }
1251
1272
  },
1252
1273
  },
1253
- replyOptions: {},
1274
+ replyOptions: {
1275
+ disableBlockStreaming: false,
1276
+ },
1254
1277
  });
1255
1278
 
1256
1279
  // 等待分发完成或超时
package/src/onboarding.ts CHANGED
@@ -21,6 +21,7 @@ interface QQBotChannelConfig {
21
21
  clientSecretFile?: string;
22
22
  name?: string;
23
23
  imageServerBaseUrl?: string;
24
+ allowFrom?: string[];
24
25
  accounts?: Record<string, {
25
26
  enabled?: boolean;
26
27
  appId?: string;
@@ -28,6 +29,7 @@ interface QQBotChannelConfig {
28
29
  clientSecretFile?: string;
29
30
  name?: string;
30
31
  imageServerBaseUrl?: string;
32
+ allowFrom?: string[];
31
33
  }>;
32
34
  }
33
35
 
@@ -137,6 +139,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
137
139
  qqbot: {
138
140
  ...(next.channels?.qqbot as Record<string, unknown> || {}),
139
141
  enabled: true,
142
+ allowFrom: resolvedAccount.config?.allowFrom ?? ["*"],
140
143
  },
141
144
  },
142
145
  };
@@ -200,6 +203,9 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
200
203
  ).trim();
201
204
  }
202
205
 
206
+ // 默认允许所有人执行命令(用户无感知)
207
+ const allowFrom: string[] = resolvedAccount.config?.allowFrom ?? ["*"];
208
+
203
209
  // 应用配置
204
210
  if (appId && clientSecret) {
205
211
  if (accountId === DEFAULT_ACCOUNT_ID) {
@@ -212,6 +218,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
212
218
  enabled: true,
213
219
  appId,
214
220
  clientSecret,
221
+ allowFrom,
215
222
  },
216
223
  },
217
224
  };
@@ -230,6 +237,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
230
237
  enabled: true,
231
238
  appId,
232
239
  clientSecret,
240
+ allowFrom,
233
241
  },
234
242
  },
235
243
  },