openclaw-plugin-yuanbao 2.3.1 → 2.5.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/dist/openclaw.plugin.json +1 -1
- package/dist/src/channel.js +51 -39
- package/dist/src/config-schema.js +2 -2
- package/dist/src/dm/directory-cache.d.ts +2 -3
- package/dist/src/dm/directory.d.ts +2 -3
- package/dist/src/dm/directory.js +4 -6
- package/dist/src/dm/handle-action.js +1 -1
- package/dist/src/dm/send-dm.js +1 -1
- package/dist/src/logger.js +1 -0
- package/dist/src/media.d.ts +1 -0
- package/dist/src/media.js +1 -1
- package/dist/src/message-handler/context.d.ts +2 -0
- package/dist/src/message-handler/inbound.js +18 -101
- package/dist/src/message-handler/outbound.d.ts +2 -0
- package/dist/src/message-handler/outbound.js +49 -27
- package/dist/src/message-tool/action-runtime.js +20 -3
- package/dist/src/message-tool/hints.js +3 -0
- package/dist/src/module/log-upload/index.js +8 -11
- package/dist/src/outbound-queue.d.ts +15 -2
- package/dist/src/outbound-queue.js +167 -25
- package/dist/src/sticker/sticker-sender.d.ts +2 -0
- package/dist/src/sticker/sticker-sender.js +2 -1
- package/dist/src/targets.d.ts +13 -0
- package/dist/src/targets.js +40 -0
- package/dist/src/tools/member.js +1 -0
- package/dist/src/trace/context.d.ts +13 -0
- package/dist/src/trace/context.js +64 -0
- package/dist/src/types.d.ts +8 -0
- package/dist/src/yuanbao-server/ws/biz-codec.js +24 -1
- package/dist/src/yuanbao-server/ws/gateway.js +18 -2
- package/dist/src/yuanbao-server/ws/proto/biz.json +28 -0
- package/dist/src/yuanbao-server/ws/types.d.ts +4 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/src/channel.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { DEFAULT_ACCOUNT_ID, deleteAccountFromConfigSection, setAccountEnabledInConfigSection,
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, deleteAccountFromConfigSection, setAccountEnabledInConfigSection, } from 'openclaw/plugin-sdk/core';
|
|
2
|
+
import { formatPairingApproveHint } from 'openclaw/plugin-sdk/mattermost';
|
|
2
3
|
import { listYuanbaoAccountIds, resolveDefaultYuanbaoAccountId, resolveYuanbaoAccount } from './accounts.js';
|
|
3
4
|
import { yuanbaoConfigSchema } from './config-schema.js';
|
|
4
5
|
import { yuanbaoOnboardingAdapter } from './onboarding.js';
|
|
5
|
-
import { createLog
|
|
6
|
+
import { createLog } from './logger.js';
|
|
6
7
|
import { yuanbaoSetupAdapter } from './setup.js';
|
|
7
8
|
import { startYuanbaoWsGateway, getActiveWsClient } from './yuanbao-server/ws/index.js';
|
|
8
9
|
import { getYuanbaoRuntime } from './runtime.js';
|
|
9
10
|
import { sendYuanbaoMessage, sendYuanbaoGroupMessage } from './message-handler/index.js';
|
|
10
11
|
import { initOutboundQueue, destroyOutboundQueue, getOutboundQueue } from './outbound-queue.js';
|
|
11
|
-
import {
|
|
12
|
+
import { ChatType, getGroupCode, parseTarget } from './targets.js';
|
|
12
13
|
import { buildMessageToolHints, yuanbaoMessageActions } from './message-tool/index.js';
|
|
13
14
|
function toChannelResult(result) {
|
|
14
15
|
return {
|
|
@@ -18,6 +19,16 @@ function toChannelResult(result) {
|
|
|
18
19
|
error: result.error ? new Error(result.error) : undefined,
|
|
19
20
|
};
|
|
20
21
|
}
|
|
22
|
+
function buildMinCtx(account, wsClient) {
|
|
23
|
+
return {
|
|
24
|
+
account,
|
|
25
|
+
config: account.config,
|
|
26
|
+
core: {},
|
|
27
|
+
log: { info: () => { }, warn: () => { }, error: () => { }, verbose: () => { } },
|
|
28
|
+
wsClient,
|
|
29
|
+
groupCode: getGroupCode(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
21
32
|
async function sendTextToTarget(account, target, text, wsClient) {
|
|
22
33
|
const minCtx = wsClient
|
|
23
34
|
? {
|
|
@@ -28,13 +39,11 @@ async function sendTextToTarget(account, target, text, wsClient) {
|
|
|
28
39
|
wsClient,
|
|
29
40
|
}
|
|
30
41
|
: undefined;
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
const { chatType, target: targetId } = parseTarget(target, account.accountId);
|
|
43
|
+
if (chatType === ChatType.GROUP) {
|
|
44
|
+
return sendYuanbaoGroupMessage({ account, groupCode: targetId, text, fromAccount: account.botId, ctx: minCtx });
|
|
33
45
|
}
|
|
34
|
-
|
|
35
|
-
return sendYuanbaoMessage({ account, toAccount: target.slice('direct:'.length), text, fromAccount: account.botId, ctx: minCtx });
|
|
36
|
-
}
|
|
37
|
-
return sendYuanbaoMessage({ account, toAccount: target, text, fromAccount: account.botId, ctx: minCtx });
|
|
46
|
+
return sendYuanbaoMessage({ account, toAccount: targetId, text, fromAccount: account.botId, ctx: minCtx });
|
|
38
47
|
}
|
|
39
48
|
const meta = {
|
|
40
49
|
id: 'yuanbao',
|
|
@@ -157,8 +166,8 @@ export const yuanbaoPlugin = {
|
|
|
157
166
|
streaming: {
|
|
158
167
|
blockStreamingChunkMaxChars: 3000,
|
|
159
168
|
blockStreamingCoalesceDefaults: {
|
|
160
|
-
minChars:
|
|
161
|
-
idleMs:
|
|
169
|
+
minChars: 2800,
|
|
170
|
+
idleMs: 5000,
|
|
162
171
|
},
|
|
163
172
|
},
|
|
164
173
|
outbound: {
|
|
@@ -166,42 +175,39 @@ export const yuanbaoPlugin = {
|
|
|
166
175
|
chunkerMode: 'markdown',
|
|
167
176
|
textChunkLimit: 3000,
|
|
168
177
|
chunker: (text, limit) => getYuanbaoRuntime()?.channel.text.chunkMarkdownText(text, limit) ?? [text],
|
|
169
|
-
sendText: async (
|
|
178
|
+
sendText: async (params) => {
|
|
179
|
+
const slog = createLog('channel.utbound');
|
|
180
|
+
const { cfg, accountId, to: _to, text } = params;
|
|
170
181
|
const to = _to.replace(/^yuanbao:/, '');
|
|
171
182
|
const account = resolveYuanbaoAccount({ cfg, accountId: accountId ?? undefined });
|
|
172
|
-
|
|
173
|
-
if (dmTarget?.kind === 'user') {
|
|
174
|
-
const dmResult = await sendDM(to, text, { account });
|
|
175
|
-
return {
|
|
176
|
-
channel: 'yuanbao',
|
|
177
|
-
ok: dmResult.ok,
|
|
178
|
-
messageId: dmResult.messageId ?? '',
|
|
179
|
-
error: dmResult.error
|
|
180
|
-
? new Error('detail' in dmResult.error ? dmResult.error.detail : dmResult.error.kind)
|
|
181
|
-
: undefined,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
183
|
+
slog.info('sendText', { accountId, to });
|
|
184
184
|
const wsClient = getActiveWsClient(account.accountId);
|
|
185
185
|
if (!wsClient) {
|
|
186
186
|
return { channel: 'yuanbao', ok: false, messageId: '', error: new Error(`WebSocket client not connected for account ${account.accountId}`) };
|
|
187
187
|
}
|
|
188
188
|
const queueManager = getOutboundQueue(account.accountId);
|
|
189
189
|
if (queueManager) {
|
|
190
|
-
const
|
|
191
|
-
const session = queueManager.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
const { chatType, target, sessionKey } = parseTarget(to, account.accountId);
|
|
191
|
+
const session = queueManager.getOrCreateSession(sessionKey, {
|
|
192
|
+
chatType,
|
|
193
|
+
account,
|
|
194
|
+
target,
|
|
195
|
+
fromAccount: account.botId,
|
|
196
|
+
ctx: buildMinCtx(account, wsClient),
|
|
197
|
+
});
|
|
198
|
+
await session.push({ type: 'text', text });
|
|
199
|
+
await session.flush();
|
|
200
|
+
return { channel: 'yuanbao', ok: true, messageId: '' };
|
|
197
201
|
}
|
|
198
202
|
return toChannelResult(await sendTextToTarget(account, to, text, wsClient));
|
|
199
203
|
},
|
|
200
|
-
sendMedia: async (
|
|
204
|
+
sendMedia: async (params) => {
|
|
205
|
+
const slog = createLog('channel.outbound');
|
|
206
|
+
const { cfg, accountId, to: _to, mediaUrl, text, mediaLocalRoots } = params;
|
|
201
207
|
const to = _to.replace(/^yuanbao:/, '');
|
|
202
208
|
const account = resolveYuanbaoAccount({ cfg, accountId: accountId ?? undefined });
|
|
203
209
|
const wsClient = getActiveWsClient(account.accountId);
|
|
204
|
-
|
|
210
|
+
slog.info('sendMedia', { accountId, to, mediaUrl, text });
|
|
205
211
|
if (!wsClient) {
|
|
206
212
|
return { channel: 'yuanbao', ok: false, messageId: '', error: new Error(`WebSocket client not connected for account ${account.accountId}`) };
|
|
207
213
|
}
|
|
@@ -210,14 +216,20 @@ export const yuanbaoPlugin = {
|
|
|
210
216
|
}
|
|
211
217
|
const queueManager = getOutboundQueue(account.accountId);
|
|
212
218
|
if (queueManager) {
|
|
213
|
-
const
|
|
214
|
-
const session = queueManager.
|
|
215
|
-
|
|
219
|
+
const { chatType, target, sessionKey } = parseTarget(to, account.accountId);
|
|
220
|
+
const session = queueManager.getOrCreateSession(sessionKey, {
|
|
221
|
+
chatType,
|
|
222
|
+
account,
|
|
223
|
+
target,
|
|
224
|
+
fromAccount: account.botId,
|
|
225
|
+
ctx: buildMinCtx(account, wsClient),
|
|
226
|
+
});
|
|
227
|
+
if (text?.trim()) {
|
|
216
228
|
await session.push({ type: 'text', text });
|
|
217
|
-
await session.push({ type: 'media', mediaUrl, mediaLocalRoots });
|
|
218
|
-
return { channel: 'yuanbao', ok: true, messageId: '' };
|
|
219
229
|
}
|
|
220
|
-
|
|
230
|
+
await session.push({ type: 'media', mediaUrl, mediaLocalRoots });
|
|
231
|
+
await session.flush();
|
|
232
|
+
return { channel: 'yuanbao', ok: true, messageId: '' };
|
|
221
233
|
}
|
|
222
234
|
return { channel: 'yuanbao', ok: false, messageId: '', error: new Error('No session found') };
|
|
223
235
|
},
|
|
@@ -44,7 +44,7 @@ export const yuanbaoConfigSchema = {
|
|
|
44
44
|
title: '消息聚合最小字符数',
|
|
45
45
|
description: 'merge-text 策略下,缓冲区积累到此字符数后触发发送',
|
|
46
46
|
minimum: 1,
|
|
47
|
-
default:
|
|
47
|
+
default: 2800,
|
|
48
48
|
},
|
|
49
49
|
maxChars: {
|
|
50
50
|
type: 'integer',
|
|
@@ -58,7 +58,7 @@ export const yuanbaoConfigSchema = {
|
|
|
58
58
|
title: '空闲自动发送超时 (ms)',
|
|
59
59
|
description: 'merge-text 策略下,超过该时长无新内容时自动发送缓冲区',
|
|
60
60
|
minimum: 0,
|
|
61
|
-
default:
|
|
61
|
+
default: 5000,
|
|
62
62
|
},
|
|
63
63
|
mediaMaxMb: {
|
|
64
64
|
type: 'number',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export interface CachedUserEntry {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
handle?: string;
|
|
2
|
+
userId: string;
|
|
3
|
+
nickName?: string;
|
|
5
4
|
}
|
|
6
5
|
export declare function getCachedMember(key: string): CachedUserEntry | undefined;
|
|
7
6
|
export declare function cacheMember(key: string, entry: CachedUserEntry): void;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { CachedUserEntry } from './directory-cache.js';
|
|
2
2
|
export interface DirectoryEntry {
|
|
3
3
|
kind: 'user' | 'group';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
handle?: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
nickName: string;
|
|
7
6
|
}
|
|
8
7
|
export declare function resolveUsername(nameOrHandle: string, accountId: string, groupCode?: string): CachedUserEntry | null;
|
|
9
8
|
export declare function listKnownPeers(accountId: string): DirectoryEntry[];
|
package/dist/src/dm/directory.js
CHANGED
|
@@ -19,9 +19,8 @@ export function resolveUsername(nameOrHandle, accountId, groupCode = '') {
|
|
|
19
19
|
|| u.userId.toLowerCase() === query.toLowerCase());
|
|
20
20
|
const best = exactMatch ?? results[0];
|
|
21
21
|
const entry = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
handle: best.nickName,
|
|
22
|
+
userId: best.userId,
|
|
23
|
+
nickName: best.nickName,
|
|
25
24
|
};
|
|
26
25
|
cacheMember(query, entry);
|
|
27
26
|
cacheMember(best.nickName, entry);
|
|
@@ -44,9 +43,8 @@ export function listKnownPeers(accountId) {
|
|
|
44
43
|
seen.add(u.userId);
|
|
45
44
|
entries.push({
|
|
46
45
|
kind: 'user',
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
handle: u.nickName,
|
|
46
|
+
userId: u.userId,
|
|
47
|
+
nickName: u.nickName,
|
|
50
48
|
});
|
|
51
49
|
}
|
|
52
50
|
}
|
|
@@ -52,7 +52,7 @@ export async function handleAction(ctx) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
if (target.kind === 'user') {
|
|
55
|
-
|
|
55
|
+
log.info('处理 DM 发送', { to, targetId: target.id, senderId: ctx.requesterSenderId });
|
|
56
56
|
const senderId = ctx.requesterSenderId ?? '';
|
|
57
57
|
const accessResult = enforceDMAccess(senderId, target.id, message.length, DEFAULT_DM_ACCESS_POLICY);
|
|
58
58
|
if (!accessResult.allowed) {
|
package/dist/src/dm/send-dm.js
CHANGED
package/dist/src/logger.js
CHANGED
package/dist/src/media.d.ts
CHANGED
package/dist/src/media.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk';
|
|
2
2
|
import type { ResolvedYuanbaoAccount } from '../types.js';
|
|
3
3
|
import type { YuanbaoWsClient } from '../yuanbao-server/ws/index.js';
|
|
4
|
+
import type { YuanbaoTraceContext } from '../trace/context.js';
|
|
4
5
|
export type MessageHandlerContext = {
|
|
5
6
|
groupCode?: string;
|
|
6
7
|
account: ResolvedYuanbaoAccount;
|
|
@@ -17,6 +18,7 @@ export type MessageHandlerContext = {
|
|
|
17
18
|
lastOutboundAt?: number;
|
|
18
19
|
}) => void;
|
|
19
20
|
wsClient: YuanbaoWsClient;
|
|
21
|
+
traceContext?: YuanbaoTraceContext;
|
|
20
22
|
abortSignal?: AbortSignal;
|
|
21
23
|
};
|
|
22
24
|
export declare const YUANBAO_FINAL_TEXT_CHUNK_LIMIT = 3000;
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { recordPendingHistoryEntryIfEnabled, buildPendingHistoryContextFromMap, clearHistoryEntriesIfEnabled, resolveControlCommandGate, } from 'openclaw/plugin-sdk/mattermost';
|
|
2
|
-
import { downloadMediasToLocalFiles
|
|
2
|
+
import { downloadMediasToLocalFiles } from '../media.js';
|
|
3
3
|
import { resolveOutboundSenderAccount, rewriteSlashCommand, YUANBAO_FINAL_TEXT_CHUNK_LIMIT, } from './context.js';
|
|
4
4
|
import { extractTextFromMsgBody } from './extract.js';
|
|
5
|
-
import { sendYuanbaoMessage, sendYuanbaoMessageBody,
|
|
5
|
+
import { sendYuanbaoMessage, sendYuanbaoMessageBody, sendYuanbaoGroupMessageBody, executeReply, } from './outbound.js';
|
|
6
6
|
import { buildOutboundMsgBody, prepareOutboundContent } from './handlers/index.js';
|
|
7
7
|
import { parseQuoteFromCloudCustomData, formatQuoteContext } from './quote.js';
|
|
8
8
|
import { getMember } from '../module/member.js';
|
|
9
|
-
import { createLog
|
|
9
|
+
import { createLog } from '../logger.js';
|
|
10
10
|
import { getOutboundQueue } from '../outbound-queue.js';
|
|
11
11
|
import { UPGRADE_COMMAND_NAMES } from '../commands/upgrade.js';
|
|
12
|
-
import { sendStickerYuanbao } from '../sticker/sticker-sender.js';
|
|
13
|
-
import { getCachedSticker } from '../sticker/sticker-cache.js';
|
|
14
12
|
import { dispatchSystemCallback } from './system-callbacks.js';
|
|
15
13
|
import { chatHistories, chatMediaHistories, recordMediaHistory, } from './chat-history.js';
|
|
16
14
|
import './callbacks/recall.js';
|
|
15
|
+
import { setGroupCode } from '../targets.js';
|
|
17
16
|
const conversationQueues = new Map();
|
|
18
17
|
function enqueueForConversation(key, task) {
|
|
19
18
|
const prev = conversationQueues.get(key) ?? Promise.resolve();
|
|
@@ -114,6 +113,7 @@ async function handleC2CMessage(params) {
|
|
|
114
113
|
const { core, config, account } = ctx;
|
|
115
114
|
if (msg.private_from_group_code) {
|
|
116
115
|
ctx.groupCode = msg.private_from_group_code;
|
|
116
|
+
setGroupCode(ctx.groupCode);
|
|
117
117
|
}
|
|
118
118
|
const fromAccount = msg.from_account?.trim() || 'unknown';
|
|
119
119
|
const senderNickname = msg.sender_nickname?.trim() || undefined;
|
|
@@ -125,7 +125,6 @@ async function handleC2CMessage(params) {
|
|
|
125
125
|
}
|
|
126
126
|
const { rawBody, medias } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
127
127
|
log.info(`收到消息 <- ${fromAccount}${senderNickname ? `(${senderNickname})` : ''}, msgKey: ${msg.msg_key}`);
|
|
128
|
-
log.debug('消息内容', { user_input: rawBody });
|
|
129
128
|
const quoteInfo = parseQuoteFromCloudCustomData(msg.cloud_custom_data);
|
|
130
129
|
if (quoteInfo) {
|
|
131
130
|
log.info(`检测到引用消息, 引用来自: ${quoteInfo.sender_nickname || quoteInfo.sender_id || 'unknown'}`);
|
|
@@ -178,7 +177,7 @@ async function handleC2CMessage(params) {
|
|
|
178
177
|
await sendYuanbaoMessage({
|
|
179
178
|
account,
|
|
180
179
|
toAccount: fromAccount,
|
|
181
|
-
text: '📦
|
|
180
|
+
text: '📦 正在导出问题日志并压缩打包发送,请稍后...',
|
|
182
181
|
fromAccount: outboundSender,
|
|
183
182
|
ctx,
|
|
184
183
|
});
|
|
@@ -220,6 +219,7 @@ async function handleC2CMessage(params) {
|
|
|
220
219
|
envelope: envelopeOptions,
|
|
221
220
|
body: bodyWithQuote,
|
|
222
221
|
});
|
|
222
|
+
log.debug(`[msg-trace] inject c2c inbound context: traceId=${ctx.traceContext?.traceId ?? '(none)'}, traceparent=${ctx.traceContext?.traceparent ?? '(none)'}, seqId=${ctx.traceContext?.seqId ?? '(none)'}, from=${fromAccount}`);
|
|
223
223
|
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
224
224
|
Body: body,
|
|
225
225
|
RawBody: bodyWithQuote,
|
|
@@ -235,6 +235,9 @@ async function handleC2CMessage(params) {
|
|
|
235
235
|
Provider: 'yuanbao',
|
|
236
236
|
Surface: 'yuanbao',
|
|
237
237
|
MessageSid: msg.msg_id,
|
|
238
|
+
TraceId: ctx.traceContext?.traceId,
|
|
239
|
+
Traceparent: ctx.traceContext?.traceparent,
|
|
240
|
+
SeqId: ctx.traceContext?.seqId,
|
|
238
241
|
OriginatingChannel: 'yuanbao',
|
|
239
242
|
OriginatingTo: `yuanbao:direct:${fromAccount}`,
|
|
240
243
|
CommandAuthorized: commandAuthorized,
|
|
@@ -249,11 +252,7 @@ async function handleC2CMessage(params) {
|
|
|
249
252
|
log.error('failed updating session meta', { error: String(err) });
|
|
250
253
|
},
|
|
251
254
|
});
|
|
252
|
-
const tableMode =
|
|
253
|
-
cfg: config,
|
|
254
|
-
channel: 'yuanbao',
|
|
255
|
-
accountId: account.accountId,
|
|
256
|
-
});
|
|
255
|
+
const tableMode = 'off';
|
|
257
256
|
const finalTextChunkLimit = core.channel.text.resolveTextChunkLimit(config, 'yuanbao', account.accountId, {
|
|
258
257
|
fallbackLimit: YUANBAO_FINAL_TEXT_CHUNK_LIMIT,
|
|
259
258
|
});
|
|
@@ -285,38 +284,6 @@ async function handleC2CMessage(params) {
|
|
|
285
284
|
const msgId = msg.msg_id ?? String(msg.msg_seq ?? '');
|
|
286
285
|
const queueManager = getOutboundQueue(account.accountId);
|
|
287
286
|
if (queueManager) {
|
|
288
|
-
const sendMediaOverride = async (url, fallbackText, mediaType, mediaLocalRoots) => {
|
|
289
|
-
if (mediaType === 'sticker') {
|
|
290
|
-
const sticker = getCachedSticker(url);
|
|
291
|
-
if (!sticker) {
|
|
292
|
-
return { ok: false, error: `sticker not found: ${url}` };
|
|
293
|
-
}
|
|
294
|
-
return sendStickerYuanbao({
|
|
295
|
-
account,
|
|
296
|
-
config,
|
|
297
|
-
wsClient: ctx.wsClient,
|
|
298
|
-
toAccount: fromAccount,
|
|
299
|
-
sticker,
|
|
300
|
-
core,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
try {
|
|
304
|
-
logger.info('sendMediaOverride', { url, mediaLocalRoots });
|
|
305
|
-
const uploadResult = await downloadAndUploadMedia(url, core, account, mediaLocalRoots);
|
|
306
|
-
const mime = guessMimeType(uploadResult.filename);
|
|
307
|
-
const msgBody = mime.startsWith('image/')
|
|
308
|
-
? buildImageMsgBody({ url: uploadResult.url, filename: uploadResult.filename, size: uploadResult.size, uuid: uploadResult.uuid, imageInfo: uploadResult.imageInfo })
|
|
309
|
-
: buildFileMsgBody({ url: uploadResult.url, filename: uploadResult.filename, size: uploadResult.size });
|
|
310
|
-
const result = await sendMsgBodyDirect({ account, config, target: fromAccount, msgBody: msgBody, wsClient: ctx.wsClient, core });
|
|
311
|
-
return { ok: result.ok, error: result.error };
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
315
|
-
log.error(`sendMediaOverride 失败: ${errMsg}`);
|
|
316
|
-
const fallback = fallbackText ? `${fallbackText}\n${url}` : url;
|
|
317
|
-
return sendYuanbaoMessage({ account, toAccount: fromAccount, text: fallback, fromAccount: outboundSender, ctx });
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
287
|
queueManager.registerSession(outboundSessionKey, {
|
|
321
288
|
msgId,
|
|
322
289
|
chatType: 'c2c',
|
|
@@ -325,7 +292,6 @@ async function handleC2CMessage(params) {
|
|
|
325
292
|
toAccount: fromAccount,
|
|
326
293
|
fromAccount: outboundSender,
|
|
327
294
|
ctx,
|
|
328
|
-
sendMediaOverride,
|
|
329
295
|
mergeOnFlush: account.disableBlockStreaming,
|
|
330
296
|
});
|
|
331
297
|
log.debug(`[${outboundSessionKey}] 出站队列 session 已注册,msgId: ${msgId}`);
|
|
@@ -356,19 +322,13 @@ async function handleGroupMessage(params) {
|
|
|
356
322
|
const senderNickname = msg.sender_nickname?.trim() || undefined;
|
|
357
323
|
const outboundSender = resolveOutboundSenderAccount(account);
|
|
358
324
|
const glog = createLog('inbound', ctx.log);
|
|
325
|
+
setGroupCode(groupCode);
|
|
359
326
|
if (outboundSender && fromAccount === outboundSender) {
|
|
360
327
|
glog.info('跳过机器人自身消息', { groupCode, fromAccount });
|
|
361
328
|
return;
|
|
362
329
|
}
|
|
363
330
|
const { rawBody, isAtBot, medias, mentions } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
364
|
-
glog.info('
|
|
365
|
-
user_input: rawBody,
|
|
366
|
-
groupCode,
|
|
367
|
-
fromAccount,
|
|
368
|
-
senderNickname,
|
|
369
|
-
msgSeq: msg.msg_seq,
|
|
370
|
-
isAtBot,
|
|
371
|
-
});
|
|
331
|
+
glog.info(`收到群消息 <- group:${groupCode}, from: ${fromAccount}${senderNickname ? `(${senderNickname})` : ''}, msgSeq: ${msg.msg_seq}, isAtBot: ${isAtBot}`);
|
|
372
332
|
const quoteInfo = parseQuoteFromCloudCustomData(msg.cloud_custom_data);
|
|
373
333
|
if (quoteInfo) {
|
|
374
334
|
glog.info(`群消息检测到引用消息, 引用来自: ${quoteInfo.sender_nickname || quoteInfo.sender_id || 'unknown'}`);
|
|
@@ -534,6 +494,7 @@ async function handleGroupMessage(params) {
|
|
|
534
494
|
historyLimit,
|
|
535
495
|
envelopeOptions,
|
|
536
496
|
});
|
|
497
|
+
glog.debug(`[msg-trace] inject group inbound context: traceId=${ctx.traceContext?.traceId ?? '(none)'}, traceparent=${ctx.traceContext?.traceparent ?? '(none)'}, seqId=${ctx.traceContext?.seqId ?? '(none)'}, groupCode=${groupCode}`);
|
|
537
498
|
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
538
499
|
Body: combinedBody,
|
|
539
500
|
BodyForAgent: bodyWithQuote,
|
|
@@ -552,6 +513,9 @@ async function handleGroupMessage(params) {
|
|
|
552
513
|
Provider: 'yuanbao',
|
|
553
514
|
Surface: 'yuanbao',
|
|
554
515
|
MessageSid: msg.msg_id ?? String(msg.msg_seq ?? ''),
|
|
516
|
+
TraceId: ctx.traceContext?.traceId,
|
|
517
|
+
Traceparent: ctx.traceContext?.traceparent,
|
|
518
|
+
SeqId: ctx.traceContext?.seqId,
|
|
555
519
|
OriginatingChannel: 'yuanbao',
|
|
556
520
|
OriginatingTo: `yuanbao:group:${groupCode}`,
|
|
557
521
|
CommandAuthorized: commandAuthorized,
|
|
@@ -566,11 +530,7 @@ async function handleGroupMessage(params) {
|
|
|
566
530
|
glog.error('failed updating group session meta', { error: String(err) });
|
|
567
531
|
},
|
|
568
532
|
});
|
|
569
|
-
const tableMode =
|
|
570
|
-
cfg: config,
|
|
571
|
-
channel: 'yuanbao',
|
|
572
|
-
accountId: account.accountId,
|
|
573
|
-
});
|
|
533
|
+
const tableMode = 'off';
|
|
574
534
|
const finalTextChunkLimit = core.channel.text.resolveTextChunkLimit(config, 'yuanbao', account.accountId, {
|
|
575
535
|
fallbackLimit: YUANBAO_FINAL_TEXT_CHUNK_LIMIT,
|
|
576
536
|
});
|
|
@@ -609,48 +569,6 @@ async function handleGroupMessage(params) {
|
|
|
609
569
|
const groupMsgId = msg.msg_id ?? String(msg.msg_seq ?? '');
|
|
610
570
|
const groupQueueManager = getOutboundQueue(account.accountId);
|
|
611
571
|
if (groupQueueManager) {
|
|
612
|
-
const groupSendMediaOverride = async (url, fallbackText, mediaType, mediaLocalRoots) => {
|
|
613
|
-
if (mediaType === 'sticker') {
|
|
614
|
-
const sticker = await getCachedSticker(url);
|
|
615
|
-
if (!sticker) {
|
|
616
|
-
return { ok: false, error: `sticker not found: ${url}` };
|
|
617
|
-
}
|
|
618
|
-
return sendStickerYuanbao({ account, config, wsClient: ctx.wsClient, toAccount: `group:${groupCode}`, sticker, refMsgId, core });
|
|
619
|
-
}
|
|
620
|
-
try {
|
|
621
|
-
logger.info('groupSendMediaOverride', { url, mediaLocalRoots });
|
|
622
|
-
const uploadResult = await downloadAndUploadMedia(url, core, account, mediaLocalRoots);
|
|
623
|
-
const mime = guessMimeType(uploadResult.filename);
|
|
624
|
-
const msgBody = mime.startsWith('image/')
|
|
625
|
-
? buildImageMsgBody({ url: uploadResult.url, filename: uploadResult.filename, size: uploadResult.size, uuid: uploadResult.uuid, imageInfo: uploadResult.imageInfo })
|
|
626
|
-
: buildFileMsgBody({ url: uploadResult.url, filename: uploadResult.filename, size: uploadResult.size });
|
|
627
|
-
const result = await sendMsgBodyDirect({
|
|
628
|
-
account,
|
|
629
|
-
config,
|
|
630
|
-
target: `group:${groupCode}`,
|
|
631
|
-
msgBody: msgBody,
|
|
632
|
-
wsClient: ctx.wsClient,
|
|
633
|
-
core,
|
|
634
|
-
refMsgId,
|
|
635
|
-
refFromAccount: fromAccount,
|
|
636
|
-
});
|
|
637
|
-
return { ok: result.ok, error: result.error };
|
|
638
|
-
}
|
|
639
|
-
catch (err) {
|
|
640
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
641
|
-
glog.error(`群 sendMediaOverride 失败: ${errMsg}`);
|
|
642
|
-
const fallback = fallbackText ? `${fallbackText}\n${url}` : url;
|
|
643
|
-
return sendYuanbaoGroupMessage({
|
|
644
|
-
account,
|
|
645
|
-
groupCode,
|
|
646
|
-
text: fallback,
|
|
647
|
-
fromAccount: outboundSender,
|
|
648
|
-
refMsgId,
|
|
649
|
-
refFromAccount: fromAccount,
|
|
650
|
-
ctx,
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
572
|
groupQueueManager.registerSession(outboundGroupSessionKey, {
|
|
655
573
|
msgId: groupMsgId,
|
|
656
574
|
chatType: 'group',
|
|
@@ -661,7 +579,6 @@ async function handleGroupMessage(params) {
|
|
|
661
579
|
refMsgId,
|
|
662
580
|
refFromAccount: fromAccount,
|
|
663
581
|
ctx,
|
|
664
|
-
sendMediaOverride: groupSendMediaOverride,
|
|
665
582
|
mergeOnFlush: account.disableBlockStreaming,
|
|
666
583
|
});
|
|
667
584
|
glog.debug(`[${outboundGroupSessionKey}] 群出站队列 session 已注册,msgId: ${groupMsgId}`);
|
|
@@ -4,6 +4,7 @@ import type { ResolvedYuanbaoAccount, YuanbaoMsgBodyElement } from '../types.js'
|
|
|
4
4
|
import type { YuanbaoWsClient } from '../yuanbao-server/ws/index.js';
|
|
5
5
|
import type { OutboundContentItem } from './handlers/index.js';
|
|
6
6
|
import type { MessageHandlerContext } from './context.js';
|
|
7
|
+
import type { YuanbaoTraceContext } from '../trace/context.js';
|
|
7
8
|
export declare function sendYuanbaoMessageBody(params: {
|
|
8
9
|
account: ResolvedYuanbaoAccount;
|
|
9
10
|
toAccount: string;
|
|
@@ -63,6 +64,7 @@ export declare function sendMsgBodyDirect(params: {
|
|
|
63
64
|
refFromAccount?: string;
|
|
64
65
|
wsClient: YuanbaoWsClient;
|
|
65
66
|
core: PluginRuntime;
|
|
67
|
+
traceContext?: YuanbaoTraceContext;
|
|
66
68
|
}): Promise<{
|
|
67
69
|
ok: boolean;
|
|
68
70
|
messageId?: string;
|