@satorijs/adapter-lark 3.4.1 → 3.5.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.
@@ -1,8 +1,7 @@
1
1
  export * from './internal';
2
- export * from './auth';
3
2
  export * from './event';
4
- export * from './guild';
5
3
  export * from './message';
4
+ export * from './api';
6
5
  export declare namespace Lark {
7
6
  /**
8
7
  * A user in Lark has several different IDs.
@@ -1,4 +1,4 @@
1
- import { Dict } from '@satorijs/satori';
1
+ import { Dict } from '@satorijs/core';
2
2
  import { LarkBot } from '../bot';
3
3
  export interface Internal {
4
4
  }
@@ -1,8 +1,6 @@
1
1
  import { Lark } from '..';
2
- import { Paginated, Pagination } from '../utils';
3
2
  import { MessageContent } from './content';
4
3
  export * from './content';
5
- export * from './asset';
6
4
  export type MessageType = 'text' | 'post' | 'image' | 'file' | 'audio' | 'media' | 'sticker' | 'interactive' | 'share_chat' | 'share_user';
7
5
  export interface MessageContentMap {
8
6
  'text': MessageContent.Text;
@@ -16,15 +14,6 @@ export interface MessageContentMap {
16
14
  'share_user': MessageContent.ShareUser;
17
15
  }
18
16
  export type MessageContentType<T extends MessageType> = T extends keyof MessageContentMap ? MessageContentMap[T] : any;
19
- export interface Sender extends Lark.UserIdentifiers {
20
- sender_type: string;
21
- tenant_key: string;
22
- }
23
- export interface Mention extends Lark.UserIdentifiers {
24
- key: string;
25
- name: string;
26
- tenant_key: string;
27
- }
28
17
  declare module '../event' {
29
18
  interface Events {
30
19
  /**
@@ -68,153 +57,3 @@ declare module '../event' {
68
57
  }>;
69
58
  }
70
59
  }
71
- export interface MessagePayload {
72
- receive_id: string;
73
- content: string;
74
- msg_type: string;
75
- }
76
- export interface Message {
77
- /**
78
- * The id of current message
79
- *
80
- * Should be started with `om_`
81
- */
82
- message_id: string;
83
- /**
84
- * The id of the *root* message in reply chains
85
- * @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/intro#ac79c1c2
86
- */
87
- root_id: string;
88
- /**
89
- * The id of the direct *parent* message in reply chains
90
- * @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/intro#ac79c1c2
91
- */
92
- parent_id: string;
93
- /**
94
- * The message type.
95
- * @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/im-v1/message/create_json
96
- */
97
- msg_type: MessageType;
98
- /**
99
- * The timestamp when the message is created in milliseconds.
100
- */
101
- create_time: string;
102
- /**
103
- * The timestamp when the message is last updated in milliseconds.
104
- */
105
- update_time: string;
106
- /**
107
- * Whether the message is deleted.
108
- */
109
- deleted: boolean;
110
- /**
111
- * Whether the message is updated.
112
- */
113
- updated: boolean;
114
- /**
115
- * The id of the group / channel the message is sent to.
116
- */
117
- chat_id: string;
118
- /**
119
- * The sender of the message.
120
- * Can be a user or an app.
121
- */
122
- sender: Sender;
123
- /**
124
- * The body of the message.
125
- */
126
- body: {
127
- /**
128
- * The content of the message.
129
- * Should be a string that represents the JSON object contains the message content.
130
- */
131
- content: string;
132
- };
133
- /**
134
- * Users mentioned in the message.
135
- */
136
- mentions: Mention[];
137
- /**
138
- * The id of the direct *parent* message in `merge and repost` chains.
139
- */
140
- upper_message_id: string;
141
- }
142
- export interface ReadUser {
143
- user_id_type: Lark.UserIdType;
144
- user_id: string;
145
- timestamp: string;
146
- tenant_key: string;
147
- }
148
- export interface GetMessageListParams {
149
- /**
150
- * Currently there is only 'chat' available
151
- * @see https://open.larksuite.com/document/server-docs/im-v1/message/list
152
- */
153
- container_id_type: 'p2p' | 'chat';
154
- /**
155
- * Should be in the format like `oc_234jsi43d3ssi993d43545f`
156
- */
157
- container_id: string;
158
- /** Timestamp in seconds */
159
- start_time?: string | number;
160
- /** Timestamp in seconds */
161
- end_time?: string | number;
162
- /** @default 'ByCreateTimeAsc' */
163
- sort_type?: 'ByCreateTimeAsc' | 'ByCreateTimeDesc';
164
- /** Range from 1 to 50 */
165
- page_size?: number;
166
- /**
167
- * If the current page is the first page, this field should be omitted.
168
- * Otherwise you could use the `page_token` from the previous response to
169
- * get the next page.
170
- */
171
- page_token?: string;
172
- }
173
- declare module '../internal' {
174
- interface Internal {
175
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create */
176
- sendMessage(receive_id_type: Lark.ReceiveIdType, message: MessagePayload): Promise<BaseResponse & {
177
- data: Message;
178
- }>;
179
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/reply */
180
- replyMessage(message_id: string, message: MessagePayload): Promise<BaseResponse & {
181
- data: Message;
182
- }>;
183
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/update */
184
- updateMessage(message_id: string, message: Omit<MessagePayload, 'receive_id'>): Promise<BaseResponse & {
185
- data: Message;
186
- }>;
187
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/get */
188
- getMessage(message_id: string): Promise<BaseResponse & {
189
- data: Message;
190
- }>;
191
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/delete */
192
- deleteMessage(message_id: string): Promise<BaseResponse>;
193
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/forward */
194
- forwardMessage(message_id: string, receive_id_type: Lark.ReceiveIdType, data: {
195
- receive_id: string;
196
- }): Promise<BaseResponse & {
197
- data: Message;
198
- }>;
199
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/merge_forward */
200
- mergeForwardMessage(receive_id_type: Lark.ReceiveIdType, data: {
201
- receive_id: string;
202
- message_id_list: string[];
203
- }): Promise<BaseResponse & {
204
- data: {
205
- message: Message;
206
- invalid_message_id_list: string[];
207
- };
208
- }>;
209
- /** @see https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/read_users */
210
- getMessageReadUsers(message_id: string, params: Pagination<{
211
- user_id_type: Lark.UserIdType;
212
- }>): Promise<BaseResponse & {
213
- data: Paginated<ReadUser>;
214
- }>;
215
- /** @see https://open.larksuite.com/document/server-docs/im-v1/message/list */
216
- getMessageList(params: GetMessageListParams): Promise<BaseResponse & {
217
- data: Paginated<Message>;
218
- }>;
219
- }
220
- }
package/lib/utils.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
- import { Context, Session, Universal } from '@satorijs/satori';
2
+ import { Context, Session, Universal } from '@satorijs/core';
3
3
  import { FeishuBot, LarkBot } from './bot';
4
- import { AllEvents, Events, Lark, Message as LarkMessage } from './types';
4
+ import { AllEvents, Events, GetImChatResponse, Lark } from './types';
5
5
  export type Sender = {
6
6
  sender_id: Lark.UserIds;
7
7
  sender_type?: string;
@@ -11,16 +11,16 @@ export type Sender = {
11
11
  tenant_key: string;
12
12
  });
13
13
  export declare function adaptSender(sender: Sender, session: Session): Session;
14
- export declare function adaptMessage(bot: FeishuBot, data: Events['im.message.receive_v1']['event'], session: Session): Session;
15
- export declare function adaptSession<C extends Context>(bot: FeishuBot<C>, body: AllEvents): C[typeof Context.session];
16
- export declare function decodeMessage(bot: LarkBot, body: LarkMessage): Promise<Universal.Message>;
14
+ export declare function adaptMessage(bot: FeishuBot, data: Events['im.message.receive_v1']['event'], session: Session, details?: boolean): Promise<Session>;
15
+ export declare function adaptSession<C extends Context>(bot: FeishuBot<C>, body: AllEvents): Promise<C[typeof Context.session]>;
16
+ export declare function decodeMessage(bot: LarkBot, body: Lark.Message, details?: boolean): Promise<Universal.Message>;
17
17
  /**
18
18
  * Get ID type from id string
19
19
  * @see https://open.larksuite.com/document/home/user-identity-introduction/introduction
20
20
  */
21
21
  export declare function extractIdType(id: string): Lark.ReceiveIdType;
22
- export declare function decodeChannel(guild: Lark.Guild): Universal.Channel;
23
- export declare function decodeGuild(guild: Lark.Guild): Universal.Guild;
22
+ export declare function decodeChannel(channelId: string, guild: GetImChatResponse['data']): Universal.Channel;
23
+ export declare function decodeGuild(guild: Lark.ListChat): Universal.Guild;
24
24
  export declare function decodeUser(user: Lark.User): Universal.User;
25
25
  export declare class Cipher {
26
26
  encryptKey: string;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@satorijs/adapter-lark",
3
3
  "description": "Lark (飞书) Adapter for Satorijs",
4
- "version": "3.4.1",
5
- "main": "lib/index.js",
6
- "typings": "lib/index.d.ts",
4
+ "version": "3.5.0",
5
+ "type": "module",
6
+ "main": "lib/index.cjs",
7
+ "types": "lib/index.d.ts",
7
8
  "files": [
8
9
  "lib",
9
10
  "src"
@@ -33,9 +34,9 @@
33
34
  "chat"
34
35
  ],
35
36
  "devDependencies": {
36
- "@cordisjs/server": "^0.1.8"
37
+ "@cordisjs/plugin-server": "^0.2.2"
37
38
  },
38
39
  "peerDependencies": {
39
- "@satorijs/satori": "^3.6.5"
40
+ "@satorijs/core": "^4.0.0"
40
41
  }
41
42
  }
package/src/bot.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Bot, Context, h, Quester, Schema } from '@satorijs/satori'
1
+ import { Bot, Context, h, HTTP, Schema, Universal } from '@satorijs/core'
2
2
 
3
3
  import { HttpServer } from './http'
4
4
  import { LarkMessageEncoder } from './message'
@@ -11,8 +11,8 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
11
11
 
12
12
  _token?: string
13
13
  _refresher?: NodeJS.Timeout
14
- http: Quester
15
- assetsQuester: Quester
14
+ http: HTTP
15
+ assetsQuester: HTTP
16
16
  internal: Internal
17
17
 
18
18
  constructor(ctx: C, config: LarkBot.Config) {
@@ -23,7 +23,6 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
23
23
  this.logger.warn('selfUrl is not set, some features may not work')
24
24
  }
25
25
 
26
- this.selfId = config.appId
27
26
  this.http = ctx.http.extend({
28
27
  endpoint: config.endpoint,
29
28
  })
@@ -33,13 +32,29 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
33
32
  ctx.plugin(HttpServer, this)
34
33
  }
35
34
 
35
+ get appId() {
36
+ return this.config.appId
37
+ }
38
+
36
39
  async initialize() {
37
40
  await this.refreshToken()
41
+ const { bot } = await this.http.get<{
42
+ bot: {
43
+ activate_status: number
44
+ app_name: string
45
+ avatar_url: string
46
+ ip_white_list: any[]
47
+ open_id: string
48
+ }
49
+ }>('/bot/v3/info')
50
+ this.selfId = bot.open_id
51
+ this.user.avatar = bot.avatar_url
52
+ this.user.name = bot.app_name
38
53
  this.online()
39
54
  }
40
55
 
41
56
  private async refreshToken() {
42
- const { tenant_access_token: token } = await this.internal.getTenantAccessToken({
57
+ const { tenant_access_token: token } = await this.internal.tenantAccessTokenInternalAuth({
43
58
  app_id: this.config.appId,
44
59
  app_secret: this.config.appSecret,
45
60
  })
@@ -62,35 +77,38 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
62
77
  }
63
78
 
64
79
  async editMessage(channelId: string, messageId: string, content: h.Fragment) {
65
- await this.internal.updateMessage(messageId, {
80
+ await this.internal.updateImMessage(messageId, {
66
81
  content: h.normalize(content).join(''),
67
82
  msg_type: 'text',
68
83
  })
69
84
  }
70
85
 
71
86
  async deleteMessage(channelId: string, messageId: string) {
72
- await this.internal.deleteMessage(messageId)
87
+ await this.internal.deleteImMessage(messageId)
73
88
  }
74
89
 
75
- async getMessage(channelId: string, messageId: string) {
76
- const data = await this.internal.getMessage(messageId)
77
- return await Utils.decodeMessage(this, data.data)
90
+ async getMessage(channelId: string, messageId: string, recursive = true) {
91
+ const data = await this.internal.getImMessage(messageId)
92
+ const message = await Utils.decodeMessage(this, data.data.items[0], recursive)
93
+ const im = await this.internal.getImChat(channelId)
94
+ message.channel.type = im.data.chat_mode === 'p2p' ? Universal.Channel.Type.DIRECT : Universal.Channel.Type.TEXT
95
+ return message
78
96
  }
79
97
 
80
98
  async getMessageList(channelId: string, before?: string) {
81
- const { data: messages } = await this.internal.getMessageList({ container_id_type: 'chat', container_id: channelId, page_token: before })
99
+ const { data: messages } = await this.internal.listImMessage({ container_id_type: 'chat', container_id: channelId, page_token: before })
82
100
  const data = await Promise.all(messages.items.reverse().map(data => Utils.decodeMessage(this, data)))
83
101
  return { data, next: data[0]?.id }
84
102
  }
85
103
 
86
104
  async getUser(userId: string, guildId?: string) {
87
105
  const data = await this.internal.getContactUser(userId)
88
- return Utils.decodeUser(data.data)
106
+ return Utils.decodeUser(data.data.user)
89
107
  }
90
108
 
91
109
  async getChannel(channelId: string) {
92
- const { data } = await this.internal.getGuildInfo(channelId)
93
- return Utils.decodeChannel(data)
110
+ const { data } = await this.internal.getImChat(channelId)
111
+ return Utils.decodeChannel(channelId, data)
94
112
  }
95
113
 
96
114
  async getChannelList(guildId: string) {
@@ -98,24 +116,24 @@ export class LarkBot<C extends Context = Context> extends Bot<C, LarkBot.Config>
98
116
  }
99
117
 
100
118
  async getGuild(guildId: string) {
101
- const { data } = await this.internal.getGuildInfo(guildId)
119
+ const { data } = await this.internal.getImChat(guildId)
102
120
  return Utils.decodeGuild(data)
103
121
  }
104
122
 
105
123
  async getGuildList(after?: string) {
106
- const { data: guilds } = await this.internal.getCurrentUserGuilds({ page_token: after })
124
+ const { data: guilds } = await this.internal.listImChat({ page_token: after })
107
125
  return { data: guilds.items.map(Utils.decodeGuild), next: guilds.page_token }
108
126
  }
109
127
 
110
128
  async getGuildMemberList(guildId: string, after?: string) {
111
- const { data: users } = await this.internal.getGuildMembers(guildId, { page_token: after })
129
+ const { data: users } = await this.internal.getImChatMembers(guildId, { page_token: after })
112
130
  const data = users.items.map(v => ({ user: { id: v.member_id, name: v.name }, name: v.name }))
113
131
  return { data, next: users.page_token }
114
132
  }
115
133
  }
116
134
 
117
135
  export namespace LarkBot {
118
- export interface Config extends HttpServer.Options, Quester.Config {
136
+ export interface Config extends HttpServer.Options, HTTP.Config {
119
137
  appId: string
120
138
  appSecret: string
121
139
  encryptKey?: string
@@ -135,14 +153,14 @@ export namespace LarkBot {
135
153
  Schema.object({
136
154
  platform: Schema.const('feishu').required(),
137
155
  }),
138
- Quester.createConfig('https://open.feishu.cn/open-apis/'),
156
+ HTTP.createConfig('https://open.feishu.cn/open-apis/'),
139
157
  HttpServer.createConfig('/feishu'),
140
158
  ]),
141
159
  Schema.intersect([
142
160
  Schema.object({
143
161
  platform: Schema.const('lark').required(),
144
162
  }),
145
- Quester.createConfig('https://open.larksuite.com/open-apis/'),
163
+ HTTP.createConfig('https://open.larksuite.com/open-apis/'),
146
164
  HttpServer.createConfig('/lark'),
147
165
  ]),
148
166
  ]),
package/src/http.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Readable } from 'node:stream'
2
2
  import { ReadableStream } from 'node:stream/web'
3
- import { Adapter, Context, Logger, Schema } from '@satorijs/satori'
4
- import {} from '@cordisjs/server'
3
+ import { Adapter, Context, Logger, Schema } from '@satorijs/core'
4
+ import {} from '@cordisjs/plugin-server'
5
5
 
6
6
  import { FeishuBot } from './bot'
7
7
  import { AllEvents } from './types'
@@ -72,6 +72,7 @@ export class HttpServer<C extends Context = Context> extends Adapter<C, FeishuBo
72
72
 
73
73
  // dispatch message
74
74
  bot.logger.debug('received decryped event: %o', body)
75
+ // @TODO: need await?
75
76
  this.dispatchSession(body)
76
77
 
77
78
  // Lark requires 200 OK response to make sure event is received
@@ -91,20 +92,19 @@ export class HttpServer<C extends Context = Context> extends Adapter<C, FeishuBo
91
92
  params: { type },
92
93
  responseType: 'stream',
93
94
  })
94
-
95
+ ctx.set('content-type', resp.headers.get('content-type'))
95
96
  ctx.status = 200
96
- ctx.response.headers['Content-Type'] = resp.headers.get('content-type')
97
97
  ctx.response.body = Readable.fromWeb(resp.data)
98
98
  })
99
99
  }
100
100
 
101
- dispatchSession(body: AllEvents): void {
101
+ async dispatchSession(body: AllEvents) {
102
102
  const { header } = body
103
103
  if (!header) return
104
104
  const { app_id, event_type } = header
105
105
  body.type = event_type // add type to body to ease typescript type narrowing
106
- const bot = this.bots.find((bot) => bot.selfId === app_id)
107
- const session = adaptSession(bot, body)
106
+ const bot = this.bots.find((bot) => bot.appId === app_id)
107
+ const session = await adaptSession(bot, body)
108
108
  bot.dispatch(session)
109
109
  }
110
110
 
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { FeishuBot } from './bot'
1
+ import { LarkBot } from './bot'
2
2
  import * as Lark from './types'
3
3
 
4
4
  export * from './bot'
5
5
 
6
6
  export { Lark, Lark as Feishu }
7
7
 
8
- export default FeishuBot
8
+ export default LarkBot
9
9
 
10
10
  declare module '@satorijs/core' {
11
11
  interface Session {
package/src/message.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Context, h, MessageEncoder, Quester } from '@satorijs/satori'
1
+ import { Context, h, MessageEncoder } from '@satorijs/core'
2
2
  import { LarkBot } from './bot'
3
- import { BaseResponse, Message, MessageContent, MessageType } from './types'
3
+ import { BaseResponse, Lark, MessageContent, MessageType } from './types'
4
4
  import { extractIdType } from './utils'
5
5
 
6
6
  export interface Addition {
@@ -17,22 +17,26 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
17
17
 
18
18
  async post(data?: any) {
19
19
  try {
20
- let resp: BaseResponse & { data: Message }
20
+ let resp: BaseResponse & { data?: Lark.Message }
21
21
  if (this.quote) {
22
- resp = await this.bot.internal?.replyMessage(this.quote, data)
22
+ resp = await this.bot.internal.replyImMessage(this.quote, data)
23
23
  } else {
24
24
  data.receive_id = this.channelId
25
- resp = await this.bot.internal?.sendMessage(extractIdType(this.channelId), data)
25
+ resp = await this.bot.internal?.createImMessage(data, {
26
+ receive_id_type: extractIdType(this.channelId),
27
+ })
26
28
  }
27
29
  const session = this.bot.session()
28
30
  session.messageId = resp.data.message_id
29
31
  session.timestamp = Number(resp.data.create_time) * 1000
30
32
  session.userId = resp.data.sender.id
33
+ session.channelId = this.channelId
34
+ session.guildId = this.guildId
31
35
  session.app.emit(session, 'send', session)
32
36
  this.results.push(session.event.message)
33
37
  } catch (e) {
34
38
  // try to extract error message from Lark API
35
- if (Quester.Error.is(e)) {
39
+ if (this.bot.http.isError(e)) {
36
40
  if (e.response?.data?.code) {
37
41
  const generalErrorMsg = `Check error code at https://open.larksuite.com/document/server-docs/getting-started/server-error-codes`
38
42
  e.message += ` (Lark error code ${e.response.data.code}: ${e.response.data.msg ?? generalErrorMsg})`
@@ -79,7 +83,7 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
79
83
 
80
84
  if (type === 'img' || type === 'image') {
81
85
  payload.append('image_type', 'message')
82
- const { data } = await this.bot.internal.uploadImage(payload)
86
+ const { data } = await this.bot.internal.createImImage(payload)
83
87
  return {
84
88
  type: 'image',
85
89
  file: {
@@ -105,7 +109,7 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
105
109
  }
106
110
  }
107
111
  payload.append('file_name', filename)
108
- const { data } = await this.bot.internal.uploadFile(payload)
112
+ const { data } = await this.bot.internal.createImFile(payload)
109
113
  return {
110
114
  type: msgType,
111
115
  file: {
@@ -157,6 +161,7 @@ export class LarkMessageEncoder<C extends Context = Context> extends MessageEnco
157
161
  if (attrs.src || attrs.url) {
158
162
  await this.flush()
159
163
  this.addition = await this.sendFile(type, attrs.src || attrs.url)
164
+ await this.flush()
160
165
  }
161
166
  break
162
167
  case 'figure': // FIXME: treat as message element for now