@zhin.js/adapter-lark 1.0.97 → 1.1.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/CHANGELOG.md +637 -3
- package/README.md +70 -83
- package/adapters/lark.js +36 -0
- package/adapters/lark.ts +41 -0
- package/agent/PERMITS.md +22 -0
- package/agent/tools/add_members.ts +20 -0
- package/agent/tools/create_chat.ts +18 -0
- package/agent/tools/dissolve_chat.ts +16 -0
- package/agent/tools/get_user.ts +16 -0
- package/agent/tools/remove_managers.ts +20 -0
- package/agent/tools/set_managers.ts +20 -0
- package/agent/tools/update_chat.ts +21 -0
- package/agent/tools/upload_file.ts +23 -0
- package/commands/endpoint/add/[id].js +3 -0
- package/commands/endpoint/add/[id].ts +3 -0
- package/commands/endpoint/list.js +3 -0
- package/commands/endpoint/list.ts +3 -0
- package/commands/endpoint/remove/[id].js +3 -0
- package/commands/endpoint/remove/[id].ts +3 -0
- package/lib/client.d.ts +12 -0
- package/lib/client.js +2 -0
- package/lib/endpoint.d.ts +100 -0
- package/lib/endpoint.js +497 -0
- package/lib/index.d.ts +6 -15
- package/lib/index.js +6 -209
- package/lib/lark-endpoint-commands.d.ts +1 -0
- package/lib/lark-endpoint-commands.js +19 -0
- package/lib/lark-runtime-state.d.ts +1 -0
- package/lib/lark-runtime-state.js +6 -0
- package/lib/media-upload.d.ts +15 -0
- package/lib/media-upload.js +58 -0
- package/lib/platform-permit.d.ts +14 -0
- package/lib/platform-permit.js +35 -0
- package/lib/protocol.d.ts +123 -0
- package/lib/protocol.js +273 -0
- package/lib/webhook.d.ts +14 -0
- package/lib/webhook.js +73 -0
- package/package.json +63 -20
- package/plugin.js +19 -0
- package/schema.json +97 -0
- package/src/client.ts +16 -0
- package/src/endpoint.ts +590 -0
- package/src/index.ts +51 -227
- package/src/lark-endpoint-commands.ts +20 -0
- package/src/lark-runtime-state.ts +7 -0
- package/src/media-upload.ts +69 -0
- package/src/platform-permit.ts +50 -0
- package/src/protocol.ts +402 -0
- package/src/webhook.ts +110 -0
- package/lib/adapter.d.ts +0 -17
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -45
- package/lib/adapter.js.map +0 -1
- package/lib/bot.d.ts +0 -82
- package/lib/bot.d.ts.map +0 -1
- package/lib/bot.js +0 -638
- package/lib/bot.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types.d.ts +0 -63
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -55
- package/src/bot.ts +0 -712
- package/src/types.ts +0 -58
- /package/{skills/lark/SKILL.md → agent/skills/lark.md} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,227 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
properties: {
|
|
53
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
54
|
-
user_id: { type: 'string', description: '用户 ID (open_id)' },
|
|
55
|
-
},
|
|
56
|
-
required: ['bot', 'user_id'],
|
|
57
|
-
},
|
|
58
|
-
platforms: ['lark'],
|
|
59
|
-
tags: ['lark'],
|
|
60
|
-
execute: async (args: Record<string, any>) => {
|
|
61
|
-
const bot = lark.bots.get(args.bot);
|
|
62
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
63
|
-
return await bot.getUserInfo(args.user_id);
|
|
64
|
-
},
|
|
65
|
-
}, plugin.name));
|
|
66
|
-
|
|
67
|
-
disposers.push(toolService.addTool({
|
|
68
|
-
name: 'lark_create_chat',
|
|
69
|
-
description: '创建飞书群聊',
|
|
70
|
-
parameters: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
74
|
-
name: { type: 'string', description: '群名' },
|
|
75
|
-
members: { type: 'string', description: '成员 open_id 列表,逗号分隔' },
|
|
76
|
-
owner: { type: 'string', description: '群主 open_id(可选)' },
|
|
77
|
-
},
|
|
78
|
-
required: ['bot', 'name', 'members'],
|
|
79
|
-
},
|
|
80
|
-
platforms: ['lark'],
|
|
81
|
-
tags: ['lark'],
|
|
82
|
-
execute: async (args: Record<string, any>) => {
|
|
83
|
-
const bot = lark.bots.get(args.bot);
|
|
84
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
85
|
-
const chatId = await bot.createChat(args.name, args.members.split(','), args.owner);
|
|
86
|
-
return { success: !!chatId, chat_id: chatId, message: chatId ? `群聊创建成功: ${chatId}` : '创建失败' };
|
|
87
|
-
},
|
|
88
|
-
}, plugin.name));
|
|
89
|
-
|
|
90
|
-
disposers.push(toolService.addTool({
|
|
91
|
-
name: 'lark_update_chat',
|
|
92
|
-
description: '更新飞书群聊信息(群名、描述)',
|
|
93
|
-
parameters: {
|
|
94
|
-
type: 'object',
|
|
95
|
-
properties: {
|
|
96
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
97
|
-
chat_id: { type: 'string', description: '群聊 ID' },
|
|
98
|
-
name: { type: 'string', description: '新群名(可选)' },
|
|
99
|
-
description: { type: 'string', description: '新描述(可选)' },
|
|
100
|
-
},
|
|
101
|
-
required: ['bot', 'chat_id'],
|
|
102
|
-
},
|
|
103
|
-
platforms: ['lark'],
|
|
104
|
-
tags: ['lark'],
|
|
105
|
-
execute: async (args: Record<string, any>) => {
|
|
106
|
-
const bot = lark.bots.get(args.bot);
|
|
107
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
108
|
-
const success = await bot.updateChatInfo(args.chat_id, { name: args.name, description: args.description });
|
|
109
|
-
return { success, message: success ? '群信息更新成功' : '更新失败' };
|
|
110
|
-
},
|
|
111
|
-
}, plugin.name));
|
|
112
|
-
|
|
113
|
-
disposers.push(toolService.addTool({
|
|
114
|
-
name: 'lark_add_members',
|
|
115
|
-
description: '添加飞书群成员',
|
|
116
|
-
parameters: {
|
|
117
|
-
type: 'object',
|
|
118
|
-
properties: {
|
|
119
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
120
|
-
chat_id: { type: 'string', description: '群聊 ID' },
|
|
121
|
-
user_ids: { type: 'string', description: '用户 open_id 列表,逗号分隔' },
|
|
122
|
-
},
|
|
123
|
-
required: ['bot', 'chat_id', 'user_ids'],
|
|
124
|
-
},
|
|
125
|
-
platforms: ['lark'],
|
|
126
|
-
tags: ['lark'],
|
|
127
|
-
execute: async (args: Record<string, any>) => {
|
|
128
|
-
const bot = lark.bots.get(args.bot);
|
|
129
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
130
|
-
const success = await bot.addChatMembers(args.chat_id, args.user_ids.split(','));
|
|
131
|
-
return { success, message: success ? '成员添加成功' : '添加失败' };
|
|
132
|
-
},
|
|
133
|
-
}, plugin.name));
|
|
134
|
-
|
|
135
|
-
disposers.push(toolService.addTool({
|
|
136
|
-
name: 'lark_set_managers',
|
|
137
|
-
description: '设置飞书群管理员',
|
|
138
|
-
parameters: {
|
|
139
|
-
type: 'object',
|
|
140
|
-
properties: {
|
|
141
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
142
|
-
chat_id: { type: 'string', description: '群聊 ID' },
|
|
143
|
-
user_ids: { type: 'string', description: '用户 open_id 列表,逗号分隔' },
|
|
144
|
-
},
|
|
145
|
-
required: ['bot', 'chat_id', 'user_ids'],
|
|
146
|
-
},
|
|
147
|
-
platforms: ['lark'],
|
|
148
|
-
tags: ['lark'],
|
|
149
|
-
execute: async (args: Record<string, any>) => {
|
|
150
|
-
const bot = lark.bots.get(args.bot);
|
|
151
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
152
|
-
const success = await bot.setChatManagers(args.chat_id, args.user_ids.split(','));
|
|
153
|
-
return { success, message: success ? '管理员设置成功' : '设置失败' };
|
|
154
|
-
},
|
|
155
|
-
}, plugin.name));
|
|
156
|
-
|
|
157
|
-
disposers.push(toolService.addTool({
|
|
158
|
-
name: 'lark_remove_managers',
|
|
159
|
-
description: '移除飞书群管理员',
|
|
160
|
-
parameters: {
|
|
161
|
-
type: 'object',
|
|
162
|
-
properties: {
|
|
163
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
164
|
-
chat_id: { type: 'string', description: '群聊 ID' },
|
|
165
|
-
user_ids: { type: 'string', description: '用户 open_id 列表,逗号分隔' },
|
|
166
|
-
},
|
|
167
|
-
required: ['bot', 'chat_id', 'user_ids'],
|
|
168
|
-
},
|
|
169
|
-
platforms: ['lark'],
|
|
170
|
-
tags: ['lark'],
|
|
171
|
-
execute: async (args: Record<string, any>) => {
|
|
172
|
-
const bot = lark.bots.get(args.bot);
|
|
173
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
174
|
-
const success = await bot.removeChatManagers(args.chat_id, args.user_ids.split(','));
|
|
175
|
-
return { success, message: success ? '管理员移除成功' : '移除失败' };
|
|
176
|
-
},
|
|
177
|
-
}, plugin.name));
|
|
178
|
-
|
|
179
|
-
disposers.push(toolService.addTool({
|
|
180
|
-
name: 'lark_dissolve_chat',
|
|
181
|
-
description: '解散飞书群聊(需要群主权限)',
|
|
182
|
-
parameters: {
|
|
183
|
-
type: 'object',
|
|
184
|
-
properties: {
|
|
185
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
186
|
-
chat_id: { type: 'string', description: '群聊 ID' },
|
|
187
|
-
},
|
|
188
|
-
required: ['bot', 'chat_id'],
|
|
189
|
-
},
|
|
190
|
-
platforms: ['lark'],
|
|
191
|
-
tags: ['lark'],
|
|
192
|
-
execute: async (args: Record<string, any>) => {
|
|
193
|
-
const bot = lark.bots.get(args.bot);
|
|
194
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
195
|
-
const success = await bot.dissolveChat(args.chat_id);
|
|
196
|
-
return { success, message: success ? '群聊已解散' : '解散失败' };
|
|
197
|
-
},
|
|
198
|
-
}, plugin.name));
|
|
199
|
-
|
|
200
|
-
disposers.push(toolService.addTool({
|
|
201
|
-
name: 'lark_upload_file',
|
|
202
|
-
description: '上传文件到飞书(opus/mp4/pdf/doc/xls/ppt/stream)',
|
|
203
|
-
parameters: {
|
|
204
|
-
type: 'object',
|
|
205
|
-
properties: {
|
|
206
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
207
|
-
file_path: { type: 'string', description: '本地文件路径' },
|
|
208
|
-
file_type: { type: 'string', description: '文件类型', enum: ['opus', 'mp4', 'pdf', 'doc', 'xls', 'ppt', 'stream'] },
|
|
209
|
-
},
|
|
210
|
-
required: ['bot', 'file_path', 'file_type'],
|
|
211
|
-
},
|
|
212
|
-
platforms: ['lark'],
|
|
213
|
-
tags: ['lark'],
|
|
214
|
-
execute: async (args: Record<string, any>) => {
|
|
215
|
-
const validTypes = ['opus', 'mp4', 'pdf', 'doc', 'xls', 'ppt', 'stream'];
|
|
216
|
-
if (!validTypes.includes(args.file_type)) {
|
|
217
|
-
throw new Error(`不支持的文件类型: ${args.file_type},支持: ${validTypes.join(', ')}`);
|
|
218
|
-
}
|
|
219
|
-
const bot = lark.bots.get(args.bot);
|
|
220
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
221
|
-
const result = await bot.uploadFile(args.file_path, args.file_type);
|
|
222
|
-
return { success: true, file_key: result, message: `文件已上传,file_key: ${result}` };
|
|
223
|
-
},
|
|
224
|
-
}, plugin.name));
|
|
225
|
-
|
|
226
|
-
return () => disposers.forEach(d => d());
|
|
227
|
-
});
|
|
1
|
+
export {
|
|
2
|
+
formatInboundContent,
|
|
3
|
+
formatOutboundBody,
|
|
4
|
+
generateMessageId,
|
|
5
|
+
headerValue,
|
|
6
|
+
larkInboundConversation,
|
|
7
|
+
normalizeWebhookPath,
|
|
8
|
+
readTextBody,
|
|
9
|
+
resolveChatType,
|
|
10
|
+
resolveLarkConfig,
|
|
11
|
+
resolveSender,
|
|
12
|
+
verifySignature,
|
|
13
|
+
type AccessToken,
|
|
14
|
+
type LarkAdapterConfig,
|
|
15
|
+
type LarkApiResponse,
|
|
16
|
+
type LarkEventBody,
|
|
17
|
+
type LarkMessage,
|
|
18
|
+
type LarkSendBody,
|
|
19
|
+
type LarkWireSegment,
|
|
20
|
+
type ResolvedLarkConfig,
|
|
21
|
+
} from './protocol.js';
|
|
22
|
+
|
|
23
|
+
export { larkClient, type LarkClientEventMap } from './client.js';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
checkLarkPlatformPermit,
|
|
27
|
+
larkGroupPermitResolver,
|
|
28
|
+
normalizeLarkSenderForPermit,
|
|
29
|
+
platformPermit,
|
|
30
|
+
} from './platform-permit.js';
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
LarkClient,
|
|
34
|
+
LarkEndpoint,
|
|
35
|
+
type LarkClientApi,
|
|
36
|
+
type LarkEndpointOptions,
|
|
37
|
+
type LarkFetch,
|
|
38
|
+
} from './endpoint.js';
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
buildImageUploadForm,
|
|
42
|
+
readOutboundImageMedia,
|
|
43
|
+
resolveMediaBinary,
|
|
44
|
+
type MediaBinary,
|
|
45
|
+
} from './media-upload.js';
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
registerLarkWebhookRoutes,
|
|
49
|
+
handleLarkWebhookRequest,
|
|
50
|
+
type LarkWebhookHandler,
|
|
51
|
+
} from './webhook.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lark.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
|
|
3
|
+
* commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
|
|
4
|
+
*/
|
|
5
|
+
import { createEndpointCommands } from 'zhin.js/adapter';
|
|
6
|
+
import { defineCommand } from 'zhin.js/command';
|
|
7
|
+
import { larkRuntimeStateToken } from './lark-runtime-state.js';
|
|
8
|
+
|
|
9
|
+
export const larkEndpointCommands = createEndpointCommands({
|
|
10
|
+
adapterKey: 'lark',
|
|
11
|
+
adapterDisplayName: 'Lark',
|
|
12
|
+
fields: [
|
|
13
|
+
{ key: 'appId', required: true, env: true, description: '飞书 App ID' },
|
|
14
|
+
{ key: 'appSecret', required: true, env: true, description: '飞书 App Secret' },
|
|
15
|
+
{ key: 'encryptKey', env: true, description: '事件加密 key' },
|
|
16
|
+
{ key: 'verificationToken', env: true, description: '事件验证 token' },
|
|
17
|
+
],
|
|
18
|
+
running: (use) => use(larkRuntimeStateToken).endpoints.values(),
|
|
19
|
+
describeEntry: (entry) => `appId: ${String(entry.appId)}`,
|
|
20
|
+
}, defineCommand);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
+
* 由 plugin.ts setup() provide,adapter create 与 `lark.endpoint` 命令共享(同一 owner generation)。
|
|
4
|
+
*/
|
|
5
|
+
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
|
+
|
|
7
|
+
export const larkRuntimeStateToken = defineEndpointRuntimeStateToken('lark');
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark 图片上传(im/v1 消息 image 段需要 image_key)。
|
|
3
|
+
* 接口:POST {apiBaseUrl}/im/v1/images(multipart:image_type=message + image)
|
|
4
|
+
* 与 im/v1/messages 同属 IM 接口域(同一 tenant_access_token),不引入额外授权域。
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import { basename } from 'node:path';
|
|
8
|
+
import { isMediaRef, type MediaRef } from '@zhin.js/core';
|
|
9
|
+
|
|
10
|
+
export interface MediaBinary {
|
|
11
|
+
readonly data: Buffer;
|
|
12
|
+
readonly mimeType: string;
|
|
13
|
+
readonly fileName: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MIME_EXT: Record<string, string> = {
|
|
17
|
+
'image/jpeg': 'jpg',
|
|
18
|
+
'image/png': 'png',
|
|
19
|
+
'image/gif': 'gif',
|
|
20
|
+
'image/webp': 'webp',
|
|
21
|
+
'image/bmp': 'bmp',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** 从 canonical MediaRef 解析二进制:base64 解码 / 本地读盘 / URL 下载。 */
|
|
25
|
+
export async function resolveMediaBinary(
|
|
26
|
+
media: MediaRef,
|
|
27
|
+
download: (url: string) => Promise<Buffer> = defaultDownload,
|
|
28
|
+
): Promise<MediaBinary> {
|
|
29
|
+
const mimeType = media.mime_type ?? 'image/png';
|
|
30
|
+
const ext = MIME_EXT[mimeType] ?? 'png';
|
|
31
|
+
if (media.kind === 'base64') {
|
|
32
|
+
const value = media.value.startsWith('base64://')
|
|
33
|
+
? media.value.slice('base64://'.length)
|
|
34
|
+
: media.value;
|
|
35
|
+
return { data: Buffer.from(value, 'base64'), mimeType, fileName: `image.${ext}` };
|
|
36
|
+
}
|
|
37
|
+
if (media.kind === 'path') {
|
|
38
|
+
const path = media.value.startsWith('file://') ? media.value.slice('file://'.length) : media.value;
|
|
39
|
+
return { data: await readFile(path), mimeType, fileName: basename(path) };
|
|
40
|
+
}
|
|
41
|
+
return { data: await download(media.value), mimeType, fileName: `image.${ext}` };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function defaultDownload(url: string): Promise<Buffer> {
|
|
45
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(30_000) });
|
|
46
|
+
if (!response.ok) throw new Error(`download failed: HTTP ${response.status}`);
|
|
47
|
+
return Buffer.from(await response.arrayBuffer());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** /im/v1/images 的 multipart body(image_type=message 用于消息内图片)。 */
|
|
51
|
+
export function buildImageUploadForm(binary: MediaBinary): FormData {
|
|
52
|
+
// Buffer 的 ArrayBufferLike 不满足 BlobPart(SharedArrayBuffer 分支),拷贝为 Uint8Array<ArrayBuffer>
|
|
53
|
+
const bytes = new Uint8Array(binary.data.byteLength);
|
|
54
|
+
bytes.set(binary.data);
|
|
55
|
+
const form = new FormData();
|
|
56
|
+
form.append('image_type', 'message');
|
|
57
|
+
form.append('image', new Blob([bytes], { type: binary.mimeType }), binary.fileName);
|
|
58
|
+
return form;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 出站 image 段待上传的媒体引用(canonical `data.media` 唯一来源):
|
|
63
|
+
* kind=file 为平台不透明引用(image_key/file_key),已物化无需再上传,返回 undefined。
|
|
64
|
+
*/
|
|
65
|
+
export function readOutboundImageMedia(data: Record<string, unknown>): MediaRef | undefined {
|
|
66
|
+
if (!isMediaRef(data.media)) return undefined;
|
|
67
|
+
if (data.media.kind === 'file') return undefined;
|
|
68
|
+
return data.media;
|
|
69
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书 Lark platform permit
|
|
3
|
+
*/
|
|
4
|
+
import type { PermissionSubject } from '@zhin.js/permission';
|
|
5
|
+
|
|
6
|
+
const ADAPTER = 'lark';
|
|
7
|
+
|
|
8
|
+
export function platformPermit(perm: string): string {
|
|
9
|
+
return `platform(${ADAPTER},${perm})`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
13
|
+
scene_admin: 'chat_admin',
|
|
14
|
+
scene_owner: 'chat_owner',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function larkGroupPermitResolver(logicalPerm: string): string {
|
|
18
|
+
return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function normalizeLarkSenderForPermit(input: {
|
|
22
|
+
isOwner?: boolean;
|
|
23
|
+
isAdmin?: boolean;
|
|
24
|
+
}): { role?: string; permissions?: string[] } {
|
|
25
|
+
if (input.isOwner) {
|
|
26
|
+
return { role: 'owner', permissions: ['chat_owner', 'chat_admin', 'manage_managers'] };
|
|
27
|
+
}
|
|
28
|
+
if (input.isAdmin) {
|
|
29
|
+
return { role: 'admin', permissions: ['chat_admin'] };
|
|
30
|
+
}
|
|
31
|
+
return { role: 'member', permissions: [] };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function checkLarkPlatformPermit(perm: string, subject: PermissionSubject): boolean {
|
|
35
|
+
const role = subject.sender?.role?.[0];
|
|
36
|
+
const permissions = subject.sender?.permissions ?? [];
|
|
37
|
+
const has = (t: string) => permissions.includes(t);
|
|
38
|
+
|
|
39
|
+
switch (perm) {
|
|
40
|
+
case 'chat_owner':
|
|
41
|
+
return role === 'owner' || has('chat_owner');
|
|
42
|
+
case 'chat_admin':
|
|
43
|
+
return role === 'owner' || role === 'admin' || has('chat_admin') || has('chat_owner');
|
|
44
|
+
case 'manage_managers':
|
|
45
|
+
return role === 'owner' || has('chat_owner');
|
|
46
|
+
default:
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|