@zhin.js/adapter-lark 1.0.25 → 1.0.27
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/CHANGELOG.md +20 -0
- package/README.md +73 -609
- package/lib/index.d.ts +55 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +425 -0
- package/lib/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# @zhin.js/adapter-lark
|
|
2
2
|
|
|
3
|
-
Zhin.js
|
|
3
|
+
Zhin.js 飞书 / Lark 适配器,支持飞书机器人的消息收发和群管理。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 支持飞书和 Lark 国际版
|
|
8
|
+
- Webhook 事件接收(HTTP 回调)
|
|
9
|
+
- Bearer Token 自动刷新
|
|
10
|
+
- 消息加密和签名验证(可选)
|
|
11
|
+
- URL 验证自动处理
|
|
12
|
+
- 群管理 AI 工具(通过 declareSkill 暴露给 AI)
|
|
4
13
|
|
|
5
14
|
## 安装
|
|
6
15
|
|
|
@@ -8,644 +17,99 @@ Zhin.js 飞书/Lark 适配器,支持飞书(中国版)和 Lark(国际版
|
|
|
8
17
|
pnpm add @zhin.js/adapter-lark
|
|
9
18
|
```
|
|
10
19
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
### 基础配置
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { LarkBotConfig } from '@zhin.js/adapter-lark';
|
|
17
|
-
|
|
18
|
-
const config: LarkBotConfig = {
|
|
19
|
-
context: 'lark',
|
|
20
|
-
name: 'my-lark-bot',
|
|
21
|
-
appId: 'YOUR_APP_ID', // 飞书应用 ID
|
|
22
|
-
appSecret: 'YOUR_APP_SECRET', // 飞书应用密钥
|
|
23
|
-
webhookPath: '/lark/webhook', // Webhook 路径
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### 完整配置
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
const config: LarkBotConfig = {
|
|
31
|
-
context: 'lark',
|
|
32
|
-
name: 'my-lark-bot',
|
|
33
|
-
appId: 'YOUR_APP_ID',
|
|
34
|
-
appSecret: 'YOUR_APP_SECRET',
|
|
35
|
-
webhookPath: '/lark/webhook',
|
|
36
|
-
|
|
37
|
-
// 安全配置(推荐)
|
|
38
|
-
encryptKey: 'YOUR_ENCRYPT_KEY', // 事件推送加密密钥
|
|
39
|
-
verificationToken: 'YOUR_VERIFICATION_TOKEN', // 事件推送验证令牌
|
|
40
|
-
|
|
41
|
-
// API 配置
|
|
42
|
-
isFeishu: true, // true=飞书(中国版), false=Lark(国际版), 默认false
|
|
43
|
-
apiBaseUrl: 'https://open.feishu.cn/open-apis' // 自定义API地址(可选)
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### 配置参数说明
|
|
20
|
+
## 依赖
|
|
48
21
|
|
|
49
|
-
- `
|
|
50
|
-
- `appSecret` (必需): 飞书应用密钥,在开发者后台获取
|
|
51
|
-
- `webhookPath` (必需): Webhook 路径,如 `/lark/webhook`
|
|
52
|
-
- `encryptKey` (推荐): 事件推送加密密钥,用于签名验证
|
|
53
|
-
- `verificationToken` (推荐): 事件推送验证令牌,额外安全验证
|
|
54
|
-
- `isFeishu` (可选): 是否为飞书中国版,默认 `false` (Lark 国际版)
|
|
55
|
-
- `apiBaseUrl` (可选): 自定义 API 基础地址
|
|
56
|
-
|
|
57
|
-
## 获取配置信息
|
|
58
|
-
|
|
59
|
-
### 创建飞书/Lark 应用
|
|
60
|
-
|
|
61
|
-
#### 飞书(中国版)
|
|
62
|
-
1. 访问 [飞书开放平台](https://open.feishu.cn/app)
|
|
63
|
-
2. 点击「创建应用」,选择「自建应用」
|
|
64
|
-
3. 填写应用信息并创建
|
|
65
|
-
|
|
66
|
-
#### Lark(国际版)
|
|
67
|
-
1. 访问 [Lark Developer Console](https://open.larksuite.com/app)
|
|
68
|
-
2. 点击「Create App」,选择「Custom App」
|
|
69
|
-
3. 填写应用信息并创建
|
|
70
|
-
|
|
71
|
-
### 获取应用凭证
|
|
72
|
-
|
|
73
|
-
在应用详情页面的「凭证与基础信息」中获取:
|
|
74
|
-
- **App ID**: 应用唯一标识
|
|
75
|
-
- **App Secret**: 应用密钥(注意保密)
|
|
76
|
-
|
|
77
|
-
### 配置机器人
|
|
78
|
-
|
|
79
|
-
1. **启用机器人功能**:
|
|
80
|
-
- 在应用管理页面,进入「功能配置」→「机器人」
|
|
81
|
-
- 启用机器人功能
|
|
82
|
-
|
|
83
|
-
2. **配置事件订阅**:
|
|
84
|
-
- 进入「事件订阅」页面
|
|
85
|
-
- 设置请求网址:`https://yourdomain.com/lark/webhook`
|
|
86
|
-
- 配置加密策略(推荐启用)
|
|
87
|
-
- 订阅需要的事件类型:
|
|
88
|
-
- `接收消息` - 接收用户发送的消息
|
|
89
|
-
- `消息已读` - 消息已读事件
|
|
90
|
-
- 其他所需事件
|
|
91
|
-
|
|
92
|
-
3. **配置权限**:
|
|
93
|
-
- 在「权限管理」中申请所需权限:
|
|
94
|
-
- `以应用的身份发消息` - 发送消息
|
|
95
|
-
- `获取与发送单聊、群组消息` - 收发消息
|
|
96
|
-
- `读取用户通讯录基本信息` - 获取用户信息
|
|
97
|
-
- 其他业务需要的权限
|
|
98
|
-
|
|
99
|
-
4. **发布应用**:
|
|
100
|
-
- 完成配置后,提交审核并发布应用
|
|
101
|
-
|
|
102
|
-
## 使用示例
|
|
103
|
-
|
|
104
|
-
### 基础使用
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
import { createApp } from 'zhin.js';
|
|
108
|
-
import '@zhin.js/adapter-lark';
|
|
109
|
-
import '@zhin.js/http'; // 需要 HTTP 插件支持
|
|
110
|
-
|
|
111
|
-
const app = createApp();
|
|
112
|
-
|
|
113
|
-
// 先加载 HTTP 插件
|
|
114
|
-
app.plugin(require('@zhin.js/http'));
|
|
115
|
-
|
|
116
|
-
// 配置飞书适配器
|
|
117
|
-
app.adapter('lark', {
|
|
118
|
-
context: 'lark',
|
|
119
|
-
name: 'my-bot',
|
|
120
|
-
appId: process.env.LARK_APP_ID!,
|
|
121
|
-
appSecret: process.env.LARK_APP_SECRET!,
|
|
122
|
-
webhookPath: '/lark/webhook',
|
|
123
|
-
encryptKey: process.env.LARK_ENCRYPT_KEY,
|
|
124
|
-
verificationToken: process.env.LARK_VERIFICATION_TOKEN,
|
|
125
|
-
isFeishu: true // 使用飞书中国版
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// 基础命令
|
|
129
|
-
app.command('ping').action((session) => {
|
|
130
|
-
session.send('pong! 🏓');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// 处理用户消息
|
|
134
|
-
app.middleware((session, next) => {
|
|
135
|
-
console.log(`收到来自 ${session.$sender.name} 的消息`);
|
|
136
|
-
return next();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
app.start();
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### 高级功能使用
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { createApp } from 'zhin.js';
|
|
146
|
-
import '@zhin.js/adapter-lark';
|
|
147
|
-
|
|
148
|
-
const app = createApp();
|
|
149
|
-
|
|
150
|
-
app.plugin(require('@zhin.js/http'));
|
|
151
|
-
|
|
152
|
-
const bot = app.adapter('lark', {
|
|
153
|
-
context: 'lark',
|
|
154
|
-
name: 'advanced-bot',
|
|
155
|
-
appId: process.env.LARK_APP_ID!,
|
|
156
|
-
appSecret: process.env.LARK_APP_SECRET!,
|
|
157
|
-
webhookPath: '/lark/webhook',
|
|
158
|
-
encryptKey: process.env.LARK_ENCRYPT_KEY,
|
|
159
|
-
isFeishu: true
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// 帮助命令
|
|
163
|
-
app.command('help').action(async (session) => {
|
|
164
|
-
await session.send([
|
|
165
|
-
{ type: 'text', data: { content: '🤖 机器人帮助\\n\\n' } },
|
|
166
|
-
{ type: 'text', data: { content: '📝 /help - 显示此帮助\\n' } },
|
|
167
|
-
{ type: 'text', data: { content: '🏓 /ping - 测试连通性\\n' } },
|
|
168
|
-
{ type: 'text', data: { content: '👤 /me - 查看个人信息\\n' } },
|
|
169
|
-
{ type: 'text', data: { content: '📊 /stats - 查看统计信息' } }
|
|
170
|
-
]);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// 个人信息查询
|
|
174
|
-
app.command('me').action(async (session) => {
|
|
175
|
-
const userInfo = await bot.getUserInfo(session.$sender.id);
|
|
176
|
-
if (userInfo) {
|
|
177
|
-
await session.send(`👤 用户信息:\\n姓名: ${userInfo.name}\\n邮箱: ${userInfo.email || '未设置'}`);
|
|
178
|
-
} else {
|
|
179
|
-
await session.send('❌ 无法获取用户信息');
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// 处理 @提及
|
|
184
|
-
app.middleware((session, next) => {
|
|
185
|
-
const mentions = session.content.filter(seg => seg.type === 'at');
|
|
186
|
-
if (mentions.length > 0) {
|
|
187
|
-
console.log('收到提及:', mentions.map(m => m.data.name));
|
|
188
|
-
session.send(`👋 我看到你提及了 ${mentions.length} 个用户`);
|
|
189
|
-
}
|
|
190
|
-
return next();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// 处理图片消息
|
|
194
|
-
app.middleware((session, next) => {
|
|
195
|
-
const images = session.content.filter(seg => seg.type === 'image');
|
|
196
|
-
if (images.length > 0) {
|
|
197
|
-
session.send(`📷 收到了 ${images.length} 张图片!`);
|
|
198
|
-
}
|
|
199
|
-
return next();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// 处理文件消息
|
|
203
|
-
app.middleware((session, next) => {
|
|
204
|
-
const files = session.content.filter(seg => seg.type === 'file');
|
|
205
|
-
if (files.length > 0) {
|
|
206
|
-
const fileNames = files.map(f => f.data.file_name).join(', ');
|
|
207
|
-
session.send(`📎 收到文件: ${fileNames}`);
|
|
208
|
-
}
|
|
209
|
-
return next();
|
|
210
|
-
});
|
|
22
|
+
- `@zhin.js/http` — HTTP 服务(提供 Webhook 路由)
|
|
211
23
|
|
|
212
|
-
|
|
213
|
-
app.command('card').action(async (session) => {
|
|
214
|
-
await session.send([
|
|
215
|
-
{
|
|
216
|
-
type: 'card',
|
|
217
|
-
data: {
|
|
218
|
-
config: {
|
|
219
|
-
wide_screen_mode: true
|
|
220
|
-
},
|
|
221
|
-
elements: [
|
|
222
|
-
{
|
|
223
|
-
tag: 'div',
|
|
224
|
-
text: {
|
|
225
|
-
content: '**这是一个交互式卡片**\\n\\n点击下方按钮体验功能',
|
|
226
|
-
tag: 'lark_md'
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
tag: 'action',
|
|
231
|
-
actions: [
|
|
232
|
-
{
|
|
233
|
-
tag: 'button',
|
|
234
|
-
text: {
|
|
235
|
-
content: '点赞 👍',
|
|
236
|
-
tag: 'plain_text'
|
|
237
|
-
},
|
|
238
|
-
type: 'primary',
|
|
239
|
-
value: {
|
|
240
|
-
action: 'like'
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
tag: 'button',
|
|
245
|
-
text: {
|
|
246
|
-
content: '查看详情',
|
|
247
|
-
tag: 'plain_text'
|
|
248
|
-
},
|
|
249
|
-
type: 'default',
|
|
250
|
-
value: {
|
|
251
|
-
action: 'detail'
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
]
|
|
255
|
-
}
|
|
256
|
-
],
|
|
257
|
-
header: {
|
|
258
|
-
title: {
|
|
259
|
-
content: '🎉 欢迎使用飞书机器人',
|
|
260
|
-
tag: 'plain_text'
|
|
261
|
-
},
|
|
262
|
-
template: 'blue'
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
]);
|
|
267
|
-
});
|
|
24
|
+
## 配置
|
|
268
25
|
|
|
269
|
-
|
|
26
|
+
```yaml
|
|
27
|
+
# zhin.config.yml
|
|
28
|
+
bots:
|
|
29
|
+
- context: lark
|
|
30
|
+
name: my-lark-bot
|
|
31
|
+
appId: cli_xxxxxxxxxxxx
|
|
32
|
+
appSecret: xxxxxxxxxxxxxxxxxxxxxxxx
|
|
33
|
+
webhookPath: /lark/webhook
|
|
34
|
+
# 可选配置
|
|
35
|
+
# encryptKey: your-encrypt-key
|
|
36
|
+
# verificationToken: your-token
|
|
37
|
+
# isFeishu: true # 使用飞书 API(默认)
|
|
38
|
+
# apiBaseUrl: https://open.feishu.cn/open-apis # 自定义 API 地址
|
|
39
|
+
|
|
40
|
+
plugins:
|
|
41
|
+
- adapter-lark
|
|
42
|
+
- http
|
|
270
43
|
```
|
|
271
44
|
|
|
272
|
-
|
|
45
|
+
或使用 TypeScript 配置:
|
|
273
46
|
|
|
274
47
|
```typescript
|
|
275
|
-
|
|
276
|
-
app.command('upload-image').action(async (session) => {
|
|
277
|
-
try {
|
|
278
|
-
const fileKey = await bot.uploadFile('./path/to/image.jpg', 'image');
|
|
279
|
-
if (fileKey) {
|
|
280
|
-
await session.send([
|
|
281
|
-
{ type: 'image', data: { file_key: fileKey } }
|
|
282
|
-
]);
|
|
283
|
-
}
|
|
284
|
-
} catch (error) {
|
|
285
|
-
await session.send('❌ 文件上传失败');
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// 上传并发送文件
|
|
290
|
-
app.command('upload-file').action(async (session) => {
|
|
291
|
-
try {
|
|
292
|
-
const fileKey = await bot.uploadFile('./path/to/document.pdf', 'file');
|
|
293
|
-
if (fileKey) {
|
|
294
|
-
await session.send([
|
|
295
|
-
{ type: 'file', data: { file_key: fileKey } }
|
|
296
|
-
]);
|
|
297
|
-
}
|
|
298
|
-
} catch (error) {
|
|
299
|
-
await session.send('❌ 文件上传失败');
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
## 支持的消息类型
|
|
305
|
-
|
|
306
|
-
### 接收消息类型
|
|
307
|
-
|
|
308
|
-
- **文本消息**: 支持纯文本和富文本格式
|
|
309
|
-
- **图片消息**: 支持各种格式的图片
|
|
310
|
-
- **文件消息**: 支持各种类型的文件附件
|
|
311
|
-
- **音频消息**: 支持语音和音频文件
|
|
312
|
-
- **视频消息**: 支持视频文件
|
|
313
|
-
- **贴纸消息**: 支持飞书表情包
|
|
314
|
-
- **卡片消息**: 支持交互式卡片和富文本卡片
|
|
315
|
-
- **@提及**: 支持用户提及解析
|
|
316
|
-
- **链接消息**: 自动解析文本中的链接
|
|
317
|
-
- **富文本**: 支持格式化文本内容
|
|
318
|
-
|
|
319
|
-
### 发送消息类型
|
|
48
|
+
import { defineConfig } from 'zhin.js'
|
|
320
49
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
- **文件消息**: 发送文件(需要先上传获取 file_key)
|
|
324
|
-
- **卡片消息**: 发送交互式卡片和富文本卡片
|
|
325
|
-
- **@提及**: 在消息中提及特定用户
|
|
326
|
-
- **链接消息**: 发送包含链接的富文本
|
|
327
|
-
|
|
328
|
-
## 聊天类型支持
|
|
329
|
-
|
|
330
|
-
- `private`: 私聊(单聊)
|
|
331
|
-
- `group`: 群聊
|
|
332
|
-
|
|
333
|
-
## 飞书特色功能
|
|
334
|
-
|
|
335
|
-
### 交互式卡片
|
|
336
|
-
|
|
337
|
-
飞书支持丰富的交互式卡片,可以创建按钮、表单、图表等:
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
app.command('interactive-card').action(async (session) => {
|
|
341
|
-
await session.send([
|
|
50
|
+
export default defineConfig({
|
|
51
|
+
bots: [
|
|
342
52
|
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
elements: [
|
|
349
|
-
// 文本内容
|
|
350
|
-
{
|
|
351
|
-
tag: 'div',
|
|
352
|
-
text: {
|
|
353
|
-
content: '请选择您的偏好设置:',
|
|
354
|
-
tag: 'plain_text'
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
// 分割线
|
|
358
|
-
{
|
|
359
|
-
tag: 'hr'
|
|
360
|
-
},
|
|
361
|
-
// 选择器
|
|
362
|
-
{
|
|
363
|
-
tag: 'div',
|
|
364
|
-
fields: [
|
|
365
|
-
{
|
|
366
|
-
is_short: true,
|
|
367
|
-
text: {
|
|
368
|
-
content: '**语言偏好**\\nChinese',
|
|
369
|
-
tag: 'lark_md'
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
is_short: true,
|
|
374
|
-
text: {
|
|
375
|
-
content: '**通知设置**\\n开启',
|
|
376
|
-
tag: 'lark_md'
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
]
|
|
380
|
-
},
|
|
381
|
-
// 按钮组
|
|
382
|
-
{
|
|
383
|
-
tag: 'action',
|
|
384
|
-
actions: [
|
|
385
|
-
{
|
|
386
|
-
tag: 'button',
|
|
387
|
-
text: {
|
|
388
|
-
content: '保存设置',
|
|
389
|
-
tag: 'plain_text'
|
|
390
|
-
},
|
|
391
|
-
type: 'primary',
|
|
392
|
-
value: {
|
|
393
|
-
action: 'save_settings'
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
tag: 'button',
|
|
398
|
-
text: {
|
|
399
|
-
content: '重置',
|
|
400
|
-
tag: 'plain_text'
|
|
401
|
-
},
|
|
402
|
-
type: 'danger',
|
|
403
|
-
value: {
|
|
404
|
-
action: 'reset'
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
]
|
|
408
|
-
}
|
|
409
|
-
],
|
|
410
|
-
header: {
|
|
411
|
-
title: {
|
|
412
|
-
content: '⚙️ 设置面板',
|
|
413
|
-
tag: 'plain_text'
|
|
414
|
-
},
|
|
415
|
-
template: 'green'
|
|
416
|
-
}
|
|
417
|
-
}
|
|
53
|
+
context: 'lark',
|
|
54
|
+
name: 'my-lark-bot',
|
|
55
|
+
appId: process.env.LARK_APP_ID!,
|
|
56
|
+
appSecret: process.env.LARK_APP_SECRET!,
|
|
57
|
+
webhookPath: '/lark/webhook',
|
|
418
58
|
}
|
|
419
|
-
]
|
|
420
|
-
|
|
59
|
+
],
|
|
60
|
+
plugins: ['adapter-lark', 'http']
|
|
61
|
+
})
|
|
421
62
|
```
|
|
422
63
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
飞书支持多种文件操作:
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// 获取文件信息
|
|
429
|
-
app.command('file-info <file_key>').action(async (session) => {
|
|
430
|
-
const fileKey = session.argv.file_key;
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
const response = await bot.axiosInstance.get(\`/im/v1/files/\${fileKey}\`);
|
|
434
|
-
const fileInfo = response.data.data;
|
|
435
|
-
|
|
436
|
-
await session.send(\`📄 文件信息:\\n文件名: \${fileInfo.file_name}\\n大小: \${fileInfo.file_size} bytes\\n类型: \${fileInfo.file_type}\`);
|
|
437
|
-
} catch (error) {
|
|
438
|
-
await session.send('❌ 获取文件信息失败');
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
// 下载文件
|
|
443
|
-
app.command('download <file_key>').action(async (session) => {
|
|
444
|
-
const fileKey = session.argv.file_key;
|
|
445
|
-
|
|
446
|
-
try {
|
|
447
|
-
const response = await bot.axiosInstance.get(\`/im/v1/files/\${fileKey}/download\`);
|
|
448
|
-
// 处理文件下载逻辑
|
|
449
|
-
await session.send('✅ 文件下载完成');
|
|
450
|
-
} catch (error) {
|
|
451
|
-
await session.send('❌ 文件下载失败');
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
```
|
|
64
|
+
## 使用示例
|
|
455
65
|
|
|
456
|
-
###
|
|
66
|
+
### 注册命令
|
|
457
67
|
|
|
458
68
|
```typescript
|
|
459
|
-
|
|
460
|
-
app.command('members').action(async (session) => {
|
|
461
|
-
if (session.$channel.type !== 'group') {
|
|
462
|
-
await session.send('❌ 此命令只能在群聊中使用');
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
try {
|
|
467
|
-
const chatInfo = await bot.getChatInfo(session.$channel.id);
|
|
468
|
-
const memberCount = chatInfo?.member_count || 0;
|
|
469
|
-
|
|
470
|
-
await session.send(\`👥 群组信息:\\n群名称: \${chatInfo?.name || '未知'}\\n成员数量: \${memberCount}\`);
|
|
471
|
-
} catch (error) {
|
|
472
|
-
await session.send('❌ 获取群组信息失败');
|
|
473
|
-
}
|
|
474
|
-
});
|
|
69
|
+
import { usePlugin, MessageCommand } from 'zhin.js'
|
|
475
70
|
|
|
476
|
-
|
|
477
|
-
app.command('user-info [user_id]').action(async (session) => {
|
|
478
|
-
const userId = session.argv.user_id || session.$sender.id;
|
|
479
|
-
|
|
480
|
-
try {
|
|
481
|
-
const userInfo = await bot.getUserInfo(userId);
|
|
482
|
-
if (userInfo) {
|
|
483
|
-
await session.send([
|
|
484
|
-
{ type: 'text', data: { content: \`👤 用户详情:\\n\` } },
|
|
485
|
-
{ type: 'text', data: { content: \`姓名: \${userInfo.name}\\n\` } },
|
|
486
|
-
{ type: 'text', data: { content: \`邮箱: \${userInfo.email || '未设置'}\\n\` } },
|
|
487
|
-
{ type: 'text', data: { content: \`部门: \${userInfo.department_ids?.join(', ') || '未知'}\` } }
|
|
488
|
-
]);
|
|
489
|
-
}
|
|
490
|
-
} catch (error) {
|
|
491
|
-
await session.send('❌ 获取用户信息失败');
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
## 最佳实践
|
|
497
|
-
|
|
498
|
-
### 1. 安全配置
|
|
71
|
+
const { addCommand } = usePlugin()
|
|
499
72
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
webhookPath: '/lark/webhook',
|
|
508
|
-
|
|
509
|
-
// 必须配置的安全选项
|
|
510
|
-
encryptKey: process.env.LARK_ENCRYPT_KEY!,
|
|
511
|
-
verificationToken: process.env.LARK_VERIFICATION_TOKEN!,
|
|
512
|
-
|
|
513
|
-
isFeishu: true
|
|
514
|
-
};
|
|
73
|
+
addCommand(
|
|
74
|
+
new MessageCommand('hello')
|
|
75
|
+
.desc('飞书问候')
|
|
76
|
+
.action((message) => {
|
|
77
|
+
return `你好,${message.$sender.name}!`
|
|
78
|
+
})
|
|
79
|
+
)
|
|
515
80
|
```
|
|
516
81
|
|
|
517
|
-
###
|
|
82
|
+
### 消息处理
|
|
518
83
|
|
|
519
84
|
```typescript
|
|
520
|
-
|
|
521
|
-
app.middleware(async (session, next) => {
|
|
522
|
-
try {
|
|
523
|
-
await next();
|
|
524
|
-
} catch (error) {
|
|
525
|
-
console.error('Command execution error:', error);
|
|
526
|
-
await session.send('❌ 命令执行出错,请稍后重试');
|
|
527
|
-
}
|
|
528
|
-
});
|
|
85
|
+
import { usePlugin } from 'zhin.js'
|
|
529
86
|
|
|
530
|
-
|
|
531
|
-
app.command('safe-api').action(async (session) => {
|
|
532
|
-
try {
|
|
533
|
-
const userInfo = await bot.getUserInfo(session.$sender.id);
|
|
534
|
-
// 处理成功逻辑
|
|
535
|
-
} catch (error) {
|
|
536
|
-
if (error.response?.status === 403) {
|
|
537
|
-
await session.send('❌ 权限不足,请联系管理员');
|
|
538
|
-
} else if (error.response?.status === 429) {
|
|
539
|
-
await session.send('❌ 请求频率过高,请稍后重试');
|
|
540
|
-
} else {
|
|
541
|
-
await session.send('❌ 服务暂时不可用');
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
### 3. 性能优化
|
|
87
|
+
const { addMiddleware } = usePlugin()
|
|
548
88
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const messageQueue: string[] = [];
|
|
553
|
-
|
|
554
|
-
app.middleware((session, next) => {
|
|
555
|
-
// 简单的消息队列示例
|
|
556
|
-
messageQueue.push(session.$content.map(s => s.data?.content || '').join(''));
|
|
557
|
-
|
|
558
|
-
// 每10条消息批量处理一次
|
|
559
|
-
if (messageQueue.length >= 10) {
|
|
560
|
-
console.log('Processing batch messages:', messageQueue.length);
|
|
561
|
-
messageQueue.length = 0; // 清空队列
|
|
89
|
+
addMiddleware(async (message, next) => {
|
|
90
|
+
if (message.$adapter === 'lark') {
|
|
91
|
+
console.log('收到飞书消息:', message.$content)
|
|
562
92
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
});
|
|
93
|
+
await next()
|
|
94
|
+
})
|
|
566
95
|
```
|
|
567
96
|
|
|
568
|
-
##
|
|
569
|
-
|
|
570
|
-
### 常见问题
|
|
571
|
-
|
|
572
|
-
1. **Webhook 验证失败**
|
|
573
|
-
```
|
|
574
|
-
Invalid verification token in webhook
|
|
575
|
-
```
|
|
576
|
-
- 检查 `verificationToken` 配置是否正确
|
|
577
|
-
- 确认飞书后台的验证令牌设置
|
|
578
|
-
|
|
579
|
-
2. **签名验证失败**
|
|
580
|
-
```
|
|
581
|
-
Invalid signature in webhook
|
|
582
|
-
```
|
|
583
|
-
- 检查 `encryptKey` 配置是否正确
|
|
584
|
-
- 确认飞书后台的加密设置
|
|
585
|
-
|
|
586
|
-
3. **Token 获取失败**
|
|
587
|
-
```
|
|
588
|
-
Failed to get access token
|
|
589
|
-
```
|
|
590
|
-
- 检查 `appId` 和 `appSecret` 是否正确
|
|
591
|
-
- 确认应用是否已发布且状态正常
|
|
592
|
-
- 检查网络连接和 API 地址
|
|
593
|
-
|
|
594
|
-
4. **消息发送失败**
|
|
595
|
-
```
|
|
596
|
-
Failed to send message: no permission
|
|
597
|
-
```
|
|
598
|
-
- 检查应用权限配置
|
|
599
|
-
- 确认机器人是否在目标群组中
|
|
600
|
-
- 验证用户是否允许机器人发消息
|
|
601
|
-
|
|
602
|
-
5. **文件上传失败**
|
|
603
|
-
```
|
|
604
|
-
Upload failed: file too large
|
|
605
|
-
```
|
|
606
|
-
- 检查文件大小是否超出限制
|
|
607
|
-
- 确认文件格式是否支持
|
|
608
|
-
- 检查应用是否有文件上传权限
|
|
609
|
-
|
|
610
|
-
### 调试技巧
|
|
97
|
+
## AI 工具(Skill)
|
|
611
98
|
|
|
612
|
-
|
|
613
|
-
```typescript
|
|
614
|
-
// 在配置中启用调试模式
|
|
615
|
-
app.plugin(require('@zhin.js/logger'), {
|
|
616
|
-
level: 'debug'
|
|
617
|
-
});
|
|
618
|
-
```
|
|
99
|
+
适配器内置群管理 Skill,自动注册以下工具供 AI 调用:
|
|
619
100
|
|
|
620
|
-
|
|
621
|
-
```typescript
|
|
622
|
-
app.middleware((session, next) => {
|
|
623
|
-
console.log('Raw event:', session.$raw);
|
|
624
|
-
return next();
|
|
625
|
-
});
|
|
626
|
-
```
|
|
101
|
+
- 群聊管理(成员管理、群信息修改等)
|
|
627
102
|
|
|
628
|
-
|
|
629
|
-
使用工具如 ngrok 在本地测试 Webhook 接收
|
|
103
|
+
> 工具使用飞书的 `open_id` 和 `chat_id` 格式标识用户和群聊。
|
|
630
104
|
|
|
631
|
-
##
|
|
105
|
+
## 飞书开发者配置
|
|
632
106
|
|
|
633
|
-
1.
|
|
634
|
-
2.
|
|
635
|
-
3.
|
|
636
|
-
4.
|
|
637
|
-
5.
|
|
638
|
-
6. **应用审核**: 某些功能可能需要应用通过审核才能使用
|
|
107
|
+
1. 登录 [飞书开放平台](https://open.feishu.cn/)
|
|
108
|
+
2. 创建应用并获取 `App ID` 和 `App Secret`
|
|
109
|
+
3. 配置事件回调 URL:`http://your-server:8086/api/lark/webhook`
|
|
110
|
+
4. 订阅 `im.message.receive_v1` 事件
|
|
111
|
+
5. 发布应用
|
|
639
112
|
|
|
640
|
-
##
|
|
113
|
+
## 许可证
|
|
641
114
|
|
|
642
|
-
|
|
643
|
-
- 🎉 初始版本发布
|
|
644
|
-
- 🔐 完整的安全验证机制
|
|
645
|
-
- 📱 支持飞书和 Lark 双平台
|
|
646
|
-
- 💬 丰富的消息类型支持
|
|
647
|
-
- 🎛️ 交互式卡片支持
|
|
648
|
-
- 📁 文件上传下载功能
|
|
649
|
-
- 👥 用户和群组管理
|
|
650
|
-
- 🚀 使用 `useContext('router')` 集成 HTTP 服务
|
|
651
|
-
- ⚡ 自动 token 管理和刷新
|
|
115
|
+
MIT License
|