Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,63 @@ router.post('/webhook/event', lark.adaptKoaRouter(eventDispatcher));
server.use(router.routes());
server.listen(3000);
````
#### Combined with NextJS
```typescript
// pages/api/webhook.ts
import * as lark from '@larksuiteoapi/node-sdk';

const client = new lark.Client({
appId: 'xxxxxxxxxxxxxxxxxxxxxxx',
appSecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
appType: lark.AppType.SelfBuild
});

const eventDispatcher = new lark.EventDispatcher({
verificationToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
encryptKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
}).register({
'im.message.receive_v1': async (data) => {
const chatId = data.message.chat_id;

const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: chatId,
content: JSON.stringify({text: 'hello world'}),
msg_type: 'text'
},
});
return res;
}
});

export default async (req: NextApiRequest, res: NextApiResponse) => {
await lark.adaptNextjs(eventDispatcher, {
autoChallenge: true,
})(req, res);
};
```

#### Custom adapter
If you want to adapt to services written by other libraries, you currently need to encapsulate the corresponding adapter yourself. Pass the received event data to the invoke method of the instantiated `eventDispatcher` for event processing:
If you need to adapt a service written in another library, you can refer to the following approach to call the encapsulated custom adapter.

```typescript
// Taking NextJS as an example:
export default async (req: NextApiRequest, res: NextApiResponse) => {
const result = await lark.adaptCustom(eventDispatcher, {
autoChallenge: true,
})(req.headers, req.body);
res.end(result);
};
```

If the call fails or an error occurs, please check if the req.headers and req.body formats match the following image by setting breakpoints:

![](doc/request-body.png)

If you need to encapsulate it yourself, you can refer to the following logic. Pass the received event data to the invoke method of the instantiated `eventDispatcher` for event processing:

```typescript
const data = server.getData();
Expand Down
60 changes: 59 additions & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,68 @@ router.post('/webhook/event', lark.adaptKoaRouter(eventDispatcher));
server.use(router.routes());
server.listen(3000);
```
#### 和NextJS结合

```typescript
// pages/api/webhook.ts
import * as lark from '@larksuiteoapi/node-sdk';

const client = new lark.Client({
appId: 'xxxxxxxxxxxxxxxxxxxxxxx',
appSecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
appType: lark.AppType.SelfBuild
});

const eventDispatcher = new lark.EventDispatcher({
verificationToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
encryptKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
}).register({
'im.message.receive_v1': async (data) => {
const chatId = data.message.chat_id;

const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: chatId,
content: JSON.stringify({text: 'hello world'}),
msg_type: 'text'
},
});
return res;
}
});

export default async (req: NextApiRequest, res: NextApiResponse) => {
await lark.adaptNextjs(eventDispatcher, {
autoChallenge: true,
})(req, res);
};
```

#### 自定义适配器
如果要适配其它库编写的服务,目前需要自己来封装相应的适配器。将接收到的事件数据传递给实例化的`eventDispatcher`的invoke方法进行事件的处理即可:

如果要适配其它库编写的服务,可以参考下述方式来调用已经封装的自定义适配器

```typescript
// 以 NextJS 为例
export default async (req: NextApiRequest, res: NextApiResponse) => {
const result = await lark.adaptCustom(eventDispatcher, {
autoChallenge: true,
})(req.headers, req.body);
res.end(result);
};
```

如果调用失败或者错误,请断点检查 `req.headers` 和 `req.body` 格式是否和下图中一致
![](doc/request-body.png)


如果需要自行封装,可参考下述逻辑,将接收到的事件数据传递给实例化的 `eventDispatcher` 的 invoke 方法进行事件的处理即可:

```typescript
// 注意这是伪代码
const data = server.getData();
const result = await dispatcher.invoke(data);
server.sendResult(result);
Expand Down
43 changes: 43 additions & 0 deletions adaptor/custom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import get from 'lodash.get';
import { EventDispatcher } from '@node-sdk/dispatcher/event';
import { CardActionHandler } from '@node-sdk/dispatcher/card';
import { generateChallenge } from './services/challenge';

export const adaptCustom =
(
dispatcher: EventDispatcher | CardActionHandler,
options?: {
autoChallenge?: boolean;
}
) =>
async (headers, body) => {
if (!body || !headers) {
return;
}

const data = Object.assign(
Object.create({
headers: headers,
}),
body
);

const autoChallenge = get(options, 'autoChallenge', false);
if (autoChallenge) {
const { isChallenge, challenge } = generateChallenge(data, {
encryptKey: dispatcher.encryptKey,
});

if (isChallenge) {
return JSON.stringify(challenge);
}
}

const value = await dispatcher.invoke(data);

// event don't need response
if (dispatcher instanceof CardActionHandler) {
return JSON.stringify(value);
}
return '';
};
22 changes: 22 additions & 0 deletions adaptor/nextjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import get from 'lodash.get';
import { EventDispatcher } from '@node-sdk/dispatcher/event';
import { CardActionHandler } from '@node-sdk/dispatcher/card';
import { adaptCustom } from './custom';

export const adaptNextjs =
(
dispatcher: EventDispatcher | CardActionHandler,
options?: {
autoChallenge?: boolean;
}
) =>
async (req, res) => {
if (!req?.body || !req?.headers) {
return;
}
res.end(
await adaptCustom(dispatcher, {
autoChallenge: get(options, 'autoChallenge', false),
})(req.headers, req.body)
);
};
Binary file added doc/request-body.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.