openzca 0.1.14 → 0.1.16

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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/cli.js +110 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -206,7 +206,10 @@ It also includes stable routing fields for downstream tools:
206
206
 
207
207
  - `threadId`, `targetId`, `conversationId`
208
208
  - `senderId`, `toId`, `chatType`, `msgType`, `timestamp`
209
+ - `mentions` (normalized mention entities: `uid`, `pos`, `len`, `type`, optional `text`)
210
+ - `mentionIds` (flattened mention user IDs)
209
211
  - `metadata.threadId`, `metadata.targetId`, `metadata.senderId`, `metadata.toId`
212
+ - `metadata.mentions`, `metadata.mentionIds`, `metadata.mentionCount`
210
213
  - `quote` and `metadata.quote` when the inbound message is a reply to a previous message
211
214
  - Includes parsed `quote.attach` and extracted `quote.mediaUrls` when attachment URLs are present.
212
215
  - `quoteMediaPath`, `quoteMediaPaths`, `quoteMediaUrl`, `quoteMediaUrls`, `quoteMediaType`, `quoteMediaTypes`
package/dist/cli.js CHANGED
@@ -1459,6 +1459,98 @@ function buildReplyMediaAttachedText(params) {
1459
1459
  }
1460
1460
  return lines.join("\n");
1461
1461
  }
1462
+ function parseOptionalInt(value) {
1463
+ const numeric = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
1464
+ if (!Number.isFinite(numeric)) return void 0;
1465
+ return Math.trunc(numeric);
1466
+ }
1467
+ function buildInboundMention(record, rawText) {
1468
+ const uid = getStringCandidate(record, ["uid", "userId", "user_id", "id"]);
1469
+ if (!uid) return null;
1470
+ const pos = parseOptionalInt(record.pos ?? record.offset ?? record.start ?? record.index);
1471
+ const len = parseOptionalInt(record.len ?? record.length);
1472
+ const type = parseOptionalInt(record.type ?? record.kind);
1473
+ let text = getStringCandidate(record, ["text", "label", "name"]) || (typeof pos === "number" && typeof len === "number" && len > 0 && pos >= 0 && pos < rawText.length ? rawText.slice(pos, Math.min(rawText.length, pos + len)) : "");
1474
+ if (!text.trim()) {
1475
+ text = "";
1476
+ }
1477
+ return {
1478
+ uid,
1479
+ ...typeof pos === "number" ? { pos } : {},
1480
+ ...typeof len === "number" ? { len } : {},
1481
+ ...typeof type === "number" ? { type } : {},
1482
+ ...text ? { text } : {}
1483
+ };
1484
+ }
1485
+ function collectInboundMentions(value, sink, rawText, depth = 0) {
1486
+ if (depth > 6 || sink.size >= 64 || value === null || value === void 0) {
1487
+ return;
1488
+ }
1489
+ if (typeof value === "string") {
1490
+ if (!looksLikeStructuredJsonString(value)) return;
1491
+ try {
1492
+ const parsed = JSON.parse(value);
1493
+ collectInboundMentions(parsed, sink, rawText, depth + 1);
1494
+ } catch {
1495
+ }
1496
+ return;
1497
+ }
1498
+ if (Array.isArray(value)) {
1499
+ for (const item of value) {
1500
+ const mention = asObject(item) ? buildInboundMention(item, rawText) : null;
1501
+ if (mention) {
1502
+ const key = `${mention.uid}|${mention.pos ?? ""}|${mention.len ?? ""}|${mention.type ?? ""}`;
1503
+ sink.set(key, mention);
1504
+ continue;
1505
+ }
1506
+ collectInboundMentions(item, sink, rawText, depth + 1);
1507
+ if (sink.size >= 64) return;
1508
+ }
1509
+ return;
1510
+ }
1511
+ const record = asObject(value);
1512
+ if (!record) return;
1513
+ const direct = buildInboundMention(record, rawText);
1514
+ if (direct) {
1515
+ const key = `${direct.uid}|${direct.pos ?? ""}|${direct.len ?? ""}|${direct.type ?? ""}`;
1516
+ sink.set(key, direct);
1517
+ }
1518
+ const mentionKeys = [
1519
+ "mentions",
1520
+ "mentionInfo",
1521
+ "mention_info",
1522
+ "mentionList",
1523
+ "mention_list",
1524
+ "mention"
1525
+ ];
1526
+ for (const key of mentionKeys) {
1527
+ if (!(key in record)) continue;
1528
+ collectInboundMentions(record[key], sink, rawText, depth + 1);
1529
+ if (sink.size >= 64) return;
1530
+ }
1531
+ for (const nested of Object.values(record)) {
1532
+ collectInboundMentions(nested, sink, rawText, depth + 1);
1533
+ if (sink.size >= 64) return;
1534
+ }
1535
+ }
1536
+ function extractInboundMentions(params) {
1537
+ const sink = /* @__PURE__ */ new Map();
1538
+ const candidates = [
1539
+ params.messageData.mentions,
1540
+ params.messageData.mentionInfo,
1541
+ params.messageData.mention_info,
1542
+ params.messageData.mentionList,
1543
+ params.messageData.mention_list,
1544
+ params.messageData.mention,
1545
+ params.messageData.content,
1546
+ params.parsedContent
1547
+ ];
1548
+ for (const candidate of candidates) {
1549
+ collectInboundMentions(candidate, sink, params.rawText);
1550
+ if (sink.size >= 64) break;
1551
+ }
1552
+ return [...sink.values()];
1553
+ }
1462
1554
  function normalizeFriendLookupRows(value) {
1463
1555
  const queue = [value];
1464
1556
  const rows = [];
@@ -2781,11 +2873,22 @@ ${replyContextText}` : replyContextText;
2781
2873
  }
2782
2874
  const chatType = message.type === ThreadType.Group ? "group" : "user";
2783
2875
  const senderId = getStringCandidate(messageData, ["uidFrom"]) || message.data.uidFrom;
2784
- const senderDisplayNameRaw = getStringCandidate(messageData, ["dName"]);
2876
+ const senderDisplayNameRaw = getStringCandidate(messageData, [
2877
+ "dName",
2878
+ "fromD",
2879
+ "senderName",
2880
+ "displayName"
2881
+ ]);
2785
2882
  const senderDisplayName = senderDisplayNameRaw || void 0;
2786
2883
  const senderNameForMetadata = message.type === ThreadType.Group ? senderDisplayName : void 0;
2787
2884
  const toId = getStringCandidate(messageData, ["idTo"]) || void 0;
2788
2885
  const threadName = typeof messageData.threadName === "string" ? messageData.threadName : typeof messageData.tName === "string" ? messageData.tName : void 0;
2886
+ const mentions = extractInboundMentions({
2887
+ messageData,
2888
+ parsedContent,
2889
+ rawText
2890
+ });
2891
+ const mentionIds = mentions.map((item) => item.uid);
2789
2892
  const timestamp = toEpochSeconds(message.data.ts);
2790
2893
  const payload = {
2791
2894
  threadId: message.threadId,
@@ -2811,6 +2914,8 @@ ${replyContextText}` : replyContextText;
2811
2914
  mediaType,
2812
2915
  mediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0,
2813
2916
  mediaKind: mediaKind ?? void 0,
2917
+ mentions: mentions.length > 0 ? mentions : void 0,
2918
+ mentionIds: mentionIds.length > 0 ? mentionIds : void 0,
2814
2919
  metadata: {
2815
2920
  isGroup: message.type === ThreadType.Group,
2816
2921
  chatType,
@@ -2837,7 +2942,10 @@ ${replyContextText}` : replyContextText;
2837
2942
  mediaUrls: mediaUrls.length > 0 ? mediaUrls : void 0,
2838
2943
  mediaType,
2839
2944
  mediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0,
2840
- mediaKind: mediaKind ?? void 0
2945
+ mediaKind: mediaKind ?? void 0,
2946
+ mentions: mentions.length > 0 ? mentions : void 0,
2947
+ mentionIds: mentionIds.length > 0 ? mentionIds : void 0,
2948
+ mentionCount: mentions.length > 0 ? mentions.length : void 0
2841
2949
  },
2842
2950
  // Backward-compatible convenience fields.
2843
2951
  chatType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzca",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Open-source zca-compatible CLI to integrate Zalo with OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {