basant307/AI_Governance_Project
048
1# 飞书开放接口SDK2 3456 7[English](https://github.com/larksuite/node-sdk/blob/main/README.md)8 9## 概述10 11[飞书开放平台](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)提供了一系列服务端的原子api来实现多元化的功能,但在实际编码过程中感受不是很顺畅,原因在于使用这些api完成功能时,需要考虑很多额外的工作,如token的获取及其维护、数据加解密、请求的验签等等;再者,在实际编码过程中,少了函数调用的语义化描述,类型系统的支持,使得心智负担过重。12 13凡此种种,都使得整体的开发体验不佳,基于此,为了让开放能力变得易用,我们编写了该SDK,将所有冗长的逻辑内置处理,提供完备的类型系统,对外提供语义化的编程接口,提高编码体验。😙14 15以下是一些基于该sdk的官方教程:16 17- [快速开发互动卡片](https://open.feishu.cn/document/home/quickly-develop-interactive-cards/introduction)18- [快速开发人员及考勤管理系统](https://open.feishu.cn/document/home/quick-start-of-personnel-and-attendance-management-system/overview)19- [快速接入多维表格](https://open.feishu.cn/document/home/quick-access-to-base/preparation)20- [基于网页应用实现部门人员管理](https://open.feishu.cn/document/home/quick-access-to-base/department-personnel-management-based-on-web-app/overview)21- [快速接入通讯录](https://open.feishu.cn/document/home/quick-access-to-contact-api/introduction)22- [基于审批实现自动考勤管理](https://open.feishu.cn/document/home/automatic-attendance-management-based-on-approval/introduction)23 24## 概念25 26- 开发文档:开放平台的开放接口的参考,**开发者必看,可以使用搜索功能,高效的查询文档**。[更多介绍说明](https://open.feishu.cn/document/) 。27- 开发者后台:开发者开发应用的管理后台,[更多介绍说明](https://open.feishu.cn/app/) 。28- 企业自建应用:应用仅仅可在本企业内安装使用,[更多介绍说明](https://open.feishu.cn/document/uQjL04CN/ukzM04SOzQjL5MDN) 。29- 应用商店应用:应用会在 [应用目录](https://app.feishu.cn/?lang=zh-CN)30 展示,各个企业可以选择安装,[更多介绍说明](https://open.feishu.cn/document/uQjL04CN/ugTO5UjL4kTO14CO5kTN) 。31 32## 安装33 34npm35 36```shell37npm install @larksuiteoapi/node-sdk38```39 40yarn41 42```43yarn add @larksuiteoapi/node-sdk44```45 46## 如何使用47 48提供ECMAScript,CommonJS2个版本,支持原生Javascript和Typescript的使用,示例均以Typescript为例。49 50Typescript51 52```typescript53import * as lark from '@larksuiteoapi/node-sdk';54```55 56CommonJS57 58```javascript59const lark = require('@larksuiteoapi/node-sdk');60```61 62ECMAScript63 64```javascript65import * as lark from '@larksuiteoapi/node-sdk';66```67 68### api调用69 70飞书开放平台开放的所有 API 列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/server-api-list)。71 72SDK提供了语义化的调用方式,只需要依据相关参数构造出client实例,接着使用其上的语义化方法(*client.业务域.资源.方法*)即可完成api调用,调用过程及调用结果均有完备的类型进行提示,如向群聊中发送消息:73 74```typescript75import * as lark from '@larksuiteoapi/node-sdk';76 77const client = new lark.Client({78 appId: 'app id',79 appSecret: 'app secret',80 appType: lark.AppType.SelfBuild,81 domain: lark.Domain.Feishu,82});83 84const res = await client.im.message.create({85 params: {86 receive_id_type: 'chat_id',87 },88 data: {89 receive_id: 'receive_id',90 content: JSON.stringify({text: 'hello world'}),91 msg_type: 'text',92 },93});94```95 96> tips:97>98> - 如果想调试某个api,可以点击注释中的链接进入api调试台进行调试:99> 100> - 如何获取语义化调用接口:[点击这里](https://github.com/larksuite/node-sdk/issues/42)101 102#### 创建client103 104对于自建应用,可以使用下面的代码创建一个client:105 106```typescript107import * as lark from '@larksuiteoapi/node-sdk';108 109const client = new lark.Client({110 appId: 'app id',111 appSecret: 'app secret'112});113```114 115对于商店应用,需要显示的指定appType为lark.AppType.ISV:116 117```typescript118import * as lark from '@larksuiteoapi/node-sdk';119 120const client = new lark.Client({121 appId: 'app id',122 appSecret: 'app secret',123 appType: lark.AppType.ISV,124});125```126 127**使用创建好的商店应用的client发起api调用时,还需在请求时手动传递[tenant\_key](https://open.feishu.cn/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/g#d15ab5d)**,可以使用lark.withTenantKey来完成:128 129```typescript130client.im.message.create({131 params: {132 receive_id_type: 'chat_id',133 },134 data: {135 receive_id: 'chat_id',136 content: JSON.stringify({text: 'hello world'}),137 msg_type: 'text'138 },139}, lark.withTenantKey('tenant key'));140```141 142#### `Client`构造参数:143 144| 参数 | 描述 | 类型 | 必须 | 默认 |145| ----------------- | ---------------------------------------------------------------------------------- | ---------------- | -- | -------------------------------------------------------------- |146| appId | 应用的id | string | 是 | - |147| appSecret | 应用的密码 | string | 是 | - |148| domain | 应用的域,分为飞书(<https://open.feishu.cn)、lark(https://open.larksuite.com)、其它(需要传递完整的域名)> | Domain \| string | 否 | Domain.Feishu |149| httpInstance | sdk发送请求的http实例。*sdk内部默认使用axios.create()构造出一个defaultHttpInstance来进行http调用。* | HttpInstance | 否 | defaultHttpInstance。*可以从sdk中import它,在其上添加interceptors来完成业务需求。* |150| loggerLevel | 日志级别 | LoggerLevel | 否 | info |151| logger | - | Logger | 否 | - |152| cache | 缓存器 | Cache | 否 | - |153| disableTokenCache | 是否禁用缓存,如若禁用,则token等不会进行缓存,每次需要使用时都会重新拉取 | boolean | 否 | false |154| appType | 应用的类型,分为商店应用或者自建应用 | AppType | 否 | AppType.SelfBuild |155| helpDeskId | 服务台id | string | 否 | - |156| helpDeskToken | 服务台token | string | 否 | - |157 158#### 分页159 160针对返回值以分页形式呈现的接口,对其提供了迭代器方式的封装(方法名后缀为WithIterator),提高易用性,消弭了根据page\_token来反复获取数据的繁琐操作,如获取用户列表:161 162```typescript163// 每次处理20条数据164for await (const items of await client.contact.user.listWithIterator({165 params: {166 department_id: '0',167 page_size: 20,168 },169})) {170 console.log(items);171}172 173// 也可用next来手动控制迭代,每次取20条数据174const listIterator = await SDKClient.contact.user.listWithIterator({175 params: {176 department_id: '0',177 page_size: 20,178 },179});180const { value } = await listIterator[Symbol.asyncIterator]().next();181console.log(value);182```183 184*当然也可以使用无迭代器封装的版本,这时候需要自己每次根据返回的page\_token来手动进行分页调用。*185 186#### 文件上传187 188和调用普通api的方式一样,按类型提示传递参数即可,内部封装了对文件上传的处理,如:189 190```typescript191const res = await client.im.file.create({192 data: {193 file_type: 'mp4',194 file_name: 'test.mp4',195 file: fs.readFileSync('file path'),196 },197});198```199 200#### 文件下载201 202对返回的二进制流进行了封装,消弭了对流本身的处理,只需调用writeFile方法即可将数据写入文件,如:203 204```typescript205const resp = await client.im.file.get({206 path: {207 file_key: 'file key',208 },209});210await resp.writeFile(`filepath.suffix`);211```212 213如果想要自定义对流的处理,可以调用getReadableStream方法获取到流,如将流写入文件:214 215```typescript216import * as fs from 'fs';217 218const resp = await client.im.file.get({219 path: {220 file_key: 'file key',221 },222});223const readableStream = resp.getReadableStream();224const writableStream = fs.createWriteStream('file url');225readableStream.pipe(writableStream);226```227 228> 注意:流只能被消费一次,即如果使用了writeFile消费了流,则getReadableStream获取流会报错/获取到的流为空;如需消费多次流,可以使用getReadableStream获取流,然后读取流中的数据做缓存,将缓存的数据给消费方使用。229 230#### 普通调用231 232某些老版本的开放接口,无法生成对应的语义化调用方法,需要使用client上的request方法来进行手动调用:233 234```typescript235import * as lark from '@larksuiteoapi/node-sdk';236 237const client = new lark.Client({238 appId: 'app id',239 appSecret: 'app secret',240 appType: lark.AppType.SelfBuild,241 domain: lark.Domain.Feishu,242});243 244const res = await client.request({245 method: 'POST',246 url: 'xxx',247 data: {},248 params: {},249});250```251 252#### 消息卡片253 254在发送[消息卡片](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)信息时,会先在[消息卡片搭建工具](https://open.feishu.cn/document/ukTMukTMukTM/uYzM3QjL2MzN04iNzcDN/message-card-builder)中搭建出消息卡片的模版,拿到生成的模版json,用数据替换其中内容相关的部分,将结果作为支持消息卡片api的参数来使用。如发送一个简单的具有`title`和`content`的消息卡片:255 256```typescript257client.im.message.create({258 params: {259 receive_id_type: 'chat_id',260 },261 data: {262 receive_id: 'your receive_id',263 content: JSON.stringify({264 "config": {265 "wide_screen_mode": true266 },267 "elements": [268 {269 "tag": "markdown",270 "content": "Card Content"271 }272 ],273 "header": {274 "template": "blue",275 "title": {276 "content": "Card Title",277 "tag": "plain_text"278 }279 }280 }281 ),282 msg_type: 'interactive'283 }284})285```286 287288这样使用会有一个问题:**如果消息卡片内容比较丰富,生成的模版json比较大,与之相关需要数据填充的内容部分也会比较多,手动维护比较繁琐**。针对这个问题,开放平台提供了[模版消息](https://open.feishu.cn/document/tools-and-resources/message-card-builder#3e1f2c7c)的能力,发送消息卡片时只需要提供模版id和模版的数据内容即可。sdk对这个能力进行了调用上的封装,支持消息卡片的接口会同步的增加一个ByCard的调用方式,只需要传递`template_id`和`template_variable`即可。如上面的调用可以改写成:289 290```typescript291client.im.message.createByCard({292 params: {293 receive_id_type: 'chat_id',294 },295 data: {296 receive_id: 'your receive_id',297 template_id: 'your template_id',298 template_variable: {299 content: "Card Content",300 title: "Card Title"301 }302 }303});304```305 306如果想要快速体验消息卡片,可以使用sdk中内置的一个基础卡片:307 308```typescript309import * as lark from '@larksuiteoapi/node-sdk';310 311client.im.message.create({312 params: {313 receive_id_type: 'chat_id',314 },315 data: {316 receive_id: 'your receive_id',317 content: lark.messageCard.defaultCard({318 title: 'Card Title',319 content: 'Card Content'320 }),321 msg_type: 'interactive'322 }323})324```325 326效果同上:327328 329#### 配置请求选项330 331如果想在api调用过程中修改请求的参数,如携带一些header,自定义tenantToken等,则可以使用请求方法的第二个参数来进行修改:332 333```typescript334await client.im.message.create({335 params: {336 receive_id_type: 'chat_id',337 },338 data: {339 receive_id: 'receive_id',340 content: JSON.stringify({text: 'hello world'}),341 msg_type: 'text',342 },343}, {344 headers: {345 customizedHeaderKey: 'customizedHeaderValue'346 }347});348```349 350SDK亦将常用的修改操作封装成了方法,可以使用:351 352| 方法 | 描述 |353| ---------------------- | ------------------------------------------------------------------------------------- |354| withTenantKey | 设置tenant key |355| withTenantToken | 设置tenant token |356| withHelpDeskCredential | 是否在请求中带入[服务台token](https://open.feishu.cn/document/ukTMukTMukTM/ugDOyYjL4gjM24CO4IjN) |357| withUserAccessToken | 设置access token |358| withAll | 将上述方法的结果合并起来 |359 360```typescript361await client.im.message.create({362 params: {363 receive_id_type: 'chat_id',364 },365 data: {366 receive_id: 'receive_id',367 content: JSON.stringify({text: 'hello world'}),368 msg_type: 'text',369 },370}, lark.withTenantToken('tenant token'));371 372await client.im.message.create({373 params: {374 receive_id_type: 'chat_id',375 },376 data: {377 receive_id: 'receive_id',378 content: JSON.stringify({text: 'hello world'}),379 msg_type: 'text',380 },381}, lark.withAll([382 lark.withTenantToken('tenant token'),383 lark.withTenantKey('tenant key')384]));385```386 387### 处理事件388 389飞书开放平台开放的所有事件列表,可点击[这里查看](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list)。390 391针对事件处理的场景,我们所关心的仅是**监听何种事件**,以及事件发生后我们**做些什么**,其它诸如数据解密等工作是我们不想关心的。SDK提供了直观的方式来描述这部分逻辑:392 3931. 构造事件处理器`EventDispatcher`的实例;3942. 在实例上注册需要监听的事件及其处理函数;3953. 将实例和服务进行绑定;396 397`EventDispatcher`内部会进行数据解密等操作,如果没有传递相关参数,则会自动忽略。398 399```typescript400import http from 'http';401import * as lark from '@larksuiteoapi/node-sdk';402 403const eventDispatcher = new lark.EventDispatcher({404 encryptKey: 'encrypt key'405}).register({406 'im.message.receive_v1': async (data) => {407 const chatId = data.message.chat_id;408 409 const res = await client.im.message.create({410 params: {411 receive_id_type: 'chat_id',412 },413 data: {414 receive_id: chatId,415 content: JSON.stringify({text: 'hello world'}),416 msg_type: 'text'417 },418 });419 return res;420 }421});422 423const server = http.createServer();424server.on('request', lark.adaptDefault('/webhook/event', eventDispatcher));425server.listen(3000);426```427 428#### `EventDispatcher`构造参数429 430| 参数 | 描述 | 类型 | 必须 | 默认 |431| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ----------- | -- | --------------------- |432| [encryptKey](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case) | 推送数据加密的key,开启加密推送时需要使用来进行数据解密 | string | 否 | - |433| loggerLevel | 日志级别 | LoggerLevel | 否 | lark.LoggerLevel.info |434| logger | - | Logger | 否 | - |435| cache | 缓存器 | Cache | 否 | - |436 437> 注:有一些事件是v1.0版本且已经不在维护了,SDK保留了对其的支持,强烈建议使用与之功能相一致的新版事件代替。鼠标移动到相应事件订阅函数上即可看到相关文档:438> 439 440#### 和express结合441 442SDK提供了针对experss的适配器,用于将eventDispatcher转化为express的中间件,可无缝与使用express编写的服务相结合(*示例中的bodyParser的使用不是必须的,但社区大多用其来格式化body数据*):443 444```typescript445import * as lark from '@larksuiteoapi/node-sdk';446import express from 'express';447import bodyParser from 'body-parser';448 449const server = express();450server.use(bodyParser.json());451 452const eventDispatcher = new lark.EventDispatcher({453 encryptKey: 'encryptKey',454}).register({455 'im.message.receive_v1': async (data) => {456 const chatId = data.message.chat_id;457 458 const res = await client.im.message.create({459 params: {460 receive_id_type: 'chat_id',461 },462 data: {463 receive_id: chatId,464 content: JSON.stringify({text: 'hello world'}),465 msg_type: 'text'466 },467 });468 return res;469 }470});471 472server.use('/webhook/event', lark.adaptExpress(eventDispatcher));473server.listen(3000);474```475 476#### 和koa结合477 478SDK提供了针对koa的适配器,用于将eventDispatcher转化为koa的中间件,可无缝与使用koa编写的服务相结合(*示例中的koaBody的使用不是必须的,但社区大多用其来格式化body数据*):479 480```typescript481import * as lark from '@larksuiteoapi/node-sdk';482import Koa from 'koa';483import koaBody from 'koa-body';484 485const server = new Koa();486server.use(koaBody());487 488const eventDispatcher = new lark.EventDispatcher({489 encryptKey: 'encryptKey',490}).register({491 'im.message.receive_v1': async (data) => {492 const open_chat_id = data.message.chat_id;493 494 const res = await client.im.message.create({495 params: {496 receive_id_type: 'chat_id',497 },498 data: {499 receive_id: open_chat_id,500 content: JSON.stringify({text: 'hello world'}),501 msg_type: 'text'502 },503 });504 505 return res;506 },507});508 509server.use(lark.adaptKoa('/webhook/event', eventDispatcher));510server.listen(3000);511```512 513#### 和koa-router结合514 515在使用koa来编写服务时,大多情况下会配合使用koa-router来对路由进行处理,因此SDK也提供了针对这一情况的适配:516 517```typescript518import * as lark from '@larksuiteoapi/node-sdk';519import Koa from 'koa';520import Router from '@koa/router';521import koaBody from 'koa-body';522 523const server = new Koa();524const router = new Router();525server.use(koaBody());526 527const eventDispatcher = new lark.EventDispatcher({528 encryptKey: 'encryptKey',529}).register({530 'im.message.receive_v1': async (data) => {531 const open_chat_id = data.message.chat_id;532 533 const res = await client.im.message.create({534 params: {535 receive_id_type: 'chat_id',536 },537 data: {538 receive_id: open_chat_id,539 content: JSON.stringify({text: 'hello world'}),540 msg_type: 'text'541 },542 });543 544 return res;545 },546});547 548router.post('/webhook/event', lark.adaptKoaRouter(eventDispatcher));549server.use(router.routes());550server.listen(3000);551```552 553#### 自定义适配器554 555如果要适配其它库编写的服务,目前需要自己来封装相应的适配器。将接收到的事件数据和请求头传递给实例化的 `eventDispatcher` 的 invoke 方法进行事件的处理即可:556 557```typescript558const data = server.getData();559const headers = server.getHeaders();560const assigned = Object.assign(561 Object.create({ headers }),562 data,563);564const result = await dispatcher.invoke(assigned);565server.sendResult(result);566```567 568#### challenge校验569 570在配置事件请求地址时,开放平台会向请求地址推送一个`application/json`格式的 POST请求,该POST请求用于验证所配置的请求地址的合法性,请求体中会携带一个`challenge`字段,**应用需要在 1 秒内,将接收到的challenge值原样返回给飞书开放平台**。详见:[文档](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case)571 572上面sdk提供出的适配器内部封装了这部分验证的逻辑,将options参数中的`autoChallenge`字段设为true即可启用:573 574```typescript575// adaptDefault576lark.adaptDefault('/webhook/event', eventDispatcher, {577 autoChallenge: true,578});579// express580lark.adaptExpress(eventDispatcher, {581 autoChallenge: true,582});583// koa584lark.adaptKoa('/webhook/event', eventDispatcher, {585 autoChallenge: true,586});587// koa-router588router.post(589 '/webhook/event',590 lark.adaptKoaRouter(eventDispatcher, {591 autoChallenge: true,592 })593);594```595 596### 使用长链模式处理事件597 598官方文档:[文档](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/event-subscription-guide/long-connection-mode#62c8b8c8)599 600开发者通过集成飞书 SDK 与开放平台建立一条 WebSocket 全双工通道,当有事件回调发生时,开放平台会通过该通道向开发者发送消息。与传统的 Webhook 模式相比,长连接模式大大降低了接入成本,将原先 1 周左右的开发周期降低到 5 分钟。具体优势如下:601 602- 测试阶段**无需使用内网穿透工具**,通过长连接模式在本地开发环境中即可接收事件回调。603- 只在建连时进行鉴权,后续事件推送均为明文数据,无需开发者再处理解密和验签逻辑。604- 只需保证运行环境具备访问公网能力即可,无需提供公网 IP 或域名。605- 无需部署防火墙和配置白名单。606 607> 注意事项608 6091. 与 Webhook 相同, 长连接模式下开发者接收到消息后,需要在 3 秒内处理完成,否则会触发超时重推。6102. 消息推送为 集群模式,不支持广播,即如果同一应用部署了多个客户端,那么只有其中随机一个客户端会收到消息。6113. 目前长连接模式仅支持事件订阅,不支持回调订阅。612 613SDK支持了该功能集成,`1.24.0`及之后的版本可用,示例代码:614 615```typescript616import * as Lark from '@larksuiteoapi/node-sdk';617 618const baseConfig = {619 appId: 'xxx',620 appSecret: 'xxx'621}622 623const client = new Lark.Client(baseConfig);624 625const wsClient = new Lark.WSClient({...baseConfig, loggerLevel: Lark.LoggerLevel.info});626 627wsClient.start({628 eventDispatcher: new Lark.EventDispatcher({}).register({629 'im.message.receive_v1': async (data) => {630 const {631 message: { chat_id, content}632 } = data;633 await client.im.v1.message.create({634 params: {635 receive_id_type: "chat_id"636 },637 data: {638 receive_id: chat_id,639 content: Lark.messageCard.defaultCard({640 title: `reply: ${JSON.parse(content).text}`,641 content: 'hello'642 }),643 msg_type: 'interactive'644 }645 });646 }647 })648});649```650 651### Channel 模块652 653`Channel` 是在 `WSClient` / `Client` 之上封装的高层模块,一站式封装飞书机器人接入的传输、消息归一化、安全策略、出站发送、流式回复、媒体上传、卡片交互等杂活,适合会话式机器人(AI 对话、流式回复、卡片按钮等)场景。654 655```typescript656import { createLarkChannel } from '@larksuiteoapi/node-sdk';657 658const channel = createLarkChannel({ appId, appSecret });659 660channel.on('message', async (msg) => {661 await channel.send(662 msg.chatId,663 { markdown: `收到:${msg.content}` },664 { replyTo: msg.messageId },665 );666});667 668await channel.connect();669```670 671完整用法(事件监听、发送消息、流式回复、底层能力、错误处理、配置项等)见 [docs/channel.zh.md](./docs/channel.zh.md)。672 673### [消息卡片](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)674 675对消息卡片的处理亦是对事件处理的一种,两者的不同点仅在于消息卡片的处理器用于响应用户与消息卡片交互所产生的事件,若处理器有返回值(*返回值的数据结构理应为符合[消息卡片结构](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)所定义的结构*),则返回值被用来更新被响应的消息卡片:676 677```typescript678import http from 'http';679import * as lark from '@larksuiteoapi/node-sdk';680import type { InteractiveCardActionEvent, InteractiveCard } from '@larksuiteoapi/node-sdk';681 682const cardDispatcher = new lark.CardActionHandler(683 {684 encryptKey: 'encrypt key',685 verificationToken: 'verification token'686 },687 async (data: InteractiveCardActionEvent) => {688 console.log(data);689 const newCard: InteractiveCard = {690 // your new interactive card content691 header: {},692 elements: []693 };694 return newCard;695 }696);697 698const server = http.createServer();699server.on('request', lark.adaptDefault('/webhook/card', cardDispatcher));700server.listen(3000);701```702 703#### `CardActionHandler`构造参数704 705| 参数 | 描述 | 类型 | 必须 | 默认 |706| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ----------- | -- | ---------------- |707| [encryptKey](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case) | 推送数据加密的key,开启加密推送时需要使用来进行数据解密 | string | 否 | - |708| [verificationToken](https://open.feishu.cn/document/ukTMukTMukTM/uYzMxEjL2MTMx4iNzETM) | 安全校验,开启消息的安全校验时需要使用 | string | 否 | - |709| loggerLevel | 日志级别 | LoggerLevel | 否 | LoggerLevel.info |710| logger | - | Logger | 否 | - |711| cache | 缓存器 | Cache | 否 | - |712 713### 一键创建应用714 715SDK提供了`registerApp`方法,基于 OAuth 2.0 Device Authorization Grant(RFC 8628)协议实现一键创建应用。该方法会返回一个验证链接,用户在飞书/Lark中打开该链接完成授权后,即可自动注册应用并获取凭据,无需手动到开发者后台创建。716 717```typescript718import * as lark from '@larksuiteoapi/node-sdk';719 720try {721 const result = await lark.registerApp({722 onQRCodeReady(info) {723 console.log(`请扫码: ${info.url}`);724 console.log(`链接将在 ${info.expireIn} 秒后过期`);725 },726 onStatusChange(info) {727 // 处理状态变化:'polling' | 'slow_down' | 'domain_switched'728 },729 });730 731 console.log('App ID:', result.client_id);732 console.log('App Secret:', result.client_secret);733 734 // 用获取到的凭据初始化 Client735 const client = new lark.Client({736 appId: result.client_id,737 appSecret: result.client_secret,738 });739} catch (e) {740 // e.code: 'access_denied' | 'expired_token' | 'abort' | ...741 // e.description: 错误描述742 console.error(e.code, e.description);743}744```745 746#### `registerApp`参数747 748| 参数 | 描述 | 类型 | 必须 | 默认 |749| -------------- | ---------------------------------------------------------------------------------------- | ----------- | -- | ------------------------ |750| domain | 自定义认证域名(仅 host 部分) | string | 否 | `accounts.feishu.cn` |751| larkDomain | 自定义 Lark 认证域名(仅 host 部分),检测到 Lark 租户时自动切换 | string | 否 | `accounts.larksuite.com` |752| source | 来源标识,拼入二维码 URL 的 `from` 参数,格式为 `node-sdk/{source}` | string | 否 | - |753| signal | 用于取消轮询的 `AbortSignal` | AbortSignal | 否 | - |754| onQRCodeReady | 验证链接就绪时的回调,参数为 `{ url, expireIn }`。可将 URL 渲染为二维码供用户扫码,或直接作为链接展示 | function | 是 | - |755| onStatusChange | 轮询状态变化时的回调,参数为 `{ status, interval? }`。status 取值:`polling`、`slow_down`、`domain_switched` | function | 否 | - |756 757#### 返回值758 759| 字段 | 类型 | 描述 |760| ------------------------ | ---------- | --------------------- |761| client\_id | string | App ID |762| client\_secret | string | App Secret |763| user\_info | object(可选) | 扫码用户信息 |764| user\_info.open\_id | string(可选) | 扫码用户的 open\_id |765| user\_info.tenant\_brand | string(可选) | `"feishu"` 或 `"lark"` |766 767#### 错误处理768 769抛出的错误对象包含 `code` 和 `description` 字段:770 771| code | 描述 |772| --------------- | ----------------- |773| `access_denied` | 用户拒绝了授权 |774| `expired_token` | 二维码过期或轮询超时 |775| `abort` | 通过 AbortSignal 取消 |776 777### 工具方法778 779#### AESCipher780 781解密。如果配置了[加密推送](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/encrypt-key-encryption-configuration-case),开放平台会推送加密的数据,这时候需要对数据进行解密处理,调用此方法可以便捷的进行解密。(一般情况下,SDK中内置了解密逻辑,不需要手动进行处理)。782 783```typescript784import * as lark from '@larksuiteoapi/node-sdk';785 786new lark.AESCipher('encrypt key').decrypt('content');787```788 789## Examples790 791[快速开发自动回复机器人](https://github.com/larksuite/lark-samples/blob/main/react_and_nodejs/robot/README.zh.md)792 793## Blog794 795[ISV(商店应用)开发指南](https://bytedance.feishu.cn/docx/RUZKdGwdyoH4KexMJgDcITQnn0b)796 797## 许可协议798 799MIT800 801## 联系我们802 803点击[服务端SDK](https://open.feishu.cn/document/ukTMukTMukTM/uETO1YjLxkTN24SM5UjN) 页面右上角【这篇文档是否对你有帮助?】提交反馈😘804 