@larksuiteoapi/node-sdk 1.2.3 → 1.3.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.
- package/README.md +30 -0
- package/README.zh.md +30 -0
- package/es/index.js +66 -1
- package/lib/index.js +66 -1
- package/package.json +9 -4
- package/types/index.d.ts +6 -1
package/README.md
CHANGED
|
@@ -403,6 +403,32 @@ const resule = await dispatcher.invoke(data);
|
|
|
403
403
|
server.sendResult(result);
|
|
404
404
|
````
|
|
405
405
|
|
|
406
|
+
#### challenge校验
|
|
407
|
+
When configuring the event request address, The Open Platform will push a POST request in `application/json` format to the request address. The POST request is used to verify the validity of the configured request address, and the request body will carry a `challenge` field , **The application needs to return the received challenge value to the Open Platform within 1 second**. See: [ Document](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case)
|
|
408
|
+
|
|
409
|
+
The adapter provided by the sdk above encapsulates the verification logic. Set the `autoChallenge` field in the options parameter to true to enable:
|
|
410
|
+
```typescript
|
|
411
|
+
// adaptDefault
|
|
412
|
+
lark.adaptDefault('/webhook/event', eventDispatcher, {
|
|
413
|
+
autoChallenge: true,
|
|
414
|
+
});
|
|
415
|
+
// express
|
|
416
|
+
lark.adaptExpress(eventDispatcher, {
|
|
417
|
+
autoChallenge: true,
|
|
418
|
+
});
|
|
419
|
+
// koa
|
|
420
|
+
lark.adaptKoa('/webhook/event', eventDispatcher, {
|
|
421
|
+
autoChallenge: true,
|
|
422
|
+
});
|
|
423
|
+
// koa-router
|
|
424
|
+
router.post(
|
|
425
|
+
'/webhook/event',
|
|
426
|
+
lark.adaptKoaRouter(eventDispatcher, {
|
|
427
|
+
autoChallenge: true,
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
```
|
|
431
|
+
|
|
406
432
|
### [Message Card](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
|
|
407
433
|
The processing of the Message Card is also a kind of Event processing. The only difference between the two is that the processor of the Message Card is used to respond to the events generated by the interaction between the user and the Message Card. If the processor has a return value (*the value structure should be in line with the structure defined by [Message Card Structure](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)*), then the return value is used to update the responded message card:
|
|
408
434
|
|
|
@@ -443,6 +469,10 @@ import * as lark from '@larksuiteoapi/node-sdk';
|
|
|
443
469
|
|
|
444
470
|
new lark.AESCipher('encrypt key').decrypt('content');
|
|
445
471
|
````
|
|
472
|
+
|
|
473
|
+
## Blog
|
|
474
|
+
[ISV Application Development Guide](https://bytedance.feishu.cn/docx/RUZKdGwdyoH4KexMJgDcITQnn0b)
|
|
475
|
+
|
|
446
476
|
## LICENSE
|
|
447
477
|
MIT
|
|
448
478
|
|
package/README.zh.md
CHANGED
|
@@ -402,6 +402,32 @@ const resule = await dispatcher.invoke(data);
|
|
|
402
402
|
server.sendResult(result);
|
|
403
403
|
```
|
|
404
404
|
|
|
405
|
+
#### challenge校验
|
|
406
|
+
在配置事件请求地址时,开放平台会向请求地址推送一个`application/json`格式的 POST请求,该POST请求用于验证所配置的请求地址的合法性,请求体中会携带一个`challenge`字段,**应用需要在 1 秒内,将接收到的challenge值原样返回给飞书开放平台**。详见:[文档](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case)
|
|
407
|
+
|
|
408
|
+
上面sdk提供出的适配器内部封装了这部分验证的逻辑,将options参数中的`autoChallenge`字段设为true即可启用:
|
|
409
|
+
```typescript
|
|
410
|
+
// adaptDefault
|
|
411
|
+
lark.adaptDefault('/webhook/event', eventDispatcher, {
|
|
412
|
+
autoChallenge: true,
|
|
413
|
+
});
|
|
414
|
+
// express
|
|
415
|
+
lark.adaptExpress(eventDispatcher, {
|
|
416
|
+
autoChallenge: true,
|
|
417
|
+
});
|
|
418
|
+
// koa
|
|
419
|
+
lark.adaptKoa('/webhook/event', eventDispatcher, {
|
|
420
|
+
autoChallenge: true,
|
|
421
|
+
});
|
|
422
|
+
// koa-router
|
|
423
|
+
router.post(
|
|
424
|
+
'/webhook/event',
|
|
425
|
+
lark.adaptKoaRouter(eventDispatcher, {
|
|
426
|
+
autoChallenge: true,
|
|
427
|
+
})
|
|
428
|
+
);
|
|
429
|
+
```
|
|
430
|
+
|
|
405
431
|
### [消息卡片](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
|
|
406
432
|
对消息卡片的处理亦是对事件处理的一种,两者的不同点仅在于消息卡片的处理器用于响应用户与消息卡片交互所产生的事件,若处理器有返回值(*返回值的数据结构理应为符合[消息卡片结构](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)所定义的结构*),则返回值被用来更新被响应的消息卡片:
|
|
407
433
|
|
|
@@ -442,6 +468,10 @@ import * as lark from '@larksuiteoapi/node-sdk';
|
|
|
442
468
|
|
|
443
469
|
new lark.AESCipher('encrypt key').decrypt('content');
|
|
444
470
|
```
|
|
471
|
+
|
|
472
|
+
## Blog
|
|
473
|
+
[ISV(商店应用)开发指南](https://bytedance.feishu.cn/docx/RUZKdGwdyoH4KexMJgDcITQnn0b)
|
|
474
|
+
|
|
445
475
|
## 许可协议
|
|
446
476
|
MIT
|
|
447
477
|
|
package/es/index.js
CHANGED
|
@@ -19214,13 +19214,40 @@ const pickRequestData = (req) => new Promise((resolve) => {
|
|
|
19214
19214
|
});
|
|
19215
19215
|
});
|
|
19216
19216
|
|
|
19217
|
-
const
|
|
19217
|
+
const generateChallenge = (data, options) => {
|
|
19218
|
+
if ('encrypt' in data && !options.encryptKey) {
|
|
19219
|
+
throw new Error('auto-challenge need encryptKey, please check for missing in dispatcher');
|
|
19220
|
+
}
|
|
19221
|
+
const targetData = 'encrypt' in data
|
|
19222
|
+
? JSON.parse(new AESCipher(options.encryptKey).decrypt(data.encrypt))
|
|
19223
|
+
: data;
|
|
19224
|
+
return {
|
|
19225
|
+
isChallenge: targetData.type === 'url_verification',
|
|
19226
|
+
challenge: {
|
|
19227
|
+
challenge: targetData.challenge,
|
|
19228
|
+
},
|
|
19229
|
+
};
|
|
19230
|
+
};
|
|
19231
|
+
|
|
19232
|
+
const adaptDefault = (path, dispatcher, options) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19218
19233
|
if (req.url !== path) {
|
|
19219
19234
|
return;
|
|
19220
19235
|
}
|
|
19221
19236
|
const data = Object.assign(Object.create({
|
|
19222
19237
|
headers: req.headers,
|
|
19223
19238
|
}), yield pickRequestData(req));
|
|
19239
|
+
// 是否自动响应challange事件:
|
|
19240
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19241
|
+
const autoChallenge = get(options, 'autoChallenge', false);
|
|
19242
|
+
if (autoChallenge) {
|
|
19243
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19244
|
+
encryptKey: dispatcher.encryptKey,
|
|
19245
|
+
});
|
|
19246
|
+
if (isChallenge) {
|
|
19247
|
+
res.end(JSON.stringify(challenge));
|
|
19248
|
+
return;
|
|
19249
|
+
}
|
|
19250
|
+
}
|
|
19224
19251
|
const value = yield dispatcher.invoke(data);
|
|
19225
19252
|
// event don't need response
|
|
19226
19253
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19244,6 +19271,18 @@ const adaptExpress = (dispatcher, options) => (req, res) => __awaiter(void 0, vo
|
|
|
19244
19271
|
const data = Object.assign(Object.create({
|
|
19245
19272
|
headers: req.headers,
|
|
19246
19273
|
}), reqData);
|
|
19274
|
+
// 是否自动响应challange事件:
|
|
19275
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19276
|
+
const autoChallenge = get(options, 'autoChallenge', false);
|
|
19277
|
+
if (autoChallenge) {
|
|
19278
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19279
|
+
encryptKey: dispatcher.encryptKey,
|
|
19280
|
+
});
|
|
19281
|
+
if (isChallenge) {
|
|
19282
|
+
res.json(challenge);
|
|
19283
|
+
return;
|
|
19284
|
+
}
|
|
19285
|
+
}
|
|
19247
19286
|
const value = yield dispatcher.invoke(data);
|
|
19248
19287
|
// event don't need response
|
|
19249
19288
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19269,6 +19308,19 @@ const adaptKoa = (path, dispatcher, options) => (ctx, next) => __awaiter(void 0,
|
|
|
19269
19308
|
const data = Object.assign(Object.create({
|
|
19270
19309
|
headers: req.headers,
|
|
19271
19310
|
}), reqData);
|
|
19311
|
+
// 是否自动响应challange事件:
|
|
19312
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19313
|
+
const autoChallenge = get(options, 'autoChallenge', false);
|
|
19314
|
+
if (autoChallenge) {
|
|
19315
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19316
|
+
encryptKey: dispatcher.encryptKey,
|
|
19317
|
+
});
|
|
19318
|
+
if (isChallenge) {
|
|
19319
|
+
ctx.body = challenge;
|
|
19320
|
+
yield next();
|
|
19321
|
+
return;
|
|
19322
|
+
}
|
|
19323
|
+
}
|
|
19272
19324
|
const value = yield dispatcher.invoke(data);
|
|
19273
19325
|
// event don't need response
|
|
19274
19326
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19297,6 +19349,19 @@ const adaptKoaRouter = (dispatcher, options) => (ctx, next) => __awaiter(void 0,
|
|
|
19297
19349
|
const data = Object.assign(Object.create({
|
|
19298
19350
|
headers: req.headers,
|
|
19299
19351
|
}), reqData);
|
|
19352
|
+
// 是否自动响应challange事件:
|
|
19353
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19354
|
+
const autoChallenge = get(options, 'autoChallenge', false);
|
|
19355
|
+
if (autoChallenge) {
|
|
19356
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19357
|
+
encryptKey: dispatcher.encryptKey,
|
|
19358
|
+
});
|
|
19359
|
+
if (isChallenge) {
|
|
19360
|
+
ctx.body = challenge;
|
|
19361
|
+
yield next();
|
|
19362
|
+
return;
|
|
19363
|
+
}
|
|
19364
|
+
}
|
|
19300
19365
|
const value = yield dispatcher.invoke(data);
|
|
19301
19366
|
// event don't need response
|
|
19302
19367
|
if (dispatcher instanceof CardActionHandler) {
|
package/lib/index.js
CHANGED
|
@@ -19229,13 +19229,40 @@ const pickRequestData = (req) => new Promise((resolve) => {
|
|
|
19229
19229
|
});
|
|
19230
19230
|
});
|
|
19231
19231
|
|
|
19232
|
-
const
|
|
19232
|
+
const generateChallenge = (data, options) => {
|
|
19233
|
+
if ('encrypt' in data && !options.encryptKey) {
|
|
19234
|
+
throw new Error('auto-challenge need encryptKey, please check for missing in dispatcher');
|
|
19235
|
+
}
|
|
19236
|
+
const targetData = 'encrypt' in data
|
|
19237
|
+
? JSON.parse(new AESCipher(options.encryptKey).decrypt(data.encrypt))
|
|
19238
|
+
: data;
|
|
19239
|
+
return {
|
|
19240
|
+
isChallenge: targetData.type === 'url_verification',
|
|
19241
|
+
challenge: {
|
|
19242
|
+
challenge: targetData.challenge,
|
|
19243
|
+
},
|
|
19244
|
+
};
|
|
19245
|
+
};
|
|
19246
|
+
|
|
19247
|
+
const adaptDefault = (path, dispatcher, options) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19233
19248
|
if (req.url !== path) {
|
|
19234
19249
|
return;
|
|
19235
19250
|
}
|
|
19236
19251
|
const data = Object.assign(Object.create({
|
|
19237
19252
|
headers: req.headers,
|
|
19238
19253
|
}), yield pickRequestData(req));
|
|
19254
|
+
// 是否自动响应challange事件:
|
|
19255
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19256
|
+
const autoChallenge = get__default["default"](options, 'autoChallenge', false);
|
|
19257
|
+
if (autoChallenge) {
|
|
19258
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19259
|
+
encryptKey: dispatcher.encryptKey,
|
|
19260
|
+
});
|
|
19261
|
+
if (isChallenge) {
|
|
19262
|
+
res.end(JSON.stringify(challenge));
|
|
19263
|
+
return;
|
|
19264
|
+
}
|
|
19265
|
+
}
|
|
19239
19266
|
const value = yield dispatcher.invoke(data);
|
|
19240
19267
|
// event don't need response
|
|
19241
19268
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19259,6 +19286,18 @@ const adaptExpress = (dispatcher, options) => (req, res) => __awaiter(void 0, vo
|
|
|
19259
19286
|
const data = Object.assign(Object.create({
|
|
19260
19287
|
headers: req.headers,
|
|
19261
19288
|
}), reqData);
|
|
19289
|
+
// 是否自动响应challange事件:
|
|
19290
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19291
|
+
const autoChallenge = get__default["default"](options, 'autoChallenge', false);
|
|
19292
|
+
if (autoChallenge) {
|
|
19293
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19294
|
+
encryptKey: dispatcher.encryptKey,
|
|
19295
|
+
});
|
|
19296
|
+
if (isChallenge) {
|
|
19297
|
+
res.json(challenge);
|
|
19298
|
+
return;
|
|
19299
|
+
}
|
|
19300
|
+
}
|
|
19262
19301
|
const value = yield dispatcher.invoke(data);
|
|
19263
19302
|
// event don't need response
|
|
19264
19303
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19284,6 +19323,19 @@ const adaptKoa = (path, dispatcher, options) => (ctx, next) => __awaiter(void 0,
|
|
|
19284
19323
|
const data = Object.assign(Object.create({
|
|
19285
19324
|
headers: req.headers,
|
|
19286
19325
|
}), reqData);
|
|
19326
|
+
// 是否自动响应challange事件:
|
|
19327
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19328
|
+
const autoChallenge = get__default["default"](options, 'autoChallenge', false);
|
|
19329
|
+
if (autoChallenge) {
|
|
19330
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19331
|
+
encryptKey: dispatcher.encryptKey,
|
|
19332
|
+
});
|
|
19333
|
+
if (isChallenge) {
|
|
19334
|
+
ctx.body = challenge;
|
|
19335
|
+
yield next();
|
|
19336
|
+
return;
|
|
19337
|
+
}
|
|
19338
|
+
}
|
|
19287
19339
|
const value = yield dispatcher.invoke(data);
|
|
19288
19340
|
// event don't need response
|
|
19289
19341
|
if (dispatcher instanceof CardActionHandler) {
|
|
@@ -19312,6 +19364,19 @@ const adaptKoaRouter = (dispatcher, options) => (ctx, next) => __awaiter(void 0,
|
|
|
19312
19364
|
const data = Object.assign(Object.create({
|
|
19313
19365
|
headers: req.headers,
|
|
19314
19366
|
}), reqData);
|
|
19367
|
+
// 是否自动响应challange事件:
|
|
19368
|
+
// https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
|
|
19369
|
+
const autoChallenge = get__default["default"](options, 'autoChallenge', false);
|
|
19370
|
+
if (autoChallenge) {
|
|
19371
|
+
const { isChallenge, challenge } = generateChallenge(data, {
|
|
19372
|
+
encryptKey: dispatcher.encryptKey,
|
|
19373
|
+
});
|
|
19374
|
+
if (isChallenge) {
|
|
19375
|
+
ctx.body = challenge;
|
|
19376
|
+
yield next();
|
|
19377
|
+
return;
|
|
19378
|
+
}
|
|
19379
|
+
}
|
|
19315
19380
|
const value = yield dispatcher.invoke(data);
|
|
19316
19381
|
// event don't need response
|
|
19317
19382
|
if (dispatcher instanceof CardActionHandler) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larksuiteoapi/node-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "larksuite open sdk for nodejs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"feishu",
|
|
@@ -37,26 +37,31 @@
|
|
|
37
37
|
"lodash.pickby": "^4.6.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@
|
|
40
|
+
"@koa/router": "^12.0.0",
|
|
41
41
|
"@rollup/plugin-alias": "^3.1.9",
|
|
42
42
|
"@rollup/plugin-typescript": "^8.3.2",
|
|
43
43
|
"@types/jest": "^28.1.3",
|
|
44
44
|
"@types/lodash.get": "^4.4.7",
|
|
45
45
|
"@types/lodash.merge": "^4.6.7",
|
|
46
46
|
"@types/lodash.pick": "^4.4.7",
|
|
47
|
+
"@typescript-eslint/parser": "^5.27.1",
|
|
48
|
+
"body-parser": "^1.20.1",
|
|
47
49
|
"eslint": "^8.17.0",
|
|
48
50
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
49
51
|
"eslint-config-prettier": "^8.5.0",
|
|
50
52
|
"eslint-plugin-import": "^2.26.0",
|
|
51
|
-
"
|
|
53
|
+
"express": "^4.18.2",
|
|
52
54
|
"jest": "^28.1.1",
|
|
55
|
+
"koa": "^2.13.4",
|
|
56
|
+
"koa-body": "^5.0.0",
|
|
57
|
+
"prettier": "2.6.2",
|
|
53
58
|
"rollup": "^2.75.5",
|
|
54
59
|
"rollup-plugin-dts": "^4.2.2",
|
|
55
60
|
"rollup-plugin-license": "^2.8.1",
|
|
56
61
|
"ts-jest": "^28.0.5",
|
|
62
|
+
"ts-node": "^10.8.1",
|
|
57
63
|
"tsconfig-paths": "^4.0.0",
|
|
58
64
|
"tslib": "^2.4.0",
|
|
59
|
-
"ts-node": "^10.8.1",
|
|
60
65
|
"typescript": "^4.7.3"
|
|
61
66
|
},
|
|
62
67
|
"publishConfig": {
|
package/types/index.d.ts
CHANGED
|
@@ -39443,18 +39443,23 @@ declare class CardActionHandler {
|
|
|
39443
39443
|
invoke(data: any): Promise<any>;
|
|
39444
39444
|
}
|
|
39445
39445
|
|
|
39446
|
-
declare const adaptDefault: (path: string, dispatcher: EventDispatcher | CardActionHandler
|
|
39446
|
+
declare const adaptDefault: (path: string, dispatcher: EventDispatcher | CardActionHandler, options?: {
|
|
39447
|
+
autoChallenge?: boolean;
|
|
39448
|
+
}) => (req: any, res: any) => Promise<void>;
|
|
39447
39449
|
|
|
39448
39450
|
declare const adaptExpress: (dispatcher: EventDispatcher | CardActionHandler, options?: {
|
|
39449
39451
|
logger?: Logger;
|
|
39452
|
+
autoChallenge?: boolean;
|
|
39450
39453
|
}) => (req: any, res: any) => Promise<void>;
|
|
39451
39454
|
|
|
39452
39455
|
declare const adaptKoa: (path: string, dispatcher: EventDispatcher | CardActionHandler, options?: {
|
|
39453
39456
|
logger?: Logger;
|
|
39457
|
+
autoChallenge?: boolean;
|
|
39454
39458
|
}) => (ctx: any, next: any) => Promise<void>;
|
|
39455
39459
|
|
|
39456
39460
|
declare const adaptKoaRouter: (dispatcher: EventDispatcher | CardActionHandler, options?: {
|
|
39457
39461
|
logger?: Logger;
|
|
39462
|
+
autoChallenge?: boolean;
|
|
39458
39463
|
}) => (ctx: any, next: any) => Promise<void>;
|
|
39459
39464
|
|
|
39460
39465
|
export { AESCipher, AppType, CAppTicket, CardActionHandler, Client, Domain, EventDispatcher, IHandles as EventHandles, LoggerLevel, adaptDefault, adaptExpress, adaptKoa, adaptKoaRouter, withAll, withHelpDeskCredential, withTenantKey, withTenantToken, withUserAccessToken };
|