@zhin.js/adapter-lark 1.0.56 → 1.0.57
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 +9 -0
- package/README.md +1 -1
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +1 -6
- package/lib/adapter.js.map +1 -1
- package/package.json +9 -5
- package/skills/lark/SKILL.md +18 -0
- package/src/adapter.ts +265 -0
- package/src/bot.ts +714 -0
- package/src/index.ts +38 -0
- package/src/types.ts +58 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/lib/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,OAAO,EACP,MAAM,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,OAAO,EACP,MAAM,EAGP,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,WAAY,SAAQ,OAAO,CAAC,OAAO,CAAC;;gBAGjC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG;IAKvC,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO;IAMnC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMzD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM1C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAM3C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;OAEG;IACH,OAAO,CAAC,yBAAyB;CA0MpC"}
|
package/lib/adapter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 飞书/Lark 适配器
|
|
3
3
|
*/
|
|
4
|
-
import { Adapter, createGroupManagementTools,
|
|
4
|
+
import { Adapter, createGroupManagementTools, } from "zhin.js";
|
|
5
5
|
import { LarkBot } from "./bot.js";
|
|
6
6
|
export class LarkAdapter extends Adapter {
|
|
7
7
|
#router;
|
|
@@ -42,11 +42,6 @@ export class LarkAdapter extends Adapter {
|
|
|
42
42
|
this.registerLarkPlatformTools();
|
|
43
43
|
const groupTools = createGroupManagementTools(this, this.name);
|
|
44
44
|
groupTools.forEach((t) => this.addTool(t));
|
|
45
|
-
this.declareSkill({
|
|
46
|
-
description: '飞书/Lark 群管理:踢人、禁言、设管理员、改群名、查成员等。仅有昵称时请先 list_members 获取 user_id 再操作。',
|
|
47
|
-
keywords: GROUP_MANAGEMENT_SKILL_KEYWORDS,
|
|
48
|
-
tags: GROUP_MANAGEMENT_SKILL_TAGS,
|
|
49
|
-
});
|
|
50
45
|
await super.start();
|
|
51
46
|
}
|
|
52
47
|
/**
|
package/lib/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,OAAO,EAEP,0BAA0B,
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-lark",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.57",
|
|
4
4
|
"description": "Zhin.js adapter for Lark/Feishu (飞书)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"typescript": "^5.3.0",
|
|
39
39
|
"@types/node": "^22.9.0",
|
|
40
40
|
"@types/koa": "^2.15.0",
|
|
41
|
-
"zhin.js": "1.0.
|
|
41
|
+
"zhin.js": "1.0.52"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"zhin.js": "1.0.
|
|
45
|
-
"@zhin.js/http": "1.0.
|
|
44
|
+
"zhin.js": "1.0.52",
|
|
45
|
+
"@zhin.js/http": "1.0.46"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@zhin.js/http": {
|
|
@@ -50,9 +50,13 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
|
+
"src",
|
|
53
54
|
"lib",
|
|
54
|
-
"
|
|
55
|
+
"client",
|
|
56
|
+
"dist",
|
|
57
|
+
"skills",
|
|
55
58
|
"README.md",
|
|
59
|
+
"node",
|
|
56
60
|
"CHANGELOG.md"
|
|
57
61
|
],
|
|
58
62
|
"publishConfig": {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lark
|
|
3
|
+
description: 飞书/Lark 群管理:踢人、禁言、设管理员、改群名、查成员等。仅有昵称时请先 list_members 获取 user_id 再操作。
|
|
4
|
+
keywords:
|
|
5
|
+
- lark
|
|
6
|
+
- 飞书
|
|
7
|
+
- adapter:lark
|
|
8
|
+
- 群管理
|
|
9
|
+
- list_members
|
|
10
|
+
tags:
|
|
11
|
+
- group
|
|
12
|
+
- management
|
|
13
|
+
tools: []
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 执行规则
|
|
17
|
+
|
|
18
|
+
- 使用 `lark_*` 工具;先 list_members 解析 user_id。
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书/Lark 适配器
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
Adapter,
|
|
6
|
+
Plugin,
|
|
7
|
+
createGroupManagementTools,
|
|
8
|
+
type IGroupManagement,
|
|
9
|
+
} from "zhin.js";
|
|
10
|
+
import { LarkBot } from "./bot.js";
|
|
11
|
+
import type { LarkBotConfig } from "./types.js";
|
|
12
|
+
|
|
13
|
+
export class LarkAdapter extends Adapter<LarkBot> {
|
|
14
|
+
#router: any;
|
|
15
|
+
|
|
16
|
+
constructor(plugin: Plugin, router: any) {
|
|
17
|
+
super(plugin, 'lark', []);
|
|
18
|
+
this.#router = router;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createBot(config: LarkBotConfig): LarkBot {
|
|
22
|
+
return new LarkBot(this, this.#router, config);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── IGroupManagement 标准群管方法 ──────────────────────────────────
|
|
26
|
+
|
|
27
|
+
async kickMember(botId: string, sceneId: string, userId: string) {
|
|
28
|
+
const bot = this.bots.get(botId);
|
|
29
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
30
|
+
return bot.removeChatMembers(sceneId, [userId]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async listMembers(botId: string, sceneId: string) {
|
|
34
|
+
const bot = this.bots.get(botId);
|
|
35
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
36
|
+
return bot.getChatMembers(sceneId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getGroupInfo(botId: string, sceneId: string) {
|
|
40
|
+
const bot = this.bots.get(botId);
|
|
41
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
42
|
+
return bot.getChatInfo(sceneId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async setGroupName(botId: string, sceneId: string, name: string) {
|
|
46
|
+
const bot = this.bots.get(botId);
|
|
47
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
48
|
+
return bot.updateChatInfo(sceneId, { name });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── 生命周期 ───────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
async start(): Promise<void> {
|
|
54
|
+
this.registerLarkPlatformTools();
|
|
55
|
+
const groupTools = createGroupManagementTools(this as unknown as IGroupManagement, this.name);
|
|
56
|
+
groupTools.forEach((t) => this.addTool(t));
|
|
57
|
+
await super.start();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 注册飞书平台特有工具(获取用户、设管等)
|
|
62
|
+
*/
|
|
63
|
+
private registerLarkPlatformTools(): void {
|
|
64
|
+
// 获取用户信息工具
|
|
65
|
+
this.addTool({
|
|
66
|
+
name: 'lark_get_user',
|
|
67
|
+
description: '获取飞书用户信息',
|
|
68
|
+
parameters: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
72
|
+
user_id: { type: 'string', description: '用户 ID (open_id)' },
|
|
73
|
+
},
|
|
74
|
+
required: ['bot', 'user_id'],
|
|
75
|
+
},
|
|
76
|
+
platforms: ['lark'],
|
|
77
|
+
scopes: ['group', 'private'],
|
|
78
|
+
permissionLevel: 'user',
|
|
79
|
+
execute: async (args) => {
|
|
80
|
+
const { bot: botId, user_id } = args;
|
|
81
|
+
const bot = this.bots.get(botId);
|
|
82
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
83
|
+
return await bot.getUserInfo(user_id);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 创建群聊工具
|
|
88
|
+
this.addTool({
|
|
89
|
+
name: 'lark_create_chat',
|
|
90
|
+
description: '创建飞书群聊',
|
|
91
|
+
parameters: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
95
|
+
name: { type: 'string', description: '群名' },
|
|
96
|
+
members: { type: 'array', items: { type: 'string' }, description: '成员 open_id 列表' },
|
|
97
|
+
owner: { type: 'string', description: '群主 open_id(可选)' },
|
|
98
|
+
},
|
|
99
|
+
required: ['bot', 'name', 'members'],
|
|
100
|
+
},
|
|
101
|
+
platforms: ['lark'],
|
|
102
|
+
scopes: ['group', 'private'],
|
|
103
|
+
permissionLevel: 'group_admin',
|
|
104
|
+
execute: async (args) => {
|
|
105
|
+
const { bot: botId, name, members, owner } = args;
|
|
106
|
+
const bot = this.bots.get(botId);
|
|
107
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
108
|
+
const chatId = await bot.createChat(name, members, owner);
|
|
109
|
+
return { success: !!chatId, chat_id: chatId, message: chatId ? `群聊创建成功: ${chatId}` : '创建失败' };
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 更新群信息工具
|
|
114
|
+
this.addTool({
|
|
115
|
+
name: 'lark_update_chat',
|
|
116
|
+
description: '更新飞书群聊信息(群名、描述)',
|
|
117
|
+
parameters: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
121
|
+
chat_id: { type: 'string', description: '群聊 ID' },
|
|
122
|
+
name: { type: 'string', description: '新群名(可选)' },
|
|
123
|
+
description: { type: 'string', description: '新描述(可选)' },
|
|
124
|
+
},
|
|
125
|
+
required: ['bot', 'chat_id'],
|
|
126
|
+
},
|
|
127
|
+
platforms: ['lark'],
|
|
128
|
+
scopes: ['group'],
|
|
129
|
+
permissionLevel: 'group_admin',
|
|
130
|
+
execute: async (args) => {
|
|
131
|
+
const { bot: botId, chat_id, name, description } = args;
|
|
132
|
+
const bot = this.bots.get(botId);
|
|
133
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
134
|
+
const success = await bot.updateChatInfo(chat_id, { name, description });
|
|
135
|
+
return { success, message: success ? '群信息更新成功' : '更新失败' };
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// 添加群成员工具
|
|
140
|
+
this.addTool({
|
|
141
|
+
name: 'lark_add_members',
|
|
142
|
+
description: '添加飞书群成员',
|
|
143
|
+
parameters: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
147
|
+
chat_id: { type: 'string', description: '群聊 ID' },
|
|
148
|
+
user_ids: { type: 'array', items: { type: 'string' }, description: '用户 open_id 列表' },
|
|
149
|
+
},
|
|
150
|
+
required: ['bot', 'chat_id', 'user_ids'],
|
|
151
|
+
},
|
|
152
|
+
platforms: ['lark'],
|
|
153
|
+
scopes: ['group'],
|
|
154
|
+
permissionLevel: 'group_admin',
|
|
155
|
+
execute: async (args) => {
|
|
156
|
+
const { bot: botId, chat_id, user_ids } = args;
|
|
157
|
+
const bot = this.bots.get(botId);
|
|
158
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
159
|
+
const success = await bot.addChatMembers(chat_id, user_ids);
|
|
160
|
+
return { success, message: success ? '成员添加成功' : '添加失败' };
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 设置群管理员工具
|
|
165
|
+
this.addTool({
|
|
166
|
+
name: 'lark_set_managers',
|
|
167
|
+
description: '设置飞书群管理员',
|
|
168
|
+
parameters: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
172
|
+
chat_id: { type: 'string', description: '群聊 ID' },
|
|
173
|
+
user_ids: { type: 'array', items: { type: 'string' }, description: '用户 open_id 列表' },
|
|
174
|
+
},
|
|
175
|
+
required: ['bot', 'chat_id', 'user_ids'],
|
|
176
|
+
},
|
|
177
|
+
platforms: ['lark'],
|
|
178
|
+
scopes: ['group'],
|
|
179
|
+
permissionLevel: 'group_owner',
|
|
180
|
+
execute: async (args) => {
|
|
181
|
+
const { bot: botId, chat_id, user_ids } = args;
|
|
182
|
+
const bot = this.bots.get(botId);
|
|
183
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
184
|
+
const success = await bot.setChatManagers(chat_id, user_ids);
|
|
185
|
+
return { success, message: success ? '管理员设置成功' : '设置失败' };
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// 移除群管理员工具
|
|
190
|
+
this.addTool({
|
|
191
|
+
name: 'lark_remove_managers',
|
|
192
|
+
description: '移除飞书群管理员',
|
|
193
|
+
parameters: {
|
|
194
|
+
type: 'object',
|
|
195
|
+
properties: {
|
|
196
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
197
|
+
chat_id: { type: 'string', description: '群聊 ID' },
|
|
198
|
+
user_ids: { type: 'array', items: { type: 'string' }, description: '用户 open_id 列表' },
|
|
199
|
+
},
|
|
200
|
+
required: ['bot', 'chat_id', 'user_ids'],
|
|
201
|
+
},
|
|
202
|
+
platforms: ['lark'],
|
|
203
|
+
scopes: ['group'],
|
|
204
|
+
permissionLevel: 'group_owner',
|
|
205
|
+
execute: async (args) => {
|
|
206
|
+
const { bot: botId, chat_id, user_ids } = args;
|
|
207
|
+
const bot = this.bots.get(botId);
|
|
208
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
209
|
+
const success = await bot.removeChatManagers(chat_id, user_ids);
|
|
210
|
+
return { success, message: success ? '管理员移除成功' : '移除失败' };
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// 解散群聊工具
|
|
215
|
+
this.addTool({
|
|
216
|
+
name: 'lark_dissolve_chat',
|
|
217
|
+
description: '解散飞书群聊(需要群主权限)',
|
|
218
|
+
parameters: {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
222
|
+
chat_id: { type: 'string', description: '群聊 ID' },
|
|
223
|
+
},
|
|
224
|
+
required: ['bot', 'chat_id'],
|
|
225
|
+
},
|
|
226
|
+
platforms: ['lark'],
|
|
227
|
+
scopes: ['group'],
|
|
228
|
+
permissionLevel: 'group_owner',
|
|
229
|
+
execute: async (args) => {
|
|
230
|
+
const { bot: botId, chat_id } = args;
|
|
231
|
+
const bot = this.bots.get(botId);
|
|
232
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
233
|
+
const success = await bot.dissolveChat(chat_id);
|
|
234
|
+
return { success, message: success ? '群聊已解散' : '解散失败' };
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// 文件上传
|
|
239
|
+
this.addTool({
|
|
240
|
+
name: 'lark_upload_file',
|
|
241
|
+
description: '上传文件到飞书(图片/文件/视频/音频)',
|
|
242
|
+
parameters: {
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: {
|
|
245
|
+
bot: { type: 'string', description: 'Bot 名称' },
|
|
246
|
+
file_path: { type: 'string', description: '本地文件路径' },
|
|
247
|
+
file_type: { type: 'string', description: '文件类型:image/file/video/audio', enum: ['image', 'file', 'video', 'audio'] },
|
|
248
|
+
},
|
|
249
|
+
required: ['bot', 'file_path', 'file_type'],
|
|
250
|
+
},
|
|
251
|
+
platforms: ['lark'],
|
|
252
|
+
scopes: ['group', 'private'],
|
|
253
|
+
permissionLevel: 'user',
|
|
254
|
+
execute: async (args) => {
|
|
255
|
+
const { bot: botId, file_path, file_type } = args;
|
|
256
|
+
const bot = this.bots.get(botId);
|
|
257
|
+
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
258
|
+
const result = await bot.uploadFile(file_path, file_type);
|
|
259
|
+
return { success: true, file_key: result, message: `文件已上传,file_key: ${result}` };
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
this.plugin.logger.debug('已注册飞书平台群组管理工具');
|
|
264
|
+
}
|
|
265
|
+
}
|
package/src/bot.ts
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书/Lark Bot 实现
|
|
3
|
+
*/
|
|
4
|
+
import type { Context } from "koa";
|
|
5
|
+
import axios, { type AxiosInstance } from "axios";
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
import {
|
|
8
|
+
Bot,
|
|
9
|
+
Message,
|
|
10
|
+
SendOptions,
|
|
11
|
+
SendContent,
|
|
12
|
+
MessageSegment,
|
|
13
|
+
segment,
|
|
14
|
+
} from "zhin.js";
|
|
15
|
+
import type { LarkBotConfig, LarkMessage, LarkEvent, AccessToken } from "./types.js";
|
|
16
|
+
import type { LarkAdapter } from "./adapter.js";
|
|
17
|
+
|
|
18
|
+
export class LarkBot implements Bot<LarkBotConfig, LarkMessage> {
|
|
19
|
+
$connected: boolean
|
|
20
|
+
private router: any
|
|
21
|
+
private accessToken: AccessToken
|
|
22
|
+
private axiosInstance: AxiosInstance
|
|
23
|
+
|
|
24
|
+
get logger() {
|
|
25
|
+
return this.adapter.plugin.logger;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get $id() {
|
|
29
|
+
return this.$config.name;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
constructor(public adapter: LarkAdapter, router: any, public $config: LarkBotConfig) {
|
|
33
|
+
this.router = router;
|
|
34
|
+
this.$connected = false;
|
|
35
|
+
this.accessToken = { token: '', expires_in: 0, timestamp: 0 };
|
|
36
|
+
|
|
37
|
+
// 设置 API 基础 URL
|
|
38
|
+
const baseURL = $config.apiBaseUrl || ($config.isFeishu ?
|
|
39
|
+
'https://open.feishu.cn/open-apis' :
|
|
40
|
+
'https://open.larksuite.com/open-apis'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
this.axiosInstance = axios.create({
|
|
44
|
+
baseURL,
|
|
45
|
+
timeout: 30000,
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 设置请求拦截器,自动添加 access_token
|
|
52
|
+
this.axiosInstance.interceptors.request.use(async (config) => {
|
|
53
|
+
await this.ensureAccessToken();
|
|
54
|
+
config.headers = config.headers;
|
|
55
|
+
config.headers['Authorization'] = `Bearer ${this.accessToken.token}`;
|
|
56
|
+
return config;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 设置 webhook 路由
|
|
60
|
+
this.setupWebhookRoute();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private setupWebhookRoute(): void {
|
|
64
|
+
this.router.post(this.$config.webhookPath, (ctx: Context) => {
|
|
65
|
+
this.handleWebhook(ctx);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async handleWebhook(ctx: Context): Promise<void> {
|
|
70
|
+
try {
|
|
71
|
+
const body = (ctx.request as any).body;
|
|
72
|
+
const headers = ctx.request.headers;
|
|
73
|
+
|
|
74
|
+
// 验证请求(如果配置了验证令牌)
|
|
75
|
+
if (this.$config.verificationToken) {
|
|
76
|
+
const token = headers['x-lark-request-token'] as string;
|
|
77
|
+
if (token !== this.$config.verificationToken) {
|
|
78
|
+
this.logger.warn('Invalid verification token in webhook');
|
|
79
|
+
ctx.status = 403;
|
|
80
|
+
ctx.body = 'Forbidden';
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 签名验证(如果配置了加密密钥)
|
|
86
|
+
if (this.$config.encryptKey) {
|
|
87
|
+
const timestamp = headers['x-lark-request-timestamp'] as string;
|
|
88
|
+
const nonce = headers['x-lark-request-nonce'] as string;
|
|
89
|
+
const signature = headers['x-lark-signature'] as string;
|
|
90
|
+
const bodyStr = JSON.stringify(body);
|
|
91
|
+
|
|
92
|
+
if (!this.verifySignature(timestamp, nonce, bodyStr, signature)) {
|
|
93
|
+
this.logger.warn('Invalid signature in webhook');
|
|
94
|
+
ctx.status = 403;
|
|
95
|
+
ctx.body = 'Forbidden';
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const event: LarkEvent = body;
|
|
101
|
+
|
|
102
|
+
// URL 验证挑战(首次配置 webhook 时)
|
|
103
|
+
if (event.type === 'url_verification') {
|
|
104
|
+
ctx.body = { challenge: (event as any).challenge };
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 处理消息事件
|
|
109
|
+
if (event.type === 'event_callback' && event.event) {
|
|
110
|
+
await this.handleEvent(event.event);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ctx.status = 200;
|
|
114
|
+
ctx.body = { code: 0, msg: 'success' };
|
|
115
|
+
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.logger.error('Webhook error:', error);
|
|
118
|
+
ctx.status = 500;
|
|
119
|
+
ctx.body = { code: -1, msg: 'Internal Server Error' };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private verifySignature(timestamp: string, nonce: string, body: string, signature: string): boolean {
|
|
124
|
+
if (!this.$config.encryptKey) return true;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const stringToSign = `${timestamp}${nonce}${this.$config.encryptKey}${body}`;
|
|
128
|
+
const calculatedSignature = createHash('sha256').update(stringToSign).digest('hex');
|
|
129
|
+
return calculatedSignature === signature;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
this.logger.error('Signature verification error:', error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private async handleEvent(event: any): Promise<void> {
|
|
137
|
+
// 处理消息事件
|
|
138
|
+
if (event.message) {
|
|
139
|
+
const message = this.$formatMessage(event.message, event);
|
|
140
|
+
this.adapter.emit('message.receive', message);
|
|
141
|
+
this.logger.info(`${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}): ${segment.raw(message.$content)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ================================================================================================
|
|
146
|
+
// Token 管理
|
|
147
|
+
// ================================================================================================
|
|
148
|
+
|
|
149
|
+
private async ensureAccessToken(): Promise<void> {
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
// 提前 5 分钟刷新 token
|
|
152
|
+
if (this.accessToken.token && now < (this.accessToken.timestamp + (this.accessToken.expires_in - 300) * 1000)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await this.refreshAccessToken();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async refreshAccessToken(): Promise<void> {
|
|
160
|
+
try {
|
|
161
|
+
const response = await axios.post(
|
|
162
|
+
`${this.$config.apiBaseUrl || (this.$config.isFeishu ?
|
|
163
|
+
'https://open.feishu.cn/open-apis' :
|
|
164
|
+
'https://open.larksuite.com/open-apis'
|
|
165
|
+
)}/auth/v3/tenant_access_token/internal`,
|
|
166
|
+
{
|
|
167
|
+
app_id: this.$config.appId,
|
|
168
|
+
app_secret: this.$config.appSecret
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (response.data.code === 0) {
|
|
173
|
+
this.accessToken = {
|
|
174
|
+
token: response.data.tenant_access_token,
|
|
175
|
+
expires_in: response.data.expire,
|
|
176
|
+
timestamp: Date.now()
|
|
177
|
+
};
|
|
178
|
+
this.logger.debug('Access token refreshed successfully');
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error(`Failed to get access token: ${response.data.msg}`);
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this.logger.error('Failed to refresh access token:', error);
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ================================================================================================
|
|
189
|
+
// 消息格式化
|
|
190
|
+
// ================================================================================================
|
|
191
|
+
|
|
192
|
+
$formatMessage(msg: LarkMessage, event?: any): Message<LarkMessage> {
|
|
193
|
+
const content = this.parseMessageContent(msg);
|
|
194
|
+
|
|
195
|
+
// 确定聊天类型
|
|
196
|
+
const chatType = msg.chat_id?.startsWith('oc_') ? 'group' : 'private';
|
|
197
|
+
|
|
198
|
+
return Message.from(msg, {
|
|
199
|
+
$id: msg.message_id || Date.now().toString(),
|
|
200
|
+
$adapter: 'lark',
|
|
201
|
+
$bot: this.$config.name,
|
|
202
|
+
$sender: {
|
|
203
|
+
id: msg.sender?.sender_id?.open_id || 'unknown',
|
|
204
|
+
name: msg.sender?.sender_id?.user_id || msg.sender?.sender_id?.open_id || 'Unknown User'
|
|
205
|
+
},
|
|
206
|
+
$channel: {
|
|
207
|
+
id: msg.chat_id || 'unknown',
|
|
208
|
+
type: chatType as any
|
|
209
|
+
},
|
|
210
|
+
$content: content,
|
|
211
|
+
$raw: JSON.stringify(msg),
|
|
212
|
+
$timestamp: msg.create_time ? parseInt(msg.create_time) : Date.now(),
|
|
213
|
+
$recall: async () => {
|
|
214
|
+
await this.$recallMessage(msg.message_id || '');
|
|
215
|
+
},
|
|
216
|
+
$reply: async (content: SendContent): Promise<string> => {
|
|
217
|
+
return await this.adapter.sendMessage({
|
|
218
|
+
context: 'lark',
|
|
219
|
+
bot: this.$config.name,
|
|
220
|
+
id: msg.chat_id || 'unknown',
|
|
221
|
+
type: chatType,
|
|
222
|
+
content: content
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private parseMessageContent(msg: LarkMessage): MessageSegment[] {
|
|
229
|
+
const content: MessageSegment[] = [];
|
|
230
|
+
|
|
231
|
+
if (!msg.content || !msg.message_type) {
|
|
232
|
+
return content;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const messageContent = JSON.parse(msg.content);
|
|
237
|
+
|
|
238
|
+
switch (msg.message_type) {
|
|
239
|
+
case 'text':
|
|
240
|
+
if (messageContent.text) {
|
|
241
|
+
content.push(segment('text', { content: messageContent.text }));
|
|
242
|
+
|
|
243
|
+
// 处理 @提及
|
|
244
|
+
if (msg.mentions) {
|
|
245
|
+
for (const mention of msg.mentions) {
|
|
246
|
+
if (mention.key && messageContent.text.includes(mention.key)) {
|
|
247
|
+
content.push(segment('at', {
|
|
248
|
+
id: mention.id?.open_id,
|
|
249
|
+
name: mention.name
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case 'image':
|
|
258
|
+
content.push(segment('image', {
|
|
259
|
+
file_key: messageContent.image_key,
|
|
260
|
+
url: `https://open.feishu.cn/open-apis/im/v1/messages/${msg.message_id}/resources/${messageContent.image_key}`
|
|
261
|
+
}));
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
case 'file':
|
|
265
|
+
content.push(segment('file', {
|
|
266
|
+
file_key: messageContent.file_key,
|
|
267
|
+
file_name: messageContent.file_name,
|
|
268
|
+
file_size: messageContent.file_size
|
|
269
|
+
}));
|
|
270
|
+
break;
|
|
271
|
+
|
|
272
|
+
case 'audio':
|
|
273
|
+
content.push(segment('audio', {
|
|
274
|
+
file_key: messageContent.file_key,
|
|
275
|
+
duration: messageContent.duration
|
|
276
|
+
}));
|
|
277
|
+
break;
|
|
278
|
+
|
|
279
|
+
case 'video':
|
|
280
|
+
content.push(segment('video', {
|
|
281
|
+
file_key: messageContent.file_key,
|
|
282
|
+
duration: messageContent.duration,
|
|
283
|
+
width: messageContent.width,
|
|
284
|
+
height: messageContent.height
|
|
285
|
+
}));
|
|
286
|
+
break;
|
|
287
|
+
|
|
288
|
+
case 'sticker':
|
|
289
|
+
content.push(segment('sticker', {
|
|
290
|
+
file_key: messageContent.file_key
|
|
291
|
+
}));
|
|
292
|
+
break;
|
|
293
|
+
|
|
294
|
+
case 'rich_text':
|
|
295
|
+
// 富文本消息处理(简化)
|
|
296
|
+
if (messageContent.content) {
|
|
297
|
+
this.parseRichTextContent(messageContent.content, content);
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case 'post':
|
|
302
|
+
// 卡片消息处理
|
|
303
|
+
content.push(segment('card', messageContent));
|
|
304
|
+
break;
|
|
305
|
+
|
|
306
|
+
default:
|
|
307
|
+
content.push(segment('text', { content: `[不支持的消息类型: ${msg.message_type}]` }));
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
} catch (error) {
|
|
311
|
+
this.logger.error('Failed to parse message content:', error);
|
|
312
|
+
content.push(segment('text', { content: '[消息解析失败]' }));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return content;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private parseRichTextContent(richContent: any, content: MessageSegment[]): void {
|
|
319
|
+
// 简化的富文本解析
|
|
320
|
+
if (Array.isArray(richContent)) {
|
|
321
|
+
for (const block of richContent) {
|
|
322
|
+
if (block.tag === 'text' && block.text) {
|
|
323
|
+
content.push(segment('text', { content: block.text }));
|
|
324
|
+
} else if (block.tag === 'a' && block.href) {
|
|
325
|
+
content.push(segment('link', {
|
|
326
|
+
url: block.href,
|
|
327
|
+
text: block.text || block.href
|
|
328
|
+
}));
|
|
329
|
+
} else if (block.tag === 'at' && block.user_id) {
|
|
330
|
+
content.push(segment('at', {
|
|
331
|
+
id: block.user_id,
|
|
332
|
+
name: block.user_name
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ================================================================================================
|
|
340
|
+
// 消息发送
|
|
341
|
+
// ================================================================================================
|
|
342
|
+
|
|
343
|
+
async $sendMessage(options: SendOptions): Promise<string> {
|
|
344
|
+
const chatId = options.id;
|
|
345
|
+
const content = this.formatSendContent(options.content);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const response = await this.axiosInstance.post('/im/v1/messages', {
|
|
349
|
+
receive_id: chatId,
|
|
350
|
+
receive_id_type: 'chat_id',
|
|
351
|
+
msg_type: content.msg_type,
|
|
352
|
+
content: content.content
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
if (response.data.code !== 0) {
|
|
356
|
+
throw new Error(`Failed to send message: ${response.data.msg}`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.logger.debug('Message sent successfully:', response.data.data?.message_id);
|
|
360
|
+
return response.data.data?.message_id || '';
|
|
361
|
+
} catch (error) {
|
|
362
|
+
this.logger.error('Failed to send message:', error);
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async $recallMessage(id:string):Promise<void> {
|
|
367
|
+
await this.axiosInstance.post('/im/v1/messages/recall', {
|
|
368
|
+
message_id: id
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private formatSendContent(content: SendContent): { msg_type: string, content: string } {
|
|
373
|
+
if (typeof content === 'string') {
|
|
374
|
+
return {
|
|
375
|
+
msg_type: 'text',
|
|
376
|
+
content: JSON.stringify({ text: content })
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (Array.isArray(content)) {
|
|
381
|
+
const textParts: string[] = [];
|
|
382
|
+
let hasMedia = false;
|
|
383
|
+
let mediaContent: any = null;
|
|
384
|
+
|
|
385
|
+
for (const item of content) {
|
|
386
|
+
if (typeof item === 'string') {
|
|
387
|
+
textParts.push(item);
|
|
388
|
+
} else {
|
|
389
|
+
const segment = item as MessageSegment;
|
|
390
|
+
switch (segment.type) {
|
|
391
|
+
case 'text':
|
|
392
|
+
textParts.push(segment.data.content || segment.data.text || '');
|
|
393
|
+
break;
|
|
394
|
+
|
|
395
|
+
case 'at':
|
|
396
|
+
textParts.push(`<at user_id="${segment.data.id}">${segment.data.name || segment.data.id}</at>`);
|
|
397
|
+
break;
|
|
398
|
+
|
|
399
|
+
case 'image':
|
|
400
|
+
if (!hasMedia) {
|
|
401
|
+
hasMedia = true;
|
|
402
|
+
mediaContent = {
|
|
403
|
+
msg_type: 'image',
|
|
404
|
+
content: JSON.stringify({
|
|
405
|
+
image_key: segment.data.file_key || segment.data.key
|
|
406
|
+
})
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
break;
|
|
410
|
+
|
|
411
|
+
case 'file':
|
|
412
|
+
if (!hasMedia) {
|
|
413
|
+
hasMedia = true;
|
|
414
|
+
mediaContent = {
|
|
415
|
+
msg_type: 'file',
|
|
416
|
+
content: JSON.stringify({
|
|
417
|
+
file_key: segment.data.file_key || segment.data.key
|
|
418
|
+
})
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
422
|
+
|
|
423
|
+
case 'card':
|
|
424
|
+
if (!hasMedia) {
|
|
425
|
+
hasMedia = true;
|
|
426
|
+
mediaContent = {
|
|
427
|
+
msg_type: 'interactive',
|
|
428
|
+
content: JSON.stringify(segment.data)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 优先发送媒体内容
|
|
437
|
+
if (hasMedia && mediaContent) {
|
|
438
|
+
return mediaContent;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// 否则发送文本内容
|
|
442
|
+
return {
|
|
443
|
+
msg_type: 'text',
|
|
444
|
+
content: JSON.stringify({ text: textParts.join('') })
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
msg_type: 'text',
|
|
450
|
+
content: JSON.stringify({ text: String(content) })
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ================================================================================================
|
|
455
|
+
// Bot 生命周期
|
|
456
|
+
// ================================================================================================
|
|
457
|
+
|
|
458
|
+
async $connect(): Promise<void> {
|
|
459
|
+
try {
|
|
460
|
+
// 获取 access token
|
|
461
|
+
await this.refreshAccessToken();
|
|
462
|
+
|
|
463
|
+
this.$connected = true;
|
|
464
|
+
this.logger.info(`Lark bot connected: ${this.$config.name}`);
|
|
465
|
+
this.logger.info(`Webhook URL: ${this.$config.webhookPath}`);
|
|
466
|
+
|
|
467
|
+
} catch (error) {
|
|
468
|
+
this.logger.error('Failed to connect Lark bot:', error);
|
|
469
|
+
throw error;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async $disconnect(): Promise<void> {
|
|
474
|
+
try {
|
|
475
|
+
this.$connected = false;
|
|
476
|
+
this.logger.info('Lark bot disconnected');
|
|
477
|
+
} catch (error) {
|
|
478
|
+
this.logger.error('Error disconnecting Lark bot:', error);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// ================================================================================================
|
|
483
|
+
// 工具方法
|
|
484
|
+
// ================================================================================================
|
|
485
|
+
|
|
486
|
+
// 获取用户信息
|
|
487
|
+
async getUserInfo(userId: string, userIdType: 'open_id' | 'user_id' | 'union_id' = 'open_id'): Promise<any> {
|
|
488
|
+
try {
|
|
489
|
+
const response = await this.axiosInstance.get(`/contact/v3/users/${userId}`, {
|
|
490
|
+
params: { user_id_type: userIdType }
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
return response.data.data?.user;
|
|
494
|
+
} catch (error) {
|
|
495
|
+
this.logger.error('Failed to get user info:', error);
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// 获取群聊信息
|
|
501
|
+
async getChatInfo(chatId: string): Promise<any> {
|
|
502
|
+
try {
|
|
503
|
+
const response = await this.axiosInstance.get(`/im/v1/chats/${chatId}`);
|
|
504
|
+
return response.data.data;
|
|
505
|
+
} catch (error) {
|
|
506
|
+
this.logger.error('Failed to get chat info:', error);
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 上传文件
|
|
512
|
+
async uploadFile(filePath: string, fileType: 'image' | 'file' | 'video' | 'audio' = 'file'): Promise<string | null> {
|
|
513
|
+
try {
|
|
514
|
+
const FormData = require('form-data');
|
|
515
|
+
const fs = require('fs');
|
|
516
|
+
|
|
517
|
+
const form = new FormData();
|
|
518
|
+
form.append('file', fs.createReadStream(filePath));
|
|
519
|
+
form.append('file_type', fileType);
|
|
520
|
+
|
|
521
|
+
const response = await this.axiosInstance.post('/im/v1/files', form, {
|
|
522
|
+
headers: {
|
|
523
|
+
...form.getHeaders()
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
if (response.data.code === 0) {
|
|
528
|
+
return response.data.data.file_key;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
throw new Error(`Upload failed: ${response.data.msg}`);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
this.logger.error('Failed to upload file:', error);
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ==================== 群组管理 API ====================
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* 创建群聊
|
|
542
|
+
* @param name 群名
|
|
543
|
+
* @param userIds 成员 open_id 列表
|
|
544
|
+
* @param ownerId 群主 open_id
|
|
545
|
+
*/
|
|
546
|
+
async createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null> {
|
|
547
|
+
try {
|
|
548
|
+
const response = await this.axiosInstance.post('/im/v1/chats', {
|
|
549
|
+
name,
|
|
550
|
+
user_id_list: userIds,
|
|
551
|
+
owner_id: ownerId
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
if (response.data.code === 0) {
|
|
555
|
+
this.logger.info(`创建群聊成功: ${response.data.data.chat_id}`);
|
|
556
|
+
return response.data.data.chat_id;
|
|
557
|
+
}
|
|
558
|
+
throw new Error(`Failed to create chat: ${response.data.msg}`);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
this.logger.error('Failed to create chat:', error);
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* 更新群信息
|
|
567
|
+
* @param chatId 群聊 ID
|
|
568
|
+
* @param options 更新选项
|
|
569
|
+
*/
|
|
570
|
+
async updateChatInfo(chatId: string, options: {
|
|
571
|
+
name?: string;
|
|
572
|
+
description?: string;
|
|
573
|
+
}): Promise<boolean> {
|
|
574
|
+
try {
|
|
575
|
+
const response = await this.axiosInstance.put(`/im/v1/chats/${chatId}`, options);
|
|
576
|
+
|
|
577
|
+
if (response.data.code === 0) {
|
|
578
|
+
this.logger.info(`更新群信息成功: ${chatId}`);
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
throw new Error(`Failed to update chat: ${response.data.msg}`);
|
|
582
|
+
} catch (error) {
|
|
583
|
+
this.logger.error('Failed to update chat:', error);
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* 添加群成员
|
|
590
|
+
* @param chatId 群聊 ID
|
|
591
|
+
* @param userIds 用户 ID 列表
|
|
592
|
+
*/
|
|
593
|
+
async addChatMembers(chatId: string, userIds: string[]): Promise<boolean> {
|
|
594
|
+
try {
|
|
595
|
+
const response = await this.axiosInstance.post(`/im/v1/chats/${chatId}/members`, {
|
|
596
|
+
id_list: userIds
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
if (response.data.code === 0) {
|
|
600
|
+
this.logger.info(`添加群成员成功: ${chatId}`);
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
603
|
+
throw new Error(`Failed to add members: ${response.data.msg}`);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
this.logger.error('Failed to add chat members:', error);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* 移除群成员
|
|
612
|
+
* @param chatId 群聊 ID
|
|
613
|
+
* @param userIds 用户 ID 列表
|
|
614
|
+
*/
|
|
615
|
+
async removeChatMembers(chatId: string, userIds: string[]): Promise<boolean> {
|
|
616
|
+
try {
|
|
617
|
+
const response = await this.axiosInstance.delete(`/im/v1/chats/${chatId}/members`, {
|
|
618
|
+
data: { id_list: userIds }
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
if (response.data.code === 0) {
|
|
622
|
+
this.logger.info(`移除群成员成功: ${chatId}`);
|
|
623
|
+
return true;
|
|
624
|
+
}
|
|
625
|
+
throw new Error(`Failed to remove members: ${response.data.msg}`);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
this.logger.error('Failed to remove chat members:', error);
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* 获取群成员列表
|
|
634
|
+
* @param chatId 群聊 ID
|
|
635
|
+
*/
|
|
636
|
+
async getChatMembers(chatId: string): Promise<any[]> {
|
|
637
|
+
try {
|
|
638
|
+
const response = await this.axiosInstance.get(`/im/v1/chats/${chatId}/members`);
|
|
639
|
+
|
|
640
|
+
if (response.data.code === 0) {
|
|
641
|
+
return response.data.data.items || [];
|
|
642
|
+
}
|
|
643
|
+
throw new Error(`Failed to get members: ${response.data.msg}`);
|
|
644
|
+
} catch (error) {
|
|
645
|
+
this.logger.error('Failed to get chat members:', error);
|
|
646
|
+
return [];
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* 解散群聊
|
|
652
|
+
* @param chatId 群聊 ID
|
|
653
|
+
*/
|
|
654
|
+
async dissolveChat(chatId: string): Promise<boolean> {
|
|
655
|
+
try {
|
|
656
|
+
const response = await this.axiosInstance.delete(`/im/v1/chats/${chatId}`);
|
|
657
|
+
|
|
658
|
+
if (response.data.code === 0) {
|
|
659
|
+
this.logger.info(`解散群聊成功: ${chatId}`);
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
throw new Error(`Failed to dissolve chat: ${response.data.msg}`);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
this.logger.error('Failed to dissolve chat:', error);
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* 设置群管理员
|
|
671
|
+
* @param chatId 群聊 ID
|
|
672
|
+
* @param userIds 用户 ID 列表
|
|
673
|
+
*/
|
|
674
|
+
async setChatManagers(chatId: string, userIds: string[]): Promise<boolean> {
|
|
675
|
+
try {
|
|
676
|
+
const response = await this.axiosInstance.post(`/im/v1/chats/${chatId}/managers/add_managers`, {
|
|
677
|
+
manager_ids: userIds
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
if (response.data.code === 0) {
|
|
681
|
+
this.logger.info(`设置群管理员成功: ${chatId}`);
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
throw new Error(`Failed to set managers: ${response.data.msg}`);
|
|
685
|
+
} catch (error) {
|
|
686
|
+
this.logger.error('Failed to set chat managers:', error);
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* 移除群管理员
|
|
693
|
+
* @param chatId 群聊 ID
|
|
694
|
+
* @param userIds 用户 ID 列表
|
|
695
|
+
*/
|
|
696
|
+
async removeChatManagers(chatId: string, userIds: string[]): Promise<boolean> {
|
|
697
|
+
try {
|
|
698
|
+
const response = await this.axiosInstance.post(`/im/v1/chats/${chatId}/managers/delete_managers`, {
|
|
699
|
+
manager_ids: userIds
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
if (response.data.code === 0) {
|
|
703
|
+
this.logger.info(`移除群管理员成功: ${chatId}`);
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
throw new Error(`Failed to remove managers: ${response.data.msg}`);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
this.logger.error('Failed to remove chat managers:', error);
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// 定义 Adapter 类
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书/Lark 适配器入口:类型扩展、导出、注册
|
|
3
|
+
*/
|
|
4
|
+
import { usePlugin, type Plugin, type Context } from "zhin.js";
|
|
5
|
+
import { LarkAdapter } from "./adapter.js";
|
|
6
|
+
|
|
7
|
+
declare module "zhin.js" {
|
|
8
|
+
namespace Plugin {
|
|
9
|
+
interface Contexts {
|
|
10
|
+
router: import("@zhin.js/http").Router;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
interface Adapters {
|
|
14
|
+
lark: LarkAdapter;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export * from "./types.js";
|
|
19
|
+
export { LarkBot } from "./bot.js";
|
|
20
|
+
export { LarkAdapter } from "./adapter.js";
|
|
21
|
+
|
|
22
|
+
const plugin = usePlugin();
|
|
23
|
+
const { provide, useContext } = plugin;
|
|
24
|
+
|
|
25
|
+
(useContext as (key: string, fn: (router: any) => void) => void)("router", (router) => {
|
|
26
|
+
provide({
|
|
27
|
+
name: "lark",
|
|
28
|
+
description: "Lark/Feishu Bot Adapter",
|
|
29
|
+
mounted: async (p: Plugin) => {
|
|
30
|
+
const adapter = new LarkAdapter(p, router);
|
|
31
|
+
await adapter.start();
|
|
32
|
+
return adapter;
|
|
33
|
+
},
|
|
34
|
+
dispose: async (adapter: LarkAdapter) => {
|
|
35
|
+
await adapter.stop();
|
|
36
|
+
},
|
|
37
|
+
} as Context<"lark">);
|
|
38
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书/Lark 适配器类型定义
|
|
3
|
+
*/
|
|
4
|
+
export interface LarkBotConfig {
|
|
5
|
+
context: "lark";
|
|
6
|
+
name: string;
|
|
7
|
+
appId: string;
|
|
8
|
+
appSecret: string;
|
|
9
|
+
encryptKey?: string;
|
|
10
|
+
verificationToken?: string;
|
|
11
|
+
webhookPath: string;
|
|
12
|
+
apiBaseUrl?: string;
|
|
13
|
+
isFeishu?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LarkMessage {
|
|
17
|
+
message_id?: string;
|
|
18
|
+
root_id?: string;
|
|
19
|
+
parent_id?: string;
|
|
20
|
+
create_time?: string;
|
|
21
|
+
update_time?: string;
|
|
22
|
+
chat_id?: string;
|
|
23
|
+
sender?: {
|
|
24
|
+
sender_id?: { user_id?: string; open_id?: string; union_id?: string };
|
|
25
|
+
sender_type?: string;
|
|
26
|
+
tenant_key?: string;
|
|
27
|
+
};
|
|
28
|
+
message_type?: string;
|
|
29
|
+
content?: string;
|
|
30
|
+
mentions?: Array<{
|
|
31
|
+
key?: string;
|
|
32
|
+
id?: { user_id?: string; open_id?: string; union_id?: string };
|
|
33
|
+
name?: string;
|
|
34
|
+
tenant_key?: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface LarkEvent {
|
|
39
|
+
uuid?: string;
|
|
40
|
+
token?: string;
|
|
41
|
+
ts?: string;
|
|
42
|
+
type?: string;
|
|
43
|
+
event?: {
|
|
44
|
+
sender?: any;
|
|
45
|
+
message?: LarkMessage;
|
|
46
|
+
[key: string]: any;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AccessToken {
|
|
51
|
+
token: string;
|
|
52
|
+
expires_in: number;
|
|
53
|
+
timestamp: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface LarkBot {
|
|
57
|
+
$config: LarkBotConfig;
|
|
58
|
+
}
|