@xpert-ai/plugin-community-wechat 0.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/README.md +353 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +139 -0
- package/dist/lib/constants.d.ts +23 -0
- package/dist/lib/constants.js +23 -0
- package/dist/lib/conversation-user-key.d.ts +13 -0
- package/dist/lib/conversation-user-key.js +28 -0
- package/dist/lib/conversation.service.d.ts +215 -0
- package/dist/lib/conversation.service.js +1179 -0
- package/dist/lib/decorators.d.ts +2 -0
- package/dist/lib/decorators.js +3 -0
- package/dist/lib/entities/index.d.ts +4 -0
- package/dist/lib/entities/index.js +4 -0
- package/dist/lib/entities/wechat-personal-account.entity.d.ts +19 -0
- package/dist/lib/entities/wechat-personal-account.entity.js +83 -0
- package/dist/lib/entities/wechat-personal-conversation-binding.entity.d.ts +14 -0
- package/dist/lib/entities/wechat-personal-conversation-binding.entity.js +65 -0
- package/dist/lib/entities/wechat-personal-message-log.entity.d.ts +27 -0
- package/dist/lib/entities/wechat-personal-message-log.entity.js +108 -0
- package/dist/lib/entities/wechat-personal-trigger-binding.entity.d.ts +17 -0
- package/dist/lib/entities/wechat-personal-trigger-binding.entity.js +71 -0
- package/dist/lib/handoff/index.d.ts +4 -0
- package/dist/lib/handoff/index.js +4 -0
- package/dist/lib/handoff/wechat-personal-chat-callback.processor.d.ts +26 -0
- package/dist/lib/handoff/wechat-personal-chat-callback.processor.js +312 -0
- package/dist/lib/handoff/wechat-personal-chat-dispatch.service.d.ts +26 -0
- package/dist/lib/handoff/wechat-personal-chat-dispatch.service.js +187 -0
- package/dist/lib/handoff/wechat-personal-chat-run-state.service.d.ts +21 -0
- package/dist/lib/handoff/wechat-personal-chat-run-state.service.js +39 -0
- package/dist/lib/handoff/wechat-personal-chat.types.d.ts +69 -0
- package/dist/lib/handoff/wechat-personal-chat.types.js +2 -0
- package/dist/lib/message.d.ts +49 -0
- package/dist/lib/message.js +64 -0
- package/dist/lib/remote-components/wechat-personal-workbench/app.js +1831 -0
- package/dist/lib/tokens.d.ts +1 -0
- package/dist/lib/tokens.js +1 -0
- package/dist/lib/types.d.ts +48 -0
- package/dist/lib/types.js +365 -0
- package/dist/lib/views/wechat-personal-view.provider.d.ts +17 -0
- package/dist/lib/views/wechat-personal-view.provider.js +441 -0
- package/dist/lib/wechat-personal-channel.strategy.d.ts +33 -0
- package/dist/lib/wechat-personal-channel.strategy.js +197 -0
- package/dist/lib/wechat-personal-integration.strategy.d.ts +56 -0
- package/dist/lib/wechat-personal-integration.strategy.js +217 -0
- package/dist/lib/wechat-personal.client.d.ts +29 -0
- package/dist/lib/wechat-personal.client.js +146 -0
- package/dist/lib/wechat-personal.controller.d.ts +50 -0
- package/dist/lib/wechat-personal.controller.js +270 -0
- package/dist/lib/wechat-personal.middleware.d.ts +20 -0
- package/dist/lib/wechat-personal.middleware.js +267 -0
- package/dist/lib/wechat-personal.plugin.d.ts +2 -0
- package/dist/lib/wechat-personal.plugin.js +58 -0
- package/dist/lib/wechat-personal.templates.d.ts +2 -0
- package/dist/lib/wechat-personal.templates.js +100 -0
- package/dist/lib/workflow/index.d.ts +5 -0
- package/dist/lib/workflow/index.js +5 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.service.d.ts +10 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.service.js +39 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.types.d.ts +30 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.types.js +2 -0
- package/dist/lib/workflow/wechat-personal-trigger-flush.processor.d.ts +8 -0
- package/dist/lib/workflow/wechat-personal-trigger-flush.processor.js +39 -0
- package/dist/lib/workflow/wechat-personal-trigger.strategy.d.ts +65 -0
- package/dist/lib/workflow/wechat-personal-trigger.strategy.js +511 -0
- package/dist/lib/workflow/wechat-personal-trigger.types.d.ts +10 -0
- package/dist/lib/workflow/wechat-personal-trigger.types.js +2 -0
- package/dist/xpert-wechat-personal-admin-assistant.yaml +103 -0
- package/dist/xpert-wechat-personal-user-assistant.yaml +127 -0
- package/package.json +79 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var WechatPersonalTriggerStrategy_1;
|
|
14
|
+
import { Inject, Injectable, Logger } from '@nestjs/common';
|
|
15
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
16
|
+
import { HANDOFF_PERMISSION_SERVICE_TOKEN, INTEGRATION_PERMISSION_SERVICE_TOKEN, RequestContext, WorkflowTriggerStrategy } from '@xpert-ai/plugin-sdk';
|
|
17
|
+
import { randomUUID } from 'crypto';
|
|
18
|
+
import { Repository } from 'typeorm';
|
|
19
|
+
import { WECHAT_PERSONAL_ICON, WECHAT_PERSONAL_PROVIDER_KEY } from '../constants.js';
|
|
20
|
+
import { WechatPersonalTriggerBindingEntity } from '../entities/wechat-personal-trigger-binding.entity.js';
|
|
21
|
+
import { WechatPersonalChatDispatchService } from '../handoff/wechat-personal-chat-dispatch.service.js';
|
|
22
|
+
import { WechatPersonalMessage } from '../message.js';
|
|
23
|
+
import { WECHAT_PERSONAL_PLUGIN_CONTEXT } from '../tokens.js';
|
|
24
|
+
import { normalizeGroupTriggerMode, normalizeKeywords } from '../types.js';
|
|
25
|
+
import { WechatPersonalChannelStrategy } from '../wechat-personal-channel.strategy.js';
|
|
26
|
+
import { WECHAT_PERSONAL_TRIGGER_FLUSH_MESSAGE_TYPE } from './wechat-personal-trigger-aggregation.types.js';
|
|
27
|
+
import { WechatPersonalTriggerAggregationService } from './wechat-personal-trigger-aggregation.service.js';
|
|
28
|
+
import { WechatPersonalTrigger } from './wechat-personal-trigger.types.js';
|
|
29
|
+
const DEFAULT_SESSION_TIMEOUT_SECONDS = 3600;
|
|
30
|
+
const DEFAULT_SUMMARY_WINDOW_SECONDS = 0;
|
|
31
|
+
let WechatPersonalTriggerStrategy = WechatPersonalTriggerStrategy_1 = class WechatPersonalTriggerStrategy {
|
|
32
|
+
constructor(dispatchService, aggregationService, wechatChannel, bindingRepository, pluginContext) {
|
|
33
|
+
this.dispatchService = dispatchService;
|
|
34
|
+
this.aggregationService = aggregationService;
|
|
35
|
+
this.wechatChannel = wechatChannel;
|
|
36
|
+
this.bindingRepository = bindingRepository;
|
|
37
|
+
this.pluginContext = pluginContext;
|
|
38
|
+
this.logger = new Logger(WechatPersonalTriggerStrategy_1.name);
|
|
39
|
+
this.callbacks = new Map();
|
|
40
|
+
this.meta = {
|
|
41
|
+
name: WechatPersonalTrigger,
|
|
42
|
+
label: {
|
|
43
|
+
en_US: 'Personal WeChat Trigger',
|
|
44
|
+
zh_Hans: '个人微信触发器'
|
|
45
|
+
},
|
|
46
|
+
icon: {
|
|
47
|
+
type: 'svg',
|
|
48
|
+
value: WECHAT_PERSONAL_ICON
|
|
49
|
+
},
|
|
50
|
+
configSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
enabled: {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
title: {
|
|
56
|
+
en_US: 'Enabled',
|
|
57
|
+
zh_Hans: '启用'
|
|
58
|
+
},
|
|
59
|
+
default: true
|
|
60
|
+
},
|
|
61
|
+
integrationId: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
title: {
|
|
64
|
+
en_US: 'Personal WeChat Integration',
|
|
65
|
+
zh_Hans: '个人微信集成'
|
|
66
|
+
},
|
|
67
|
+
'x-ui': {
|
|
68
|
+
component: 'remoteSelect',
|
|
69
|
+
selectUrl: '/api/wechat-personal/integration-select-options'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
sessionTimeoutSeconds: {
|
|
73
|
+
type: 'number',
|
|
74
|
+
title: {
|
|
75
|
+
en_US: 'Session Timeout (seconds)',
|
|
76
|
+
zh_Hans: '会话超时时间(秒)'
|
|
77
|
+
},
|
|
78
|
+
default: DEFAULT_SESSION_TIMEOUT_SECONDS
|
|
79
|
+
},
|
|
80
|
+
summaryWindowSeconds: {
|
|
81
|
+
type: 'number',
|
|
82
|
+
title: {
|
|
83
|
+
en_US: 'Summary Window (seconds)',
|
|
84
|
+
zh_Hans: '汇总时间(秒)'
|
|
85
|
+
},
|
|
86
|
+
description: {
|
|
87
|
+
en_US: 'Messages received in this window are merged before dispatching to the agent.',
|
|
88
|
+
zh_Hans: '窗口内收到的连续文本会合并后再发送给 Agent。'
|
|
89
|
+
},
|
|
90
|
+
default: DEFAULT_SUMMARY_WINDOW_SECONDS
|
|
91
|
+
},
|
|
92
|
+
groupTriggerMode: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
title: {
|
|
95
|
+
en_US: 'Group Trigger Mode',
|
|
96
|
+
zh_Hans: '群聊触发方式'
|
|
97
|
+
},
|
|
98
|
+
enum: ['mention_or_keywords', 'all', 'mentions', 'keywords', 'off'],
|
|
99
|
+
default: 'mention_or_keywords'
|
|
100
|
+
},
|
|
101
|
+
groupKeywords: {
|
|
102
|
+
type: 'array',
|
|
103
|
+
title: {
|
|
104
|
+
en_US: 'Group Keywords',
|
|
105
|
+
zh_Hans: '群聊关键词'
|
|
106
|
+
},
|
|
107
|
+
items: {
|
|
108
|
+
type: 'string'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
required: ['enabled', 'integrationId']
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
this.bootstrap = {
|
|
116
|
+
mode: 'replay_publish',
|
|
117
|
+
critical: false
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
get integrationPermissionService() {
|
|
121
|
+
if (!this._integrationPermissionService) {
|
|
122
|
+
this._integrationPermissionService = this.pluginContext.resolve(INTEGRATION_PERMISSION_SERVICE_TOKEN);
|
|
123
|
+
}
|
|
124
|
+
return this._integrationPermissionService;
|
|
125
|
+
}
|
|
126
|
+
get handoffPermissionService() {
|
|
127
|
+
if (!this._handoffPermissionService) {
|
|
128
|
+
this._handoffPermissionService = this.pluginContext.resolve(HANDOFF_PERMISSION_SERVICE_TOKEN);
|
|
129
|
+
}
|
|
130
|
+
return this._handoffPermissionService;
|
|
131
|
+
}
|
|
132
|
+
async validate(payload) {
|
|
133
|
+
const { xpertId, node, config } = payload;
|
|
134
|
+
const items = [];
|
|
135
|
+
const nodeKey = node?.key;
|
|
136
|
+
if (!config?.integrationId) {
|
|
137
|
+
items.push({
|
|
138
|
+
node: nodeKey,
|
|
139
|
+
ruleCode: 'TRIGGER_WECHAT_PERSONAL_INTEGRATION_REQUIRED',
|
|
140
|
+
field: 'integrationId',
|
|
141
|
+
value: '',
|
|
142
|
+
message: {
|
|
143
|
+
en_US: 'Personal WeChat integration is required',
|
|
144
|
+
zh_Hans: '需要选择个人微信集成'
|
|
145
|
+
},
|
|
146
|
+
level: 'error'
|
|
147
|
+
});
|
|
148
|
+
return items;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const integration = await this.integrationPermissionService.read(config.integrationId);
|
|
152
|
+
if (!integration || integration.provider !== WECHAT_PERSONAL_PROVIDER_KEY) {
|
|
153
|
+
items.push({
|
|
154
|
+
node: nodeKey,
|
|
155
|
+
ruleCode: 'TRIGGER_WECHAT_PERSONAL_INTEGRATION_NOT_FOUND',
|
|
156
|
+
field: 'integrationId',
|
|
157
|
+
value: config.integrationId,
|
|
158
|
+
message: {
|
|
159
|
+
en_US: `Personal WeChat integration "${config.integrationId}" not found`,
|
|
160
|
+
zh_Hans: `个人微信集成 "${config.integrationId}" 不存在`
|
|
161
|
+
},
|
|
162
|
+
level: 'error'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.logger.warn(`Validate integration "${config.integrationId}" failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
168
|
+
}
|
|
169
|
+
if (!config.enabled) {
|
|
170
|
+
return items;
|
|
171
|
+
}
|
|
172
|
+
const existingXpertId = await this.getBoundXpertId(config.integrationId);
|
|
173
|
+
if (existingXpertId && existingXpertId !== xpertId) {
|
|
174
|
+
items.push({
|
|
175
|
+
node: nodeKey,
|
|
176
|
+
ruleCode: 'TRIGGER_WECHAT_PERSONAL_INTEGRATION_CONFLICT',
|
|
177
|
+
field: 'integrationId',
|
|
178
|
+
value: config.integrationId,
|
|
179
|
+
message: {
|
|
180
|
+
en_US: `Integration "${config.integrationId}" is already bound to another xpert`,
|
|
181
|
+
zh_Hans: `个人微信集成 "${config.integrationId}" 已绑定到其他专家`
|
|
182
|
+
},
|
|
183
|
+
level: 'error'
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return items;
|
|
187
|
+
}
|
|
188
|
+
async publish(payload, callback) {
|
|
189
|
+
const { xpertId, config } = payload;
|
|
190
|
+
if (!config?.enabled || !config.integrationId) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const integrationId = config.integrationId;
|
|
194
|
+
const existingXpertId = await this.getBoundXpertId(integrationId);
|
|
195
|
+
if (existingXpertId && existingXpertId !== xpertId) {
|
|
196
|
+
throw new Error(`Personal WeChat trigger integration "${integrationId}" is already bound to xpert "${existingXpertId}"`);
|
|
197
|
+
}
|
|
198
|
+
const context = await this.resolveBindingContext(integrationId);
|
|
199
|
+
await this.bindingRepository.upsert({
|
|
200
|
+
integrationId,
|
|
201
|
+
xpertId,
|
|
202
|
+
sessionTimeoutSeconds: this.normalizePositiveSeconds(config.sessionTimeoutSeconds, DEFAULT_SESSION_TIMEOUT_SECONDS),
|
|
203
|
+
summaryWindowSeconds: this.normalizeNonNegativeSeconds(config.summaryWindowSeconds, DEFAULT_SUMMARY_WINDOW_SECONDS),
|
|
204
|
+
groupTriggerMode: normalizeGroupTriggerMode(config.groupTriggerMode),
|
|
205
|
+
groupKeywords: normalizeKeywords(config.groupKeywords),
|
|
206
|
+
tenantId: context.tenantId ?? null,
|
|
207
|
+
organizationId: context.organizationId ?? null,
|
|
208
|
+
createdById: context.createdById ?? null,
|
|
209
|
+
updatedById: context.updatedById ?? null
|
|
210
|
+
}, ['integrationId']);
|
|
211
|
+
this.callbacks.set(integrationId, callback);
|
|
212
|
+
}
|
|
213
|
+
async stop(payload) {
|
|
214
|
+
const { xpertId, config } = payload;
|
|
215
|
+
const integrationId = config?.integrationId;
|
|
216
|
+
if (integrationId) {
|
|
217
|
+
this.callbacks.delete(integrationId);
|
|
218
|
+
await this.removeBindingFromStore(integrationId, xpertId);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const scope = this.resolveRequestTenantScope();
|
|
222
|
+
const persistedBindings = await this.bindingRepository.find({
|
|
223
|
+
where: this.scopedWhere({ xpertId }, scope)
|
|
224
|
+
});
|
|
225
|
+
for (const binding of persistedBindings) {
|
|
226
|
+
this.callbacks.delete(binding.integrationId);
|
|
227
|
+
}
|
|
228
|
+
await this.removeBindingsByXpertId(xpertId);
|
|
229
|
+
}
|
|
230
|
+
async getBinding(integrationId, scope) {
|
|
231
|
+
if (!integrationId) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const resolvedScope = scope ?? (await this.resolveQueryTenantScope(integrationId));
|
|
235
|
+
return this.bindingRepository.findOne({
|
|
236
|
+
where: this.scopedWhere({ integrationId }, resolvedScope)
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
async getBoundXpertId(integrationId) {
|
|
240
|
+
const binding = await this.getBinding(integrationId);
|
|
241
|
+
return binding?.xpertId ?? null;
|
|
242
|
+
}
|
|
243
|
+
async getBindingByXpertId(xpertId) {
|
|
244
|
+
if (!xpertId) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
const scope = this.resolveRequestTenantScope();
|
|
248
|
+
return this.bindingRepository.findOne({
|
|
249
|
+
where: this.scopedWhere({ xpertId }, scope),
|
|
250
|
+
order: {
|
|
251
|
+
updatedAt: 'DESC'
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async getBoundIntegrationId(xpertId) {
|
|
256
|
+
const binding = await this.getBindingByXpertId(xpertId);
|
|
257
|
+
return binding?.integrationId ?? null;
|
|
258
|
+
}
|
|
259
|
+
async clearBufferedConversation(conversationUserKey) {
|
|
260
|
+
const aggregateKey = this.normalizeAggregateKey(conversationUserKey);
|
|
261
|
+
if (!aggregateKey) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
await this.aggregationService.clear(aggregateKey);
|
|
265
|
+
}
|
|
266
|
+
async handleInboundMessage(params) {
|
|
267
|
+
const binding = await this.getBinding(params.integrationId, params);
|
|
268
|
+
if (!binding?.xpertId) {
|
|
269
|
+
this.logger.debug(`[wechat-personal-trigger] binding miss integrationId=${params.integrationId}`);
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
const aggregateKey = this.normalizeAggregateKey(params.conversationUserKey);
|
|
273
|
+
if (!aggregateKey) {
|
|
274
|
+
this.logger.warn(`[wechat-personal-trigger] aggregation key missing integrationId=${params.integrationId}`);
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const summaryWindowSeconds = this.normalizeNonNegativeSeconds(binding.summaryWindowSeconds, DEFAULT_SUMMARY_WINDOW_SECONDS);
|
|
278
|
+
if (summaryWindowSeconds <= 0) {
|
|
279
|
+
await this.dispatchInboundMessage({
|
|
280
|
+
integrationId: params.integrationId,
|
|
281
|
+
xpertId: binding.xpertId,
|
|
282
|
+
dispatchMode: 'immediate',
|
|
283
|
+
dispatchPayload: {
|
|
284
|
+
xpertId: binding.xpertId,
|
|
285
|
+
input: params.input || '',
|
|
286
|
+
wechatMessage: params.wechatMessage,
|
|
287
|
+
conversationId: params.conversationId,
|
|
288
|
+
conversationUserKey: aggregateKey,
|
|
289
|
+
tenantId: params.tenantId,
|
|
290
|
+
organizationId: params.organizationId,
|
|
291
|
+
executorUserId: params.executorUserId,
|
|
292
|
+
endUserId: params.endUserId
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
const currentState = await this.aggregationService.get(aggregateKey);
|
|
298
|
+
const sameRoutingTarget = currentState?.integrationId === params.integrationId && currentState?.xpertId === binding.xpertId;
|
|
299
|
+
const nextVersion = (currentState?.version ?? 0) + 1;
|
|
300
|
+
const aggregateState = {
|
|
301
|
+
aggregateKey,
|
|
302
|
+
integrationId: params.integrationId,
|
|
303
|
+
conversationUserKey: aggregateKey,
|
|
304
|
+
xpertId: binding.xpertId,
|
|
305
|
+
version: nextVersion,
|
|
306
|
+
inputParts: [...(sameRoutingTarget ? currentState?.inputParts ?? [] : []), params.input || ''],
|
|
307
|
+
lastMessageAt: Date.now(),
|
|
308
|
+
conversationId: params.conversationId ?? (sameRoutingTarget ? currentState?.conversationId : undefined),
|
|
309
|
+
tenantId: params.tenantId,
|
|
310
|
+
organizationId: params.organizationId,
|
|
311
|
+
executorUserId: params.executorUserId,
|
|
312
|
+
endUserId: params.endUserId,
|
|
313
|
+
latestMessage: {
|
|
314
|
+
integrationId: params.wechatMessage.integrationId,
|
|
315
|
+
uuid: params.wechatMessage.uuid,
|
|
316
|
+
ownerWxid: params.wechatMessage.ownerWxid,
|
|
317
|
+
contactId: params.wechatMessage.contactId,
|
|
318
|
+
chatType: params.wechatMessage.chatType,
|
|
319
|
+
senderId: params.wechatMessage.senderId,
|
|
320
|
+
language: params.wechatMessage.language,
|
|
321
|
+
messageId: params.wechatMessage.messageId
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const ttlSeconds = Math.max(DEFAULT_SESSION_TIMEOUT_SECONDS, this.normalizePositiveSeconds(binding.sessionTimeoutSeconds, DEFAULT_SESSION_TIMEOUT_SECONDS), summaryWindowSeconds * 3);
|
|
325
|
+
await this.aggregationService.save(aggregateState, ttlSeconds);
|
|
326
|
+
await this.handoffPermissionService.enqueue(this.buildFlushMessage(aggregateState), {
|
|
327
|
+
delayMs: summaryWindowSeconds * 1000
|
|
328
|
+
});
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
async flushBufferedConversation(payload) {
|
|
332
|
+
const aggregateKey = this.normalizeAggregateKey(payload.aggregateKey);
|
|
333
|
+
if (!aggregateKey) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
const state = await this.aggregationService.get(aggregateKey);
|
|
337
|
+
if (!state || state.version !== payload.version) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
await this.dispatchInboundMessage({
|
|
341
|
+
integrationId: state.integrationId,
|
|
342
|
+
xpertId: state.xpertId,
|
|
343
|
+
dispatchMode: `buffered version=${state.version}`,
|
|
344
|
+
dispatchPayload: {
|
|
345
|
+
xpertId: state.xpertId,
|
|
346
|
+
input: state.inputParts.join('\n'),
|
|
347
|
+
wechatMessage: new WechatPersonalMessage({
|
|
348
|
+
integrationId: state.latestMessage.integrationId,
|
|
349
|
+
uuid: state.latestMessage.uuid,
|
|
350
|
+
ownerWxid: state.latestMessage.ownerWxid,
|
|
351
|
+
contactId: state.latestMessage.contactId,
|
|
352
|
+
chatType: state.latestMessage.chatType,
|
|
353
|
+
senderId: state.latestMessage.senderId,
|
|
354
|
+
wechatChannel: this.wechatChannel
|
|
355
|
+
}, {
|
|
356
|
+
status: 'thinking',
|
|
357
|
+
language: state.latestMessage.language,
|
|
358
|
+
messageId: state.latestMessage.messageId
|
|
359
|
+
}),
|
|
360
|
+
conversationId: state.conversationId,
|
|
361
|
+
conversationUserKey: state.conversationUserKey,
|
|
362
|
+
tenantId: state.tenantId,
|
|
363
|
+
organizationId: state.organizationId,
|
|
364
|
+
executorUserId: state.executorUserId,
|
|
365
|
+
endUserId: state.endUserId
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
await this.aggregationService.clear(aggregateKey);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
buildFlushMessage(state) {
|
|
372
|
+
return {
|
|
373
|
+
id: `wechat-personal-trigger-flush-${randomUUID()}`,
|
|
374
|
+
type: WECHAT_PERSONAL_TRIGGER_FLUSH_MESSAGE_TYPE,
|
|
375
|
+
version: 1,
|
|
376
|
+
tenantId: state.tenantId,
|
|
377
|
+
sessionKey: state.aggregateKey,
|
|
378
|
+
businessKey: state.aggregateKey,
|
|
379
|
+
attempt: 1,
|
|
380
|
+
maxAttempts: 1,
|
|
381
|
+
enqueuedAt: Date.now(),
|
|
382
|
+
traceId: `${state.aggregateKey}:${state.version}`,
|
|
383
|
+
payload: {
|
|
384
|
+
aggregateKey: state.aggregateKey,
|
|
385
|
+
version: state.version
|
|
386
|
+
},
|
|
387
|
+
headers: {
|
|
388
|
+
...(state.organizationId ? { organizationId: state.organizationId } : {}),
|
|
389
|
+
...(state.executorUserId ? { userId: state.executorUserId } : {}),
|
|
390
|
+
source: 'api',
|
|
391
|
+
requestedLane: 'main',
|
|
392
|
+
handoffQueue: 'integration',
|
|
393
|
+
...(state.integrationId ? { integrationId: state.integrationId } : {})
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
async dispatchInboundMessage(params) {
|
|
398
|
+
const callback = this.callbacks.get(params.integrationId);
|
|
399
|
+
if (!callback) {
|
|
400
|
+
this.logger.debug(`[wechat-personal-trigger] runtime callback miss, enqueue dispatch integrationId=${params.integrationId} xpertId=${params.xpertId} mode=${params.dispatchMode}`);
|
|
401
|
+
await this.dispatchService.enqueueDispatch(params.dispatchPayload);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const handoffMessage = await this.dispatchService.buildDispatchMessage(params.dispatchPayload);
|
|
405
|
+
await Promise.resolve(callback({
|
|
406
|
+
from: WechatPersonalTrigger,
|
|
407
|
+
xpertId: params.xpertId,
|
|
408
|
+
handoffMessage
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
async resolveBindingContext(integrationId) {
|
|
412
|
+
const tenantId = RequestContext.currentTenantId();
|
|
413
|
+
const organizationId = RequestContext.getOrganizationId();
|
|
414
|
+
const userId = RequestContext.currentUserId();
|
|
415
|
+
if (tenantId && organizationId) {
|
|
416
|
+
return {
|
|
417
|
+
tenantId,
|
|
418
|
+
organizationId,
|
|
419
|
+
createdById: userId,
|
|
420
|
+
updatedById: userId
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const integration = await this.integrationPermissionService.read(integrationId);
|
|
424
|
+
return {
|
|
425
|
+
tenantId: tenantId ?? integration?.tenantId ?? null,
|
|
426
|
+
organizationId: organizationId ?? integration?.organizationId ?? null,
|
|
427
|
+
createdById: userId ?? integration?.createdById ?? null,
|
|
428
|
+
updatedById: userId ?? integration?.updatedById ?? userId ?? null
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
normalizeAggregateKey(value) {
|
|
432
|
+
if (typeof value !== 'string') {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
const normalized = value.trim();
|
|
436
|
+
return normalized || undefined;
|
|
437
|
+
}
|
|
438
|
+
normalizePositiveSeconds(value, defaultValue) {
|
|
439
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
440
|
+
return Math.floor(value);
|
|
441
|
+
}
|
|
442
|
+
return defaultValue;
|
|
443
|
+
}
|
|
444
|
+
normalizeNonNegativeSeconds(value, defaultValue) {
|
|
445
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
446
|
+
return Math.floor(value);
|
|
447
|
+
}
|
|
448
|
+
return defaultValue;
|
|
449
|
+
}
|
|
450
|
+
async removeBindingFromStore(integrationId, expectedXpertId) {
|
|
451
|
+
if (!integrationId) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const scope = await this.resolveQueryTenantScope(integrationId);
|
|
455
|
+
if (expectedXpertId) {
|
|
456
|
+
await this.bindingRepository.delete(this.scopedWhere({ integrationId, xpertId: expectedXpertId }, scope));
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
await this.bindingRepository.delete(this.scopedWhere({ integrationId }, scope));
|
|
460
|
+
}
|
|
461
|
+
async removeBindingsByXpertId(xpertId) {
|
|
462
|
+
if (!xpertId) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
await this.bindingRepository.delete(this.scopedWhere({ xpertId }, this.resolveRequestTenantScope()));
|
|
466
|
+
}
|
|
467
|
+
resolveRequestTenantScope() {
|
|
468
|
+
return {
|
|
469
|
+
tenantId: RequestContext.currentTenantId() ?? null,
|
|
470
|
+
organizationId: RequestContext.getOrganizationId() ?? null
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async resolveQueryTenantScope(integrationId, fallback) {
|
|
474
|
+
const requestScope = this.resolveRequestTenantScope();
|
|
475
|
+
if (requestScope.tenantId || requestScope.organizationId || fallback?.tenantId || fallback?.organizationId) {
|
|
476
|
+
return {
|
|
477
|
+
tenantId: fallback?.tenantId ?? requestScope.tenantId ?? null,
|
|
478
|
+
organizationId: fallback?.organizationId ?? requestScope.organizationId ?? null
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (!integrationId) {
|
|
482
|
+
return requestScope;
|
|
483
|
+
}
|
|
484
|
+
const bindingContext = await this.resolveBindingContext(integrationId);
|
|
485
|
+
return {
|
|
486
|
+
tenantId: bindingContext.tenantId,
|
|
487
|
+
organizationId: bindingContext.organizationId
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
scopedWhere(where, scope) {
|
|
491
|
+
const scoped = { ...where };
|
|
492
|
+
if (scope?.tenantId) {
|
|
493
|
+
scoped.tenantId = scope.tenantId;
|
|
494
|
+
}
|
|
495
|
+
if (scope?.organizationId) {
|
|
496
|
+
scoped.organizationId = scope.organizationId;
|
|
497
|
+
}
|
|
498
|
+
return scoped;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
WechatPersonalTriggerStrategy = WechatPersonalTriggerStrategy_1 = __decorate([
|
|
502
|
+
Injectable(),
|
|
503
|
+
WorkflowTriggerStrategy(WechatPersonalTrigger),
|
|
504
|
+
__param(3, InjectRepository(WechatPersonalTriggerBindingEntity)),
|
|
505
|
+
__param(4, Inject(WECHAT_PERSONAL_PLUGIN_CONTEXT)),
|
|
506
|
+
__metadata("design:paramtypes", [WechatPersonalChatDispatchService,
|
|
507
|
+
WechatPersonalTriggerAggregationService,
|
|
508
|
+
WechatPersonalChannelStrategy,
|
|
509
|
+
Repository, Object])
|
|
510
|
+
], WechatPersonalTriggerStrategy);
|
|
511
|
+
export { WechatPersonalTriggerStrategy };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WechatPersonalGroupTriggerMode } from '../types.js';
|
|
2
|
+
export declare const WechatPersonalTrigger = "wechat_personal";
|
|
3
|
+
export type TWechatPersonalTriggerConfig = {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
integrationId: string;
|
|
6
|
+
sessionTimeoutSeconds?: number;
|
|
7
|
+
summaryWindowSeconds?: number;
|
|
8
|
+
groupTriggerMode?: WechatPersonalGroupTriggerMode;
|
|
9
|
+
groupKeywords?: string[] | string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
team:
|
|
2
|
+
name: wechat-personal-admin-assistant
|
|
3
|
+
type: agent
|
|
4
|
+
title: 个人微信管理员
|
|
5
|
+
description: 管理组织内 wx2.0 个人微信集成、账号、会话、消息日志和回调配置的 Agent
|
|
6
|
+
avatar:
|
|
7
|
+
emoji:
|
|
8
|
+
id: toolbox
|
|
9
|
+
background: rgb(220, 252, 231)
|
|
10
|
+
options:
|
|
11
|
+
templateKey: wechat-personal-admin-assistant
|
|
12
|
+
dataXpert:
|
|
13
|
+
managedBy: data-xpert
|
|
14
|
+
templateKey: wechat-personal-admin-assistant
|
|
15
|
+
assistantKind: wechat-personal-admin-assistant
|
|
16
|
+
businessDomain: wechat-personal
|
|
17
|
+
wechatPersonalRole: admin
|
|
18
|
+
requiredPlugin: '@xpert-ai/plugin-community-wechat'
|
|
19
|
+
requiredPlugins:
|
|
20
|
+
- '@xpert-ai/plugin-community-wechat'
|
|
21
|
+
requiredCapabilities:
|
|
22
|
+
- wechat_personal_bridge
|
|
23
|
+
- wechat-personal-runtime
|
|
24
|
+
- wechat-personal-workbench
|
|
25
|
+
agentConfig:
|
|
26
|
+
recursionLimit: 10000
|
|
27
|
+
features:
|
|
28
|
+
sandbox:
|
|
29
|
+
enabled: false
|
|
30
|
+
version: "1"
|
|
31
|
+
agent:
|
|
32
|
+
key: Agent_WechatPersonalAdmin
|
|
33
|
+
copilotModel:
|
|
34
|
+
referencedId: null
|
|
35
|
+
modelType: llm
|
|
36
|
+
model: qwen3.6-plus
|
|
37
|
+
options:
|
|
38
|
+
context_size: 1000000
|
|
39
|
+
temperature: 0.2
|
|
40
|
+
maxRetries: 4
|
|
41
|
+
max_tokens: 4096
|
|
42
|
+
top_p: 0.9
|
|
43
|
+
knowledgebases: []
|
|
44
|
+
toolsets: []
|
|
45
|
+
tags: []
|
|
46
|
+
nodes:
|
|
47
|
+
- type: agent
|
|
48
|
+
key: Agent_WechatPersonalAdmin
|
|
49
|
+
position:
|
|
50
|
+
x: 820
|
|
51
|
+
y: 20
|
|
52
|
+
entity:
|
|
53
|
+
key: Agent_WechatPersonalAdmin
|
|
54
|
+
name: wechat-personal-admin-assistant
|
|
55
|
+
title: 个人微信管理员
|
|
56
|
+
description: 管理组织内 wx2.0 个人微信集成、账号、会话、消息日志和回调配置的 Agent
|
|
57
|
+
avatar:
|
|
58
|
+
emoji:
|
|
59
|
+
id: toolbox
|
|
60
|
+
background: rgb(220, 252, 231)
|
|
61
|
+
prompt: >
|
|
62
|
+
You are the Personal WeChat administrator assistant for this organization.
|
|
63
|
+
|
|
64
|
+
Your job is to help administrators configure and operate wx2.0 personal WeChat integrations:
|
|
65
|
+
review organization-wide integration status, callback URLs, account callback health, conversation
|
|
66
|
+
bindings, message logs, dispatch errors, and outbound reply failures. Use the Personal WeChat
|
|
67
|
+
runtime tools when answering operational questions.
|
|
68
|
+
|
|
69
|
+
This assistant is not connected to inbound WeChat messages and must not act like an end-user
|
|
70
|
+
chat responder. Do not configure a Personal WeChat trigger in this administrator assistant.
|
|
71
|
+
|
|
72
|
+
When an operation can affect a specific wx2.0 integration or account, ask for or use an explicit
|
|
73
|
+
integrationId and uuid before taking action. Never guess an integration from a similar name when
|
|
74
|
+
multiple integrations exist.
|
|
75
|
+
|
|
76
|
+
Do not reveal API tokens, callback secrets, hidden routing details, queue identifiers, or system
|
|
77
|
+
configuration that is not needed by the administrator. Callback URLs may be shown only as setup
|
|
78
|
+
instructions for administrators.
|
|
79
|
+
|
|
80
|
+
Prefer Chinese for Chinese administrators; otherwise mirror the user's language. Keep answers concise
|
|
81
|
+
and include concrete next steps when diagnosing failures.
|
|
82
|
+
collaboratorNames: []
|
|
83
|
+
toolsetIds: []
|
|
84
|
+
knowledgebaseIds: []
|
|
85
|
+
hash: wechat-personal-admin-agent-v1
|
|
86
|
+
- type: workflow
|
|
87
|
+
key: Middleware_WechatPersonalRuntime
|
|
88
|
+
position:
|
|
89
|
+
x: 1140
|
|
90
|
+
y: 280
|
|
91
|
+
entity:
|
|
92
|
+
type: middleware
|
|
93
|
+
key: Middleware_WechatPersonalRuntime
|
|
94
|
+
title: Personal WeChat Runtime Tools
|
|
95
|
+
provider: WechatPersonalRuntimeMiddleware
|
|
96
|
+
required: true
|
|
97
|
+
options: {}
|
|
98
|
+
hash: wechat-personal-admin-middleware-v1
|
|
99
|
+
connections:
|
|
100
|
+
- type: workflow
|
|
101
|
+
key: Agent_WechatPersonalAdmin/Middleware_WechatPersonalRuntime
|
|
102
|
+
from: Agent_WechatPersonalAdmin
|
|
103
|
+
to: Middleware_WechatPersonalRuntime
|