@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
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 客户统计模块
|
|
3
|
+
* API 章节:十三 - 统计管理
|
|
4
|
+
* 包含:联系客户统计、群聊数据统计
|
|
5
|
+
*
|
|
6
|
+
* =====================================================
|
|
7
|
+
* ⚠️ 企业微信官方强制规范 (2026-03-21)
|
|
8
|
+
* =====================================================
|
|
9
|
+
*
|
|
10
|
+
* 1. 【必须逐员工调用】
|
|
11
|
+
* 联系客户统计接口不支持批量,必须按 userid 逐个请求
|
|
12
|
+
*
|
|
13
|
+
* 2. 【必须先检查本地员工数据】
|
|
14
|
+
* 无员工数据时,禁止直接调用 API
|
|
15
|
+
*
|
|
16
|
+
* 3. 【无员工必须提示】
|
|
17
|
+
* 提示语:请先同步企业微信员工数据
|
|
18
|
+
*
|
|
19
|
+
* =====================================================
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const WeComSDK = require('../../sdk');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
class ContactStats extends WeComSDK {
|
|
27
|
+
constructor(config) {
|
|
28
|
+
super(config);
|
|
29
|
+
this._storagePath = path.join(process.cwd(), 'data', 'wecom-stats.json');
|
|
30
|
+
this._todayStats = this._loadStats();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ====================
|
|
34
|
+
// 持久化存储
|
|
35
|
+
// ====================
|
|
36
|
+
|
|
37
|
+
_loadStats() {
|
|
38
|
+
const today = new Date().toISOString().split('T')[0];
|
|
39
|
+
try {
|
|
40
|
+
if (fs.existsSync(this._storagePath)) {
|
|
41
|
+
const data = JSON.parse(fs.readFileSync(this._storagePath, 'utf8'));
|
|
42
|
+
if (data.date === today) {
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
return this._createEmptyStats(today);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_createEmptyStats(date) {
|
|
51
|
+
return {
|
|
52
|
+
date,
|
|
53
|
+
newCustomers: 0,
|
|
54
|
+
lostCustomers: 0,
|
|
55
|
+
messagesSent: 0,
|
|
56
|
+
chatsCount: 0,
|
|
57
|
+
applications: 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_saveStats() {
|
|
62
|
+
try {
|
|
63
|
+
const dir = path.dirname(this._storagePath);
|
|
64
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
65
|
+
fs.writeFileSync(this._storagePath, JSON.stringify(this._todayStats, null, 2));
|
|
66
|
+
} catch (e) {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ====================
|
|
70
|
+
// 事件回调更新
|
|
71
|
+
// ====================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 从事件回调更新当日统计数据
|
|
75
|
+
* @param {string} eventType 事件类型
|
|
76
|
+
* @param {object} eventData 事件数据
|
|
77
|
+
*/
|
|
78
|
+
updateTodayStats(eventType, eventData) {
|
|
79
|
+
const today = new Date().toISOString().split('T')[0];
|
|
80
|
+
if (this._todayStats.date !== today) {
|
|
81
|
+
this._todayStats = this._createEmptyStats(today);
|
|
82
|
+
this._saveStats();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
switch (eventType) {
|
|
86
|
+
case 'add_external_contact':
|
|
87
|
+
this._todayStats.newCustomers++;
|
|
88
|
+
break;
|
|
89
|
+
case 'del_external_contact':
|
|
90
|
+
this._todayStats.lostCustomers++;
|
|
91
|
+
break;
|
|
92
|
+
case 'change_external_contact':
|
|
93
|
+
if (eventData.state === 'unsubscribe') {
|
|
94
|
+
this._todayStats.lostCustomers++;
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
case 'new_msg':
|
|
98
|
+
case 'msgsend':
|
|
99
|
+
case 'kf_msg':
|
|
100
|
+
case 'kf_send_msg':
|
|
101
|
+
this._todayStats.messagesSent++;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
this._saveStats();
|
|
105
|
+
return this._todayStats;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 获取当日统计数据
|
|
110
|
+
*/
|
|
111
|
+
getTodayStats() {
|
|
112
|
+
const today = new Date().toISOString().split('T')[0];
|
|
113
|
+
if (this._todayStats.date !== today) {
|
|
114
|
+
this._todayStats = this._createEmptyStats(today);
|
|
115
|
+
}
|
|
116
|
+
return { ...this._todayStats };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ====================
|
|
120
|
+
// 【核心】获取联系客户统计 - 企业微信官方规范
|
|
121
|
+
// ====================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 获取单个员工的联系客户统计
|
|
125
|
+
*
|
|
126
|
+
* ⚠️ 企业微信官方强制规范:
|
|
127
|
+
* 1. 必须逐员工调用
|
|
128
|
+
* 2. 必须先检查本地员工数据是否存在
|
|
129
|
+
* 3. 无员工数据时必须提示先同步
|
|
130
|
+
*
|
|
131
|
+
* @param {string} userId 成员ID
|
|
132
|
+
* @param {string} startDate 开始日期(YYYYMMDD格式)
|
|
133
|
+
* @param {string} endDate 结束日期(YYYYMMDD格式)
|
|
134
|
+
* @param {object} addressBookCache 通讯录缓存(用于验证员工是否存在)
|
|
135
|
+
* @returns {Promise<object>}
|
|
136
|
+
*/
|
|
137
|
+
async getUserClientStat(userId, startDate, endDate, addressBookCache = null) {
|
|
138
|
+
// 1. 先查本地是否有该员工信息
|
|
139
|
+
if (addressBookCache) {
|
|
140
|
+
const user = addressBookCache.getUser(userId);
|
|
141
|
+
if (!user) {
|
|
142
|
+
return {
|
|
143
|
+
error: true,
|
|
144
|
+
code: 'USER_NOT_FOUND',
|
|
145
|
+
message: '请先同步企业微信员工数据',
|
|
146
|
+
tip: '联系客户统计接口必须按员工调用,请先执行:同步组织架构'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 2. 有员工 → 逐员工调用 API(注意:企业微信用 userid 而非 userid_list)
|
|
152
|
+
return this.post('/externalcontact/get_user_behavior_data', {
|
|
153
|
+
userid: [userId],
|
|
154
|
+
start_time: startDate,
|
|
155
|
+
end_time: endDate
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 批量获取联系客户统计(逐员工调用)
|
|
161
|
+
*
|
|
162
|
+
* ⚠️ 企业微信官方强制规范:必须逐员工调用
|
|
163
|
+
*
|
|
164
|
+
* @param {string[]} userIds 成员ID列表
|
|
165
|
+
* @param {string} startDate 开始日期(YYYYMMDD格式)
|
|
166
|
+
* @param {string} endDate 结束日期(YYYYMMDD格式)
|
|
167
|
+
* @param {object} addressBookCache 通讯录缓存
|
|
168
|
+
* @returns {Promise<object>} { success: [], failed: [], notFound: [] }
|
|
169
|
+
*/
|
|
170
|
+
async getUserClientStatByList(userIds, startDate, endDate, addressBookCache = null) {
|
|
171
|
+
const results = { success: [], failed: [], notFound: [] };
|
|
172
|
+
|
|
173
|
+
for (const userId of userIds) {
|
|
174
|
+
// 1. 先查本地是否有该员工
|
|
175
|
+
if (addressBookCache) {
|
|
176
|
+
const user = addressBookCache.getUser(userId);
|
|
177
|
+
if (!user) {
|
|
178
|
+
results.notFound.push({ userId, error: '员工不存在,请先同步组织架构' });
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 2. 有员工 → 逐员工调用 API(注意:企业微信用 userid 而非 userid_list)
|
|
184
|
+
try {
|
|
185
|
+
const result = await this.post('/externalcontact/get_user_behavior_data', {
|
|
186
|
+
userid: [userId],
|
|
187
|
+
start_time: startDate,
|
|
188
|
+
end_time: endDate
|
|
189
|
+
});
|
|
190
|
+
results.success.push({ userId, result });
|
|
191
|
+
} catch (error) {
|
|
192
|
+
results.failed.push({ userId, error: error.message });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return results;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 获取全员工联系客户统计
|
|
201
|
+
*
|
|
202
|
+
* ⚠️ 企业微信官方强制规范:必须逐员工调用
|
|
203
|
+
*
|
|
204
|
+
* @param {string} startDate 开始日期(YYYYMMDD格式)
|
|
205
|
+
* @param {string} endDate 结束日期(YYYYMMDD格式)
|
|
206
|
+
* @param {object} addressBookCache 通讯录缓存
|
|
207
|
+
* @returns {Promise<object>}
|
|
208
|
+
*/
|
|
209
|
+
async getAllUserClientStat(startDate, endDate, addressBookCache = null) {
|
|
210
|
+
if (!addressBookCache) {
|
|
211
|
+
return {
|
|
212
|
+
error: true,
|
|
213
|
+
code: 'CACHE_NOT_INITIALIZED',
|
|
214
|
+
message: '请先初始化通讯录缓存',
|
|
215
|
+
tip: '调用 syncFromAPI() 同步组织架构'
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const users = addressBookCache.getUsers();
|
|
220
|
+
const userIds = users.map(u => u.userid);
|
|
221
|
+
const result = await this.getUserClientStatByList(userIds, startDate, endDate, addressBookCache);
|
|
222
|
+
|
|
223
|
+
// 添加汇总数据
|
|
224
|
+
result.summary = {
|
|
225
|
+
totalUsers: userIds.length,
|
|
226
|
+
successCount: result.success.length,
|
|
227
|
+
failedCount: result.failed.length,
|
|
228
|
+
notFoundCount: result.notFound?.length || 0,
|
|
229
|
+
totalNewContact: 0,
|
|
230
|
+
totalNewApply: 0,
|
|
231
|
+
totalChat: 0,
|
|
232
|
+
totalMessage: 0,
|
|
233
|
+
totalNegative: 0
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// 累加统计数据
|
|
237
|
+
for (const item of result.success) {
|
|
238
|
+
if (item.result?.behavior_data?.length > 0) {
|
|
239
|
+
const d = item.result.behavior_data[0];
|
|
240
|
+
result.summary.totalNewContact += d.new_contact_cnt || 0;
|
|
241
|
+
result.summary.totalNewApply += d.new_apply_cnt || 0;
|
|
242
|
+
result.summary.totalChat += d.chat_cnt || 0;
|
|
243
|
+
result.summary.totalMessage += d.message_cnt || 0;
|
|
244
|
+
result.summary.totalNegative += d.negative_feedback_cnt || 0;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 智能获取联系客户统计(自动处理今天/历史)
|
|
253
|
+
*
|
|
254
|
+
* @param {string} startDate 开始日期(YYYYMMDD格式)
|
|
255
|
+
* @param {string} endDate 结束日期(YYYYMMDD格式)
|
|
256
|
+
* @param {object} addressBookCache 通讯录缓存
|
|
257
|
+
* @returns {Promise<object>}
|
|
258
|
+
*/
|
|
259
|
+
async getUserClientStatSmart(startDate, endDate, addressBookCache = null) {
|
|
260
|
+
const today = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
261
|
+
const todayNum = parseInt(today);
|
|
262
|
+
const startNum = parseInt(String(startDate));
|
|
263
|
+
const endNum = parseInt(String(endDate));
|
|
264
|
+
|
|
265
|
+
const includesToday = endNum >= todayNum;
|
|
266
|
+
|
|
267
|
+
const result = {
|
|
268
|
+
isTodayIncluded: includesToday,
|
|
269
|
+
startDate: String(startDate),
|
|
270
|
+
endDate: String(endDate),
|
|
271
|
+
errors: [],
|
|
272
|
+
data: {}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
if (includesToday) {
|
|
276
|
+
const yesterday = new Date();
|
|
277
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
278
|
+
const yesterdayStr = yesterday.toISOString().split('T')[0].replace(/-/g, '');
|
|
279
|
+
|
|
280
|
+
if (startNum < parseInt(yesterdayStr)) {
|
|
281
|
+
try {
|
|
282
|
+
result.data.historical = await this.getAllUserClientStat(startDate, yesterdayStr, addressBookCache);
|
|
283
|
+
} catch (e) {
|
|
284
|
+
result.errors.push({ type: 'historical', message: e.message });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
result.data.today = this.getTodayStats();
|
|
288
|
+
} else {
|
|
289
|
+
try {
|
|
290
|
+
result.data.historical = await this.getAllUserClientStat(startDate, endDate, addressBookCache);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
result.errors.push({ type: 'historical', message: e.message });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ====================
|
|
300
|
+
// 其他统计接口
|
|
301
|
+
// ====================
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 获取成员联系客户统计明细
|
|
305
|
+
*/
|
|
306
|
+
async getUserClientDetail(userId, startDate, endDate) {
|
|
307
|
+
return this.post('/externalcontact/get_user_client_detail', {
|
|
308
|
+
userid: userId,
|
|
309
|
+
start_time: String(startDate),
|
|
310
|
+
end_time: String(endDate)
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* 获取群聊数据统计
|
|
316
|
+
*/
|
|
317
|
+
async getGroupChatStat(startDate, endDate, userId = '', departmentId = '') {
|
|
318
|
+
return this.post('/externalcontact/get_group_chat_data', {
|
|
319
|
+
start_time: String(startDate),
|
|
320
|
+
end_time: String(endDate),
|
|
321
|
+
userid: userId,
|
|
322
|
+
department_id: departmentId
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 获取群聊统计详情
|
|
328
|
+
*/
|
|
329
|
+
async getGroupChatDetail(chatId, startDate, endDate) {
|
|
330
|
+
return this.post('/externalcontact/get_group_chat_detail', {
|
|
331
|
+
chat_id: chatId,
|
|
332
|
+
start_time: String(startDate),
|
|
333
|
+
end_time: String(endDate)
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 获取客户流失统计
|
|
339
|
+
*/
|
|
340
|
+
async getUserLostStat(startDate, endDate, userId = '') {
|
|
341
|
+
return this.post('/externalcontact/get_user_lost_data', {
|
|
342
|
+
start_time: String(startDate),
|
|
343
|
+
end_time: String(endDate),
|
|
344
|
+
userid: userId
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
module.exports = ContactStats;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微信客服模块
|
|
3
|
+
* API 章节:十四
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const WeComSDK = require('../../sdk');
|
|
7
|
+
|
|
8
|
+
class Custom extends WeComSDK {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super(config);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ========== 客服账号管理 ==========
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 添加客服账号
|
|
17
|
+
* @param {string} name 客服名称
|
|
18
|
+
* @param {string} mediaId 客服头像 media_id
|
|
19
|
+
*/
|
|
20
|
+
async addCustomAccount(name, mediaId) {
|
|
21
|
+
return this.post('/customservice/add_kf_account', {
|
|
22
|
+
name,
|
|
23
|
+
media_id: mediaId
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 删除客服账号
|
|
29
|
+
* @param {string} account 客服账号 (格式: openid@corpid)
|
|
30
|
+
*/
|
|
31
|
+
async deleteCustomAccount(account) {
|
|
32
|
+
return this.post('/customservice/del_kf_account', { kf_account: account });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 修改客服账号
|
|
37
|
+
* @param {string} account 客服账号
|
|
38
|
+
* @param {string} name 客服名称
|
|
39
|
+
* @param {string} mediaId 客服头像 media_id
|
|
40
|
+
*/
|
|
41
|
+
async updateCustomAccount(account, name, mediaId) {
|
|
42
|
+
return this.post('/customservice/update_kf_account', {
|
|
43
|
+
kf_account: account,
|
|
44
|
+
name,
|
|
45
|
+
media_id: mediaId
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 获取客服账号列表
|
|
51
|
+
*/
|
|
52
|
+
async getCustomAccountList() {
|
|
53
|
+
return this.post('/customservice/get_kf_list', {});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 获取客服账号链接
|
|
58
|
+
* @param {string} account 客服账号
|
|
59
|
+
* @param {string} openId 客户 openid
|
|
60
|
+
*/
|
|
61
|
+
async getCustomAccountLink(account, openId) {
|
|
62
|
+
return this.post('/customservice/get_kf_link', {
|
|
63
|
+
kf_account: account,
|
|
64
|
+
openid: openId
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ========== 接待人员管理 ==========
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 添加接待人员
|
|
72
|
+
* @param {string} userId 成员 userid
|
|
73
|
+
*/
|
|
74
|
+
async addServicer(userId) {
|
|
75
|
+
return this.post('/customservice/add_servicer', { userid: userId });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 删除接待人员
|
|
80
|
+
* @param {string} userId 成员 userid
|
|
81
|
+
*/
|
|
82
|
+
async removeServicer(userId) {
|
|
83
|
+
return this.post('/customservice/del_servicer', { userid: userId });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 获取接待人员列表
|
|
88
|
+
*/
|
|
89
|
+
async getServicerList() {
|
|
90
|
+
return this.post('/customservice/get_servicer_list', {});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ========== 会话分配 ==========
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 分配客服会话
|
|
97
|
+
* @param {string} openId 客户 openid
|
|
98
|
+
* @param {string} account 客服账号
|
|
99
|
+
* @param {string} acceptAccount 接起会话的客服账号
|
|
100
|
+
*/
|
|
101
|
+
async assignSession(openId, account, acceptAccount) {
|
|
102
|
+
return this.post('/customservice/transfer_customer', {
|
|
103
|
+
openid: openId,
|
|
104
|
+
kf_account: account,
|
|
105
|
+
accept_kf_account: acceptAccount
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ========== 消息收发 ==========
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 发送消息给客户
|
|
113
|
+
* @param {string} openId 客户 openid
|
|
114
|
+
* @param {object} msg 消息内容
|
|
115
|
+
*/
|
|
116
|
+
async sendMessage(openId, msg) {
|
|
117
|
+
return this.post('/customservice/send_msg', {
|
|
118
|
+
touser: openId,
|
|
119
|
+
msgtype: msg.msgType,
|
|
120
|
+
content: msg.content
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 发送欢迎语
|
|
126
|
+
* @param {string} openId 客户 openid
|
|
127
|
+
* @param {string} welcomeCode 欢迎语 code
|
|
128
|
+
* @param {object} msg 消息内容
|
|
129
|
+
*/
|
|
130
|
+
async sendWelcome(openId, welcomeCode, msg) {
|
|
131
|
+
return this.post('/customservice/send_welcome_msg', {
|
|
132
|
+
openid: openId,
|
|
133
|
+
welcome_code: welcomeCode,
|
|
134
|
+
msgtype: msg.msgType,
|
|
135
|
+
content: msg.content
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = Custom;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const WeComSDK = require("../../sdk");
|
|
2
|
+
|
|
3
|
+
class Customer extends WeComSDK {
|
|
4
|
+
/** 获取客户列表(GET方法) */
|
|
5
|
+
async getCustomerList(userid) {
|
|
6
|
+
return this.get("/externalcontact/list", { userid });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** 获取客户详情 */
|
|
10
|
+
async getCustomerDetail(externalUserId) {
|
|
11
|
+
return this.get("/externalcontact/get", { external_userid: externalUserId });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** 批量获取客户详情 */
|
|
15
|
+
async batchGetByUser(userids) {
|
|
16
|
+
return this.post("/externalcontact/batch/get_by_user", { userid_list: userids });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 获取客户群列表 */
|
|
20
|
+
async getGroupChatList(statusFilter, creatorUserid, limit, cursor) {
|
|
21
|
+
return this.post("/externalcontact/groupchat/list", {
|
|
22
|
+
status_filter: statusFilter !== undefined ? statusFilter : 0,
|
|
23
|
+
creator_userid: creatorUserid || "",
|
|
24
|
+
limit: limit || 100,
|
|
25
|
+
cursor: cursor || ""
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 获取客户群详情 */
|
|
30
|
+
async getGroupChat(chatId) {
|
|
31
|
+
return this.post("/externalcontact/groupchat/get", { chat_id: chatId });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 获取企业标签列表 */
|
|
35
|
+
async getCorpTagList() {
|
|
36
|
+
return this.get("/externalcontact/get_corp_tag_list", {});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 设置客户标签(需同时传userid和external_userid) */
|
|
40
|
+
async markTag(externalUserId, tagIds, userid, operation) {
|
|
41
|
+
const op = operation || "add";
|
|
42
|
+
return this.post("/externalcontact/mark_tag", {
|
|
43
|
+
userid: userid,
|
|
44
|
+
external_userid: externalUserId,
|
|
45
|
+
add_tag: op === "add" ? tagIds : [],
|
|
46
|
+
remove_tag: op === "remove" ? tagIds : []
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = Customer;
|