@satorijs/adapter-lark 3.6.0 → 3.6.2

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/lib/http.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { Adapter, Context, Schema } from '@satorijs/core';
2
- import { FeishuBot } from './bot';
2
+ import { LarkBot } from './bot';
3
3
  import { EventPayload } from './types';
4
- export declare class HttpServer<C extends Context = Context> extends Adapter<C, FeishuBot<C>> {
4
+ export declare class HttpServer<C extends Context = Context> extends Adapter<C, LarkBot<C>> {
5
5
  static inject: string[];
6
6
  private logger;
7
7
  private ciphers;
8
- constructor(ctx: C, bot: FeishuBot<C>);
9
- fork(ctx: C, bot: FeishuBot<C>): Promise<void>;
10
- connect(bot: FeishuBot): Promise<void>;
8
+ constructor(ctx: C, bot: LarkBot<C>);
9
+ fork(ctx: C, bot: LarkBot<C>): Promise<void>;
10
+ connect(bot: LarkBot): Promise<void>;
11
11
  dispatchSession(body: EventPayload): Promise<void>;
12
12
  private _tryDecryptBody;
13
13
  private _refreshCipher;
package/lib/index.cjs CHANGED
@@ -116,6 +116,15 @@ async function adaptSession(bot, body) {
116
116
  adaptSender(body.event.sender, session);
117
117
  await adaptMessage(bot, body.event, session);
118
118
  break;
119
+ case "application.bot.menu_v6":
120
+ if (body.event.event_key.startsWith("command:")) {
121
+ session.type = "interaction/command";
122
+ session.content = body.event.event_key.slice(8);
123
+ session.channelId = body.event.operator.operator_id.open_id;
124
+ session.userId = body.event.operator.operator_id.open_id;
125
+ session.isDirect = true;
126
+ }
127
+ break;
119
128
  case "card.action.trigger":
120
129
  if (body.event.action.value?._satori_type === "command") {
121
130
  session.type = "interaction/command";
@@ -138,11 +147,16 @@ async function adaptSession(bot, body) {
138
147
  for (const [key, value] of Object.entries(options)) {
139
148
  content += ` --${key} ${value}`;
140
149
  }
150
+ if (body.event.action.input_value) {
151
+ content += ` ${body.event.action.input_value}`;
152
+ }
141
153
  session.content = content;
142
154
  session.messageId = body.event.context.open_message_id;
143
155
  session.channelId = body.event.context.open_chat_id;
144
156
  session.guildId = body.event.context.open_chat_id;
145
157
  session.userId = body.event.operator.open_id;
158
+ const { data } = await bot.internal.getImChat(session.channelId);
159
+ session.isDirect = data.chat_mode === "p2p";
146
160
  }
147
161
  break;
148
162
  }
@@ -314,6 +328,7 @@ var HttpServer = class extends import_core2.Adapter {
314
328
  }
315
329
  bot.logger.debug("received decryped event: %o", body);
316
330
  this.dispatchSession(body);
331
+ ctx.body = {};
317
332
  return ctx.status = 200;
318
333
  });
319
334
  bot.ctx.server.get(path + "/assets/:type/:message_id/:key", async (ctx) => {
@@ -385,18 +400,22 @@ var LarkMessageEncoder = class extends import_core3.MessageEncoder {
385
400
  __name(this, "LarkMessageEncoder");
386
401
  }
387
402
  quote;
388
- content = "";
389
- addition;
390
- // TODO: currently not used, would be supported in the future
391
- richText;
403
+ textContent = "";
404
+ richContent = [];
405
+ card;
406
+ noteElements;
407
+ actionElements = [];
392
408
  async post(data) {
393
409
  try {
394
410
  let resp;
395
- if (this.quote) {
396
- resp = await this.bot.internal.replyImMessage(this.quote, data);
411
+ if (this.quote?.id) {
412
+ resp = await this.bot.internal.replyImMessage(this.quote.id, {
413
+ ...data,
414
+ reply_in_thread: this.quote.replyInThread
415
+ });
397
416
  } else {
398
417
  data.receive_id = this.channelId;
399
- resp = await this.bot.internal?.createImMessage(data, {
418
+ resp = await this.bot.internal.createImMessage(data, {
400
419
  receive_id_type: extractIdType(this.channelId)
401
420
  });
402
421
  }
@@ -418,121 +437,264 @@ var LarkMessageEncoder = class extends import_core3.MessageEncoder {
418
437
  this.errors.push(e);
419
438
  }
420
439
  }
421
- async flush() {
422
- if (this.content === "" && !this.addition && !this.richText) return;
423
- let message;
424
- if (this.addition) {
425
- message = {
426
- ...message,
427
- ...this.addition.file
428
- };
440
+ flushText(button = false) {
441
+ if ((this.textContent || !button) && this.actionElements.length) {
442
+ this.card.elements.push({ tag: "action", actions: this.actionElements, layout: "flow" });
443
+ this.actionElements = [];
429
444
  }
430
- if (this.richText) {
431
- message = { zh_cn: this.richText };
445
+ if (this.textContent) {
446
+ this.richContent.push([{ tag: "md", text: this.textContent }]);
447
+ if (this.noteElements) {
448
+ this.noteElements.push({ tag: "plain_text", content: this.textContent });
449
+ } else if (this.card) {
450
+ this.card.elements.push({ tag: "markdown", content: this.textContent });
451
+ }
452
+ this.textContent = "";
432
453
  }
433
- if (this.content) {
434
- message = { text: this.content };
454
+ }
455
+ async flush() {
456
+ this.flushText();
457
+ if (!this.card && !this.richContent.length) return;
458
+ if (this.card) {
459
+ this.bot.logger.debug("card", JSON.stringify(this.card.elements));
460
+ await this.post({
461
+ msg_type: "interactive",
462
+ content: JSON.stringify({
463
+ elements: this.card.elements
464
+ })
465
+ });
466
+ } else {
467
+ await this.post({
468
+ msg_type: "post",
469
+ content: JSON.stringify({
470
+ zh_cn: {
471
+ content: this.richContent
472
+ }
473
+ })
474
+ });
435
475
  }
436
- await this.post({
437
- msg_type: this.richText ? "post" : this.addition ? this.addition.type : "text",
438
- content: JSON.stringify(message)
439
- });
440
476
  this.quote = void 0;
441
- this.content = "";
442
- this.addition = void 0;
443
- this.richText = void 0;
477
+ this.textContent = "";
478
+ this.richContent = [];
479
+ this.card = void 0;
444
480
  }
445
- async sendFile(type, url) {
481
+ async createImage(url) {
482
+ const { filename, type, data } = await this.bot.assetsQuester.file(url);
446
483
  const payload = new FormData();
447
- const assetKey = type === "img" || type === "image" ? "image" : "file";
448
- const { filename, mime, data } = await this.bot.assetsQuester.file(url);
449
- payload.append(assetKey, new Blob([data], { type: mime }), filename);
450
- if (type === "img" || type === "image") {
451
- payload.append("image_type", "message");
452
- const { data: data2 } = await this.bot.internal.createImImage(payload);
453
- return {
454
- type: "image",
455
- file: {
456
- image_key: data2.image_key
457
- }
458
- };
484
+ payload.append("image", new Blob([data], { type }), filename);
485
+ payload.append("image_type", "message");
486
+ const { data: { image_key } } = await this.bot.internal.createImImage(payload);
487
+ return image_key;
488
+ }
489
+ async sendFile(_type, attrs) {
490
+ const url = attrs.src || attrs.url;
491
+ const payload = new FormData();
492
+ const { filename, type, data } = await this.bot.assetsQuester.file(url);
493
+ payload.append("file", new Blob([data], { type }), filename);
494
+ payload.append("file_name", filename);
495
+ if (attrs.duration) {
496
+ payload.append("duration", attrs.duration);
497
+ }
498
+ if (_type === "audio") {
499
+ payload.append("file_type", "opus");
500
+ } else if (_type === "video") {
501
+ payload.append("file_type", "mp4");
459
502
  } else {
460
- let msgType = "file";
461
- if (type === "audio") {
462
- payload.append("file_type", "opus");
463
- msgType = "audio";
464
- } else if (type === "video") {
465
- payload.append("file_type", "mp4");
466
- msgType = "media";
503
+ const ext = filename.split(".").pop();
504
+ if (["doc", "xls", "ppt", "pdf"].includes(ext)) {
505
+ payload.append("file_type", ext);
467
506
  } else {
468
- const ext = filename.split(".").pop();
469
- if (["xls", "ppt", "pdf"].includes(ext)) {
470
- payload.append("file_type", ext);
471
- } else {
472
- payload.append("file_type", "stream");
473
- }
507
+ payload.append("file_type", "stream");
474
508
  }
475
- payload.append("file_name", filename);
476
- const { data: data2 } = await this.bot.internal.createImFile(payload);
477
- return {
478
- type: msgType,
479
- file: {
480
- file_key: data2.file_key
509
+ }
510
+ const { data: { file_key } } = await this.bot.internal.createImFile(payload);
511
+ await this.post({
512
+ msg_type: _type === "video" ? "media" : _type,
513
+ content: JSON.stringify({ file_key })
514
+ });
515
+ }
516
+ createBehavior(attrs) {
517
+ const behaviors = [];
518
+ if (attrs.type === "link") {
519
+ behaviors.push({
520
+ type: "open_url",
521
+ default_url: attrs.href
522
+ });
523
+ } else if (attrs.type === "input") {
524
+ behaviors.push({
525
+ type: "callback",
526
+ value: {
527
+ _satori_type: "command",
528
+ content: attrs.text
481
529
  }
482
- };
530
+ });
531
+ } else if (attrs.type === "action") {
483
532
  }
533
+ return behaviors.length ? behaviors : void 0;
484
534
  }
485
535
  async visit(element) {
486
536
  const { type, attrs, children } = element;
487
- switch (type) {
488
- case "text":
489
- this.content += attrs.content;
490
- break;
491
- case "at": {
492
- if (attrs.type === "all") {
493
- this.content += `<at user_id="all">${attrs.name ?? "所有人"}</at>`;
494
- } else {
495
- this.content += `<at user_id="${attrs.id}">${attrs.name}</at>`;
496
- }
497
- break;
537
+ if (type === "text") {
538
+ this.textContent += attrs.content;
539
+ } else if (type === "at") {
540
+ if (attrs.type === "all") {
541
+ this.textContent += `<at user_id="all">${attrs.name ?? "所有人"}</at>`;
542
+ } else {
543
+ this.textContent += `<at user_id="${attrs.id}">${attrs.name}</at>`;
498
544
  }
499
- case "a":
500
- await this.render(children);
501
- if (attrs.href) this.content += ` (${attrs.href})`;
502
- break;
503
- case "p":
504
- if (!this.content.endsWith("\n")) this.content += "\n";
505
- await this.render(children);
506
- if (!this.content.endsWith("\n")) this.content += "\n";
507
- break;
508
- case "br":
509
- this.content += "\n";
510
- break;
511
- case "sharp":
512
- break;
513
- case "quote":
545
+ } else if (type === "a") {
546
+ await this.render(children);
547
+ if (attrs.href) this.textContent += ` (${attrs.href})`;
548
+ } else if (type === "p") {
549
+ if (!this.textContent.endsWith("\n")) this.textContent += "\n";
550
+ await this.render(children);
551
+ if (!this.textContent.endsWith("\n")) this.textContent += "\n";
552
+ } else if (type === "br") {
553
+ this.textContent += "\n";
554
+ } else if (type === "sharp") {
555
+ } else if (type === "quote") {
556
+ await this.flush();
557
+ this.quote = attrs;
558
+ } else if (type === "img" || type === "image") {
559
+ const image_key = await this.createImage(attrs.src || attrs.url);
560
+ this.textContent += `![${attrs.alt ?? "图片"}](${image_key})`;
561
+ this.flushText();
562
+ this.richContent.push([{ tag: "img", image_key }]);
563
+ } else if (["video", "audio", "file"].includes(type)) {
564
+ await this.flush();
565
+ await this.sendFile(type, attrs);
566
+ } else if (type === "figure" || type === "message") {
567
+ await this.flush();
568
+ await this.render(children, true);
569
+ } else if (type === "hr") {
570
+ this.flushText();
571
+ this.richContent.push([{ tag: "hr" }]);
572
+ this.card?.elements.push({ tag: "hr" });
573
+ } else if (type === "form") {
574
+ this.flushText();
575
+ const length = this.card?.elements.length;
576
+ await this.render(children);
577
+ if (this.card?.elements.length > length) {
578
+ const elements = this.card?.elements.slice(length);
579
+ this.card.elements.push({
580
+ tag: "form",
581
+ name: attrs.name || "Form",
582
+ elements
583
+ });
584
+ }
585
+ } else if (type === "input") {
586
+ this.flushText();
587
+ this.card?.elements.push({
588
+ tag: "action",
589
+ actions: [{
590
+ tag: "input",
591
+ name: attrs.name,
592
+ width: attrs.width,
593
+ label: attrs.label && {
594
+ tag: "plain_text",
595
+ content: attrs.label
596
+ },
597
+ placeholder: attrs.placeholder && {
598
+ tag: "plain_text",
599
+ content: attrs.placeholder
600
+ },
601
+ behaviors: this.createBehavior(attrs)
602
+ }]
603
+ });
604
+ } else if (type === "button") {
605
+ this.card ??= { elements: [] };
606
+ this.flushText(true);
607
+ await this.render(children);
608
+ this.actionElements.push({
609
+ tag: "button",
610
+ text: {
611
+ tag: "plain_text",
612
+ content: this.textContent
613
+ },
614
+ disabled: attrs.disabled,
615
+ behaviors: this.createBehavior(attrs)
616
+ });
617
+ this.textContent = "";
618
+ } else if (type === "button-group") {
619
+ this.flushText();
620
+ await this.render(children);
621
+ this.flushText();
622
+ } else if (type.startsWith("lark:") || type.startsWith("feishu:")) {
623
+ const tag = type.slice(type.split(":", 1)[0].length + 1);
624
+ if (tag === "share-chat") {
514
625
  await this.flush();
515
- this.quote = attrs.id;
516
- break;
517
- case "img":
518
- case "image":
519
- case "video":
520
- case "audio":
521
- case "file":
522
- if (attrs.src || attrs.url) {
523
- await this.flush();
524
- this.addition = await this.sendFile(type, attrs.src || attrs.url);
525
- await this.flush();
526
- }
527
- break;
528
- case "figure":
529
- // FIXME: treat as message element for now
530
- case "message":
626
+ await this.post({
627
+ msg_type: "share_chat",
628
+ content: JSON.stringify({ chat_id: attrs.chatId })
629
+ });
630
+ } else if (tag === "share-user") {
631
+ await this.flush();
632
+ await this.post({
633
+ msg_type: "share_user",
634
+ content: JSON.stringify({ user_id: attrs.userId })
635
+ });
636
+ } else if (tag === "system") {
637
+ await this.flush();
638
+ await this.render(children);
639
+ await this.post({
640
+ msg_type: "system",
641
+ content: JSON.stringify({
642
+ type: "divider",
643
+ params: { divider_text: { text: this.textContent } },
644
+ options: { need_rollup: attrs.needRollup }
645
+ })
646
+ });
647
+ this.textContent = "";
648
+ } else if (tag === "card") {
531
649
  await this.flush();
650
+ this.card = {
651
+ elements: [],
652
+ header: attrs.title && {
653
+ template: attrs.color,
654
+ ud_icon: attrs.icon && {
655
+ tag: "standard_icon",
656
+ token: attrs.icon
657
+ },
658
+ title: {
659
+ tag: "plain_text",
660
+ content: attrs.title
661
+ },
662
+ subtitle: attrs.subtitle && {
663
+ tag: "plain_text",
664
+ content: attrs.subtitle
665
+ }
666
+ }
667
+ };
532
668
  await this.render(children, true);
533
- break;
534
- default:
669
+ } else if (tag === "div") {
670
+ this.flushText();
671
+ await this.render(children);
672
+ this.card?.elements.push({
673
+ tag: "markdown",
674
+ text_align: attrs.align,
675
+ text_size: attrs.size,
676
+ content: this.textContent
677
+ });
678
+ this.textContent = "";
679
+ } else if (tag === "note") {
680
+ this.flushText();
681
+ this.noteElements = [];
535
682
  await this.render(children);
683
+ this.flushText();
684
+ this.card?.elements.push({
685
+ tag: "note",
686
+ elements: this.noteElements
687
+ });
688
+ this.noteElements = void 0;
689
+ } else if (tag === "icon") {
690
+ this.flushText();
691
+ this.noteElements?.push({
692
+ tag: "standard_icon",
693
+ token: attrs.token
694
+ });
695
+ }
696
+ } else {
697
+ await this.render(children);
536
698
  }
537
699
  }
538
700
  };