最近在使用nodejs开发飞书自建应用,简单记录一下。
可以借助飞书的SDK(@larksuiteoapi/node-sdk),提升开发效率。
事件订阅
事件订阅方式分为 使用长连接接收事件 和 将事件发送至开发者服务器 两种。
使用长连接接收事件
长连接是飞书 SDK 内提供的能力,你可以在本地服务器集成飞书 SDK, 与开放平台建立一条 WebSocket 全双工通道(你的服务器需要能够访问公网)。后续当应用订阅的事件发生时,开放平台会通过该通道向你的服务器发送事件消息。
创建长连接客户端:
import { WSClient } from '@larksuiteoapi/node-sdk';
public createWsClient() {
// 初始化飞书SDK配置
const config = {
appId: 飞书应用ID,
appSecret: 飞书应用秘钥,
};
const wsClient = new WSClient(config);
return wsClient;
}
监听事件:
import { EventDispatcher } from '@larksuiteoapi/node-sdk';
const wsClient = createWsClient()
const eventDispatcher = new EventDispatcher({}).register({
'corehr.job_data.employed_v1': async data => {
console.log(data);
},
});
// 建立长连接 Establish persistent connection
await wsClient.start({ eventDispatcher });
将事件发送至开发者服务器
自建一个服务器,通过 Webhook 模式接收飞书开放平台的事件订阅通知,需要提供服务器的公网访问地址。
import http from "http";
import * as lark from "@larksuiteoapi/node-sdk";
const eventDispatcher = new lark.EventDispatcher({
encryptKey: "encrypt key", // 加密密钥
}).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;
},
});
const server = http.createServer();
server.on(
"request",
lark.adaptDefault("/webhook/event", eventDispatcher, {
autoChallenge: true,
})
);
server.listen(3000);
接口请求
创建http客户端:
import { Client } from '@larksuiteoapi/node-sdk';
public createClient() {
// 初始化飞书SDK配置
const config = {
appId,
appSecret,
};
const client = new Client(config);
return client;
}
api调用
/**
* 发送消息
* @see https://open.feishu.cn/document/server-docs/im-v1/message/create
*/
public async sendMessage(params: {
client: Client;
/** 接收对象ID */
receiveId: string;
/** 内容 */
content: string;
/** 消息类型 */
msgType?: string;
/** 接收对象ID类型 */
receiveIdType?: "open_id" | "user_id" | "union_id" | "email" | "chat_id";
}) {
const {
client,
receiveId,
content,
msgType = "text",
receiveIdType = "chat_id",
} = params;
try {
const response = await this.ctx.helper.throttleRun(
"im.v1.message.create",
{ intervalCap: 16, interval: 1000 },
() =>
client.im.v1.message.create({
params: {
receive_id_type: receiveIdType,
},
data: {
receive_id: receiveId,
msg_type: msgType,
content,
},
})
);
if (response.code !== 0) {
throw new Error(`发送消息失败: ${response.msg}`);
}
return response.data?.message_id;
} catch (error) {
this.logger.error(`发送消息错误: ${(error as Error).message}`);
throw error;
}
}
常见问题
频率限制
飞书的open api一般都有调用频率限制,简单的可以使用p-queue来限制请求频率,如上面调用Api所示,传入{ intervalCap: 16, interval: 1000 },限制每秒最多调用16次。
import PQueue from "p-queue";
const pool = new Map<string, PQueue>();
/**
* 频控运行
* @param key 队列唯一标识
* @param queueOpts PQueue队列参数
* @param factory promise函数
*/
export function throttleRun<T>(
key: string,
queueOpts: any,
factory: () => Promise<T>
): Promise<T> {
if (!pool.has(key)) {
pool.set(
key,
new PQueue({
...queueOpts,
timeout: 30000, // 队列任务超时时间
// carryoverConcurrencyCount: true,
})
);
}
return pool.get(key)!.add(factory);
}
bot is invisible to user ids错误
调用Api的时候返回机器人无法访问用户ID的错误
{
code: 232043,
msg: "Your request contains unavailable ids, ext=bot is invisible to user ids: [ou_xxx ou_xxx]",
error: [Object],
log_id: "xxx",
troubleshooter: "排查建议查看(Troubleshooting suggestions): https://open.feishu.cn/search?from=openapi&log_id=xxx&code=232043&method_id=6936075528890908700",
}
这是因为应用权限或数据权限缺少问题,在飞书开发者后台添加Api权限,或者在发布版本中更新应用可用范围。
还没有评论,快来抢第一吧