chattercatcher 0.1.30 → 0.1.31

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/cli.js CHANGED
@@ -8,7 +8,7 @@ import fs15 from "fs/promises";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "chattercatcher",
11
- version: "0.1.30",
11
+ version: "0.1.31",
12
12
  description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
13
13
  type: "module",
14
14
  main: "dist/index.js",
@@ -4439,6 +4439,166 @@ var FeishuQuestionHandler = class {
4439
4439
  // src/feishu/sender.ts
4440
4440
  import * as lark from "@larksuiteoapi/node-sdk";
4441
4441
  import fs9 from "fs/promises";
4442
+
4443
+ // src/feishu/markdown-post.ts
4444
+ function escapeAtText(value) {
4445
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4446
+ }
4447
+ function formatTextWithMentions(text, options) {
4448
+ const mentions = options?.mentions ?? [];
4449
+ if (mentions.length === 0) return text;
4450
+ const prefix = mentions.map((mention) => `<at user_id="${escapeAtText(mention.openId)}">${escapeAtText(mention.name)}</at>`).join(" ");
4451
+ return `${prefix} ${text}`.trim();
4452
+ }
4453
+ function findMarkdownLinkEnd(text, start) {
4454
+ let depth = 0;
4455
+ for (let index2 = start; index2 < text.length; index2 += 1) {
4456
+ const char = text[index2];
4457
+ if (char === "(") {
4458
+ depth += 1;
4459
+ } else if (char === ")") {
4460
+ if (depth === 0) return index2;
4461
+ depth -= 1;
4462
+ }
4463
+ }
4464
+ return -1;
4465
+ }
4466
+ function parseInline(text) {
4467
+ const elements = [];
4468
+ let index2 = 0;
4469
+ while (index2 < text.length) {
4470
+ const linkStart = text.indexOf("[", index2);
4471
+ const boldStarStart = text.indexOf("**", index2);
4472
+ const boldUnderscoreStart = text.indexOf("__", index2);
4473
+ const candidates = [linkStart, boldStarStart, boldUnderscoreStart].filter((value) => value >= 0);
4474
+ const next = candidates.length ? Math.min(...candidates) : -1;
4475
+ if (next < 0) {
4476
+ elements.push({ tag: "text", text: text.slice(index2) });
4477
+ break;
4478
+ }
4479
+ if (next > index2) {
4480
+ elements.push({ tag: "text", text: text.slice(index2, next) });
4481
+ }
4482
+ if (next === linkStart) {
4483
+ const labelEnd = text.indexOf("](", next);
4484
+ if (labelEnd > next) {
4485
+ const hrefStart = labelEnd + 2;
4486
+ const hrefEnd = findMarkdownLinkEnd(text, hrefStart);
4487
+ const href = hrefEnd >= 0 ? text.slice(hrefStart, hrefEnd) : "";
4488
+ if (hrefEnd >= 0 && /^https?:\/\/\S+$/.test(href)) {
4489
+ elements.push({ tag: "a", text: text.slice(next + 1, labelEnd), href });
4490
+ index2 = hrefEnd + 1;
4491
+ continue;
4492
+ }
4493
+ }
4494
+ elements.push({ tag: "text", text: text[next] });
4495
+ index2 = next + 1;
4496
+ continue;
4497
+ }
4498
+ const marker = next === boldStarStart ? "**" : "__";
4499
+ const close = text.indexOf(marker, next + marker.length);
4500
+ if (close > next + marker.length) {
4501
+ elements.push({ tag: "text", text: text.slice(next + marker.length, close), style: ["bold"] });
4502
+ index2 = close + marker.length;
4503
+ continue;
4504
+ }
4505
+ elements.push({ tag: "text", text: marker });
4506
+ index2 = next + marker.length;
4507
+ }
4508
+ const compacted = elements.filter((element) => element.tag !== "text" || element.text.length > 0);
4509
+ return compacted.length ? compacted : [{ tag: "text", text: " " }];
4510
+ }
4511
+ function pushParagraph(content, lines) {
4512
+ if (lines.length === 0) return;
4513
+ content.push(parseInline(lines.join("\n")));
4514
+ lines.length = 0;
4515
+ }
4516
+ function parseMarkdownBlocks(markdown) {
4517
+ if (!markdown.trim()) {
4518
+ return [[{ tag: "text", text: " " }]];
4519
+ }
4520
+ const content = [];
4521
+ const paragraph = [];
4522
+ const code = [];
4523
+ let inCodeBlock = false;
4524
+ for (const rawLine of markdown.replace(/\r\n/g, "\n").split("\n")) {
4525
+ const line = rawLine.trimEnd();
4526
+ if (line.startsWith("```")) {
4527
+ if (inCodeBlock) {
4528
+ content.push([{ tag: "text", text: `\`\`\`
4529
+ ${code.join("\n")}
4530
+ \`\`\`` }]);
4531
+ code.length = 0;
4532
+ inCodeBlock = false;
4533
+ } else {
4534
+ pushParagraph(content, paragraph);
4535
+ inCodeBlock = true;
4536
+ }
4537
+ continue;
4538
+ }
4539
+ if (inCodeBlock) {
4540
+ code.push(rawLine);
4541
+ continue;
4542
+ }
4543
+ if (!line.trim()) {
4544
+ pushParagraph(content, paragraph);
4545
+ continue;
4546
+ }
4547
+ const heading = line.match(/^#{1,6}\s+(.+)$/);
4548
+ if (heading) {
4549
+ pushParagraph(content, paragraph);
4550
+ content.push([{ tag: "text", text: heading[1], style: ["bold"] }]);
4551
+ continue;
4552
+ }
4553
+ const unordered = line.match(/^[-*]\s+(.+)$/);
4554
+ if (unordered) {
4555
+ pushParagraph(content, paragraph);
4556
+ content.push(parseInline(`\u2022 ${unordered[1]}`));
4557
+ continue;
4558
+ }
4559
+ const ordered = line.match(/^(\d+)\.\s+(.+)$/);
4560
+ if (ordered) {
4561
+ pushParagraph(content, paragraph);
4562
+ content.push(parseInline(`${ordered[1]}. ${ordered[2]}`));
4563
+ continue;
4564
+ }
4565
+ paragraph.push(line);
4566
+ }
4567
+ if (inCodeBlock) {
4568
+ content.push([{ tag: "text", text: `\`\`\`
4569
+ ${code.join("\n")}` }]);
4570
+ }
4571
+ pushParagraph(content, paragraph);
4572
+ return content.length ? content : [[{ tag: "text", text: markdown }]];
4573
+ }
4574
+ function buildFeishuPostContent(markdown, options) {
4575
+ const content = parseMarkdownBlocks(markdown);
4576
+ const mentions = options?.mentions ?? [];
4577
+ if (mentions.length) {
4578
+ const mentionElements = mentions.map((mention) => ({
4579
+ tag: "at",
4580
+ user_id: mention.openId,
4581
+ user_name: mention.name
4582
+ }));
4583
+ const firstLine = content[0] ?? [];
4584
+ const firstText = firstLine[0];
4585
+ if (firstText?.tag === "text") {
4586
+ content[0] = [...mentionElements, { ...firstText, text: ` ${firstText.text}` }, ...firstLine.slice(1)];
4587
+ } else {
4588
+ content[0] = [...mentionElements, { tag: "text", text: " " }, ...firstLine];
4589
+ }
4590
+ }
4591
+ return {
4592
+ post: {
4593
+ zh_cn: {
4594
+ title: "",
4595
+ content
4596
+ }
4597
+ }
4598
+ };
4599
+ }
4600
+
4601
+ // src/feishu/sender.ts
4442
4602
  function mapDomain(domain) {
4443
4603
  return domain === "lark" ? lark.Domain.Lark : lark.Domain.Feishu;
4444
4604
  }
@@ -4454,14 +4614,21 @@ function extractImageKey(response) {
4454
4614
  }
4455
4615
  throw new Error("\u98DE\u4E66\u56FE\u7247\u4E0A\u4F20\u54CD\u5E94\u7F3A\u5C11 image_key\u3002");
4456
4616
  }
4457
- function escapeAtText(value) {
4458
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4617
+ function isRichTextCompatibilityError(error) {
4618
+ const value = error && typeof error === "object" ? error : {};
4619
+ const code = value.code ?? value.errorCode;
4620
+ const message = error instanceof Error ? error.message : String(error);
4621
+ return code === 230001 || /post|msg_type|content|unsupported|invalid/i.test(message);
4459
4622
  }
4460
- function formatTextWithMentions(text, options) {
4461
- const mentions = options?.mentions ?? [];
4462
- if (mentions.length === 0) return text;
4463
- const prefix = mentions.map((mention) => `<at user_id="${escapeAtText(mention.openId)}">${escapeAtText(mention.name)}</at>`).join(" ");
4464
- return `${prefix} ${text}`.trim();
4623
+ async function sendWithTextFallback(input2) {
4624
+ try {
4625
+ await input2.sendPost();
4626
+ } catch (error) {
4627
+ if (!isRichTextCompatibilityError(error)) {
4628
+ throw error;
4629
+ }
4630
+ await input2.sendText();
4631
+ }
4465
4632
  }
4466
4633
  var FeishuMessageSender = class _FeishuMessageSender {
4467
4634
  constructor(client) {
@@ -4477,7 +4644,17 @@ var FeishuMessageSender = class _FeishuMessageSender {
4477
4644
  return new _FeishuMessageSender(client);
4478
4645
  }
4479
4646
  async sendTextToChat(chatId, text, options) {
4480
- const payload = {
4647
+ const postPayload = {
4648
+ data: {
4649
+ receive_id: chatId,
4650
+ msg_type: "post",
4651
+ content: JSON.stringify(buildFeishuPostContent(text, options))
4652
+ },
4653
+ params: {
4654
+ receive_id_type: "chat_id"
4655
+ }
4656
+ };
4657
+ const textPayload = {
4481
4658
  data: {
4482
4659
  receive_id: chatId,
4483
4660
  msg_type: "text",
@@ -4488,16 +4665,20 @@ var FeishuMessageSender = class _FeishuMessageSender {
4488
4665
  }
4489
4666
  };
4490
4667
  if (this.client.im.v1?.message.create) {
4491
- await this.client.im.v1.message.create(payload);
4668
+ await sendWithTextFallback({
4669
+ sendPost: () => this.client.im.v1.message.create(postPayload),
4670
+ sendText: () => this.client.im.v1.message.create(textPayload)
4671
+ });
4492
4672
  return;
4493
4673
  }
4494
4674
  if (this.client.im.message?.create) {
4495
- await this.client.im.message.create(payload);
4675
+ await sendWithTextFallback({
4676
+ sendPost: () => this.client.im.message.create(postPayload),
4677
+ sendText: () => this.client.im.message.create(textPayload)
4678
+ });
4496
4679
  return;
4497
4680
  }
4498
- {
4499
- throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u53D1\u9001\u63A5\u53E3\u3002");
4500
- }
4681
+ throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u53D1\u9001\u63A5\u53E3\u3002");
4501
4682
  }
4502
4683
  async sendImageToChat(chatId, imagePath) {
4503
4684
  const imageCreate = this.client.im.v1?.image?.create;
@@ -4533,7 +4714,16 @@ var FeishuMessageSender = class _FeishuMessageSender {
4533
4714
  throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u53D1\u9001\u63A5\u53E3\u3002");
4534
4715
  }
4535
4716
  async replyTextToMessage(messageId, text) {
4536
- const payload = {
4717
+ const postPayload = {
4718
+ path: {
4719
+ message_id: messageId
4720
+ },
4721
+ data: {
4722
+ msg_type: "post",
4723
+ content: JSON.stringify(buildFeishuPostContent(text))
4724
+ }
4725
+ };
4726
+ const textPayload = {
4537
4727
  path: {
4538
4728
  message_id: messageId
4539
4729
  },
@@ -4543,11 +4733,17 @@ var FeishuMessageSender = class _FeishuMessageSender {
4543
4733
  }
4544
4734
  };
4545
4735
  if (this.client.im.v1?.message.reply) {
4546
- await this.client.im.v1.message.reply(payload);
4736
+ await sendWithTextFallback({
4737
+ sendPost: () => this.client.im.v1.message.reply(postPayload),
4738
+ sendText: () => this.client.im.v1.message.reply(textPayload)
4739
+ });
4547
4740
  return;
4548
4741
  }
4549
4742
  if (this.client.im.message?.reply) {
4550
- await this.client.im.message.reply(payload);
4743
+ await sendWithTextFallback({
4744
+ sendPost: () => this.client.im.message.reply(postPayload),
4745
+ sendText: () => this.client.im.message.reply(textPayload)
4746
+ });
4551
4747
  return;
4552
4748
  }
4553
4749
  throw new Error("\u5F53\u524D\u98DE\u4E66 SDK \u4E0D\u652F\u6301\u6D88\u606F\u56DE\u590D\u63A5\u53E3\u3002");