openzca 0.1.15 → 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 +104 -1
  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 = [];
@@ -2791,6 +2883,12 @@ ${replyContextText}` : replyContextText;
2791
2883
  const senderNameForMetadata = message.type === ThreadType.Group ? senderDisplayName : void 0;
2792
2884
  const toId = getStringCandidate(messageData, ["idTo"]) || void 0;
2793
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);
2794
2892
  const timestamp = toEpochSeconds(message.data.ts);
2795
2893
  const payload = {
2796
2894
  threadId: message.threadId,
@@ -2816,6 +2914,8 @@ ${replyContextText}` : replyContextText;
2816
2914
  mediaType,
2817
2915
  mediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0,
2818
2916
  mediaKind: mediaKind ?? void 0,
2917
+ mentions: mentions.length > 0 ? mentions : void 0,
2918
+ mentionIds: mentionIds.length > 0 ? mentionIds : void 0,
2819
2919
  metadata: {
2820
2920
  isGroup: message.type === ThreadType.Group,
2821
2921
  chatType,
@@ -2842,7 +2942,10 @@ ${replyContextText}` : replyContextText;
2842
2942
  mediaUrls: mediaUrls.length > 0 ? mediaUrls : void 0,
2843
2943
  mediaType,
2844
2944
  mediaTypes: mediaTypes.length > 0 ? mediaTypes : void 0,
2845
- 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
2846
2949
  },
2847
2950
  // Backward-compatible convenience fields.
2848
2951
  chatType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzca",
3
- "version": "0.1.15",
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": {