@jiexiaoyin/wecom-api 0.0.2
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 +228 -0
- package/config.example.json +7 -0
- package/config.js +76 -0
- package/docs/approval-templates.example.json +11 -0
- package/docs/nginx-mirror.md +193 -0
- package/openclaw.plugin.json +15 -0
- package/package.json +34 -0
- package/plugin.cjs +172 -0
- package/plugin.ts +136 -0
- package/skills/wecom-api/SKILL.md +40 -0
- package/skills/wecom-api/index.js +288 -0
- package/skills/wecom-api/openclaw.plugin.json +10 -0
- package/src/callback-helper.js +198 -0
- package/src/config.cjs +286 -0
- package/src/core/permission.js +479 -0
- package/src/crypto.js +130 -0
- package/src/index.js +199 -0
- package/src/modules/addressbook/index.js +413 -0
- package/src/modules/addressbook_cache/index.js +365 -0
- package/src/modules/advanced/index.js +159 -0
- package/src/modules/app/index.js +102 -0
- package/src/modules/approval/index.js +146 -0
- package/src/modules/auth/index.js +103 -0
- package/src/modules/callback/index.js +1180 -0
- package/src/modules/chain/index.js +193 -0
- package/src/modules/checkin/index.js +142 -0
- package/src/modules/checkin_rules/index.js +251 -0
- package/src/modules/contact/index.js +481 -0
- package/src/modules/contact_stats/index.js +349 -0
- package/src/modules/custom/index.js +140 -0
- package/src/modules/customer/index.js +51 -0
- package/src/modules/disk/index.js +245 -0
- package/src/modules/document/index.js +282 -0
- package/src/modules/hr/index.js +93 -0
- package/src/modules/intelligence/index.js +346 -0
- package/src/modules/kf/index.js +74 -0
- package/src/modules/live/index.js +122 -0
- package/src/modules/media/index.js +183 -0
- package/src/modules/meeting/index.js +665 -0
- package/src/modules/message/index.js +402 -0
- package/src/modules/messenger/index.js +208 -0
- package/src/modules/moments/index.js +161 -0
- package/src/modules/msgaudit/index.js +24 -0
- package/src/modules/notify/index.js +81 -0
- package/src/modules/oceanengine/index.js +199 -0
- package/src/modules/openchat/index.js +197 -0
- package/src/modules/phone/index.js +45 -0
- package/src/modules/room/index.js +178 -0
- package/src/modules/schedule/index.js +246 -0
- package/src/modules/school/index.js +199 -0
- package/src/modules/security/index.js +223 -0
- package/src/modules/sensitive/index.js +170 -0
- package/src/modules/thirdparty/index.js +145 -0
- package/src/sdk/index.js +269 -0
- package/src/utils/callback-helper.js +198 -0
- package/test/callback-crypto.test.js +55 -0
- package/test/crypto.test.js +85 -0
- package/test/permission.test.js +115 -0
package/plugin.cjs
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw 插件入口 - 企业微信工具
|
|
3
|
+
*
|
|
4
|
+
* 继承 OpenClaw HTTP 服务,提供回调处理和 32 个 API 模块
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// 导入配置加载器
|
|
10
|
+
const { getConfig } = require('./config.js');
|
|
11
|
+
|
|
12
|
+
// 导入回调处理模块
|
|
13
|
+
const CallbackModule = require('./src/modules/callback/index.js');
|
|
14
|
+
const CallbackClass = CallbackModule.default || CallbackModule;
|
|
15
|
+
|
|
16
|
+
// 导入统计模块
|
|
17
|
+
const ContactStats = require('./src/modules/contact_stats/index.js');
|
|
18
|
+
|
|
19
|
+
// 导入通讯录缓存模块
|
|
20
|
+
const AddressBookCache = require('./src/modules/addressbook_cache/index.js');
|
|
21
|
+
const AddressBook = require('./src/modules/addressbook/index.js');
|
|
22
|
+
|
|
23
|
+
// 导入回调工具函数
|
|
24
|
+
const { createCallbackHandler } = require('./src/callback-helper.js');
|
|
25
|
+
|
|
26
|
+
let callbackInstance = null;
|
|
27
|
+
let callbackHandler = null;
|
|
28
|
+
|
|
29
|
+
const plugin = {
|
|
30
|
+
id: "wecom-api",
|
|
31
|
+
name: "WeCom API (企业微信API)",
|
|
32
|
+
description: "企业微信 API 工具集,支持事件回调和 32 个 API 模块",
|
|
33
|
+
configSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
corpId: { type: "string" },
|
|
37
|
+
corpSecret: { type: "string" },
|
|
38
|
+
agentId: { type: "string" },
|
|
39
|
+
token: { type: "string" },
|
|
40
|
+
encodingAESKey: { type: "string" },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 注册插件
|
|
46
|
+
*/
|
|
47
|
+
register(api) {
|
|
48
|
+
console.log('[wecom-api] 插件注册中...');
|
|
49
|
+
|
|
50
|
+
// 加载配置
|
|
51
|
+
const config = getConfig();
|
|
52
|
+
|
|
53
|
+
// 初始化回调处理实例
|
|
54
|
+
if (config.corpId && config.corpSecret && config.agentId) {
|
|
55
|
+
callbackInstance = new CallbackClass(config);
|
|
56
|
+
console.log('[wecom-api] 回调处理已初始化');
|
|
57
|
+
} else {
|
|
58
|
+
console.log('[wecom-api] 配置不完整,跳过回调初始化');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 初始化统计模块
|
|
62
|
+
let contactStats = null;
|
|
63
|
+
if (config.corpId && config.corpSecret) {
|
|
64
|
+
contactStats = new ContactStats(config);
|
|
65
|
+
console.log('[wecom-api] 统计模块已初始化');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 初始化通讯录缓存模块
|
|
69
|
+
let addressBookCache = null;
|
|
70
|
+
let addressBook = null;
|
|
71
|
+
if (config.corpId && config.corpSecret) {
|
|
72
|
+
addressBook = new AddressBook(config);
|
|
73
|
+
addressBookCache = new AddressBookCache(config, {
|
|
74
|
+
onConfirm: () => {
|
|
75
|
+
// 用户确认后开始同步
|
|
76
|
+
addressBookCache.syncFromAPI(addressBook);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 检查是否需要同步
|
|
81
|
+
// 如果缓存文件已存在,标记为已同步(无需用户再确认)
|
|
82
|
+
if (addressBookCache.getSyncStatus().lastSyncTime) {
|
|
83
|
+
addressBookCache.syncConfirmed = true;
|
|
84
|
+
}
|
|
85
|
+
const syncStatus = addressBookCache.requestSync();
|
|
86
|
+
|
|
87
|
+
if (syncStatus.needConfirm) {
|
|
88
|
+
console.log('[wecom-api] 通讯录缓存未初始化,请回复"同步通讯录"开始同步');
|
|
89
|
+
// 不自动同步,等待用户确认
|
|
90
|
+
} else if (syncStatus.alreadySynced) {
|
|
91
|
+
console.log('[wecom-api] 通讯录缓存已就绪');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 创建回调处理器
|
|
96
|
+
callbackHandler = createCallbackHandler({
|
|
97
|
+
callbackInstance,
|
|
98
|
+
mode: 'independent',
|
|
99
|
+
alwaysReturnSuccess: true,
|
|
100
|
+
onMessage: (message, info) => {
|
|
101
|
+
const eventType = message.Event || message.MsgType || 'unknown';
|
|
102
|
+
const fromUser = message.FromUserName || 'unknown';
|
|
103
|
+
|
|
104
|
+
if (callbackInstance && callbackInstance._recordEvent) {
|
|
105
|
+
callbackInstance._recordEvent({
|
|
106
|
+
type: eventType,
|
|
107
|
+
fromUserName: fromUser,
|
|
108
|
+
raw: message,
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
mode: info.mode,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 更新客户统计(从事件回调收集)
|
|
115
|
+
if (contactStats && message.Event) {
|
|
116
|
+
contactStats.updateTodayStats(message.Event, {
|
|
117
|
+
userId: message.UserID || message.FromUserName,
|
|
118
|
+
externalUserId: message.ExternalUserID || message.ExternalUserName,
|
|
119
|
+
state: message.State,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// 记录统计日志
|
|
123
|
+
if (['add_external_contact', 'del_external_contact', 'change_external_contact'].includes(message.Event)) {
|
|
124
|
+
console.log(`[wecom-api] 客户统计: ${message.Event} - ${message.UserID || 'unknown'}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 更新通讯录缓存(事件驱动)
|
|
129
|
+
if (addressBookCache && message.Event) {
|
|
130
|
+
switch (message.Event) {
|
|
131
|
+
case 'change_member':
|
|
132
|
+
addressBookCache.onUserUpdate({
|
|
133
|
+
userid: message.UserID,
|
|
134
|
+
name: message.Name,
|
|
135
|
+
// 其他字段
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
case 'change_department':
|
|
139
|
+
addressBookCache.onDepartmentUpdate({
|
|
140
|
+
id: message.Id,
|
|
141
|
+
name: message.Name,
|
|
142
|
+
parentid: message.ParentId,
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`[wecom-api] 事件: ${eventType} from ${fromUser}`);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// 注册 HTTP 路由
|
|
153
|
+
api.registerHttpRoute({
|
|
154
|
+
path: "/plugins/wecom-api/callback",
|
|
155
|
+
handler: callbackHandler,
|
|
156
|
+
auth: "plugin",
|
|
157
|
+
match: "prefix",
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 兼容旧路径
|
|
161
|
+
api.registerHttpRoute({
|
|
162
|
+
path: "/wecom-api/callback",
|
|
163
|
+
handler: callbackHandler,
|
|
164
|
+
auth: "plugin",
|
|
165
|
+
match: "prefix",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
console.log('[wecom-api] 插件注册完成');
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
module.exports = plugin;
|
package/plugin.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw 插件入口 - 企业微信工具
|
|
3
|
+
*
|
|
4
|
+
* 继承 OpenClaw HTTP 服务,提供回调处理和 API 能力
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
9
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
10
|
+
|
|
11
|
+
// 动态导入 CommonJS 模块(避免 ESM/CJS 混用问题)
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { dirname } from 'node:path';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
|
|
19
|
+
// 导入配置加载器
|
|
20
|
+
const { getConfig } = require('./config.js');
|
|
21
|
+
|
|
22
|
+
// 导入回调处理模块
|
|
23
|
+
const Callback = require('./src/modules/callback/index.js');
|
|
24
|
+
|
|
25
|
+
// 插件配置
|
|
26
|
+
interface PluginConfig {
|
|
27
|
+
corpId?: string;
|
|
28
|
+
corpSecret?: string;
|
|
29
|
+
agentId?: string;
|
|
30
|
+
token?: string;
|
|
31
|
+
encodingAESKey?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let callbackInstance: any = null;
|
|
35
|
+
|
|
36
|
+
const plugin = {
|
|
37
|
+
id: "wecom-api",
|
|
38
|
+
name: "WeCom Tool (企业微信工具)",
|
|
39
|
+
description: "企业微信 API 工具集,支持回调处理和 32 个 API 模块",
|
|
40
|
+
configSchema: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: {
|
|
43
|
+
corpId: { type: "string" },
|
|
44
|
+
corpSecret: { type: "string" },
|
|
45
|
+
agentId: { type: "string" },
|
|
46
|
+
token: { type: "string" },
|
|
47
|
+
encodingAESKey: { type: "string" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 注册插件
|
|
53
|
+
*/
|
|
54
|
+
register(api: OpenClawPluginApi) {
|
|
55
|
+
console.log('[wecom-api] 插件注册中...');
|
|
56
|
+
|
|
57
|
+
// 加载配置
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
|
|
60
|
+
// 初始化回调处理实例
|
|
61
|
+
if (config.corpId && config.corpSecret && config.agentId) {
|
|
62
|
+
callbackInstance = new Callback.default ? new Callback.default(config) : new Callback(config);
|
|
63
|
+
console.log('[wecom-api] 回调处理已初始化');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 注册 HTTP 路由 - 回调入口
|
|
67
|
+
api.registerHttpRoute({
|
|
68
|
+
path: "/plugins/wecom-api/callback",
|
|
69
|
+
handler: handleCallbackRequest,
|
|
70
|
+
auth: "none", // 企业微信需要验证签名,不在网关层验证
|
|
71
|
+
match: "prefix",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 兼容旧路径
|
|
75
|
+
api.registerHttpRoute({
|
|
76
|
+
path: "/wecom-api/callback",
|
|
77
|
+
handler: handleCallbackRequest,
|
|
78
|
+
auth: "none",
|
|
79
|
+
match: "prefix",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
console.log('[wecom-api] 插件注册完成');
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 处理企业微信回调请求
|
|
88
|
+
*/
|
|
89
|
+
async function handleCallbackRequest(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
|
|
90
|
+
if (!callbackInstance) {
|
|
91
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
92
|
+
res.end('wecom-api plugin not initialized');
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const url = req.url || '';
|
|
97
|
+
const query = new URLSearchParams(url.split('?')[1] || '');
|
|
98
|
+
|
|
99
|
+
const params = {
|
|
100
|
+
msgSignature: query.get('msg_signature') || query.get('signature') || '',
|
|
101
|
+
timestamp: query.get('timestamp') || '',
|
|
102
|
+
nonce: query.get('nonce') || '',
|
|
103
|
+
echostr: query.get('echostr') || '',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 读取 body
|
|
107
|
+
const chunks: Buffer[] = [];
|
|
108
|
+
for await (const chunk of req) {
|
|
109
|
+
chunks.push(chunk);
|
|
110
|
+
}
|
|
111
|
+
const body = Buffer.concat(chunks).toString();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// 调用回调处理
|
|
115
|
+
const result = await callbackInstance.handle({
|
|
116
|
+
...params,
|
|
117
|
+
xmlBody: body,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (result.type === 'success') {
|
|
121
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
122
|
+
res.end(result.body);
|
|
123
|
+
} else {
|
|
124
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
125
|
+
res.end(result.message || 'error');
|
|
126
|
+
}
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
console.error('[wecom-api] 回调处理失败:', error);
|
|
129
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
130
|
+
res.end('error');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default plugin;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wecom-api
|
|
3
|
+
description: |
|
|
4
|
+
企业微信 API 工具库。激活当用户提到企业微信 API、wecom-api,或需要调用企业微信 API 时。
|
|
5
|
+
同时支持交互式配置:说"配置企业微信"或"设置wecom-api"来启动配置向导。
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# WeCom Tool
|
|
9
|
+
|
|
10
|
+
企业微信 API 封装,提供 30+ 个模块的接口调用能力。
|
|
11
|
+
|
|
12
|
+
## 交互式配置
|
|
13
|
+
|
|
14
|
+
当用户说"配置企业微信"、"设置wecom"或"wecom配置"时,触发配置向导:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
配置企业微信
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
配置向导会引导用户输入:
|
|
21
|
+
1. 企业ID (corpId)
|
|
22
|
+
2. 应用Secret (corpSecret)
|
|
23
|
+
3. 应用AgentID (agentId)
|
|
24
|
+
4. 回调Token(可选)
|
|
25
|
+
5. 回调EncodingAESKey(可选)
|
|
26
|
+
|
|
27
|
+
配置完成后会自动保存到 config.json。
|
|
28
|
+
|
|
29
|
+
## 使用示例
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# 测试连接
|
|
33
|
+
/skill wecom-api test_connection
|
|
34
|
+
|
|
35
|
+
# 发送消息
|
|
36
|
+
/skill wecom-api send_message --userId user001 --content "Hello"
|
|
37
|
+
|
|
38
|
+
# 获取客户列表
|
|
39
|
+
/skill wecom-api get_customer_list --userId user001
|
|
40
|
+
```
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 OpenClaw Skill 入口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
// 插件根目录:skills/wecom-api 上两级
|
|
9
|
+
const pluginRoot = path.join(__dirname, '..', '..');
|
|
10
|
+
const WeComPlugin = require(path.join(pluginRoot, 'src/index'));
|
|
11
|
+
const CONFIG_PATH = path.join(pluginRoot, 'config.json');
|
|
12
|
+
|
|
13
|
+
// 缓存插件实例
|
|
14
|
+
let wecomInstance = null;
|
|
15
|
+
|
|
16
|
+
// 配置状态(用于交互式配置)
|
|
17
|
+
let configState = {
|
|
18
|
+
step: 0, // 0: 未开始, 1: corpId, 2: corpSecret, 3: agentId, 4: token, 5: encodingAESKey, 6: 完成
|
|
19
|
+
pending: {}, // 临时存储配置
|
|
20
|
+
channel: null // 配置来源渠道
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 获取插件实例
|
|
25
|
+
*/
|
|
26
|
+
function getWecom(config = null) {
|
|
27
|
+
const userConfig = config || loadConfig();
|
|
28
|
+
if (!wecomInstance || wecomInstance.config.corpId !== userConfig.corpId) {
|
|
29
|
+
wecomInstance = new WeComPlugin(userConfig);
|
|
30
|
+
}
|
|
31
|
+
return wecomInstance;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 加载配置文件
|
|
36
|
+
*/
|
|
37
|
+
function loadConfig() {
|
|
38
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 保存配置文件
|
|
50
|
+
*/
|
|
51
|
+
function saveConfig(config) {
|
|
52
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 开始交互式配置
|
|
57
|
+
*/
|
|
58
|
+
function startConfig(channel = 'cli') {
|
|
59
|
+
configState = {
|
|
60
|
+
step: 1,
|
|
61
|
+
pending: {},
|
|
62
|
+
channel
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
message: '📝 **企业微信配置向导**\n\n请输入您的 **企业ID (corpId)**:\n\n获取方式:企业微信后台 → 我的企业 → 企业信息 → 企业ID',
|
|
67
|
+
action: 'await_input'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 处理配置输入
|
|
73
|
+
*/
|
|
74
|
+
function handleConfigInput(input, channel = 'cli') {
|
|
75
|
+
const trimmed = input.trim();
|
|
76
|
+
|
|
77
|
+
switch (configState.step) {
|
|
78
|
+
case 1: // corpId
|
|
79
|
+
if (!trimmed) {
|
|
80
|
+
return { message: '❌ 企业ID不能为空,请重新输入:' };
|
|
81
|
+
}
|
|
82
|
+
configState.pending.corpId = trimmed;
|
|
83
|
+
configState.step = 2;
|
|
84
|
+
return {
|
|
85
|
+
message: '✅ 企业ID已保存\n\n请输入您的 **应用Secret (corpSecret)**:\n\n获取方式:企业微信后台 → 应用管理 → 自建应用 → 查看详情 → Secret'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
case 2: // corpSecret
|
|
89
|
+
if (!trimmed) {
|
|
90
|
+
return { message: '❌ Secret不能为空,请重新输入:' };
|
|
91
|
+
}
|
|
92
|
+
configState.pending.corpSecret = trimmed;
|
|
93
|
+
configState.step = 3;
|
|
94
|
+
return {
|
|
95
|
+
message: '✅ Secret已保存\n\n请输入您的 **应用AgentID**:\n\n获取方式:企业微信后台 → 应用管理 → 自建应用 → 查看详情 → AgentId'
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
case 3: // agentId
|
|
99
|
+
if (!trimmed) {
|
|
100
|
+
return { message: '❌ AgentID不能为空,请重新输入:' };
|
|
101
|
+
}
|
|
102
|
+
configState.pending.agentId = trimmed;
|
|
103
|
+
configState.step = 4;
|
|
104
|
+
return {
|
|
105
|
+
message: '✅ AgentID已保存\n\n请输入您的 **回调Token**(可选,直接回车跳过):\n\n用于接收企业微信回调事件,如不需要回调功能可跳过'
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
case 4: // token
|
|
109
|
+
configState.pending.token = trimmed || '';
|
|
110
|
+
configState.step = 5;
|
|
111
|
+
return {
|
|
112
|
+
message: '✅ Token已保存\n\n请输入您的 **回调EncodingAESKey**(可选,直接回车跳过):\n\n回调消息加解密密钥,如不需要回调功能可跳过'
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
case 5: // encodingAESKey
|
|
116
|
+
configState.pending.encodingAESKey = trimmed || '';
|
|
117
|
+
configState.step = 6;
|
|
118
|
+
|
|
119
|
+
// 保存配置
|
|
120
|
+
const finalConfig = {
|
|
121
|
+
...loadConfig(),
|
|
122
|
+
...configState.pending
|
|
123
|
+
};
|
|
124
|
+
saveConfig(finalConfig);
|
|
125
|
+
|
|
126
|
+
// 重置实例
|
|
127
|
+
wecomInstance = null;
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
message: `✅ 配置完成!\n\n已保存配置:\n- 企业ID: ${finalConfig.corpId}\n- AgentID: ${finalConfig.agentId}\n- Token: ${finalConfig.token ? '已设置' : '未设置'}\n\n现在可以测试连接:/skill wecom test_connection`
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
default:
|
|
134
|
+
return { message: '❌ 配置已结束,请重新开始:/skill wecom config' };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 显示当前配置状态
|
|
140
|
+
*/
|
|
141
|
+
function showConfig() {
|
|
142
|
+
const config = loadConfig();
|
|
143
|
+
|
|
144
|
+
if (!config.corpId || config.corpId === 'YOUR_CORP_ID') {
|
|
145
|
+
return {
|
|
146
|
+
message: '⚠️ 企业微信尚未配置\n\n请运行:/skill wecom config\n或说"配置企业微信"开始配置'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
message: `📋 **当前配置**\n\n- 企业ID: ${config.corpId}\n- AgentID: ${config.agentId}\n- Token: ${config.token ? '✅ 已设置' : '❌ 未设置'}\n- EncodingAESKey: ${config.encodingAESKey ? '✅ 已设置' : '❌ 未设置'}\n\n如需修改配置,请运行:/skill wecom config`
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 测试连接
|
|
157
|
+
*/
|
|
158
|
+
async function testConnection() {
|
|
159
|
+
try {
|
|
160
|
+
const wecom = getWecom();
|
|
161
|
+
const result = await wecom.testConnection();
|
|
162
|
+
|
|
163
|
+
if (result.success) {
|
|
164
|
+
return {
|
|
165
|
+
message: `✅ **连接成功!**\n\n- 企业ID: ${wecom.config.corpId}\n- Token: ${result.token.substring(0, 20)}...`
|
|
166
|
+
};
|
|
167
|
+
} else {
|
|
168
|
+
return {
|
|
169
|
+
message: `❌ **连接失败**\n\n${result.message}\n\n请检查配置是否正确,或重新配置:/skill wecom config`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {
|
|
173
|
+
return {
|
|
174
|
+
message: `❌ **错误**\n\n${e.message}\n\n请先配置:/skill wecom config`
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Skill 主入口
|
|
181
|
+
*/
|
|
182
|
+
async function wecomSkill(params) {
|
|
183
|
+
const { action, text, _channel } = params;
|
|
184
|
+
|
|
185
|
+
// 检测是否触发配置
|
|
186
|
+
const configKeywords = ['config', '配置', '设置', 'setup'];
|
|
187
|
+
const isConfigRequest = configKeywords.some(kw =>
|
|
188
|
+
(action && action.toLowerCase().includes(kw)) ||
|
|
189
|
+
(text && text.toLowerCase().includes(kw))
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// 配置模式
|
|
193
|
+
if (isConfigRequest || configState.step > 0) {
|
|
194
|
+
// 如果有文本输入,处理配置
|
|
195
|
+
if (text && configState.step > 0) {
|
|
196
|
+
return handleConfigInput(text, _channel);
|
|
197
|
+
}
|
|
198
|
+
// 开始新配置
|
|
199
|
+
if (configState.step === 0 || configState.step === 6) {
|
|
200
|
+
return startConfig(_channel);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 查看配置
|
|
205
|
+
if (action === 'show_config' || action === 'status') {
|
|
206
|
+
return showConfig();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 测试连接
|
|
210
|
+
if (action === 'test_connection') {
|
|
211
|
+
return await testConnection();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 其他操作需要先配置
|
|
215
|
+
const config = loadConfig();
|
|
216
|
+
if (!config.corpId || config.corpId === 'YOUR_CORP_ID') {
|
|
217
|
+
return {
|
|
218
|
+
message: '⚠️ 请先配置企业微信\n\n运行:/skill wecom config\n或说"配置企业微信"开始配置'
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 执行具体操作
|
|
223
|
+
return await executeAction(action, params);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 执行具体操作
|
|
228
|
+
*/
|
|
229
|
+
async function executeAction(action, params) {
|
|
230
|
+
const wecom = getWecom();
|
|
231
|
+
const args = { ...params };
|
|
232
|
+
delete args.action;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
switch (action) {
|
|
236
|
+
case 'send_message':
|
|
237
|
+
return await wecom.message.sendText(args.userId, args.content, args.agentId);
|
|
238
|
+
|
|
239
|
+
case 'get_customer_list':
|
|
240
|
+
return await wecom.contact.getCustomerList(args.userId);
|
|
241
|
+
|
|
242
|
+
case 'get_customer_detail':
|
|
243
|
+
return await wecom.contact.getCustomerDetail(args.userId, args.externalUserId);
|
|
244
|
+
|
|
245
|
+
case 'get_approval_list':
|
|
246
|
+
return await wecom.approval.getApprovalIds(args.startTime, args.endTime);
|
|
247
|
+
|
|
248
|
+
case 'get_approval_detail':
|
|
249
|
+
return await wecom.approval.getApprovalDetail(args.spNo);
|
|
250
|
+
|
|
251
|
+
case 'create_meeting':
|
|
252
|
+
return await wecom.meeting.createMeeting({
|
|
253
|
+
topic: args.topic,
|
|
254
|
+
startTime: args.startTime,
|
|
255
|
+
endTime: args.endTime,
|
|
256
|
+
organizers: [args.userId]
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
case 'get_user_list':
|
|
260
|
+
return await wecom.addressbook.getDepartmentUsers(args.departmentId || 1, args.fetchChild || false);
|
|
261
|
+
|
|
262
|
+
case 'get_department_list':
|
|
263
|
+
return await wecom.addressbook.getDepartmentList(args.departmentId);
|
|
264
|
+
|
|
265
|
+
case 'get_checkin_records':
|
|
266
|
+
return await wecom.checkin.getCheckInRecords(args.startTime, args.endTime, args.userId);
|
|
267
|
+
|
|
268
|
+
case 'get_corp_tags':
|
|
269
|
+
return await wecom.contact.getCorpTags();
|
|
270
|
+
|
|
271
|
+
case 'get_groupchat_list':
|
|
272
|
+
return await wecom.contact.getGroupChatList(args.cursor, args.size);
|
|
273
|
+
|
|
274
|
+
case 'get_agent_list':
|
|
275
|
+
return await wecom.app.getAgentList();
|
|
276
|
+
|
|
277
|
+
case 'get_token':
|
|
278
|
+
return await wecom.approval.getAccessToken();
|
|
279
|
+
|
|
280
|
+
default:
|
|
281
|
+
return { message: `未知的操作: ${action}` };
|
|
282
|
+
}
|
|
283
|
+
} catch (e) {
|
|
284
|
+
return { message: `❌ 执行失败: ${e.message}` };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = { wecomSkill };
|