wechaty-web-panel 1.6.112 → 1.6.113
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/dist/bot/chatgpt/index.js +235 -0
- package/dist/bot/coze/sdk/index.js +110 -0
- package/dist/bot/dify/sdk/index.js +461 -0
- package/dist/bot/dify/sdk/office.js +319 -0
- package/dist/bot/fastgpt/index.js +98 -0
- package/dist/bot/qanything/index.js +136 -0
- package/dist/botInstance/coze.js +167 -0
- package/dist/botInstance/cozev3.js +157 -0
- package/dist/botInstance/dify.js +160 -0
- package/dist/botInstance/fastgpt.js +130 -0
- package/dist/botInstance/gpt4v.js +95 -0
- package/dist/botInstance/officialOpenAi.js +186 -0
- package/dist/botInstance/qany.js +144 -0
- package/dist/botInstance/sdk/chatGPT4V.js +89 -0
- package/dist/botInstance/sdk/coze.js +200 -0
- package/dist/botInstance/sdk/difyClient.js +354 -0
- package/dist/botInstance/sdk/pTimeout.js +97 -0
- package/dist/botInstance/sdk/qanything.js +137 -0
- package/dist/botInstance/sdk/quick-lru.js +237 -0
- package/dist/common/hook.js +66 -0
- package/dist/common/index.js +513 -0
- package/dist/common/multiReply.js +158 -0
- package/dist/common/reply.js +23 -0
- package/dist/const/puppet-type.js +71 -0
- package/dist/db/aiDb.js +27 -0
- package/dist/db/aichatDb.js +84 -0
- package/dist/db/chatHistory.js +137 -0
- package/dist/db/configDb.js +97 -0
- package/dist/db/global.js +62 -0
- package/dist/db/gptConfig.js +85 -0
- package/dist/db/nedb.js +88 -0
- package/dist/db/puppetDb.js +58 -0
- package/dist/db/roomDb.js +83 -0
- package/dist/db/rssConfig.js +82 -0
- package/dist/db/rssHistory.js +88 -0
- package/dist/db/userDb.js +27 -0
- package/dist/handlers/on-callback-message.js +183 -0
- package/dist/handlers/on-error.js +5 -0
- package/dist/handlers/on-friend.js +62 -0
- package/dist/handlers/on-heartbeat.js +20 -0
- package/dist/handlers/on-login.js +58 -0
- package/dist/handlers/on-logout.js +17 -0
- package/dist/handlers/on-message.js +644 -0
- package/dist/handlers/on-ready.js +36 -0
- package/dist/handlers/on-record-message.js +56 -0
- package/dist/handlers/on-roomjoin.js +42 -0
- package/dist/handlers/on-roomleave.js +12 -0
- package/dist/handlers/on-roomtopic.js +16 -0
- package/dist/handlers/on-scan.js +64 -0
- package/dist/handlers/on-verifycode.js +42 -0
- package/dist/index.js +81 -69306
- package/dist/lib/contentCensor.js +23 -0
- package/dist/lib/index.js +562 -0
- package/dist/lib/oss.js +43 -0
- package/dist/lib/s3oss.js +33 -0
- package/dist/mcp/mcp-server.js +26 -0
- package/dist/mcp/src/config/database.js +51 -0
- package/dist/mcp/src/index.js +238 -0
- package/dist/mcp/src/mcp/schemas.js +178 -0
- package/dist/mcp/src/mcp/server.js +421 -0
- package/dist/mcp/src/mcp/streamable-server.js +690 -0
- package/dist/mcp/src/models/ChatMessage.js +151 -0
- package/dist/mcp/src/models/Friend.js +64 -0
- package/dist/mcp/src/models/Group.js +55 -0
- package/dist/mcp/src/models/GroupMember.js +67 -0
- package/dist/mcp/src/models/index.js +27 -0
- package/dist/mcp/src/scripts/migrate.js +21 -0
- package/dist/mcp/src/services/ChatDataService.js +284 -0
- package/dist/mcp/src/services/McpService.js +521 -0
- package/dist/mcp/src/services/McpTools.js +504 -0
- package/dist/mcp/streamable-examples.js +283 -0
- package/dist/mcp/streamable-server.js +79 -0
- package/dist/mcp/test-mcp.js +64 -0
- package/dist/mcp/test-streamable-server.js +86 -0
- package/dist/package-json.js +89 -0
- package/dist/proxy/aibotk.js +829 -0
- package/dist/proxy/api.js +431 -0
- package/dist/proxy/apib.js +587 -0
- package/dist/proxy/bot/chatgpt.js +38 -0
- package/dist/proxy/bot/coze.js +38 -0
- package/dist/proxy/bot/cozev3.js +38 -0
- package/dist/proxy/bot/dify.js +38 -0
- package/dist/proxy/bot/dispatch.js +81 -0
- package/dist/proxy/bot/fastgpt.js +27 -0
- package/dist/proxy/bot/qany.js +27 -0
- package/dist/proxy/config.js +14 -0
- package/dist/proxy/cozeAi.js +60 -0
- package/dist/proxy/cozeV3Ai.js +60 -0
- package/dist/proxy/difyAi.js +58 -0
- package/dist/proxy/fastgpt.js +55 -0
- package/dist/proxy/mqtt.js +275 -0
- package/dist/proxy/multimodal.js +122 -0
- package/dist/proxy/openAi.js +63 -0
- package/dist/proxy/outapi.js +62 -0
- package/dist/proxy/qAnyAi.js +57 -0
- package/dist/proxy/superagent.js +200 -0
- package/dist/proxy/tencent-open.js +255 -0
- package/dist/service/event-dispatch-service.js +309 -0
- package/dist/service/gpt4vService.js +45 -0
- package/dist/service/msg-filter-service.js +121 -0
- package/dist/service/msg-filters.js +645 -0
- package/dist/service/room-async-service.js +455 -0
- package/dist/task/index.js +535 -0
- package/dist/task/rss.js +174 -0
- package/package.json +2 -2
- package/src/package-json.js +2 -2
- package/tsconfig.json +3 -12
- package/dist/index.d.ts +0 -9
- package/tsconfig.cjs.json +0 -12
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import McpService from './McpService.js';
|
|
2
|
+
import { Friend, Group, GroupMember, ChatMessage } from '../models/index.js';
|
|
3
|
+
import { Op } from 'sequelize';
|
|
4
|
+
/**
|
|
5
|
+
* MCP 工具集 - 提供便捷的查询和分析工具
|
|
6
|
+
*/
|
|
7
|
+
class McpTools {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.mcpService = new McpService();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 快速查找工具 - 根据部分信息快速定位用户
|
|
13
|
+
* @param {Object} params - 查询参数
|
|
14
|
+
* @returns {Promise<Object>} 查找结果
|
|
15
|
+
*/
|
|
16
|
+
async quickFind(params = {}) {
|
|
17
|
+
const { robotId, query, type = 'auto' } = params;
|
|
18
|
+
if (!query) {
|
|
19
|
+
throw new Error('查询内容不能为空');
|
|
20
|
+
}
|
|
21
|
+
const results = {
|
|
22
|
+
friends: [],
|
|
23
|
+
groups: [],
|
|
24
|
+
members: [],
|
|
25
|
+
exactMatch: null
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
// 自动检测查询类型
|
|
29
|
+
const isWxId = query.includes('@') || query.startsWith('wxid_');
|
|
30
|
+
const isGroupId = query.includes('@@') || query.includes('chatroom');
|
|
31
|
+
if (type === 'auto' || type === 'friend') {
|
|
32
|
+
// 搜索好友
|
|
33
|
+
const friendSearchParams = {
|
|
34
|
+
robotId,
|
|
35
|
+
keyword: query,
|
|
36
|
+
limit: 10
|
|
37
|
+
};
|
|
38
|
+
const friendResults = await this.mcpService.searchFriends(friendSearchParams);
|
|
39
|
+
results.friends = friendResults.data;
|
|
40
|
+
// 精确匹配检查
|
|
41
|
+
if (isWxId) {
|
|
42
|
+
const exactFriend = await Friend.findOne({
|
|
43
|
+
where: { robotId, wxid: query }
|
|
44
|
+
});
|
|
45
|
+
if (exactFriend)
|
|
46
|
+
results.exactMatch = { type: 'friend', data: exactFriend };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (type === 'auto' || type === 'group') {
|
|
50
|
+
// 搜索群组
|
|
51
|
+
const groupSearchParams = {
|
|
52
|
+
robotId,
|
|
53
|
+
keyword: query,
|
|
54
|
+
includeMembers: true,
|
|
55
|
+
limit: 10
|
|
56
|
+
};
|
|
57
|
+
const groupResults = await this.mcpService.searchGroups(groupSearchParams);
|
|
58
|
+
results.groups = groupResults.data;
|
|
59
|
+
// 精确匹配检查
|
|
60
|
+
if (isGroupId) {
|
|
61
|
+
const exactGroup = await Group.findOne({
|
|
62
|
+
where: { robotId, wxid: query }
|
|
63
|
+
});
|
|
64
|
+
if (exactGroup)
|
|
65
|
+
results.exactMatch = { type: 'group', data: exactGroup };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (type === 'auto' || type === 'member') {
|
|
69
|
+
// 搜索群成员
|
|
70
|
+
const whereClause = { robotId };
|
|
71
|
+
if (query) {
|
|
72
|
+
whereClause[Op.or] = [
|
|
73
|
+
{ name: { [Op.iLike]: `%${query}%` } },
|
|
74
|
+
{ alias: { [Op.iLike]: `%${query}%` } },
|
|
75
|
+
{ remark: { [Op.iLike]: `%${query}%` } },
|
|
76
|
+
{ wxid: query }
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
const members = await GroupMember.findAll({
|
|
80
|
+
where: whereClause,
|
|
81
|
+
limit: 10,
|
|
82
|
+
include: [{
|
|
83
|
+
model: Group,
|
|
84
|
+
as: 'group',
|
|
85
|
+
attributes: ['name', 'wxid']
|
|
86
|
+
}]
|
|
87
|
+
});
|
|
88
|
+
results.members = members;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
query,
|
|
92
|
+
type,
|
|
93
|
+
results,
|
|
94
|
+
summary: {
|
|
95
|
+
totalFound: results.friends.length + results.groups.length + results.members.length,
|
|
96
|
+
hasExactMatch: !!results.exactMatch
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.error('❌ 快速查找失败:', error);
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 关系分析工具 - 分析用户间的关系
|
|
107
|
+
* @param {Object} params - 分析参数
|
|
108
|
+
* @returns {Promise<Object>} 关系分析结果
|
|
109
|
+
*/
|
|
110
|
+
async analyzeRelationships(params = {}) {
|
|
111
|
+
const { robotId, userId, analysisType = 'comprehensive' } = params;
|
|
112
|
+
if (!robotId || !userId) {
|
|
113
|
+
throw new Error('robotId 和 userId 是必需参数');
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const result = {
|
|
117
|
+
userId,
|
|
118
|
+
relationships: {
|
|
119
|
+
directFriend: null,
|
|
120
|
+
commonGroups: [],
|
|
121
|
+
mutualFriends: [],
|
|
122
|
+
conversationHistory: null
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
// 检查是否为直接好友
|
|
126
|
+
const directFriend = await Friend.findOne({
|
|
127
|
+
where: { robotId, wxid: userId }
|
|
128
|
+
});
|
|
129
|
+
result.relationships.directFriend = directFriend;
|
|
130
|
+
// 查找共同群组
|
|
131
|
+
const userGroups = await GroupMember.findAll({
|
|
132
|
+
where: { robotId, wxid: userId },
|
|
133
|
+
include: [{
|
|
134
|
+
model: Group,
|
|
135
|
+
as: 'group'
|
|
136
|
+
}]
|
|
137
|
+
});
|
|
138
|
+
result.relationships.commonGroups = userGroups.map(membership => ({
|
|
139
|
+
group: membership.group,
|
|
140
|
+
memberInfo: {
|
|
141
|
+
name: membership.name,
|
|
142
|
+
alias: membership.alias,
|
|
143
|
+
remark: membership.remark
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
// 分析会话历史
|
|
147
|
+
const conversationStats = await this.mcpService.getConversationDetails({
|
|
148
|
+
robotId,
|
|
149
|
+
conversationId: userId
|
|
150
|
+
});
|
|
151
|
+
if (conversationStats) {
|
|
152
|
+
result.relationships.conversationHistory = {
|
|
153
|
+
totalMessages: conversationStats.statistics.totalMessages,
|
|
154
|
+
firstContact: conversationStats.statistics.firstMessageTime,
|
|
155
|
+
lastContact: conversationStats.statistics.lastMessageTime,
|
|
156
|
+
activityLevel: this._calculateActivityLevel(conversationStats.statistics.totalMessages)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// 如果需要全面分析,查找共同好友
|
|
160
|
+
if (analysisType === 'comprehensive' && userGroups.length > 0) {
|
|
161
|
+
const mutualFriends = await this._findMutualFriends(robotId, userId, userGroups);
|
|
162
|
+
result.relationships.mutualFriends = mutualFriends;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error('❌ 关系分析失败:', error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 活动分析工具 - 分析用户或群组的活动模式
|
|
173
|
+
* @param {Object} params - 分析参数
|
|
174
|
+
* @returns {Promise<Object>} 活动分析结果
|
|
175
|
+
*/
|
|
176
|
+
async analyzeActivity(params = {}) {
|
|
177
|
+
const { robotId, targetId, targetType = 'auto', // auto, user, group
|
|
178
|
+
timeRange = 30, // 天数
|
|
179
|
+
granularity = 'day' } = params;
|
|
180
|
+
if (!robotId || !targetId) {
|
|
181
|
+
throw new Error('robotId 和 targetId 是必需参数');
|
|
182
|
+
}
|
|
183
|
+
const endTime = Date.now();
|
|
184
|
+
const startTime = endTime - (timeRange * 24 * 60 * 60 * 1000);
|
|
185
|
+
try {
|
|
186
|
+
// 自动检测目标类型
|
|
187
|
+
let detectedType = targetType;
|
|
188
|
+
if (targetType === 'auto') {
|
|
189
|
+
if (targetId.includes('@@') || targetId.includes('chatroom')) {
|
|
190
|
+
detectedType = 'group';
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
detectedType = 'user';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// 获取活动热力图
|
|
197
|
+
const heatmapData = await this.mcpService.getMessageHeatmap({
|
|
198
|
+
robotId,
|
|
199
|
+
conversationId: targetId,
|
|
200
|
+
startDate: new Date(startTime),
|
|
201
|
+
endDate: new Date(endTime),
|
|
202
|
+
granularity
|
|
203
|
+
});
|
|
204
|
+
// 获取详细统计
|
|
205
|
+
const messageStats = await this.mcpService.searchChatMessages({
|
|
206
|
+
robotId,
|
|
207
|
+
conversationId: targetId,
|
|
208
|
+
startTime,
|
|
209
|
+
endTime,
|
|
210
|
+
limit: 1000
|
|
211
|
+
});
|
|
212
|
+
// 计算活动指标
|
|
213
|
+
const metrics = this._calculateActivityMetrics(messageStats.data);
|
|
214
|
+
// 活动模式分析
|
|
215
|
+
const patterns = this._analyzeActivityPatterns(heatmapData);
|
|
216
|
+
return {
|
|
217
|
+
targetId,
|
|
218
|
+
targetType: detectedType,
|
|
219
|
+
timeRange: {
|
|
220
|
+
startTime: new Date(startTime),
|
|
221
|
+
endTime: new Date(endTime),
|
|
222
|
+
days: timeRange
|
|
223
|
+
},
|
|
224
|
+
metrics,
|
|
225
|
+
patterns,
|
|
226
|
+
heatmapData,
|
|
227
|
+
summary: {
|
|
228
|
+
totalMessages: messageStats.total,
|
|
229
|
+
averagePerDay: Math.round(messageStats.total / timeRange),
|
|
230
|
+
peakActivity: patterns.peakPeriod,
|
|
231
|
+
activityLevel: metrics.activityLevel
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error('❌ 活动分析失败:', error);
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 内容分析工具 - 分析消息内容和类型
|
|
242
|
+
* @param {Object} params - 分析参数
|
|
243
|
+
* @returns {Promise<Object>} 内容分析结果
|
|
244
|
+
*/
|
|
245
|
+
async analyzeContent(params = {}) {
|
|
246
|
+
const { robotId, conversationId, timeRange = 30, analysisTypes = ['contentType', 'keywords', 'sentiment'] } = params;
|
|
247
|
+
const endTime = Date.now();
|
|
248
|
+
const startTime = endTime - (timeRange * 24 * 60 * 60 * 1000);
|
|
249
|
+
try {
|
|
250
|
+
const messages = await this.mcpService.searchChatMessages({
|
|
251
|
+
robotId,
|
|
252
|
+
conversationId,
|
|
253
|
+
startTime,
|
|
254
|
+
endTime,
|
|
255
|
+
limit: 5000
|
|
256
|
+
});
|
|
257
|
+
const analysis = {
|
|
258
|
+
totalMessages: messages.total,
|
|
259
|
+
timeRange: { startTime, endTime, days: timeRange },
|
|
260
|
+
analysis: {}
|
|
261
|
+
};
|
|
262
|
+
// 内容类型分析
|
|
263
|
+
if (analysisTypes.includes('contentType')) {
|
|
264
|
+
analysis.analysis.contentTypes = this._analyzeContentTypes(messages.data);
|
|
265
|
+
}
|
|
266
|
+
// 关键词分析
|
|
267
|
+
if (analysisTypes.includes('keywords')) {
|
|
268
|
+
analysis.analysis.keywords = this._extractKeywords(messages.data);
|
|
269
|
+
}
|
|
270
|
+
// 情感分析(简单的基于关键词的方法)
|
|
271
|
+
if (analysisTypes.includes('sentiment')) {
|
|
272
|
+
analysis.analysis.sentiment = this._analyzeSentiment(messages.data);
|
|
273
|
+
}
|
|
274
|
+
// 活跃用户分析
|
|
275
|
+
analysis.analysis.activeUsers = this._analyzeActiveUsers(messages.data);
|
|
276
|
+
return analysis;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console.error('❌ 内容分析失败:', error);
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* 导出数据工具 - 导出指定格式的数据
|
|
285
|
+
* @param {Object} params - 导出参数
|
|
286
|
+
* @returns {Promise<Object>} 导出结果
|
|
287
|
+
*/
|
|
288
|
+
async exportData(params = {}) {
|
|
289
|
+
const { robotId, exportType = 'messages', // messages, friends, groups, statistics
|
|
290
|
+
conversationId, format = 'json', // json, csv, txt
|
|
291
|
+
timeRange = 30, includeMetadata = true } = params;
|
|
292
|
+
try {
|
|
293
|
+
let data = null;
|
|
294
|
+
let metadata = {};
|
|
295
|
+
switch (exportType) {
|
|
296
|
+
case 'messages':
|
|
297
|
+
const endTime = Date.now();
|
|
298
|
+
const startTime = endTime - (timeRange * 24 * 60 * 60 * 1000);
|
|
299
|
+
const messages = await this.mcpService.searchChatMessages({
|
|
300
|
+
robotId,
|
|
301
|
+
conversationId,
|
|
302
|
+
startTime,
|
|
303
|
+
endTime,
|
|
304
|
+
limit: 10000
|
|
305
|
+
});
|
|
306
|
+
data = messages.data;
|
|
307
|
+
metadata = {
|
|
308
|
+
totalMessages: messages.total,
|
|
309
|
+
timeRange: { startTime, endTime },
|
|
310
|
+
conversationId
|
|
311
|
+
};
|
|
312
|
+
break;
|
|
313
|
+
case 'friends':
|
|
314
|
+
const friends = await this.mcpService.searchFriends({
|
|
315
|
+
robotId,
|
|
316
|
+
limit: 10000
|
|
317
|
+
});
|
|
318
|
+
data = friends.data;
|
|
319
|
+
metadata = { totalFriends: friends.total };
|
|
320
|
+
break;
|
|
321
|
+
case 'groups':
|
|
322
|
+
const groups = await this.mcpService.searchGroups({
|
|
323
|
+
robotId,
|
|
324
|
+
includeMembers: true,
|
|
325
|
+
limit: 10000
|
|
326
|
+
});
|
|
327
|
+
data = groups.data;
|
|
328
|
+
metadata = { totalGroups: groups.total };
|
|
329
|
+
break;
|
|
330
|
+
case 'statistics':
|
|
331
|
+
data = await this.mcpService.getRobotStatistics(robotId);
|
|
332
|
+
metadata = { generatedAt: new Date() };
|
|
333
|
+
break;
|
|
334
|
+
default:
|
|
335
|
+
throw new Error(`不支持的导出类型: ${exportType}`);
|
|
336
|
+
}
|
|
337
|
+
// 格式化数据
|
|
338
|
+
const formattedData = this._formatExportData(data, format, includeMetadata ? metadata : null);
|
|
339
|
+
return {
|
|
340
|
+
exportType,
|
|
341
|
+
format,
|
|
342
|
+
recordCount: Array.isArray(data) ? data.length : 1,
|
|
343
|
+
generatedAt: new Date(),
|
|
344
|
+
data: formattedData
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
console.error('❌ 数据导出失败:', error);
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// 私有辅助方法
|
|
353
|
+
_calculateActivityLevel(messageCount) {
|
|
354
|
+
if (messageCount === 0)
|
|
355
|
+
return 'inactive';
|
|
356
|
+
if (messageCount < 10)
|
|
357
|
+
return 'low';
|
|
358
|
+
if (messageCount < 100)
|
|
359
|
+
return 'medium';
|
|
360
|
+
if (messageCount < 1000)
|
|
361
|
+
return 'high';
|
|
362
|
+
return 'very_high';
|
|
363
|
+
}
|
|
364
|
+
async _findMutualFriends(robotId, userId, userGroups) {
|
|
365
|
+
// 实现查找共同好友的逻辑
|
|
366
|
+
const groupIds = userGroups.map(g => g.roomWxid);
|
|
367
|
+
const mutualMembers = await GroupMember.findAll({
|
|
368
|
+
where: {
|
|
369
|
+
robotId,
|
|
370
|
+
roomWxid: { [Op.in]: groupIds },
|
|
371
|
+
wxid: { [Op.ne]: userId }
|
|
372
|
+
},
|
|
373
|
+
include: [{
|
|
374
|
+
model: Group,
|
|
375
|
+
as: 'group',
|
|
376
|
+
attributes: ['name', 'wxid']
|
|
377
|
+
}]
|
|
378
|
+
});
|
|
379
|
+
return mutualMembers.slice(0, 20); // 限制返回数量
|
|
380
|
+
}
|
|
381
|
+
_calculateActivityMetrics(messages) {
|
|
382
|
+
if (!messages || messages.length === 0) {
|
|
383
|
+
return { activityLevel: 'inactive', averageLength: 0, responseTime: null };
|
|
384
|
+
}
|
|
385
|
+
const totalLength = messages.reduce((sum, msg) => sum + (msg.content?.length || 0), 0);
|
|
386
|
+
const averageLength = Math.round(totalLength / messages.length);
|
|
387
|
+
return {
|
|
388
|
+
activityLevel: this._calculateActivityLevel(messages.length),
|
|
389
|
+
averageLength,
|
|
390
|
+
messageFrequency: messages.length,
|
|
391
|
+
contentTypes: [...new Set(messages.map(m => m.contentType))]
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
_analyzeActivityPatterns(heatmapData) {
|
|
395
|
+
if (!heatmapData || heatmapData.length === 0) {
|
|
396
|
+
return { peakPeriod: null, quietPeriod: null, pattern: 'unknown' };
|
|
397
|
+
}
|
|
398
|
+
const sorted = [...heatmapData].sort((a, b) => b.messageCount - a.messageCount);
|
|
399
|
+
return {
|
|
400
|
+
peakPeriod: sorted[0],
|
|
401
|
+
quietPeriod: sorted[sorted.length - 1],
|
|
402
|
+
pattern: this._identifyPattern(heatmapData)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
_identifyPattern(heatmapData) {
|
|
406
|
+
// 简单的模式识别逻辑
|
|
407
|
+
return 'regular'; // 可以扩展为更复杂的模式识别
|
|
408
|
+
}
|
|
409
|
+
_analyzeContentTypes(messages) {
|
|
410
|
+
const typeCount = {};
|
|
411
|
+
messages.forEach(msg => {
|
|
412
|
+
const type = msg.contentType || '文字';
|
|
413
|
+
typeCount[type] = (typeCount[type] || 0) + 1;
|
|
414
|
+
});
|
|
415
|
+
return Object.entries(typeCount)
|
|
416
|
+
.map(([type, count]) => ({ type, count }))
|
|
417
|
+
.sort((a, b) => b.count - a.count);
|
|
418
|
+
}
|
|
419
|
+
_extractKeywords(messages) {
|
|
420
|
+
// 简单的关键词提取
|
|
421
|
+
const words = {};
|
|
422
|
+
messages.forEach(msg => {
|
|
423
|
+
if (msg.content && msg.contentType === '文字') {
|
|
424
|
+
const content = msg.content.toLowerCase();
|
|
425
|
+
const wordsArray = content.match(/[\u4e00-\u9fa5]+/g) || [];
|
|
426
|
+
wordsArray.forEach(word => {
|
|
427
|
+
if (word.length > 1) {
|
|
428
|
+
words[word] = (words[word] || 0) + 1;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
return Object.entries(words)
|
|
434
|
+
.map(([word, count]) => ({ word, count }))
|
|
435
|
+
.sort((a, b) => b.count - a.count)
|
|
436
|
+
.slice(0, 20);
|
|
437
|
+
}
|
|
438
|
+
_analyzeSentiment(messages) {
|
|
439
|
+
// 简单的情感分析
|
|
440
|
+
const positiveWords = ['好', '棒', '赞', '喜欢', '开心', '高兴', '满意'];
|
|
441
|
+
const negativeWords = ['不好', '差', '不满', '生气', '难过', '失望'];
|
|
442
|
+
let positive = 0, negative = 0, neutral = 0;
|
|
443
|
+
messages.forEach(msg => {
|
|
444
|
+
if (msg.content && msg.contentType === '文字') {
|
|
445
|
+
const content = msg.content.toLowerCase();
|
|
446
|
+
const hasPositive = positiveWords.some(word => content.includes(word));
|
|
447
|
+
const hasNegative = negativeWords.some(word => content.includes(word));
|
|
448
|
+
if (hasPositive && !hasNegative)
|
|
449
|
+
positive++;
|
|
450
|
+
else if (hasNegative && !hasPositive)
|
|
451
|
+
negative++;
|
|
452
|
+
else
|
|
453
|
+
neutral++;
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
const total = positive + negative + neutral;
|
|
457
|
+
return {
|
|
458
|
+
positive: { count: positive, percentage: total ? Math.round(positive / total * 100) : 0 },
|
|
459
|
+
negative: { count: negative, percentage: total ? Math.round(negative / total * 100) : 0 },
|
|
460
|
+
neutral: { count: neutral, percentage: total ? Math.round(neutral / total * 100) : 0 }
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
_analyzeActiveUsers(messages) {
|
|
464
|
+
const userStats = {};
|
|
465
|
+
messages.forEach(msg => {
|
|
466
|
+
const userId = msg.chatUserId;
|
|
467
|
+
if (!userStats[userId]) {
|
|
468
|
+
userStats[userId] = {
|
|
469
|
+
userId,
|
|
470
|
+
userName: msg.chatUserName,
|
|
471
|
+
messageCount: 0,
|
|
472
|
+
lastActive: 0
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
userStats[userId].messageCount++;
|
|
476
|
+
userStats[userId].lastActive = Math.max(userStats[userId].lastActive, msg.timestamp);
|
|
477
|
+
});
|
|
478
|
+
return Object.values(userStats)
|
|
479
|
+
.sort((a, b) => b.messageCount - a.messageCount)
|
|
480
|
+
.slice(0, 10);
|
|
481
|
+
}
|
|
482
|
+
_formatExportData(data, format, metadata) {
|
|
483
|
+
switch (format) {
|
|
484
|
+
case 'json':
|
|
485
|
+
return metadata ? { metadata, data } : data;
|
|
486
|
+
case 'csv':
|
|
487
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
488
|
+
const headers = Object.keys(data[0]).join(',');
|
|
489
|
+
const rows = data.map(item => Object.values(item).map(val => typeof val === 'string' ? `"${val.replace(/"/g, '""')}"` : val).join(','));
|
|
490
|
+
return [headers, ...rows].join('\n');
|
|
491
|
+
}
|
|
492
|
+
return '';
|
|
493
|
+
case 'txt':
|
|
494
|
+
if (Array.isArray(data)) {
|
|
495
|
+
return data.map(item => JSON.stringify(item)).join('\n');
|
|
496
|
+
}
|
|
497
|
+
return JSON.stringify(data, null, 2);
|
|
498
|
+
default:
|
|
499
|
+
return data;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
export default McpTools;
|
|
504
|
+
//# sourceMappingURL=McpTools.js.map
|