@zhin.js/adapter-wecom 0.0.1
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/README.md +263 -0
- package/lib/adapter.d.ts +15 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +20 -0
- package/lib/adapter.js.map +1 -0
- package/lib/endpoint.d.ts +43 -0
- package/lib/endpoint.d.ts.map +1 -0
- package/lib/endpoint.js +586 -0
- package/lib/endpoint.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +115 -0
- package/lib/index.js.map +1 -0
- package/lib/platform-permit.d.ts +16 -0
- package/lib/platform-permit.d.ts.map +1 -0
- package/lib/platform-permit.js +39 -0
- package/lib/platform-permit.js.map +1 -0
- package/lib/types.d.ts +48 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +5 -0
- package/lib/types.js.map +1 -0
- package/package.json +69 -0
- package/plugin.yml +3 -0
- package/skills/wecom/PERMITS.md +19 -0
- package/skills/wecom/SKILL.md +68 -0
- package/src/adapter.ts +26 -0
- package/src/endpoint.ts +636 -0
- package/src/index.ts +134 -0
- package/src/platform-permit.ts +53 -0
- package/src/types.ts +51 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信适配器入口:类型扩展、导出、注册
|
|
3
|
+
*/
|
|
4
|
+
import { usePlugin, type Plugin, type ToolFeature } from 'zhin.js';
|
|
5
|
+
import type { Router } from '@zhin.js/host-router/router';
|
|
6
|
+
import { WecomAdapter } from './adapter.js';
|
|
7
|
+
import {
|
|
8
|
+
registerWecomPlatformPermitChecker,
|
|
9
|
+
platformPermit,
|
|
10
|
+
} from './platform-permit.js';
|
|
11
|
+
|
|
12
|
+
declare module 'zhin.js' {
|
|
13
|
+
namespace Plugin {
|
|
14
|
+
interface Contexts {
|
|
15
|
+
router: import('@zhin.js/host-router').Router;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
interface Adapters {
|
|
19
|
+
wecom: WecomAdapter;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export * from './types.js';
|
|
24
|
+
export { WecomEndpoint } from './endpoint.js';
|
|
25
|
+
export { WecomAdapter } from './adapter.js';
|
|
26
|
+
|
|
27
|
+
const plugin = usePlugin();
|
|
28
|
+
const { provide, useContext } = plugin;
|
|
29
|
+
|
|
30
|
+
useContext('router', (router: Router) => {
|
|
31
|
+
provide({
|
|
32
|
+
name: 'wecom',
|
|
33
|
+
description: 'WeCom (企业微信) Endpoint Adapter',
|
|
34
|
+
mounted: async (p: Plugin) => {
|
|
35
|
+
const adapter = new WecomAdapter(p, router);
|
|
36
|
+
await adapter.start();
|
|
37
|
+
return adapter;
|
|
38
|
+
},
|
|
39
|
+
dispose: async (adapter: WecomAdapter) => {
|
|
40
|
+
await adapter.stop();
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
useContext('tool', 'wecom', (toolService: ToolFeature, wecom: WecomAdapter) => {
|
|
46
|
+
const disposers: (() => void)[] = [];
|
|
47
|
+
disposers.push(registerWecomPlatformPermitChecker());
|
|
48
|
+
|
|
49
|
+
disposers.push(toolService.addTool({
|
|
50
|
+
name: 'wecom_get_user',
|
|
51
|
+
description: '获取企业微信用户信息',
|
|
52
|
+
parameters: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
56
|
+
user_id: { type: 'string', description: '用户 ID' },
|
|
57
|
+
},
|
|
58
|
+
required: ['endpoint_id', 'user_id'],
|
|
59
|
+
},
|
|
60
|
+
platforms: ['wecom'],
|
|
61
|
+
tags: ['wecom'],
|
|
62
|
+
execute: async (args: Record<string, any>) => {
|
|
63
|
+
const endpoint = wecom.endpoints.get(args.endpoint_id);
|
|
64
|
+
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
65
|
+
return await endpoint.getUserInfo(args.user_id);
|
|
66
|
+
},
|
|
67
|
+
}, plugin.name));
|
|
68
|
+
|
|
69
|
+
disposers.push(toolService.addTool({
|
|
70
|
+
name: 'wecom_get_dept_users',
|
|
71
|
+
description: '获取企业微信部门用户列表',
|
|
72
|
+
parameters: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
76
|
+
dept_id: { type: 'string', description: '部门 ID' },
|
|
77
|
+
},
|
|
78
|
+
required: ['endpoint_id', 'dept_id'],
|
|
79
|
+
},
|
|
80
|
+
platforms: ['wecom'],
|
|
81
|
+
tags: ['wecom'],
|
|
82
|
+
execute: async (args: Record<string, any>) => {
|
|
83
|
+
const endpoint = wecom.endpoints.get(args.endpoint_id);
|
|
84
|
+
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
85
|
+
const users = await endpoint.getDepartmentUsers(Number(args.dept_id));
|
|
86
|
+
return { users, count: users.length };
|
|
87
|
+
},
|
|
88
|
+
}, plugin.name));
|
|
89
|
+
|
|
90
|
+
disposers.push(toolService.addTool({
|
|
91
|
+
name: 'wecom_list_departments',
|
|
92
|
+
description: '获取企业微信部门列表',
|
|
93
|
+
parameters: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
97
|
+
dept_id: { type: 'string', description: '父部门 ID,默认 1(跟部门)' },
|
|
98
|
+
},
|
|
99
|
+
required: ['endpoint_id'],
|
|
100
|
+
},
|
|
101
|
+
platforms: ['wecom'],
|
|
102
|
+
tags: ['wecom'],
|
|
103
|
+
execute: async (args: Record<string, any>) => {
|
|
104
|
+
const endpoint = wecom.endpoints.get(args.endpoint_id);
|
|
105
|
+
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
106
|
+
const departments = await endpoint.getDepartmentList(Number(args.dept_id) || 1);
|
|
107
|
+
return { departments, count: departments.length };
|
|
108
|
+
},
|
|
109
|
+
}, plugin.name));
|
|
110
|
+
|
|
111
|
+
disposers.push(toolService.addTool({
|
|
112
|
+
name: 'wecom_send_text',
|
|
113
|
+
description: '向指定企业微信用户发送文本消息',
|
|
114
|
+
parameters: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
118
|
+
user_id: { type: 'string', description: '用户 ID' },
|
|
119
|
+
content: { type: 'string', description: '消息内容' },
|
|
120
|
+
},
|
|
121
|
+
required: ['endpoint_id', 'user_id', 'content'],
|
|
122
|
+
},
|
|
123
|
+
platforms: ['wecom'],
|
|
124
|
+
tags: ['wecom'],
|
|
125
|
+
execute: async (args: Record<string, any>) => {
|
|
126
|
+
const endpoint = wecom.endpoints.get(args.endpoint_id);
|
|
127
|
+
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
128
|
+
const success = await endpoint.sendTextMessage(args.user_id, args.content);
|
|
129
|
+
return { success, message: success ? '消息已发送' : '发送失败' };
|
|
130
|
+
},
|
|
131
|
+
}, plugin.name));
|
|
132
|
+
|
|
133
|
+
return () => disposers.forEach(d => d());
|
|
134
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 WeCom platform permit
|
|
3
|
+
*/
|
|
4
|
+
import type { Message } from 'zhin.js';
|
|
5
|
+
import { registerPlatformPermitChecker } from 'zhin.js';
|
|
6
|
+
|
|
7
|
+
const ADAPTER = 'wecom';
|
|
8
|
+
|
|
9
|
+
export function platformPermit(perm: string): string {
|
|
10
|
+
return `platform(${ADAPTER},${perm})`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
14
|
+
group_admin: 'chat_admin',
|
|
15
|
+
group_owner: 'chat_owner',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function wecomGroupPermitResolver(logicalPerm: string): string {
|
|
19
|
+
return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function normalizeWecomSenderForPermit(input: {
|
|
23
|
+
isOwner?: boolean;
|
|
24
|
+
isAdmin?: boolean;
|
|
25
|
+
}): { role?: string; permissions?: string[] } {
|
|
26
|
+
if (input.isOwner) {
|
|
27
|
+
return { role: 'owner', permissions: ['chat_owner', 'chat_admin'] };
|
|
28
|
+
}
|
|
29
|
+
if (input.isAdmin) {
|
|
30
|
+
return { role: 'admin', permissions: ['chat_admin'] };
|
|
31
|
+
}
|
|
32
|
+
return { role: 'member', permissions: [] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function checkWecomPlatformPermit(perm: string, message: Message<any>): boolean {
|
|
36
|
+
const sender = message.$sender as { role?: string; permissions?: string[] };
|
|
37
|
+
const permissions = sender.permissions ?? [];
|
|
38
|
+
const role = sender.role;
|
|
39
|
+
const has = (t: string) => permissions.includes(t);
|
|
40
|
+
|
|
41
|
+
switch (perm) {
|
|
42
|
+
case 'chat_owner':
|
|
43
|
+
return role === 'owner' || has('chat_owner');
|
|
44
|
+
case 'chat_admin':
|
|
45
|
+
return role === 'owner' || role === 'admin' || has('chat_admin') || has('chat_owner');
|
|
46
|
+
default:
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerWecomPlatformPermitChecker(): () => void {
|
|
52
|
+
return registerPlatformPermitChecker(ADAPTER, checkWecomPlatformPermit);
|
|
53
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信适配器类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface WecomEndpointConfig {
|
|
6
|
+
context: 'wecom'
|
|
7
|
+
name: string
|
|
8
|
+
corpId: string
|
|
9
|
+
agentSecret: string // 应用 Secret
|
|
10
|
+
token: string // 用于签名验证
|
|
11
|
+
encodingAESKey: string // 用于消息加解密
|
|
12
|
+
webhookPath?: string // 默认 '/wecom/callback'
|
|
13
|
+
apiBaseUrl?: string // 默认 'https://qyapi.weixin.qq.com'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WecomMessage {
|
|
17
|
+
ToUserName: string
|
|
18
|
+
FromUserName: string
|
|
19
|
+
CreateTime: number
|
|
20
|
+
MsgType: 'text' | 'image' | 'voice' | 'video' | 'shortvideo' | 'location' | 'link' | 'event'
|
|
21
|
+
Content?: string
|
|
22
|
+
MsgId?: string
|
|
23
|
+
PicUrl?: string
|
|
24
|
+
MediaId?: string
|
|
25
|
+
ThumbMediaId?: string
|
|
26
|
+
Format?: string
|
|
27
|
+
Recognition?: string
|
|
28
|
+
Location_X?: string
|
|
29
|
+
Location_Y?: string
|
|
30
|
+
Scale?: string
|
|
31
|
+
Label?: string
|
|
32
|
+
Title?: string
|
|
33
|
+
Description?: string
|
|
34
|
+
Url?: string
|
|
35
|
+
Event?: string
|
|
36
|
+
EventKey?: string
|
|
37
|
+
AgentID?: string
|
|
38
|
+
[key: string]: unknown
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AccessToken {
|
|
42
|
+
access_token: string
|
|
43
|
+
expires_in: number
|
|
44
|
+
timestamp: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WecomApiResponse {
|
|
48
|
+
errcode: number
|
|
49
|
+
errmsg?: string
|
|
50
|
+
[key: string]: unknown
|
|
51
|
+
}
|