@satorijs/adapter-lark 3.6.1 → 3.7.0

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.
@@ -15,17 +15,15 @@ type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
15
15
  export class Internal {
16
16
  constructor(private bot: LarkBot) {}
17
17
 
18
- private processReponse(response: any): BaseResponse {
19
- const { code, msg } = response
20
- if (code === 0) {
21
- return response
22
- } else {
23
- this.bot.logger.debug('response: %o', response)
24
- throw new Error(`HTTP response with non-zero status (${code}) with message "${msg}"`)
25
- }
18
+ private assertResponse(response: HTTP.Response<BaseResponse>) {
19
+ if (!response.data.code) return
20
+ this.bot.logger.debug('response: %o', response.data)
21
+ const error = new HTTP.Error(`request failed`)
22
+ error.response = response
23
+ throw error
26
24
  }
27
25
 
28
- static define(routes: Dict<Partial<Record<Method, string | string[]>>>) {
26
+ static define(routes: Dict<Partial<Record<Method, string | string[]>>>, extractData = true) {
29
27
  for (const path in routes) {
30
28
  for (const key in routes[path]) {
31
29
  const method = key as Method
@@ -49,7 +47,9 @@ export class Internal {
49
47
  } else if (args.length > 1) {
50
48
  throw new Error(`too many arguments for ${path}, received ${raw}`)
51
49
  }
52
- return this.processReponse((await this.bot.http(method, url, config)).data)
50
+ const response = await this.bot.http(method, url, config)
51
+ this.assertResponse(response.data)
52
+ return extractData ? response.data.data : response.data
53
53
  }
54
54
  }
55
55
  }
@@ -153,9 +153,10 @@ export namespace MessageContent {
153
153
  }
154
154
 
155
155
  export interface Card {
156
- config: Card.Config
156
+ config?: Card.Config
157
157
  card_link?: Card.URLs
158
- elements?: Card.Element[]
158
+ header?: Card.Header
159
+ elements: Card.Element[]
159
160
  }
160
161
 
161
162
  export namespace Card {
@@ -233,17 +234,27 @@ export namespace MessageContent {
233
234
  export type Color = 'neutral' | 'blue' | 'torqoise' | 'lime' | 'orange' | 'violet' | 'indigo' | 'wathet' | 'green' | 'yellow' | 'red' | 'purple' | 'carmine'
234
235
  }
235
236
 
236
- export interface ImageElement extends BaseElement<'image'> {
237
+ export interface BaseImageElement extends BaseElement<'image'> {
237
238
  img_key: string
238
239
  alt?: PlainTextElement
240
+ }
241
+
242
+ export interface ImageElement extends BaseImageElement {
239
243
  title?: PlainTextElement
244
+ transparent?: string
245
+ preview?: boolean
246
+ corner_radius?: string
247
+ scale_type?: 'crop_center' | 'fit_horizontal' | 'crop_top'
248
+ size?: 'large' | 'medium' | 'small' | 'tiny' | 'stretch_without_padding' | 'stretch' | string
249
+ /** @deprecated */
240
250
  custom_width?: number
251
+ /** @deprecated */
241
252
  compact_width?: boolean
253
+ /** @deprecated */
242
254
  mode?: 'crop_center' | 'fit_horizontal' | 'large' | 'medium' | 'small' | 'tiny'
243
- preview?: boolean
244
255
  }
245
256
 
246
- export interface HorizontalRuleElement extends BaseElement<'hr'> {}
257
+ export interface HRElement extends BaseElement<'hr'> {}
247
258
 
248
259
  export interface DivElement extends BaseElement<'div'> {
249
260
  text?: DivPlainTextElement
@@ -256,15 +267,77 @@ export namespace MessageContent {
256
267
  href?: Record<string, URLs>
257
268
  }
258
269
 
259
- export interface HorizontalRuleElement extends BaseElement<'hr'> {}
270
+ export interface PersonElement extends BaseElement<'person'> {
271
+ user_id: string
272
+ size?: 'large' | 'medium' | 'small' | 'extra_small'
273
+ }
260
274
 
261
- export interface ActionModule extends BaseElement<'action'> {
262
- actions: ActionElement[]
263
- layout?: 'bisected' | 'trisection' | 'flow'
275
+ export interface PersonListElement extends BaseElement<'person_list'> {
276
+ persons: { id: string }[]
277
+ size?: 'large' | 'medium' | 'small' | 'extra_small'
278
+ show_name?: boolean
279
+ show_avatar?: boolean
264
280
  }
265
281
 
266
- export type ActionElement =
267
- | ButtonElement
282
+ export interface ChartElement extends BaseElement<'chart'> {
283
+ chart_spec: {} // TODO
284
+ aspect_ratio?: '1:1' | '2:1' | '4:3' | '16:9'
285
+ color_theme?: 'brand' | 'rainbow' | 'complementary' | 'converse' | 'primary'
286
+ preview?: boolean
287
+ height?: 'auto' | string
288
+ }
289
+
290
+ export interface TableElement extends BaseElement<'table'> {
291
+ page_size?: number
292
+ row_height?: 'low' | 'medium' | 'high' | string
293
+ header_style?: TableElement.HeaderStyle
294
+ columns: TableElement.Column[]
295
+ rows: object[]
296
+ }
297
+
298
+ export namespace TableElement {
299
+ export interface HeaderStyle {
300
+ text_align?: TextAlign
301
+ text_size?: TextSize
302
+ background_style?: 'grey' | 'none'
303
+ text_color?: 'default' | 'grey'
304
+ bold?: boolean
305
+ lines?: number
306
+ }
307
+
308
+ export interface Column {
309
+ name: string
310
+ display_name?: string
311
+ width?: 'auto' | string
312
+ horizontal_align?: 'left' | 'center' | 'right'
313
+ data_type?: 'text' | 'lark_md' | 'options' | 'number' | 'persons' | 'date' | 'markdown'
314
+ format?: {
315
+ percision?: number
316
+ symbol?: string
317
+ separator?: string
318
+ }
319
+ date_format?: string
320
+ }
321
+ }
322
+
323
+ export interface NoteElement extends BaseElement<'note'> {
324
+ elements: NoteElement.InnerElement[]
325
+ }
326
+
327
+ export namespace NoteElement {
328
+ export type InnerElement = IconElement | PlainTextElement | BaseImageElement
329
+ }
330
+
331
+ export interface FormElement extends BaseElement<'form'> {
332
+ name: string
333
+ elements: Element[]
334
+ confirm?: ConfirmElement
335
+ }
336
+
337
+ export interface ActionElement extends BaseElement<'action'> {
338
+ actions: Element[]
339
+ layout?: 'bisected' | 'trisection' | 'flow'
340
+ }
268
341
 
269
342
  export type ActionBehavior =
270
343
  | OpenURLBehavior
@@ -283,26 +356,157 @@ export namespace MessageContent {
283
356
  value: Record<string, string>
284
357
  }
285
358
 
286
- export interface ButtonElement extends BaseElement<'button'> {
359
+ export interface BaseButtonElement extends BaseElement<'button'> {
287
360
  text: PlainTextElement
288
- type?: ButtonElement.Type
289
361
  size?: ButtonElement.Size
290
- width?: ButtonElement.Width
291
362
  icon?: IconElement
292
- hover_tips?: PlainTextElement
293
363
  disabled?: boolean
364
+ behaviors?: ActionBehavior[]
365
+ }
366
+
367
+ export interface ButtonElement extends BaseButtonElement {
368
+ type?: ButtonElement.Type
369
+ width?: ButtonElement.Width
370
+ hover_tips?: PlainTextElement
294
371
  disabled_tips?: PlainTextElement
295
372
  confirm?: {
296
373
  title: PlainTextElement
297
374
  text: PlainTextElement
298
375
  }
299
- behaviors?: ActionBehavior[]
300
376
  // form-related fields
301
377
  name?: string
302
378
  required?: boolean
303
379
  action_type?: 'link' | 'request' | 'multi' | 'form_submit' | 'form_reset'
304
380
  }
305
381
 
382
+ export interface ConfirmElement {
383
+ title: PlainTextElement
384
+ text: PlainTextElement
385
+ }
386
+
387
+ export interface InputElement extends BaseElement<'input'> {
388
+ name: string
389
+ required?: boolean
390
+ placeholder?: PlainTextElement
391
+ default_value?: string
392
+ disabled?: boolean
393
+ width?: 'default' | 'fill' | string
394
+ max_length?: number
395
+ input_type?: 'text' | 'multiline_text' | 'password'
396
+ show_icon?: boolean
397
+ rows?: number
398
+ auto_resize?: boolean
399
+ max_rows?: number
400
+ label?: PlainTextElement
401
+ label_position?: 'top' | 'left'
402
+ value?: string | object
403
+ behaviors?: ActionBehavior[]
404
+ confirm?: ConfirmElement
405
+ fallback?: {
406
+ tag?: string
407
+ text?: PlainTextElement
408
+ }
409
+ }
410
+
411
+ export interface OverflowElement extends BaseElement<'overflow'> {
412
+ width?: 'default' | 'fill' | string
413
+ options: OverflowElement.Option[]
414
+ value?: object
415
+ confirm?: ConfirmElement
416
+ }
417
+
418
+ export namespace OverflowElement {
419
+ export interface Option {
420
+ text?: PlainTextElement
421
+ multi_url?: URLs
422
+ value?: string
423
+ }
424
+ }
425
+
426
+ export interface BaseSelectElement<T extends string = string> extends BaseElement<T> {
427
+ type?: 'default' | 'text'
428
+ name?: string
429
+ required?: boolean
430
+ disabled?: boolean
431
+ placeholder?: PlainTextElement
432
+ width?: 'default' | 'fill' | string
433
+ confirm?: ConfirmElement
434
+ }
435
+
436
+ export interface OptionElement {
437
+ text: PlainTextElement
438
+ icon?: IconElement
439
+ value: string
440
+ }
441
+
442
+ export interface SelectElement extends BaseSelectElement<'select_static'> {
443
+ options: OptionElement[]
444
+ initial_option?: string
445
+ }
446
+
447
+ export interface MultiSelectElement extends BaseSelectElement<'multi_select_static'> {
448
+ options: OptionElement[]
449
+ selected_values?: string[]
450
+ }
451
+
452
+ export interface SelectPersonElement extends BaseSelectElement<'select_person'> {
453
+ options?: { value: string }[]
454
+ }
455
+
456
+ export interface MultiSelectPersonElement extends BaseSelectElement<'multi_select_person'> {
457
+ options?: { value: string }[]
458
+ selected_values?: string[]
459
+ }
460
+
461
+ export interface DatePickerElement extends BaseSelectElement<'date_picker'> {
462
+ initial_date?: string
463
+ value?: object
464
+ }
465
+
466
+ export interface TimePickerElement extends BaseSelectElement<'picker_time'> {
467
+ initial_time?: string
468
+ value?: object
469
+ }
470
+
471
+ export interface DateTimePickerElement extends BaseSelectElement<'picker_datetime'> {
472
+ initial_datetime?: string
473
+ value?: object
474
+ }
475
+
476
+ export interface CheckerElement extends BaseElement<'checker'> {
477
+ name?: string
478
+ checked?: boolean
479
+ disabled?: boolean
480
+ text?: CheckerElement.TextElement
481
+ overall_checkable?: boolean
482
+ button_area?: {
483
+ pc_display_rule?: 'always' | 'on_hover'
484
+ buttons?: CheckerElement.ButtonElement[]
485
+ }
486
+ checked_style?: {
487
+ show_strikethrough?: boolean
488
+ opacity?: number
489
+ }
490
+ margin?: string
491
+ padding?: string
492
+ confirm?: ConfirmElement
493
+ behaviors?: ActionBehavior[]
494
+ hover_tips?: PlainTextElement
495
+ disable_tips?: PlainTextElement
496
+ }
497
+
498
+ export namespace CheckerElement {
499
+ export interface TextElement extends PlainTextElement {
500
+ text_size?: 'normal' | 'heading' | 'notation'
501
+ text_color?: string
502
+ text_align?: TextAlign
503
+ }
504
+
505
+ export interface ButtonElement extends BaseButtonElement {
506
+ type: 'text' | 'primary_text' | 'danger_text'
507
+ }
508
+ }
509
+
306
510
  export namespace ButtonElement {
307
511
  export type Size = 'tiny' | 'small' | 'medium' | 'large'
308
512
  export type Width = 'default' | 'fill' | string
@@ -312,8 +516,15 @@ export namespace MessageContent {
312
516
  export type Element =
313
517
  | DivElement
314
518
  | MarkdownElement
315
- | HorizontalRuleElement
316
- | ActionModule
519
+ | HRElement
520
+ | ActionElement
521
+ | NoteElement
522
+ | ChartElement
523
+ | TableElement
524
+ | ImageElement
525
+ | FormElement
526
+ | InputElement
527
+ | ButtonElement
317
528
  }
318
529
 
319
530
  export interface Template {
@@ -76,5 +76,21 @@ declare module '../event' {
76
76
  open_chat_id: string
77
77
  }
78
78
  }
79
+ /**
80
+ * 机器人自定义菜单事件
81
+ * @see https://open.feishu.cn/document/client-docs/bot-v3/events/menu
82
+ */
83
+ 'application.bot.menu_v6': {
84
+ operator: {
85
+ operator_name: string
86
+ operator_id: {
87
+ union_id: string
88
+ user_id: string
89
+ open_id: string
90
+ }
91
+ }
92
+ event_key: string
93
+ timestamp: number
94
+ }
79
95
  }
80
96
  }
package/src/utils.ts CHANGED
@@ -84,6 +84,15 @@ export async function adaptSession<C extends Context>(bot: LarkBot<C>, body: Eve
84
84
  adaptSender(body.event.sender, session)
85
85
  await adaptMessage(bot, body.event, session)
86
86
  break
87
+ case 'application.bot.menu_v6':
88
+ if (body.event.event_key.startsWith('command:')) {
89
+ session.type = 'interaction/command'
90
+ session.content = body.event.event_key.slice(8)
91
+ session.channelId = body.event.operator.operator_id.open_id
92
+ session.userId = body.event.operator.operator_id.open_id
93
+ session.isDirect = true
94
+ }
95
+ break
87
96
  case 'card.action.trigger':
88
97
  if (body.event.action.value?._satori_type === 'command') {
89
98
  session.type = 'interaction/command'
@@ -106,11 +115,17 @@ export async function adaptSession<C extends Context>(bot: LarkBot<C>, body: Eve
106
115
  for (const [key, value] of Object.entries(options)) {
107
116
  content += ` --${key} ${value}`
108
117
  }
118
+ if (body.event.action.input_value) {
119
+ content += ` ${body.event.action.input_value}`
120
+ }
109
121
  session.content = content
110
122
  session.messageId = body.event.context.open_message_id
111
123
  session.channelId = body.event.context.open_chat_id
112
124
  session.guildId = body.event.context.open_chat_id
113
125
  session.userId = body.event.operator.open_id
126
+ const chat = await bot.internal.getImChat(session.channelId)
127
+ // TODO: add channel data
128
+ session.isDirect = chat.chat_mode === 'p2p'
114
129
  }
115
130
  break
116
131
  }
@@ -186,7 +201,7 @@ export function extractIdType(id: string): Lark.ReceiveIdType {
186
201
  return 'user_id'
187
202
  }
188
203
 
189
- export function decodeChannel(channelId: string, guild: GetImChatResponse['data']): Universal.Channel {
204
+ export function decodeChannel(channelId: string, guild: GetImChatResponse): Universal.Channel {
190
205
  return {
191
206
  id: channelId,
192
207
  type: Universal.Channel.Type.TEXT,
@@ -1,40 +0,0 @@
1
- import { BaseResponse } from '.';
2
- /**
3
- * Lark defines three types of token:
4
- * - app_access_token: to access the API in an app (published on App Store).
5
- * - tenant_access_token: to access the API as an enterprise or a team (tenant).
6
- * *We commonly use this one*
7
- * - user_access_token: to access the API as the specific user.
8
- *
9
- * @see https://open.larksuite.com/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM
10
- */
11
- export interface AppCredentials {
12
- app_id: string;
13
- app_secret: string;
14
- }
15
- export interface AppAccessToken extends BaseResponse {
16
- /** access token */
17
- app_access_token: string;
18
- /** expire time in seconds. e.g: 7140 (119 minutes) */
19
- expire: number;
20
- }
21
- export interface TenantAccessToken extends BaseResponse {
22
- /** access token */
23
- tenant_access_token: string;
24
- /** expire time in seconds. e.g: 7140 (119 minutes) */
25
- expire: number;
26
- }
27
- declare module './internal' {
28
- interface Internal {
29
- /**
30
- * Returns the app_access_token for the bot.
31
- * @see https://open.larksuite.com/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/auth-v3/auth/app_access_token_internal
32
- */
33
- getAppAccessToken(data: AppCredentials): Promise<AppAccessToken>;
34
- /**
35
- * Returns the tenant_access_token for the bot.
36
- * @see https://open.larksuite.com/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/auth-v3/auth/tenant_access_token_internal
37
- */
38
- getTenantAccessToken(data: AppCredentials): Promise<TenantAccessToken>;
39
- }
40
- }
@@ -1,59 +0,0 @@
1
- import { Dict } from '@satorijs/satori';
2
- import { Lark } from '.';
3
- import { Paginated, Pagination } from './utils';
4
- declare module '.' {
5
- namespace Lark {
6
- interface Guild {
7
- avatar: string;
8
- name: string;
9
- description: string;
10
- i18n_names: Dict<string>;
11
- add_member_permission: string;
12
- share_card_permission: string;
13
- at_all_permission: string;
14
- edit_permission: string;
15
- owner_id_type: string;
16
- owner_id: string;
17
- chat_id: string;
18
- chat_mode: string;
19
- chat_type: string;
20
- chat_tag: string;
21
- join_message_visibility: string;
22
- leave_message_visibility: string;
23
- membership_approval: string;
24
- moderation_permission: string;
25
- external: boolean;
26
- tenant_key: string;
27
- user_count: string;
28
- bot_count: string;
29
- }
30
- }
31
- }
32
- export interface GuildMember {
33
- member_id_type: Lark.UserIdType;
34
- member_id: string;
35
- name: string;
36
- tenant_key: string;
37
- }
38
- declare module './internal' {
39
- interface Internal {
40
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat/list */
41
- getCurrentUserGuilds(params?: Pagination<{
42
- user_id_type?: Lark.UserIdType;
43
- }>): Promise<{
44
- data: Paginated<Lark.Guild>;
45
- }>;
46
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat/get */
47
- getGuildInfo(chat_id: string, params?: {
48
- user_id_type?: string;
49
- }): Promise<BaseResponse & {
50
- data: Lark.Guild;
51
- }>;
52
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat-members/get */
53
- getGuildMembers(chat_id: string, params?: Pagination<{
54
- member_id_type?: Lark.UserIdType;
55
- }>): Promise<{
56
- data: Paginated<GuildMember>;
57
- }>;
58
- }
59
- }
@@ -1,40 +0,0 @@
1
- import { BaseResponse } from '..';
2
- export interface Asset<T> extends BaseResponse {
3
- data: T;
4
- }
5
- export type Image = Asset<{
6
- image_key: string;
7
- }>;
8
- export type File = Asset<{
9
- file_key: string;
10
- }>;
11
- declare module '../internal' {
12
- interface Internal {
13
- /**
14
- * Upload an image to obtain an `image_key` for use in sending messages or changing the avatar.
15
- *
16
- * The data should contain:
17
- * - `image_type`: 'message' | 'avatar'
18
- * - `image': Buffer
19
- * @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/image/create
20
- */
21
- uploadImage(data: FormData): Promise<Image>;
22
- /**
23
- * Upload a file to obtain a `file_key` for use in sending messages.
24
- *
25
- * The data should contain:
26
- * - `file_type`: 'opus' | 'mp4' | 'pdf' | 'xls' | 'ppt' | 'stream'
27
- * - `opus`: Opus audio file
28
- * - `mp4`: MP4 video file
29
- * - `pdf`: PDF file
30
- * - `xls`: Excel file
31
- * - `ppt`: PowerPoint file
32
- * - `stream`: Stream file, or any other file not listed above
33
- * - `file_name`: string, include extension
34
- * - `duration`?: number, the duration of audio/video file in milliseconds
35
- * - `file`: Buffer
36
- * @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create
37
- */
38
- uploadFile(data: FormData): Promise<File>;
39
- }
40
- }
@@ -1,78 +0,0 @@
1
- import { Lark } from '.';
2
- declare module '.' {
3
- namespace Lark {
4
- interface User {
5
- union_id: string;
6
- user_id?: string;
7
- open_id: string;
8
- name?: string;
9
- en_name?: string;
10
- nickname?: string;
11
- email?: string;
12
- mobile?: string;
13
- mobile_visible: boolean;
14
- gender?: Gender;
15
- avatar?: AvatarInfo;
16
- status?: UserStatus;
17
- department_ids?: string[];
18
- leader_user_id?: string;
19
- city?: string;
20
- country?: string;
21
- work_station?: string;
22
- join_time?: number;
23
- is_tenant_manager?: boolean;
24
- employee_no?: string;
25
- employee_type?: number;
26
- orders?: UserOrder[];
27
- custom_attrs?: any;
28
- enterprise_email?: string;
29
- job_title?: string;
30
- geo?: string;
31
- job_level_id?: string;
32
- job_family_id?: string;
33
- assign_info?: any;
34
- department_path?: DepartmentDetail[];
35
- }
36
- enum Gender {
37
- SECRET = 0,
38
- MALE = 1,
39
- FEMALE = 2
40
- }
41
- interface AvatarInfo {
42
- avatar_72: string;
43
- avatar_240: string;
44
- avatar_640: string;
45
- avatar_origin: string;
46
- }
47
- interface UserStatus {
48
- is_frozen: boolean;
49
- is_resigned: boolean;
50
- is_activated: boolean;
51
- is_exited: boolean;
52
- is_unjoin: boolean;
53
- }
54
- interface UserOrder {
55
- department_id: string;
56
- user_order: number;
57
- department_order: number;
58
- is_primary_dept: boolean;
59
- }
60
- interface DepartmentDetail {
61
- dotted_line_leader_user_ids: string[];
62
- }
63
- }
64
- }
65
- export interface GuildMember {
66
- member_id_type: Lark.UserIdType;
67
- member_id: string;
68
- name: string;
69
- tenant_key: string;
70
- }
71
- declare module './internal' {
72
- interface Internal {
73
- /** @see https://open.larksuite.com/document/server-docs/contact-v3/user/get */
74
- getContactUser(user_id: string, user_id_type?: Lark.UserIdType, department_id_type?: Lark.DepartmentIdType): Promise<BaseResponse & {
75
- data: Lark.User;
76
- }>;
77
- }
78
- }
@@ -1,9 +0,0 @@
1
- export type Pagination<T> = T & {
2
- page_size?: number;
3
- page_token?: string;
4
- };
5
- export interface Paginated<T> {
6
- items: T[];
7
- has_more: boolean;
8
- page_token: string;
9
- }