@larksuiteoapi/node-sdk 1.0.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/README.zh.md ADDED
@@ -0,0 +1,451 @@
1
+ # 飞书开放接口SDK
2
+ [English](https://github.com/larksuite/node-sdk/blob/main/README.md)
3
+ ## 概述
4
+ [飞书开放平台](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)提供了一系列服务端的原子api来实现多元化的功能,但在实际编码过程中感受不是很顺畅,原因在于使用这些api完成功能时,需要考虑很多额外的工作,如token的获取及其维护、数据加解密、请求的验签等等;在者,在实际编码过程中,少了函数调用的语义化描述,类型系统的支持,使得心智负担过重。
5
+
6
+ 凡此种种,都使得整体的开发体验不佳,基于此,为了让开放能力变得易用,我们编写了该SDK,将所有冗长的逻辑内置处理,提供完备的类型系统,对外提供语义化的编程接口,提高编码体验。😙
7
+
8
+ ## 概念
9
+ - 开发文档:开放平台的开放接口的参考,**开发者必看,可以使用搜索功能,高效的查询文档**。[更多介绍说明](https://open.feishu.cn/document/) 。
10
+
11
+ - 开发者后台:开发者开发应用的管理后台,[更多介绍说明](https://open.feishu.cn/app/) 。
12
+
13
+ - 企业自建应用:应用仅仅可在本企业内安装使用,[更多介绍说明](https://open.feishu.cn/document/uQjL04CN/ukzM04SOzQjL5MDN) 。
14
+
15
+ - 应用商店应用:应用会在 [应用目录](https://app.feishu.cn/?lang=zh-CN)
16
+ 展示,各个企业可以选择安装,[更多介绍说明](https://open.feishu.cn/document/uQjL04CN/ugTO5UjL4kTO14CO5kTN) 。
17
+
18
+ ## 安装
19
+ npm
20
+ ```shell script
21
+ npm install @larksuiteoapi/node-sdk
22
+ ```
23
+ yarn
24
+ ```
25
+ yarn add @larksuiteoapi/node-sdk
26
+ ```
27
+
28
+ ## 如何使用
29
+ 提供ECMAScript,CommonJS2个版本,支持原生Javascript和Typescript的使用,示例均以Typescript为例。
30
+
31
+ Typescript
32
+ ```typescript
33
+ import * as lark from '@larksuiteoapi/node-sdk';
34
+ ```
35
+ CommonJS
36
+ ```javascript
37
+ const lark = require('@larksuiteoapi/node-sdk');
38
+ ```
39
+ ECMAScript
40
+ ```javascript
41
+ import * as lark from '@larksuiteoapi/node-sdk';
42
+ ```
43
+ ### api调用
44
+ 飞书开放平台开放的所有 API 列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/server-api-list)。
45
+
46
+ SDK提供了语义化的调用方式,只需要依据相关参数构造出client实例,接着使用其上的语义化方法(*client.业务域.资源.方法*)即可完成api调用,调用过程及调用结果均有完备的类型进行提示,如向群聊中发送消息:
47
+ ```typescript
48
+ import * as lark from '@larksuiteoapi/node-sdk';
49
+
50
+ const client = new lark.Client({
51
+ appId: 'app id',
52
+ appSecret: 'app secret',
53
+ appType: lark.AppType.SelfBuild,
54
+ domain: lark.Domain.Feishu,
55
+ });
56
+
57
+ const res = await client.im.message.create({
58
+ params: {
59
+ receive_id_type: 'chat_id',
60
+ },
61
+ data: {
62
+ receive_id: 'receive_id',
63
+ content: 'hello world',
64
+ msg_type: 'text',
65
+ },
66
+ });
67
+ ```
68
+ > 注:SDK对API的调用结果做了完备的类型声明,在开发中我们绝大部分关注点在于调用结果本身,但仍有少数场景需要关注API的调用细节,SDK将这部分内容放在了调用结果的原型对象上,如果需要得到这部分信息,可以使用`res.request`来获取。
69
+
70
+ > tips: 如果想调试某个api,可以点击注释中的链接进入api调试台进行调试:
71
+ ![](doc/debugger-tip.png)
72
+
73
+ #### 创建client
74
+ 对于自建应用,可以使用下面的代码创建一个client:
75
+
76
+ ```typescript
77
+ import * as lark from '@larksuiteoapi/node-sdk';
78
+
79
+ const client = new lark.Client({
80
+ appId: 'app id',
81
+ appSecret: 'app secret'
82
+ });
83
+ ```
84
+
85
+ 对于商店应用,需要显示的指定appType为lark.AppType.ISV:
86
+ ```typescript
87
+ import * as lark from '@larksuiteoapi/node-sdk';
88
+
89
+ const client = new lark.Client({
90
+ appId: 'app id',
91
+ appSecret: 'app secret',
92
+ appType: lark.AppType.ISV,
93
+ });
94
+ ```
95
+ **使用创建好的商店应用的client发起api调用时,还需在请求时手动传递[tenant_key](https://open.feishu.cn/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/g#d15ab5d)**,可以使用lark.withTenantKey来完成:
96
+ ```typescript
97
+ client.im.message.create({
98
+ params: {
99
+ receive_id_type: 'chat_id',
100
+ },
101
+ data: {
102
+ receive_id: 'chat_id',
103
+ content: 'hello world',
104
+ msg_type: 'text'
105
+ },
106
+ }, lark.withTenantKey('tenant key'));
107
+ ```
108
+
109
+ #### `Client`构造参数:
110
+ | 参数 | 描述 | 类型 | 必须 | 默认 |
111
+ | ---- | ---- | ---- | ---- | ---- |
112
+ | appId | 应用的id | string | 是 | - |
113
+ | appSecret | 应用的密码 | string | 是 | - |
114
+ | domain | 应用的域,分为飞书(https://open.feishu.cn)、lark(https://open.larksuite.com)、其它(需要传递完整的域名) | Domain | string | 否 | Domain.Feishu |
115
+ | loggerLevel | 日志级别 | LoggerLevel | 否 | info |
116
+ | logger | - | Logger | 否 | - |
117
+ | cache | 缓存器 | Cache | 否 | - |
118
+ | disableTokenCache | 是否禁用缓存,如若禁用,则token等不会进行缓存,每次需要使用时都会重新拉取 | boolean | 否 | false |
119
+ | appType | 应用的类型,分为商店应用或者自建应用 | AppType | 否 | AppType.SelfBuild |
120
+ | helpDeskId | 服务台id | string | 否 | - |
121
+ | helpDeskToken | 服务台token | string | 否 | - |
122
+
123
+ #### 分页
124
+ 针对返回值以分页形式呈现的接口,对其提供了迭代器方式的封装(方法名后缀为WithIterator),提高易用性,消弭了根据page_token来反复获取数据的繁琐操作,如获取用户列表:
125
+ ``` typescript
126
+ // 每次处理20条数据
127
+ for await (const items of await client.contact.user.listWithIterator({
128
+ params: {
129
+ department_id: '0',
130
+ page_size: 20,
131
+ },
132
+ })) {
133
+ console.log(items);
134
+ }
135
+
136
+ // 也可用next来手动控制迭代,每次取20条数据
137
+ const listIterator = await SDKClient.contact.user.listWithIterator({
138
+ params: {
139
+ department_id: '0',
140
+ page_size: 20,
141
+ },
142
+ });
143
+ const { value } = await listIterator[Symbol.asyncIterator]().next();
144
+ console.log(value);
145
+ ```
146
+ *当然也可以使用无迭代器封装的版本,这时候需要自己每次根据返回的page_token来手动进行分页调用。*
147
+ #### 文件上传
148
+ 和调用普通api的方式一样,按类型提示传递参数即可,内部封装了对文件上传的处理,如:
149
+ ```typescript
150
+ const res = await client.im.file.create({
151
+ data: {
152
+ file_type: 'mp4',
153
+ file_name: 'test.mp4',
154
+ file: fs.readFileSync('file path'),
155
+ },
156
+ });
157
+ ```
158
+ #### 文件下载
159
+ 对返回的二进制流进行了封装,消弭了对流本身的处理,只需调用writeFile方法即可将数据写入文件,如:
160
+ ```typescript
161
+ const resp = await client.im.file.get({
162
+ path: {
163
+ file_key: 'file key',
164
+ },
165
+ });
166
+ await resp.writeFile(`filepath.suffix`);
167
+ ```
168
+
169
+ #### 普通调用
170
+ 某些老版本的开放接口,无法生成对应的语义化调用方法,需要使用client上的request方法来进行手动调用:
171
+ ```typescript
172
+ import * as lark from '@larksuiteoapi/node-sdk';
173
+
174
+ const client = new lark.Client({
175
+ appId: 'app id',
176
+ appSecret: 'app secret',
177
+ appType: lark.AppType.SelfBuild,
178
+ domain: lark.Domain.Feishu,
179
+ });
180
+
181
+ const res = await client.request({
182
+ method: 'POST',
183
+ url: 'xxx',
184
+ data: {},
185
+ params: {},
186
+ });
187
+ ```
188
+
189
+ #### 配置请求选项
190
+ 如果想在api调用过程中修改请求的参数,如携带一些header,自定义tenantToken等,则可以使用请求方法的第二个参数来进行修改:
191
+ ```typescript
192
+ await client.im.message.create({
193
+ params: {
194
+ receive_id_type: 'chat_id',
195
+ },
196
+ data: {
197
+ receive_id: 'receive_id',
198
+ content: 'hello world',
199
+ msg_type: 'text',
200
+ },
201
+ }, {
202
+ headers: {
203
+ customizedHeaderKey: 'customizedHeaderValue'
204
+ }
205
+ });
206
+ ```
207
+ SDK亦将常用的修改操作封装成了方法,可以使用:
208
+
209
+ | 方法 | 描述 |
210
+ | ---- | ---- |
211
+ | withTenantKey | 设置tenant key |
212
+ | withTenantToken | 设置tenant token |
213
+ | withHelpDeskCredential | 是否在请求中带入[服务台token](https://open.feishu.cn/document/ukTMukTMukTM/ugDOyYjL4gjM24CO4IjN) |
214
+ | withUserAccessToken | 设置access token |
215
+ | withAll | 将上述方法的结果合并起来 |
216
+
217
+ ```typescript
218
+ await client.im.message.create({
219
+ params: {
220
+ receive_id_type: 'chat_id',
221
+ },
222
+ data: {
223
+ receive_id: 'receive_id',
224
+ content: 'hello world',
225
+ msg_type: 'text',
226
+ },
227
+ }, lark.withTenantToken('tenant token'));
228
+
229
+ await client.im.message.create({
230
+ params: {
231
+ receive_id_type: 'chat_id',
232
+ },
233
+ data: {
234
+ receive_id: 'receive_id',
235
+ content: 'hello world',
236
+ msg_type: 'text',
237
+ },
238
+ }, lark.withAll([
239
+ lark.withTenantToken('tenant token'),
240
+ lark.withTenantKey('tenant key')
241
+ ]));
242
+ ```
243
+
244
+ ### 处理事件
245
+ 飞书开放平台开放的所有事件列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list)。
246
+
247
+ 针对事件处理的场景,我们所关心的仅是**监听何种事件**,以及事件发生后我们**做些什么**,其它诸如数据解密等工作是我们不想关心的。SDK提供了直观的方式来描述这部分逻辑:
248
+ 1. 构造事件处理器`EventDispatcher`的实例;
249
+ 2. 在实例上注册需要监听的事件及其处理函数;
250
+ 3. 将实例和服务进行绑定;
251
+
252
+ `EventDispatcher`内部会进行数据解密等操作,如果没有传递相关参数,则会自动忽略。
253
+ ```typescript
254
+ import http from 'http';
255
+ import * as lark from '@larksuiteoapi/node-sdk';
256
+
257
+ const eventDispatcher = new lark.EventDispatcher({
258
+ encryptKey: 'encrypt key'
259
+ }).register({
260
+ 'im.message.receive_v1': async (data) => {
261
+ const chatId = data.message.chat_id;
262
+
263
+ const res = await client.im.message.create({
264
+ params: {
265
+ receive_id_type: 'chat_id',
266
+ },
267
+ data: {
268
+ receive_id: chatId,
269
+ content: 'hello world',
270
+ msg_type: 'text'
271
+ },
272
+ });
273
+ return res;
274
+ }
275
+ });
276
+
277
+ const server = http.createServer();
278
+ server.on('request', lark.adaptDefault('/webhook/event', eventDispatcher));
279
+ server.listen(3000);
280
+ ```
281
+
282
+ #### `EventDispatcher`构造参数
283
+
284
+ | 参数 | 描述 | 类型 | 必须 | 默认 |
285
+ | ---- | ---- | ---- | ---- | ---- |
286
+ | [encryptKey](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case) | 推送数据加密的key,开启加密推送时需要使用来进行数据解密 | string | 否 | - |
287
+ | loggerLevel | 日志级别 | LoggerLevel | 否 | lark.LoggerLevel.info |
288
+ | logger | - | Logger | 否 | - |
289
+ | cache | 缓存器 | Cache | 否 | - |
290
+
291
+ > 注:有一些事件是v1.0版本且已经不在维护了,SDK保留了对其的支持,强烈建议使用与之功能相一致的新版事件代替。鼠标移动到相应事件订阅函数上即可看到相关文档:
292
+ ![](doc/deprecated.png)
293
+
294
+ #### 和express结合
295
+ SDK提供了针对experss的适配器,用于将eventDispatcher转化为express的中间件,可无缝与使用express编写的服务相结合(*示例中的bodyParser的使用不是必须的,但社区大多用其来格式化body数据*):
296
+ ```typescript
297
+ import * as lark from '@larksuiteoapi/node-sdk';
298
+ import express from 'express';
299
+ import bodyParser from 'body-parser';
300
+
301
+ const server = express();
302
+ server.use(bodyParser.json());
303
+
304
+ const eventDispatcher = new lark.EventDispatcher({
305
+ encryptKey: 'encryptKey',
306
+ }).register({
307
+ 'im.message.receive_v1': async (data) => {
308
+ const chatId = data.message.chat_id;
309
+
310
+ const res = await client.im.message.create({
311
+ params: {
312
+ receive_id_type: 'chat_id',
313
+ },
314
+ data: {
315
+ receive_id: chatId,
316
+ content: 'hello world',
317
+ msg_type: 'text'
318
+ },
319
+ });
320
+ return res;
321
+ }
322
+ });
323
+
324
+ server.use('/webhook/event', lark.adaptExpress(eventDispatcher));
325
+ server.listen(3000);
326
+ ```
327
+ #### 和koa结合
328
+ SDK提供了针对koa的适配器,用于将eventDispatcher转化为koa的中间件,可无缝与使用koa编写的服务相结合(*示例中的koaBody的使用不是必须的,但社区大多用其来格式化body数据*):
329
+ ```typescript
330
+ import * as lark from '@larksuiteoapi/node-sdk';
331
+ import Koa from 'koa';
332
+ import koaBody from 'koa-body';
333
+
334
+ const server = new Koa();
335
+ server.use(koaBody());
336
+
337
+ const eventDispatcher = new lark.EventDispatcher({
338
+ encryptKey: 'encryptKey',
339
+ }).register({
340
+ 'im.message.receive_v1': async (data) => {
341
+ const open_chat_id = data.message.chat_id;
342
+
343
+ const res = await client.im.message.create({
344
+ params: {
345
+ receive_id_type: 'chat_id',
346
+ },
347
+ data: {
348
+ receive_id: open_chat_id,
349
+ content: 'hello world',
350
+ msg_type: 'text'
351
+ },
352
+ });
353
+
354
+ return res;
355
+ },
356
+ });
357
+
358
+ server.use(nodeSdk.adaptKoa('/webhook/event', eventDispatcher));
359
+ server.listen(3000);
360
+ ```
361
+ #### 和koa-router结合
362
+ 在使用koa来编写服务时,大多情况下会配合使用koa-router来对路由进行处理,因此SDK也提供了针对这一情况的适配:
363
+ ```typescript
364
+ import * as nodeSdk from '@larksuiteoapi/node-sdk';
365
+ import Koa from 'koa';
366
+ import Router from '@koa/router';
367
+ import koaBody from 'koa-body';
368
+
369
+ const server = new Koa();
370
+ const router = new Router();
371
+ server.use(koaBody());
372
+
373
+ const eventDispatcher = new lark.EventDispatcher({
374
+ encryptKey: 'encryptKey',
375
+ }).register({
376
+ 'im.message.receive_v1': async (data) => {
377
+ const open_chat_id = data.message.chat_id;
378
+
379
+ const res = await client.im.message.create({
380
+ params: {
381
+ receive_id_type: 'chat_id',
382
+ },
383
+ data: {
384
+ receive_id: open_chat_id,
385
+ content: 'hello world',
386
+ msg_type: 'text'
387
+ },
388
+ });
389
+
390
+ return res;
391
+ },
392
+ });
393
+
394
+ router.post('/webhook/event', lark.adaptKoaRouter(eventDispatcher));
395
+ server.use(router.routes());
396
+ server.listen(3000);
397
+ ```
398
+ #### 自定义适配器
399
+ 如果要适配其它库编写的服务,目前需要自己来封装相应的适配器。将接收到的事件数据传递给实例化的`eventDispatcher`的invoke方法进行事件的处理即可:
400
+
401
+ ```typescript
402
+ const data = server.getData();
403
+ const resule = await dispatcher.invoke(data);
404
+ server.sendResult(result);
405
+ ```
406
+
407
+ ### [消息卡片](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
408
+ 对消息卡片的处理亦是对事件处理的一种,两者的不同点仅在于消息卡片的处理器用于响应用户与消息卡片交互所产生的事件,若处理器有返回值(*返回值的数据结构理应为符合[消息卡片结构](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)所定义的结构*),则返回值被用来更新被响应的消息卡片:
409
+
410
+ ```typescript
411
+ import http from 'http';
412
+ import * as lark from '@larksuiteoapi/node-sdk';
413
+
414
+ const cardDispatcher = new lark.CardActionHandler(
415
+ {
416
+ encryptKey: 'encrypt key',
417
+ verificationToken: 'verification token'
418
+ },
419
+ async (data) => {
420
+ console.log(data);
421
+ return newCard;
422
+ }
423
+ );
424
+
425
+ const server = http.createServer();
426
+ server.on('request', lark.adaptDefault('/webhook/card', cardDispatcher));
427
+ server.listen(3000);
428
+ ```
429
+ #### `CardActionHandler`构造参数
430
+
431
+ | 参数 | 描述 | 类型 | 必须 | 默认 |
432
+ | ---- | ---- | ---- | ---- | ---- |
433
+ | [encryptKey](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case) | 推送数据加密的key,开启加密推送时需要使用来进行数据解密 | string | 否 | - |
434
+ | [verificationToken](https://open.feishu.cn/document/ukTMukTMukTM/uYzMxEjL2MTMx4iNzETM) | 安全校验,开启消息的安全校验时需要使用 | string | 否 | - |
435
+ | loggerLevel | 日志级别 | LoggerLevel | 否 | LoggerLevel.info |
436
+ | logger | - | Logger | 否 | - |
437
+ | cache | 缓存器 | Cache | 否 | - |
438
+
439
+ ### 工具方法
440
+ #### AESCipher
441
+ 解密。如果配置了[加密推送](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case),开放平台会推送加密的数据,这时候需要对数据进行解密处理,调用此方法可以便捷的进行解密。(一般情况下,SDK中内置了解密逻辑,不需要手动进行处理)。
442
+ ```typescript
443
+ import * as lark from '@larksuiteoapi/node-sdk';
444
+
445
+ new lark.AESCipher('encrypt key').decrypt('content');
446
+ ```
447
+ ## 许可协议
448
+ MIT
449
+
450
+ ## 联系我们
451
+ 点击[服务端SDK](https://open.feishu.cn/document/ukTMukTMukTM/uETO1YjLxkTN24SM5UjN) 页面右上角【这篇文档是否对你有帮助?】提交反馈😘