@nextclaw/channel-runtime 0.1.24 → 0.1.26

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/dist/index.d.ts CHANGED
@@ -175,6 +175,7 @@ declare class QQChannel extends BaseChannel<Config["channels"]["qq"]> {
175
175
  private bot;
176
176
  private processedIds;
177
177
  private processedSet;
178
+ private senderNameCache;
178
179
  private reconnectTimer;
179
180
  private connectTask;
180
181
  private reconnectAttempt;
@@ -184,13 +185,18 @@ declare class QQChannel extends BaseChannel<Config["channels"]["qq"]> {
184
185
  start(): Promise<void>;
185
186
  stop(): Promise<void>;
186
187
  send(msg: OutboundMessage): Promise<void>;
188
+ private sendByMessageType;
187
189
  private handleIncoming;
188
190
  private resolveSenderName;
189
191
  private decorateSpeakerPrefix;
190
192
  private sanitizeSpeakerToken;
193
+ private extractDeclaredName;
191
194
  private isDuplicate;
192
195
  private sendWithTokenRetry;
193
196
  private isTokenExpiredError;
197
+ private isDisallowedUrlParamError;
198
+ private toQqSafeText;
199
+ private extractBlockedUrlToken;
194
200
  private tryConnect;
195
201
  private connect;
196
202
  private createBot;
package/dist/index.js CHANGED
@@ -893,10 +893,10 @@ function chunkDiscordText(text, opts = {}) {
893
893
  preserveWhitespace: wasInsideFence
894
894
  });
895
895
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
896
- const segment2 = segments[segmentIndex];
896
+ const segment = segments[segmentIndex];
897
897
  const isContinuation = segmentIndex > 0;
898
898
  const delimiter = isContinuation ? "" : current.length > 0 ? "\n" : "";
899
- const addition = `${delimiter}${segment2}`;
899
+ const addition = `${delimiter}${segment}`;
900
900
  const nextLength = current.length + addition.length;
901
901
  const nextLineCount = currentLines + (isContinuation ? 0 : 1);
902
902
  const exceedsChars = nextLength > charLimit;
@@ -910,7 +910,7 @@ function chunkDiscordText(text, opts = {}) {
910
910
  currentLines += 1;
911
911
  }
912
912
  } else {
913
- current = segment2;
913
+ current = segment;
914
914
  currentLines = 1;
915
915
  }
916
916
  }
@@ -2414,14 +2414,14 @@ function sleep3(ms) {
2414
2414
  import {
2415
2415
  Bot,
2416
2416
  ReceiverMode,
2417
- SessionEvents,
2418
- segment
2417
+ SessionEvents
2419
2418
  } from "qq-official-bot";
2420
2419
  var QQChannel = class extends BaseChannel {
2421
2420
  name = "qq";
2422
2421
  bot = null;
2423
2422
  processedIds = [];
2424
2423
  processedSet = /* @__PURE__ */ new Set();
2424
+ senderNameCache = /* @__PURE__ */ new Map();
2425
2425
  reconnectTimer = null;
2426
2426
  connectTask = null;
2427
2427
  reconnectAttempt = 0;
@@ -2458,7 +2458,20 @@ var QQChannel = class extends BaseChannel {
2458
2458
  const metadataMessageId = msg.metadata?.message_id ?? null;
2459
2459
  const sourceId = msg.replyTo ?? metadataMessageId ?? void 0;
2460
2460
  const source = sourceId ? { id: sourceId } : void 0;
2461
- const payload = this.config.markdownSupport ? segment.markdown(msg.content ?? "") : msg.content ?? "";
2461
+ const rawContent = msg.content ?? "";
2462
+ const payload = rawContent;
2463
+ try {
2464
+ await this.sendByMessageType({ messageType, qqMeta, msg, payload, source });
2465
+ } catch (error) {
2466
+ if (!this.isDisallowedUrlParamError(error)) {
2467
+ throw error;
2468
+ }
2469
+ const safeText = this.toQqSafeText(rawContent, error);
2470
+ await this.sendByMessageType({ messageType, qqMeta, msg, payload: safeText, source });
2471
+ }
2472
+ }
2473
+ async sendByMessageType(params) {
2474
+ const { messageType, qqMeta, msg, payload, source } = params;
2462
2475
  if (messageType === "group") {
2463
2476
  const groupId = qqMeta.groupId ?? msg.chatId;
2464
2477
  await this.sendWithTokenRetry(() => this.bot?.sendGroupMessage(groupId, payload, source));
@@ -2492,7 +2505,15 @@ var QQChannel = class extends BaseChannel {
2492
2505
  }
2493
2506
  const content = event.raw_message?.trim() ?? "";
2494
2507
  const normalizedContent = content || "[empty message]";
2495
- const senderName = this.resolveSenderName(rawEvent);
2508
+ const eventSenderName = this.resolveSenderName(rawEvent);
2509
+ if (eventSenderName) {
2510
+ this.senderNameCache.set(senderId, eventSenderName);
2511
+ }
2512
+ const declaredName = this.extractDeclaredName(normalizedContent);
2513
+ if (declaredName) {
2514
+ this.senderNameCache.set(senderId, declaredName);
2515
+ }
2516
+ const senderName = declaredName ?? eventSenderName ?? this.senderNameCache.get(senderId) ?? null;
2496
2517
  let chatId = senderId;
2497
2518
  let messageType = "private";
2498
2519
  const qqMeta = {};
@@ -2524,6 +2545,9 @@ var QQChannel = class extends BaseChannel {
2524
2545
  }
2525
2546
  } else {
2526
2547
  qqMeta.userId = senderId;
2548
+ if (senderName) {
2549
+ qqMeta.userName = senderName;
2550
+ }
2527
2551
  }
2528
2552
  qqMeta.messageType = messageType;
2529
2553
  const safeContent = this.decorateSpeakerPrefix({
@@ -2554,7 +2578,8 @@ var QQChannel = class extends BaseChannel {
2554
2578
  rawEvent.sender?.card,
2555
2579
  rawEvent.sender?.nickname,
2556
2580
  rawEvent.sender?.nick,
2557
- rawEvent.sender?.username
2581
+ rawEvent.sender?.username,
2582
+ rawEvent.sender?.user_name
2558
2583
  ];
2559
2584
  for (const value of candidates) {
2560
2585
  if (typeof value !== "string") {
@@ -2568,9 +2593,6 @@ var QQChannel = class extends BaseChannel {
2568
2593
  return null;
2569
2594
  }
2570
2595
  decorateSpeakerPrefix(params) {
2571
- if (params.messageType !== "group" && params.messageType !== "guild") {
2572
- return params.content;
2573
- }
2574
2596
  const userId = this.sanitizeSpeakerToken(params.senderId);
2575
2597
  if (!userId) {
2576
2598
  return params.content;
@@ -2585,6 +2607,25 @@ var QQChannel = class extends BaseChannel {
2585
2607
  sanitizeSpeakerToken(value) {
2586
2608
  return value.replace(/[\r\n;\]]/g, " ").trim();
2587
2609
  }
2610
+ extractDeclaredName(content) {
2611
+ const trimmed = content.trim();
2612
+ const patterns = [
2613
+ /^我的昵称是\s*([^\s,。!?!?,]{1,24})$/u,
2614
+ /^我叫\s*([^\s,。!?!?,]{1,24})$/u,
2615
+ /^叫我\s*([^\s,。!?!?,]{1,24})$/u
2616
+ ];
2617
+ for (const pattern of patterns) {
2618
+ const match = trimmed.match(pattern);
2619
+ if (!match) {
2620
+ continue;
2621
+ }
2622
+ const candidate = this.sanitizeSpeakerToken(match[1] ?? "");
2623
+ if (candidate) {
2624
+ return candidate;
2625
+ }
2626
+ }
2627
+ return null;
2628
+ }
2588
2629
  isDuplicate(messageId) {
2589
2630
  if (this.processedSet.has(messageId)) {
2590
2631
  return true;
@@ -2614,6 +2655,27 @@ var QQChannel = class extends BaseChannel {
2614
2655
  const message = error instanceof Error ? error.message : String(error);
2615
2656
  return message.includes("code(11244)") || message.toLowerCase().includes("token not exist or expire");
2616
2657
  }
2658
+ isDisallowedUrlParamError(error) {
2659
+ const message = error instanceof Error ? error.message : String(error);
2660
+ return message.includes("code(40034028)") || message.includes("\u8BF7\u6C42\u53C2\u6570\u4E0D\u5141\u8BB8\u5305\u542Burl");
2661
+ }
2662
+ toQqSafeText(content, error) {
2663
+ let safe = content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1").replace(/https?:\/\/\S+/gi, "[link]").replace(/www\.\S+/gi, "[link]").replace(/\b[a-z0-9._/-]+\.md\b/gi, "[file]");
2664
+ const blocked = this.extractBlockedUrlToken(error);
2665
+ if (blocked) {
2666
+ safe = safe.replaceAll(blocked, "[link]");
2667
+ }
2668
+ return safe;
2669
+ }
2670
+ extractBlockedUrlToken(error) {
2671
+ const message = error instanceof Error ? error.message : String(error);
2672
+ const match = message.match(/包含url\s+([^\s]+)/);
2673
+ if (!match) {
2674
+ return null;
2675
+ }
2676
+ const token = match[1].trim();
2677
+ return token.length > 0 ? token : null;
2678
+ }
2617
2679
  tryConnect(trigger) {
2618
2680
  if (!this.running || this.bot || this.connectTask) {
2619
2681
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/channel-runtime",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "private": false,
5
5
  "description": "Runtime implementations for NextClaw builtin channel plugins.",
6
6
  "type": "module",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "dependencies": {
17
17
  "@larksuiteoapi/node-sdk": "^1.58.0",
18
- "@nextclaw/core": "^0.6.41",
18
+ "@nextclaw/core": "^0.6.43",
19
19
  "@slack/socket-mode": "^1.3.3",
20
20
  "@slack/web-api": "^7.6.0",
21
21
  "dingtalk-stream": "^2.1.4",