@satorijs/adapter-lark 3.9.5 → 3.10.1

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/bot.d.ts CHANGED
@@ -5,17 +5,14 @@ import { Internal } from './internal';
5
5
  export declare class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config> {
6
6
  static inject: string[];
7
7
  static MessageEncoder: typeof LarkMessageEncoder;
8
- _token?: string;
9
8
  _refresher?: NodeJS.Timeout;
10
9
  http: HTTP;
11
10
  assetsQuester: HTTP;
12
- internal: Internal;
11
+ internal: Internal<C>;
13
12
  constructor(ctx: C, config: LarkBot.Config);
14
13
  getResourceUrl(type: string, message_id: string, file_key: string): string;
15
14
  initialize(): Promise<void>;
16
15
  private refreshToken;
17
- get token(): string;
18
- set token(v: string);
19
16
  editMessage(channelId: string, messageId: string, content: h.Fragment): Promise<void>;
20
17
  deleteMessage(channelId: string, messageId: string): Promise<void>;
21
18
  getMessage(channelId: string, messageId: string, recursive?: boolean): Promise<Universal.Message>;
package/lib/content.d.ts CHANGED
@@ -299,6 +299,9 @@ export declare namespace MessageContent {
299
299
  name?: string;
300
300
  required?: boolean;
301
301
  action_type?: 'link' | 'request' | 'multi' | 'form_submit' | 'form_reset';
302
+ value?: Record<string, string>;
303
+ url?: string;
304
+ multi_url?: Omit<URLs, 'url'>;
302
305
  }
303
306
  interface ConfirmElement {
304
307
  title: PlainTextElement;
@@ -421,7 +424,7 @@ export declare namespace MessageContent {
421
424
  type Width = 'default' | 'fill' | string;
422
425
  type Type = 'default' | 'primary' | 'danger' | 'text' | 'primary_text' | 'danger_text' | 'primary_filled' | 'danger_filled' | 'laser';
423
426
  }
424
- type Element = DivElement | MarkdownElement | HRElement | ActionElement | NoteElement | ChartElement | TableElement | ImageElement | FormElement | InputElement | ButtonElement;
427
+ type Element = DivElement | MarkdownElement | HRElement | ActionElement | NoteElement | ChartElement | TableElement | ImageElement | FormElement | InputElement | ButtonElement | CheckerElement;
425
428
  }
426
429
  interface Template {
427
430
  type: 'template';
package/lib/index.cjs CHANGED
@@ -136,27 +136,43 @@ async function adaptSession(bot, body) {
136
136
  session.type = "interaction/command";
137
137
  let content = body.event.action.value.content;
138
138
  const args = [], options = /* @__PURE__ */ Object.create(null);
139
+ const setOption = /* @__PURE__ */ __name((key, value) => {
140
+ if (key in options) {
141
+ options[key] += "," + value;
142
+ } else {
143
+ options[key] = value;
144
+ }
145
+ }, "setOption");
139
146
  for (const [key, value] of Object.entries(body.event.action.form_value ?? {})) {
140
- if (+key * 0 === 0) {
147
+ if (key.startsWith("@@")) {
148
+ if (value) args.push(key.slice(2));
149
+ } else if (key.startsWith("@")) {
150
+ const [_key] = key.slice(1).split("=", 1);
151
+ setOption(_key, key.slice(2 + _key.length));
152
+ } else if (+key * 0 === 0) {
141
153
  args[+key] = value;
142
154
  } else {
143
- options[key] = value;
155
+ setOption(key, value);
144
156
  }
145
157
  }
146
158
  const toArg = /* @__PURE__ */ __name((value) => {
147
159
  if (typeof value === "string") {
148
160
  return `'${value}'`;
149
- } else if (typeof value === "number") {
150
- return value;
151
161
  } else {
152
- return `''`;
162
+ return value;
153
163
  }
154
164
  }, "toArg");
155
165
  for (let i = 0; i < args.length; ++i) {
156
166
  content += ` ${toArg(args[i])}`;
157
167
  }
158
168
  for (const [key, value] of Object.entries(options)) {
159
- content += ` --${key} ${toArg(value)}`;
169
+ if (value === true) {
170
+ content += ` --${key} 1`;
171
+ } else if (value === false) {
172
+ content += ` --${key} 0`;
173
+ } else {
174
+ content += ` --${key} ${toArg(value)}`;
175
+ }
160
176
  }
161
177
  if (body.event.action.input_value) {
162
178
  content += ` ${toArg(body.event.action.input_value)}`;
@@ -322,6 +338,7 @@ var HttpServer = class extends import_core2.Adapter {
322
338
  });
323
339
  if (!result) return ctx.status = 403;
324
340
  }
341
+ if (!ctx.request.is("json")) return ctx.status = 415;
325
342
  const body = this._tryDecryptBody(ctx.request.body);
326
343
  if (body?.type === "url_verification" && body?.challenge && typeof body.challenge === "string") {
327
344
  ctx.response.body = { challenge: body.challenge };
@@ -576,13 +593,13 @@ var LarkMessageEncoder = class extends import_core3.MessageEncoder {
576
593
  } else if (type === "at") {
577
594
  if (this.card) {
578
595
  if (attrs.type === "all") {
579
- this.textContent += `<at id=all>${attrs.name ?? "所有人"}</at>`;
596
+ this.textContent += `<at id=all>${attrs.name ?? ""}</at>`;
580
597
  } else {
581
598
  this.textContent += `<at id=${attrs.id}>${attrs.name ?? ""}</at>`;
582
599
  }
583
600
  } else {
584
601
  if (attrs.type === "all") {
585
- this.textContent += `<at user_id="all">${attrs.name ?? "所有人"}</at>`;
602
+ this.textContent += `<at user_id="all">${attrs.name ?? ""}</at>`;
586
603
  } else {
587
604
  this.textContent += `<at user_id="${attrs.id}">${attrs.name ?? ""}</at>`;
588
605
  }
@@ -620,7 +637,7 @@ var LarkMessageEncoder = class extends import_core3.MessageEncoder {
620
637
  const length = this.card?.elements.length;
621
638
  await this.render(children);
622
639
  if (this.card?.elements.length > length) {
623
- const elements = this.card?.elements.slice(length);
640
+ const elements = this.card?.elements.splice(length);
624
641
  this.card.elements.push({
625
642
  tag: "form",
626
643
  name: attrs.name || "Form",
@@ -628,24 +645,56 @@ var LarkMessageEncoder = class extends import_core3.MessageEncoder {
628
645
  });
629
646
  }
630
647
  } else if (type === "input") {
631
- this.flushText();
632
- this.card?.elements.push({
633
- tag: "action",
634
- actions: [{
635
- tag: "input",
636
- name: attrs.name,
637
- width: attrs.width,
638
- label: attrs.label && {
648
+ if (attrs.type === "checkbox") {
649
+ this.flushText();
650
+ await this.render(children);
651
+ this.card?.elements.push({
652
+ tag: "checker",
653
+ name: (attrs.argument ? "@@" : attrs.option ? `@${attrs.option}=` : "") + attrs.name,
654
+ checked: attrs.checked,
655
+ text: {
639
656
  tag: "plain_text",
640
- content: attrs.label
641
- },
642
- placeholder: attrs.placeholder && {
657
+ content: this.textContent
658
+ }
659
+ });
660
+ } else if (attrs.type === "submit") {
661
+ this.flushText(true);
662
+ await this.render(children);
663
+ this.card?.elements.push({
664
+ tag: "button",
665
+ name: attrs.name,
666
+ text: {
643
667
  tag: "plain_text",
644
- content: attrs.placeholder
668
+ content: this.textContent
645
669
  },
646
- behaviors: this.createBehavior(attrs)
647
- }]
648
- });
670
+ action_type: "form_submit",
671
+ value: {
672
+ _satori_type: "command",
673
+ content: attrs.text
674
+ }
675
+ });
676
+ } else {
677
+ this.flushText();
678
+ await this.render(children);
679
+ this.card?.elements.push({
680
+ tag: "action",
681
+ actions: [{
682
+ tag: "input",
683
+ name: attrs.name,
684
+ width: attrs.width,
685
+ label: this.textContent && {
686
+ tag: "plain_text",
687
+ content: this.textContent
688
+ },
689
+ placeholder: attrs.placeholder && {
690
+ tag: "plain_text",
691
+ content: attrs.placeholder
692
+ },
693
+ behaviors: this.createBehavior(attrs)
694
+ }]
695
+ });
696
+ }
697
+ this.textContent = "";
649
698
  } else if (type === "button") {
650
699
  this.card ??= { elements: [] };
651
700
  this.flushText(true);
@@ -883,7 +932,6 @@ var LarkBot = class extends import_core5.Bot {
883
932
  }
884
933
  static inject = ["server", "http"];
885
934
  static MessageEncoder = LarkMessageEncoder;
886
- _token;
887
935
  _refresher;
888
936
  http;
889
937
  assetsQuester;
@@ -931,7 +979,7 @@ var LarkBot = class extends import_core5.Bot {
931
979
  app_secret: this.config.appSecret
932
980
  });
933
981
  this.logger.debug("refreshed token %s", token);
934
- this.token = token;
982
+ this.http.config.headers.Authorization = `Bearer ${token}`;
935
983
  } catch (error) {
936
984
  this.logger.error("failed to refresh token, retrying in 10s");
937
985
  this.logger.error(error);
@@ -941,13 +989,6 @@ var LarkBot = class extends import_core5.Bot {
941
989
  this._refresher = setTimeout(() => this.refreshToken(), timeout);
942
990
  this.online();
943
991
  }
944
- get token() {
945
- return this._token;
946
- }
947
- set token(v) {
948
- this._token = v;
949
- this.http.config.headers.Authorization = `Bearer ${v}`;
950
- }
951
992
  async editMessage(channelId, messageId, content) {
952
993
  const encoder = new LarkMessageEncoder(this, channelId);
953
994
  encoder.editMessageIds = [messageId];
package/lib/internal.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dict, HTTP } from '@satorijs/core';
1
+ import { Context, Dict, HTTP } from '@satorijs/core';
2
2
  import { LarkBot } from './bot';
3
3
  export interface Internal {
4
4
  }
@@ -28,9 +28,9 @@ export interface InternalRoute {
28
28
  multipart?: boolean;
29
29
  type?: 'raw-json' | 'binary';
30
30
  }
31
- export declare class Internal {
31
+ export declare class Internal<C extends Context = Context> {
32
32
  private bot;
33
- constructor(bot: LarkBot);
33
+ constructor(bot: LarkBot<C>);
34
34
  private _assertResponse;
35
35
  private _buildData;
36
36
  static define(routes: Dict<Partial<Record<HTTP.Method, string | InternalRoute>>>): void;
package/lib/utils.d.ts CHANGED
@@ -146,9 +146,9 @@ export type Sender = {
146
146
  tenant_key: string;
147
147
  });
148
148
  export declare function adaptSender(sender: Sender, session: Session): Session;
149
- export declare function adaptMessage(bot: LarkBot, data: Events['im.message.receive_v1'], session: Session, details?: boolean): Promise<Session>;
149
+ export declare function adaptMessage<C extends Context = Context>(bot: LarkBot<C>, data: Events['im.message.receive_v1'], session: Session, details?: boolean): Promise<Session>;
150
150
  export declare function adaptSession<C extends Context>(bot: LarkBot<C>, body: EventPayload): Promise<C[typeof import("cordis").Context.session]>;
151
- export declare function decodeMessage(bot: LarkBot, body: Message, details?: boolean): Promise<Universal.Message>;
151
+ export declare function decodeMessage<C extends Context = Context>(bot: LarkBot<C>, body: Message, details?: boolean): Promise<Universal.Message>;
152
152
  /**
153
153
  * Get ID type from id string
154
154
  * @see https://open.larksuite.com/document/home/user-identity-introduction/introduction
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@satorijs/adapter-lark",
3
3
  "description": "Lark (飞书) Adapter for Satorijs",
4
- "version": "3.9.5",
4
+ "version": "3.10.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
7
7
  "types": "lib/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "@cordisjs/plugin-server": "^0.2.5",
38
38
  "@satorijs/core": "^4.5.0",
39
39
  "cordis": "^3.18.1",
40
- "cosmokit": "^1.6.3",
40
+ "cosmokit": "^1.7.2",
41
41
  "dedent": "^1.5.3"
42
42
  },
43
43
  "peerDependencies": {
package/src/bot.ts CHANGED
@@ -18,11 +18,10 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
18
18
  static inject = ['server', 'http']
19
19
  static MessageEncoder = LarkMessageEncoder
20
20
 
21
- _token?: string
22
21
  _refresher?: NodeJS.Timeout
23
22
  http: HTTP
24
23
  assetsQuester: HTTP
25
- internal: Internal
24
+ internal: Internal<C>
26
25
 
27
26
  constructor(ctx: C, config: LarkBot.Config) {
28
27
  super(ctx, config, 'lark')
@@ -86,7 +85,7 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
86
85
  app_secret: this.config.appSecret,
87
86
  })
88
87
  this.logger.debug('refreshed token %s', token)
89
- this.token = token
88
+ this.http.config.headers!.Authorization = `Bearer ${token}`
90
89
  } catch (error) {
91
90
  this.logger.error('failed to refresh token, retrying in 10s')
92
91
  this.logger.error(error)
@@ -97,15 +96,6 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
97
96
  this.online()
98
97
  }
99
98
 
100
- get token() {
101
- return this._token
102
- }
103
-
104
- set token(v: string) {
105
- this._token = v
106
- this.http.config.headers.Authorization = `Bearer ${v}`
107
- }
108
-
109
99
  async editMessage(channelId: string, messageId: string, content: h.Fragment) {
110
100
  const encoder = new LarkMessageEncoder(this, channelId)
111
101
  encoder.editMessageIds = [messageId]
@@ -118,9 +108,9 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
118
108
 
119
109
  async getMessage(channelId: string, messageId: string, recursive = true) {
120
110
  const data = await this.internal.getImMessage(messageId)
121
- const message = await Utils.decodeMessage(this, data.items[0], recursive)
111
+ const message = await Utils.decodeMessage(this, data.items![0], recursive)
122
112
  const im = await this.internal.getImChat(channelId)
123
- message.channel.type = im.chat_mode === 'p2p' ? Universal.Channel.Type.DIRECT : Universal.Channel.Type.TEXT
113
+ message.channel!.type = im.chat_mode === 'p2p' ? Universal.Channel.Type.DIRECT : Universal.Channel.Type.TEXT
124
114
  return message
125
115
  }
126
116
 
@@ -132,7 +122,7 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
132
122
 
133
123
  async getUser(userId: string, guildId?: string) {
134
124
  const data = await this.internal.getContactUser(userId)
135
- return Utils.decodeUser(data.user)
125
+ return Utils.decodeUser(data.user!)
136
126
  }
137
127
 
138
128
  async getChannel(channelId: string) {
@@ -156,7 +146,7 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
156
146
 
157
147
  async getGuildMemberList(guildId: string, after?: string) {
158
148
  const members = await this.internal.getImChatMembers(guildId, { page_token: after })
159
- const data = members.items.map(v => ({ user: { id: v.member_id, name: v.name }, name: v.name }))
149
+ const data = members.items!.map(v => ({ user: { id: v.member_id, name: v.name }, name: v.name }))
160
150
  return { data, next: members.page_token }
161
151
  }
162
152
 
package/src/content.ts CHANGED
@@ -378,6 +378,10 @@ export namespace MessageContent {
378
378
  name?: string
379
379
  required?: boolean
380
380
  action_type?: 'link' | 'request' | 'multi' | 'form_submit' | 'form_reset'
381
+ // legacy fields
382
+ value?: Record<string, string>
383
+ url?: string
384
+ multi_url?: Omit<URLs, 'url'>
381
385
  }
382
386
 
383
387
  export interface ConfirmElement {
@@ -526,6 +530,7 @@ export namespace MessageContent {
526
530
  | FormElement
527
531
  | InputElement
528
532
  | ButtonElement
533
+ | CheckerElement
529
534
  }
530
535
 
531
536
  export interface Template {
package/src/http.ts CHANGED
@@ -43,6 +43,9 @@ export class HttpServer<C extends Context = Context> extends Adapter<C, LarkBot<
43
43
  if (!result) return (ctx.status = 403)
44
44
  }
45
45
 
46
+ // only accept JSON body
47
+ if (!ctx.request.is('json')) return ctx.status = 415
48
+
46
49
  // try to decrypt message first if encryptKey is set
47
50
  const body = this._tryDecryptBody(ctx.request.body)
48
51
  // respond challenge message
@@ -81,7 +84,7 @@ export class HttpServer<C extends Context = Context> extends Adapter<C, LarkBot<
81
84
  if (!header) return
82
85
  const { app_id, event_type } = header
83
86
  body.type = event_type // add type to body to ease typescript type narrowing
84
- const bot = this.bots.find((bot) => bot.config.appId === app_id)
87
+ const bot = this.bots.find((bot) => bot.config.appId === app_id)!
85
88
  const session = await adaptSession(bot, body)
86
89
  bot.dispatch(session)
87
90
  }
package/src/internal.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dict, HTTP, makeArray } from '@satorijs/core'
1
+ import { Context, Dict, HTTP, makeArray } from '@satorijs/core'
2
2
  import { LarkBot } from './bot'
3
3
 
4
4
  export interface Internal {}
@@ -33,8 +33,8 @@ export interface InternalRoute {
33
33
  type?: 'raw-json' | 'binary'
34
34
  }
35
35
 
36
- export class Internal {
37
- constructor(private bot: LarkBot) {}
36
+ export class Internal<C extends Context = Context> {
37
+ constructor(private bot: LarkBot<C>) {}
38
38
 
39
39
  private _assertResponse(response: HTTP.Response<BaseResponse>) {
40
40
  if (!response.data.code) return
@@ -113,12 +113,13 @@ export class Internal {
113
113
  const { argIndex, itemsKey = 'items', tokenKey = 'page_token' } = route.pagination
114
114
  const iterArgs = [...args]
115
115
  iterArgs[argIndex] = { ...args[argIndex] }
116
- let pagination: { data: any[]; next?: any } | undefined
116
+ type Pagniation = { data: any[]; next?: any }
117
+ let pagination: Pagniation | undefined
117
118
  result.next = async function () {
118
- pagination ??= await this[Symbol.for('satori.pagination')]()
119
+ pagination ??= await this[Symbol.for('satori.pagination')]() as Pagniation
119
120
  if (pagination.data.length) return { done: false, value: pagination.data.shift() }
120
121
  if (!pagination.next) return { done: true, value: undefined }
121
- pagination = await this[Symbol.for('satori.pagination')]()
122
+ pagination = await this[Symbol.for('satori.pagination')]() as Pagniation
122
123
  return this.next()
123
124
  }
124
125
  result[Symbol.asyncIterator] = function () {
package/src/message.ts CHANGED
@@ -208,13 +208,13 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
208
208
  } else if (type === 'at') {
209
209
  if (this.card) {
210
210
  if (attrs.type === 'all') {
211
- this.textContent += `<at id=all>${attrs.name ?? '所有人'}</at>`
211
+ this.textContent += `<at id=all>${attrs.name ?? ''}</at>`
212
212
  } else {
213
213
  this.textContent += `<at id=${attrs.id}>${attrs.name ?? ''}</at>`
214
214
  }
215
215
  } else {
216
216
  if (attrs.type === 'all') {
217
- this.textContent += `<at user_id="all">${attrs.name ?? '所有人'}</at>`
217
+ this.textContent += `<at user_id="all">${attrs.name ?? ''}</at>`
218
218
  } else {
219
219
  this.textContent += `<at user_id="${attrs.id}">${attrs.name ?? ''}</at>`
220
220
  }
@@ -253,7 +253,7 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
253
253
  const length = this.card?.elements.length
254
254
  await this.render(children)
255
255
  if (this.card?.elements.length > length) {
256
- const elements = this.card?.elements.slice(length)
256
+ const elements = this.card?.elements.splice(length)
257
257
  this.card.elements.push({
258
258
  tag: 'form',
259
259
  name: attrs.name || 'Form',
@@ -261,24 +261,56 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
261
261
  })
262
262
  }
263
263
  } else if (type === 'input') {
264
- this.flushText()
265
- this.card?.elements.push({
266
- tag: 'action',
267
- actions: [{
268
- tag: 'input',
269
- name: attrs.name,
270
- width: attrs.width,
271
- label: attrs.label && {
264
+ if (attrs.type === 'checkbox') {
265
+ this.flushText()
266
+ await this.render(children)
267
+ this.card?.elements.push({
268
+ tag: 'checker',
269
+ name: (attrs.argument ? '@@' : attrs.option ? `@${attrs.option}=` : '') + attrs.name,
270
+ checked: attrs.checked,
271
+ text: {
272
272
  tag: 'plain_text',
273
- content: attrs.label,
273
+ content: this.textContent,
274
274
  },
275
- placeholder: attrs.placeholder && {
275
+ })
276
+ } else if (attrs.type === 'submit') {
277
+ this.flushText(true)
278
+ await this.render(children)
279
+ this.card?.elements.push({
280
+ tag: 'button',
281
+ name: attrs.name,
282
+ text: {
276
283
  tag: 'plain_text',
277
- content: attrs.placeholder,
284
+ content: this.textContent,
278
285
  },
279
- behaviors: this.createBehavior(attrs),
280
- }],
281
- })
286
+ action_type: 'form_submit',
287
+ value: {
288
+ _satori_type: 'command',
289
+ content: attrs.text,
290
+ },
291
+ })
292
+ } else {
293
+ this.flushText()
294
+ await this.render(children)
295
+ this.card?.elements.push({
296
+ tag: 'action',
297
+ actions: [{
298
+ tag: 'input',
299
+ name: attrs.name,
300
+ width: attrs.width,
301
+ label: this.textContent && {
302
+ tag: 'plain_text',
303
+ content: this.textContent,
304
+ },
305
+ placeholder: attrs.placeholder && {
306
+ tag: 'plain_text',
307
+ content: attrs.placeholder,
308
+ },
309
+ behaviors: this.createBehavior(attrs),
310
+ }],
311
+ })
312
+ }
313
+ this.textContent = ''
282
314
  } else if (type === 'button') {
283
315
  this.card ??= { elements: [] }
284
316
  this.flushText(true)
package/src/utils.ts CHANGED
@@ -168,7 +168,12 @@ export function adaptSender(sender: Sender, session: Session): Session {
168
168
  return session
169
169
  }
170
170
 
171
- export async function adaptMessage(bot: LarkBot, data: Events['im.message.receive_v1'], session: Session, details = true): Promise<Session> {
171
+ export async function adaptMessage<C extends Context = Context>(
172
+ bot: LarkBot<C>,
173
+ data: Events['im.message.receive_v1'],
174
+ session: Session,
175
+ details = true,
176
+ ): Promise<Session> {
172
177
  const json = JSON.parse(data.message.content)
173
178
  const content: (string | h)[] = []
174
179
  switch (data.message.message_type) {
@@ -182,7 +187,7 @@ export async function adaptMessage(bot: LarkBot, data: Events['im.message.receiv
182
187
  // Lark's `at` Element would be `@user_id` in text
183
188
  text.split(' ').forEach((word) => {
184
189
  if (word.startsWith('@')) {
185
- const mention = data.message.mentions.find((mention) => mention.key === word)
190
+ const mention = data.message.mentions.find((mention) => mention.key === word)!
186
191
  content.push(h.at(mention.id.open_id, { name: mention.name }))
187
192
  } else {
188
193
  content.push(word)
@@ -251,27 +256,43 @@ export async function adaptSession<C extends Context>(bot: LarkBot<C>, body: Eve
251
256
  session.type = 'interaction/command'
252
257
  let content = body.event.action.value.content
253
258
  const args: any[] = [], options = Object.create(null)
259
+ const setOption = (key: string, value: any) => {
260
+ if (key in options) {
261
+ options[key] += ',' + value
262
+ } else {
263
+ options[key] = value
264
+ }
265
+ }
254
266
  for (const [key, value] of Object.entries(body.event.action.form_value ?? {})) {
255
- if (+key * 0 === 0) {
267
+ if (key.startsWith('@@')) {
268
+ if (value) args.push(key.slice(2))
269
+ } else if (key.startsWith('@')) {
270
+ const [_key] = key.slice(1).split('=', 1)
271
+ setOption(_key, key.slice(2 + _key.length))
272
+ } else if (+key * 0 === 0) {
256
273
  args[+key] = value
257
274
  } else {
258
- options[key] = value
275
+ setOption(key, value)
259
276
  }
260
277
  }
261
278
  const toArg = (value: any) => {
262
279
  if (typeof value === 'string') {
263
280
  return `'${value}'`
264
- } else if (typeof value === 'number') {
281
+ } else { // number, boolean
265
282
  return value
266
- } else {
267
- return `''`
268
283
  }
269
284
  }
270
285
  for (let i = 0; i < args.length; ++i) {
271
286
  content += ` ${toArg(args[i])}`
272
287
  }
273
288
  for (const [key, value] of Object.entries(options)) {
274
- content += ` --${key} ${toArg(value)}`
289
+ if (value === true) {
290
+ content += ` --${key} 1`
291
+ } else if (value === false) {
292
+ content += ` --${key} 0`
293
+ } else {
294
+ content += ` --${key} ${toArg(value)}`
295
+ }
275
296
  }
276
297
  if (body.event.action.input_value) {
277
298
  content += ` ${toArg(body.event.action.input_value)}`
@@ -291,8 +312,8 @@ export async function adaptSession<C extends Context>(bot: LarkBot<C>, body: Eve
291
312
  }
292
313
 
293
314
  // TODO: This function has many duplicated code with `adaptMessage`, should refactor them
294
- export async function decodeMessage(bot: LarkBot, body: Message, details = true): Promise<Universal.Message> {
295
- const json = JSON.parse(body.body.content)
315
+ export async function decodeMessage<C extends Context = Context>(bot: LarkBot<C>, body: Message, details = true): Promise<Universal.Message> {
316
+ const json = JSON.parse(body.body!.content)
296
317
  const content: h[] = []
297
318
  switch (body.msg_type) {
298
319
  case 'text': {
@@ -305,7 +326,7 @@ export async function decodeMessage(bot: LarkBot, body: Message, details = true)
305
326
  // Lark's `at` Element would be `@user_id` in text
306
327
  text.split(' ').forEach((word) => {
307
328
  if (word.startsWith('@')) {
308
- const mention = body.mentions.find((mention) => mention.key === word)
329
+ const mention = body.mentions!.find((mention) => mention.key === word)!
309
330
  content.push(h.at(mention.id, { name: mention.name }))
310
331
  } else {
311
332
  content.push(h.text(word))
@@ -314,37 +335,37 @@ export async function decodeMessage(bot: LarkBot, body: Message, details = true)
314
335
  break
315
336
  }
316
337
  case 'image':
317
- content.push(h.image(bot.getResourceUrl('image', body.message_id, json.image_key)))
338
+ content.push(h.image(bot.getResourceUrl('image', body.message_id!, json.image_key)))
318
339
  break
319
340
  case 'audio':
320
- content.push(h.audio(bot.getResourceUrl('file', body.message_id, json.file_key)))
341
+ content.push(h.audio(bot.getResourceUrl('file', body.message_id!, json.file_key)))
321
342
  break
322
343
  case 'media':
323
- content.push(h.video(bot.getResourceUrl('file', body.message_id, json.file_key), {
344
+ content.push(h.video(bot.getResourceUrl('file', body.message_id!, json.file_key), {
324
345
  poster: json.image_key,
325
346
  }))
326
347
  break
327
348
  case 'file':
328
- content.push(h.file(bot.getResourceUrl('file', body.message_id, json.file_key)))
349
+ content.push(h.file(bot.getResourceUrl('file', body.message_id!, json.file_key)))
329
350
  break
330
351
  }
331
352
 
332
353
  return {
333
- timestamp: +body.update_time,
334
- createdAt: +body.create_time,
335
- updatedAt: +body.update_time,
354
+ timestamp: +body.update_time!,
355
+ createdAt: +body.create_time!,
356
+ updatedAt: +body.update_time!,
336
357
  id: body.message_id,
337
358
  messageId: body.message_id,
338
359
  user: {
339
- id: body.sender.id,
360
+ id: body.sender!.id,
340
361
  },
341
362
  channel: {
342
- id: body.chat_id,
363
+ id: body.chat_id!,
343
364
  type: Universal.Channel.Type.TEXT,
344
365
  },
345
366
  content: content.map((c) => c.toString()).join(' '),
346
367
  elements: content,
347
- quote: (body.upper_message_id && details) ? await bot.getMessage(body.chat_id, body.upper_message_id, false) : undefined,
368
+ quote: (body.upper_message_id && details) ? await bot.getMessage(body.chat_id!, body.upper_message_id, false) : undefined,
348
369
  }
349
370
  }
350
371
 
@@ -371,7 +392,7 @@ export function decodeChannel(channelId: string, guild: GetImChatResponse): Univ
371
392
 
372
393
  export function decodeGuild(guild: ListChat): Universal.Guild {
373
394
  return {
374
- id: guild.chat_id,
395
+ id: guild.chat_id!,
375
396
  name: guild.name,
376
397
  avatar: guild.avatar,
377
398
  }
@@ -379,7 +400,7 @@ export function decodeGuild(guild: ListChat): Universal.Guild {
379
400
 
380
401
  export function decodeUser(user: User): Universal.User {
381
402
  return {
382
- id: user.open_id,
403
+ id: user.open_id!,
383
404
  avatar: user.avatar?.avatar_origin,
384
405
  isBot: false,
385
406
  name: user.name,