openclaw-plugin-yuanbao 2.9.0 → 2.10.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/index.js +3 -0
- package/dist/openclaw.plugin.json +1 -1
- package/dist/src/accounts.js +5 -0
- package/dist/src/commands/slash-commands/index.d.ts +27 -0
- package/dist/src/commands/slash-commands/index.js +116 -0
- package/dist/src/config-schema.d.ts +7 -0
- package/dist/src/config-schema.js +20 -0
- package/dist/src/message-handler/extract.js +1 -1
- package/dist/src/message-handler/handlers/{custom.d.ts → custom/index.d.ts} +1 -1
- package/dist/src/message-handler/handlers/custom/index.js +63 -0
- package/dist/src/message-handler/handlers/custom/link-card.d.ts +2 -0
- package/dist/src/message-handler/handlers/custom/link-card.js +64 -0
- package/dist/src/message-handler/handlers/index.d.ts +1 -1
- package/dist/src/message-handler/handlers/index.js +2 -2
- package/dist/src/message-handler/handlers/types.d.ts +2 -0
- package/dist/src/message-handler/inbound.js +76 -22
- package/dist/src/message-handler/outbound.d.ts +1 -1
- package/dist/src/message-handler/outbound.js +41 -2
- package/dist/src/outbound-queue.d.ts +1 -0
- package/dist/src/outbound-queue.js +20 -4
- package/dist/src/types.d.ts +10 -0
- package/dist/src/types.js +5 -0
- package/dist/src/yuanbao-server/http/request.d.ts +2 -1
- package/dist/src/yuanbao-server/http/request.js +1 -1
- package/dist/src/yuanbao-server/ws/biz-codec.d.ts +5 -1
- package/dist/src/yuanbao-server/ws/biz-codec.js +20 -0
- package/dist/src/yuanbao-server/ws/client.d.ts +3 -1
- package/dist/src/yuanbao-server/ws/client.js +9 -1
- package/dist/src/yuanbao-server/ws/gateway.js +38 -1
- package/dist/src/yuanbao-server/ws/index.d.ts +2 -2
- package/dist/src/yuanbao-server/ws/index.js +1 -1
- package/dist/src/yuanbao-server/ws/proto/biz.json +69 -0
- package/dist/src/yuanbao-server/ws/types.d.ts +19 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/src/message-handler/handlers/custom.js +0 -50
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { initLogger } from './src/logger.js';
|
|
|
5
5
|
import { registerTools } from './src/tools/index.js';
|
|
6
6
|
import { yuanbaoUpgradeCommand, yuanbaobotUpgradeCommand } from './src/commands/upgrade/index.js';
|
|
7
7
|
import { logUploadCommandDefinition } from './src/commands/log-upload.js';
|
|
8
|
+
import { registerPluginCommand } from './src/commands/slash-commands/index.js';
|
|
8
9
|
import { initEnv } from './src/utils/get-env.js';
|
|
9
10
|
import { initBuiltinStickers } from './src/sticker/init-builtin-stickers.js';
|
|
10
11
|
import pluginManifest from './openclaw.plugin.json' with { type: 'json' };
|
|
@@ -29,7 +30,9 @@ const plugin = {
|
|
|
29
30
|
registerTools(api);
|
|
30
31
|
api.registerCommand(yuanbaoUpgradeCommand);
|
|
31
32
|
api.registerCommand(yuanbaobotUpgradeCommand);
|
|
33
|
+
registerPluginCommand(yuanbaobotUpgradeCommand.name, yuanbaobotUpgradeCommand.description);
|
|
32
34
|
api.registerCommand(logUploadCommandDefinition);
|
|
35
|
+
registerPluginCommand(logUploadCommandDefinition.name, logUploadCommandDefinition.description);
|
|
33
36
|
initBuiltinStickers();
|
|
34
37
|
},
|
|
35
38
|
};
|
package/dist/src/accounts.js
CHANGED
|
@@ -2,6 +2,7 @@ import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk/core';
|
|
|
2
2
|
import { normalizeAccountId } from 'openclaw/plugin-sdk/account-id';
|
|
3
3
|
import { logger } from './logger.js';
|
|
4
4
|
import { getCachedBotId } from './yuanbao-server/http/request.js';
|
|
5
|
+
import { DEFAULT_SLOW_REPLY_TIMEOUT_MS, DEFAULT_SLOW_REPLY_TEXT } from './config-schema.js';
|
|
5
6
|
const DEFAULT_API_DOMAIN = 'bot.yuanbao.tencent.com';
|
|
6
7
|
const DEFAULT_WS_GATEWAY_URL = 'wss://bot-wss.yuanbao.tencent.com/wss/connection';
|
|
7
8
|
function listConfiguredAccountIds(cfg) {
|
|
@@ -93,6 +94,8 @@ export function resolveYuanbaoAccount(params) {
|
|
|
93
94
|
const requireMention = merged.requireMention !== undefined ? merged.requireMention : true;
|
|
94
95
|
const fallbackReply = merged.fallbackReply?.trim();
|
|
95
96
|
const markdownHintEnabled = merged.markdownHintEnabled !== false;
|
|
97
|
+
const slowReplyTimeoutMs = merged.slowReplyTimeoutMs ?? DEFAULT_SLOW_REPLY_TIMEOUT_MS;
|
|
98
|
+
const slowReplyText = merged.slowReplyText ?? DEFAULT_SLOW_REPLY_TEXT;
|
|
96
99
|
const configured = Boolean(appKey && appSecret);
|
|
97
100
|
if (!configured && Boolean(yuanbaoConfig)) {
|
|
98
101
|
warnIncompleteConfig(appKey, appSecret);
|
|
@@ -118,6 +121,8 @@ export function resolveYuanbaoAccount(params) {
|
|
|
118
121
|
requireMention,
|
|
119
122
|
fallbackReply,
|
|
120
123
|
markdownHintEnabled,
|
|
124
|
+
slowReplyTimeoutMs,
|
|
125
|
+
slowReplyText,
|
|
121
126
|
config: merged,
|
|
122
127
|
};
|
|
123
128
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { OpenClawConfig } from 'openclaw/plugin-sdk';
|
|
2
|
+
export type CommandItem = {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const SYNC_INFORMATION_TYPE: {
|
|
7
|
+
readonly UNSPECIFIED: 0;
|
|
8
|
+
readonly COMMANDS: 1;
|
|
9
|
+
};
|
|
10
|
+
export declare function registerPluginCommand(name: string, description: string): void;
|
|
11
|
+
export declare function getPluginCommands(): ReadonlyArray<CommandItem>;
|
|
12
|
+
export type SyncInformationPayload = {
|
|
13
|
+
syncType: number;
|
|
14
|
+
botVersion: string;
|
|
15
|
+
pluginVersion: string;
|
|
16
|
+
commandData: {
|
|
17
|
+
botCommands: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}>;
|
|
21
|
+
pluginCommands: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export declare function buildSyncCommandsPayload(config?: OpenClawConfig): Promise<SyncInformationPayload>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { getPluginVersion, getOpenclawVersion } from '../../utils/get-env.js';
|
|
2
|
+
import { createLog } from '../../logger.js';
|
|
3
|
+
let _listChatCommands = null;
|
|
4
|
+
let _listChatCommandsForConfig = null;
|
|
5
|
+
let _commandAuthLoadPromise = null;
|
|
6
|
+
function loadCommandAuth() {
|
|
7
|
+
if (_commandAuthLoadPromise)
|
|
8
|
+
return _commandAuthLoadPromise;
|
|
9
|
+
_commandAuthLoadPromise = import('openclaw/plugin-sdk/command-auth')
|
|
10
|
+
.then((mod) => {
|
|
11
|
+
_listChatCommands = mod.listChatCommands;
|
|
12
|
+
_listChatCommandsForConfig = mod.listChatCommandsForConfig;
|
|
13
|
+
})
|
|
14
|
+
.catch(() => {
|
|
15
|
+
const log = createLog('slash-commands');
|
|
16
|
+
log.warn('openclaw/plugin-sdk/command-auth 不可用(当前 OpenClaw 版本可能较旧),bot_commands 将返回兜底列表');
|
|
17
|
+
});
|
|
18
|
+
return _commandAuthLoadPromise;
|
|
19
|
+
}
|
|
20
|
+
export const SYNC_INFORMATION_TYPE = {
|
|
21
|
+
UNSPECIFIED: 0,
|
|
22
|
+
COMMANDS: 1,
|
|
23
|
+
};
|
|
24
|
+
const FALLBACK_BOT_COMMANDS = [
|
|
25
|
+
{ name: '/help', description: 'Show available commands.' },
|
|
26
|
+
{ name: '/status', description: 'Show current status.' },
|
|
27
|
+
{ name: '/stop', description: 'Stop the current run.' },
|
|
28
|
+
{ name: '/new', description: 'Start a new session.' },
|
|
29
|
+
{ name: '/restart', description: 'Restart OpenClaw.' },
|
|
30
|
+
{ name: '/compact', description: 'Compact the session context.' },
|
|
31
|
+
];
|
|
32
|
+
async function fetchBotCommands(config) {
|
|
33
|
+
const log = createLog('slash-commands');
|
|
34
|
+
await loadCommandAuth();
|
|
35
|
+
if (!_listChatCommandsForConfig && !_listChatCommands) {
|
|
36
|
+
log.info(`command-auth 模块不可用(旧版 OpenClaw),使用兜底命令列表: ${FALLBACK_BOT_COMMANDS.length} 个命令`);
|
|
37
|
+
return FALLBACK_BOT_COMMANDS;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
if (config && _listChatCommandsForConfig) {
|
|
41
|
+
const commands = _listChatCommandsForConfig(config);
|
|
42
|
+
log.debug(`使用配置感知版本获取命令列表: ${commands.length} 个命令`);
|
|
43
|
+
if (commands.length > 0) {
|
|
44
|
+
log.debug('命令原始结构示例:', {
|
|
45
|
+
sample: commands.slice(0, 3).map((c) => ({
|
|
46
|
+
name: c.name, description: c.description, textAliases: c.textAliases, keys: Object.keys(c),
|
|
47
|
+
})),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return commands;
|
|
51
|
+
}
|
|
52
|
+
if (_listChatCommands) {
|
|
53
|
+
log.debug('OpenClawConfig 不可用, 降级为无参版本');
|
|
54
|
+
const commands = _listChatCommands();
|
|
55
|
+
log.debug(`使用无参版本获取命令列表: ${commands.length} 个命令`);
|
|
56
|
+
return commands;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
log.warn(`获取命令列表失败: ${msg}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function toBotCommandItems(commands) {
|
|
67
|
+
const log = createLog('slash-commands');
|
|
68
|
+
const result = [];
|
|
69
|
+
for (const cmd of commands) {
|
|
70
|
+
const aliases = cmd.textAliases;
|
|
71
|
+
let name = '';
|
|
72
|
+
if (aliases && aliases.length > 0) {
|
|
73
|
+
[name] = aliases;
|
|
74
|
+
}
|
|
75
|
+
else if (cmd.name) {
|
|
76
|
+
name = cmd.name;
|
|
77
|
+
}
|
|
78
|
+
if (!name)
|
|
79
|
+
continue;
|
|
80
|
+
if (!name.startsWith('/'))
|
|
81
|
+
name = `/${name}`;
|
|
82
|
+
if (name.length <= 1)
|
|
83
|
+
continue;
|
|
84
|
+
result.push({
|
|
85
|
+
name,
|
|
86
|
+
description: cmd.description ?? '',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
log.debug(`toBotCommandItems 转换结果: ${result.length} 个命令`);
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
const pluginCommands = [];
|
|
93
|
+
export function registerPluginCommand(name, description) {
|
|
94
|
+
const fullName = name.startsWith('/') ? name : `/${name}`;
|
|
95
|
+
if (!pluginCommands.some(c => c.name === fullName)) {
|
|
96
|
+
pluginCommands.push({ name: fullName, description });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function getPluginCommands() {
|
|
100
|
+
return pluginCommands;
|
|
101
|
+
}
|
|
102
|
+
export async function buildSyncCommandsPayload(config) {
|
|
103
|
+
const botVersion = getOpenclawVersion() || '0.0.0';
|
|
104
|
+
const pluginVersion = getPluginVersion() || '0.0.0';
|
|
105
|
+
const rawBotCommands = await fetchBotCommands(config);
|
|
106
|
+
const botCommands = rawBotCommands ? toBotCommandItems(rawBotCommands) : [];
|
|
107
|
+
return {
|
|
108
|
+
syncType: SYNC_INFORMATION_TYPE.COMMANDS,
|
|
109
|
+
botVersion,
|
|
110
|
+
pluginVersion,
|
|
111
|
+
commandData: {
|
|
112
|
+
botCommands: botCommands.map(c => ({ name: c.name, description: c.description })),
|
|
113
|
+
pluginCommands: pluginCommands.map(c => ({ name: c.name, description: c.description })),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import type { ChannelConfigSchema } from 'openclaw/plugin-sdk';
|
|
2
|
+
export declare const DEFAULT_SLOW_REPLY_TIMEOUT_MS = 120000;
|
|
3
|
+
export declare const DEFAULT_SLOW_REPLY_TEXT = "\u4EFB\u52A1\u6709\u70B9\u590D\u6742\uFF0C\u6B63\u5728\u52AA\u529B\u5904\u7406\u4E2D\uFF0C\u8BF7\u8010\u5FC3\u7B49\u5F85...";
|
|
4
|
+
export declare const DEFAULT_MODEL_RATE_LIMIT_TEXT = "\u6A21\u578B\u670D\u52A1\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
5
|
+
export declare const DEFAULT_MODEL_BILLING_DEFAULT_TEXT = "\u4ECA\u65E5\u8BCD\u5143\u6D88\u8017\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u660E\u65E5\u518D\u6765\u548C\u6211\u5BF9\u8BDD\u5427\uFF5E";
|
|
6
|
+
export declare const DEFAULT_MODEL_BILLING_CUSTOM_TEXT = "\u8BCD\u5143\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u72B6\u6001";
|
|
7
|
+
export declare const DEFAULT_MODEL_ERROR_TEXT = "\u6A21\u578B\u670D\u52A1\u51FA\u9519\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578BAPI";
|
|
8
|
+
export declare const DEFAULT_MODEL_ONE_CLICK_ERROR_TEXT = "\u6A21\u578B\u670D\u52A1\u51FA\u9519\u4E86\uFF0C\u8BF7\u5728\"\u6211\u7684 Bot\"\u4E2D\u5C1D\u8BD5\u91CD\u542F\u6216\u8FD8\u539F Bot \u914D\u7F6E";
|
|
2
9
|
export declare const yuanbaoConfigSchema: ChannelConfigSchema;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export const DEFAULT_SLOW_REPLY_TIMEOUT_MS = 120_000;
|
|
2
|
+
export const DEFAULT_SLOW_REPLY_TEXT = '任务有点复杂,正在努力处理中,请耐心等待...';
|
|
3
|
+
export const DEFAULT_MODEL_RATE_LIMIT_TEXT = '模型服务繁忙,请稍后重试';
|
|
4
|
+
export const DEFAULT_MODEL_BILLING_DEFAULT_TEXT = '今日词元消耗已达上限,明日再来和我对话吧~';
|
|
5
|
+
export const DEFAULT_MODEL_BILLING_CUSTOM_TEXT = '词元不足,请检查模型状态';
|
|
6
|
+
export const DEFAULT_MODEL_ERROR_TEXT = '模型服务出错,请检查模型API';
|
|
7
|
+
export const DEFAULT_MODEL_ONE_CLICK_ERROR_TEXT = '模型服务出错了,请在"我的 Bot"中尝试重启或还原 Bot 配置';
|
|
1
8
|
export const yuanbaoConfigSchema = {
|
|
2
9
|
schema: {
|
|
3
10
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
@@ -98,6 +105,19 @@ export const yuanbaoConfigSchema = {
|
|
|
98
105
|
description: '开启后在系统提示词中自动注入指令,防止模型用代码块包裹整个 Markdown 回复',
|
|
99
106
|
default: true,
|
|
100
107
|
},
|
|
108
|
+
slowReplyTimeoutMs: {
|
|
109
|
+
type: 'integer',
|
|
110
|
+
title: '慢回复提醒超时 (ms)',
|
|
111
|
+
description: '收到消息后,在该时长内未推送首条回复则发送慢回复提示文案;0 表示禁用',
|
|
112
|
+
minimum: 0,
|
|
113
|
+
default: DEFAULT_SLOW_REPLY_TIMEOUT_MS,
|
|
114
|
+
},
|
|
115
|
+
slowReplyText: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
title: '慢回复提示文案',
|
|
118
|
+
description: '超过慢回复超时后推送给用户的提示文案',
|
|
119
|
+
default: DEFAULT_SLOW_REPLY_TEXT,
|
|
120
|
+
},
|
|
101
121
|
},
|
|
102
122
|
additionalProperties: false,
|
|
103
123
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getHandler } from './handlers/index.js';
|
|
2
2
|
export function extractTextFromMsgBody(ctx, msgBody) {
|
|
3
|
-
const resData = { rawBody: '', isAtBot: false, medias: [], mentions: [] };
|
|
3
|
+
const resData = { rawBody: '', isAtBot: false, medias: [], mentions: [], linkUrls: [] };
|
|
4
4
|
if (!msgBody || !Array.isArray(msgBody))
|
|
5
5
|
return resData;
|
|
6
6
|
const texts = [];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { MessageElemHandler, MsgBodyItemType } from '
|
|
1
|
+
import type { MessageElemHandler, MsgBodyItemType } from '../types.js';
|
|
2
2
|
export declare function buildAtUserMsgBodyItem(userId: string, senderNickname: any): MsgBodyItemType;
|
|
3
3
|
export declare const customHandler: MessageElemHandler;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createLog } from '../../../logger.js';
|
|
2
|
+
import { extractLinkCard, extractLinkCardUrls } from './link-card.js';
|
|
3
|
+
export function buildAtUserMsgBodyItem(userId, senderNickname) {
|
|
4
|
+
return {
|
|
5
|
+
msg_type: 'TIMCustomElem',
|
|
6
|
+
msg_content: {
|
|
7
|
+
data: JSON.stringify({ elem_type: 1002, text: `@${senderNickname ?? ''}`, user_id: userId }),
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const FALLBACK_TEXT = '[当前消息暂不支持查看]';
|
|
12
|
+
export const customHandler = {
|
|
13
|
+
msgType: 'TIMCustomElem',
|
|
14
|
+
extract(ctx, elem, resData) {
|
|
15
|
+
if (elem.msg_content?.data) {
|
|
16
|
+
try {
|
|
17
|
+
const customContent = JSON.parse(elem.msg_content?.data);
|
|
18
|
+
switch (customContent?.elem_type) {
|
|
19
|
+
case 1002: {
|
|
20
|
+
const { botId } = ctx.account;
|
|
21
|
+
const isAtBotSelf = customContent?.user_id === botId;
|
|
22
|
+
if (!resData.isAtBot) {
|
|
23
|
+
resData.isAtBot = isAtBotSelf;
|
|
24
|
+
}
|
|
25
|
+
createLog('custom').info('@消息', { text: customContent?.text, userId: customContent?.user_id, isAtBot: resData.isAtBot });
|
|
26
|
+
if (customContent?.user_id) {
|
|
27
|
+
resData.mentions.push({
|
|
28
|
+
userId: customContent.user_id,
|
|
29
|
+
text: customContent.text || '',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (isAtBotSelf && customContent.text) {
|
|
33
|
+
resData.botUsername = customContent.text.replace(/^@/, '');
|
|
34
|
+
}
|
|
35
|
+
return customContent.text || undefined;
|
|
36
|
+
}
|
|
37
|
+
case 1010:
|
|
38
|
+
case 1007: {
|
|
39
|
+
const urls = extractLinkCardUrls(customContent);
|
|
40
|
+
if (urls.length > 0)
|
|
41
|
+
resData.linkUrls.push(...urls);
|
|
42
|
+
return extractLinkCard(customContent);
|
|
43
|
+
}
|
|
44
|
+
default:
|
|
45
|
+
return FALLBACK_TEXT;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch { }
|
|
49
|
+
}
|
|
50
|
+
return FALLBACK_TEXT;
|
|
51
|
+
},
|
|
52
|
+
buildMsgBody(data) {
|
|
53
|
+
const customData = typeof data.data === 'string'
|
|
54
|
+
? data.data
|
|
55
|
+
: JSON.stringify(data.data);
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
msg_type: 'TIMCustomElem',
|
|
59
|
+
msg_content: { data: customData },
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const CONTENT_MAX_LENGTH = 1000;
|
|
2
|
+
function isSharedLinkData(data) {
|
|
3
|
+
return typeof data === 'object' && data !== null && data.elem_type === 1010;
|
|
4
|
+
}
|
|
5
|
+
function truncate(text, maxLength) {
|
|
6
|
+
if (text.length <= maxLength)
|
|
7
|
+
return text;
|
|
8
|
+
return `${text.slice(0, maxLength)}...(truncated)`;
|
|
9
|
+
}
|
|
10
|
+
function formatSharedLink(data) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
lines.push(`<share_card name="${data.title ?? ''}">`);
|
|
13
|
+
if (data.link)
|
|
14
|
+
lines.push(` <link>${data.link}</link>`);
|
|
15
|
+
if (data.card_content)
|
|
16
|
+
lines.push(` <preview_title>${truncate(data.card_content, CONTENT_MAX_LENGTH)}</preview_title>`);
|
|
17
|
+
if (data.wechat_des)
|
|
18
|
+
lines.push(` <preview_description>${truncate(data.wechat_des, CONTENT_MAX_LENGTH)}</preview_description>`);
|
|
19
|
+
lines.push(' <instruction>以上仅为分享卡片的摘要预览,请访问 link 获取完整页面内容后再进行分析回复</instruction>');
|
|
20
|
+
lines.push('</share_card>');
|
|
21
|
+
return lines.join('\n');
|
|
22
|
+
}
|
|
23
|
+
function isLinkUnderstandingData(data) {
|
|
24
|
+
return typeof data === 'object' && data !== null && data.elem_type === 1007;
|
|
25
|
+
}
|
|
26
|
+
function parseLinkFromContent(content) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(content);
|
|
29
|
+
return typeof parsed?.link === 'string' ? parsed.link : undefined;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function formatLinkUnderstanding(data) {
|
|
36
|
+
const link = data.content ? parseLinkFromContent(data.content) : undefined;
|
|
37
|
+
if (!link)
|
|
38
|
+
return undefined;
|
|
39
|
+
const lines = [];
|
|
40
|
+
lines.push('<link_understanding>');
|
|
41
|
+
lines.push(` <link>${link}</link>`);
|
|
42
|
+
lines.push(' <instruction>请访问 link 获取完整页面内容后再进行分析回复</instruction>');
|
|
43
|
+
lines.push('</link_understanding>');
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
export function extractLinkCard(customContent) {
|
|
47
|
+
if (isSharedLinkData(customContent)) {
|
|
48
|
+
return formatSharedLink(customContent);
|
|
49
|
+
}
|
|
50
|
+
if (isLinkUnderstandingData(customContent)) {
|
|
51
|
+
return formatLinkUnderstanding(customContent);
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
export function extractLinkCardUrls(customContent) {
|
|
56
|
+
if (isSharedLinkData(customContent) && customContent.link) {
|
|
57
|
+
return [customContent.link];
|
|
58
|
+
}
|
|
59
|
+
if (isLinkUnderstandingData(customContent) && customContent.content) {
|
|
60
|
+
const link = parseLinkFromContent(customContent.content);
|
|
61
|
+
return link ? [link] : [];
|
|
62
|
+
}
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
@@ -7,7 +7,7 @@ export declare function prepareOutboundContent(text: string, groupCode?: string,
|
|
|
7
7
|
export declare function buildOutboundMsgBody(items: OutboundContentItem[]): MsgBodyItemType[];
|
|
8
8
|
export type { MessageElemHandler, MsgBodyItemType, MediaItem, ExtractTextFromMsgBodyResult, OutboundContentItem, } from './types.js';
|
|
9
9
|
export { textHandler } from './text.js';
|
|
10
|
-
export { customHandler, buildAtUserMsgBodyItem } from './custom.js';
|
|
10
|
+
export { customHandler, buildAtUserMsgBodyItem } from './custom/index.js';
|
|
11
11
|
export { imageHandler } from './image.js';
|
|
12
12
|
export { soundHandler } from './sound.js';
|
|
13
13
|
export { fileHandler } from './file.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { textHandler } from './text.js';
|
|
2
|
-
import { customHandler } from './custom.js';
|
|
2
|
+
import { customHandler } from './custom/index.js';
|
|
3
3
|
import { imageHandler } from './image.js';
|
|
4
4
|
import { soundHandler } from './sound.js';
|
|
5
5
|
import { fileHandler } from './file.js';
|
|
@@ -108,7 +108,7 @@ export function buildOutboundMsgBody(items) {
|
|
|
108
108
|
return msgBody;
|
|
109
109
|
}
|
|
110
110
|
export { textHandler } from './text.js';
|
|
111
|
-
export { customHandler, buildAtUserMsgBodyItem } from './custom.js';
|
|
111
|
+
export { customHandler, buildAtUserMsgBodyItem } from './custom/index.js';
|
|
112
112
|
export { imageHandler } from './image.js';
|
|
113
113
|
export { soundHandler } from './sound.js';
|
|
114
114
|
export { fileHandler } from './file.js';
|
|
@@ -102,6 +102,21 @@ function buildGroupHistoryContext(params) {
|
|
|
102
102
|
: undefined;
|
|
103
103
|
return { combinedBody, inboundHistory };
|
|
104
104
|
}
|
|
105
|
+
function createSlowReplyTimer(params) {
|
|
106
|
+
const { timeoutMs, text, send, log } = params;
|
|
107
|
+
if (timeoutMs <= 0)
|
|
108
|
+
return () => { };
|
|
109
|
+
let fired = false;
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
fired = true;
|
|
112
|
+
log.info(`慢回复超时 (${timeoutMs}ms),推送提示: "${text}"`);
|
|
113
|
+
send().catch(err => log.error('慢回复提示发送失败', { error: String(err) }));
|
|
114
|
+
}, timeoutMs);
|
|
115
|
+
return () => {
|
|
116
|
+
if (!fired)
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
};
|
|
119
|
+
}
|
|
105
120
|
function isSkippableBracketPlaceholder(rawBody, mediaCount) {
|
|
106
121
|
if (mediaCount > 0)
|
|
107
122
|
return false;
|
|
@@ -127,7 +142,7 @@ async function handleC2CMessage(params) {
|
|
|
127
142
|
log.info(`跳过机器人自身消息 <- ${fromAccount}`);
|
|
128
143
|
return;
|
|
129
144
|
}
|
|
130
|
-
const { rawBody, medias } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
145
|
+
const { rawBody, medias, linkUrls } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
131
146
|
getMember(account.accountId).recordC2cUser(fromAccount, senderNickname || fromAccount);
|
|
132
147
|
log.info(`收到消息 <- ${fromAccount}${senderNickname ? `(${senderNickname})` : ''}, msgKey: ${msg.msg_key}`);
|
|
133
148
|
const quoteInfo = parseQuoteFromCloudCustomData(msg.cloud_custom_data);
|
|
@@ -258,6 +273,7 @@ async function handleC2CMessage(params) {
|
|
|
258
273
|
...(account.markdownHintEnabled && { GroupSystemPrompt: YUANBAO_MARKDOWN_HINT }),
|
|
259
274
|
...(mediaPaths.length > 0 && { MediaPaths: mediaPaths, MediaPath: mediaPaths[0] }),
|
|
260
275
|
...(mediaTypes.length > 0 && { MediaTypes: mediaTypes, MediaType: mediaTypes[0] }),
|
|
276
|
+
...(linkUrls.length > 0 && { LinkUnderstanding: [...new Set(linkUrls)] }),
|
|
261
277
|
});
|
|
262
278
|
await core.channel.session.recordInboundSession({
|
|
263
279
|
storePath,
|
|
@@ -295,6 +311,12 @@ async function handleC2CMessage(params) {
|
|
|
295
311
|
ctx,
|
|
296
312
|
}),
|
|
297
313
|
};
|
|
314
|
+
const cancelSlowReply = createSlowReplyTimer({
|
|
315
|
+
timeoutMs: account.slowReplyTimeoutMs,
|
|
316
|
+
text: account.slowReplyText,
|
|
317
|
+
send: () => sendYuanbaoMessage({ account, toAccount: fromAccount, text: account.slowReplyText, fromAccount: outboundSender, ctx }),
|
|
318
|
+
log,
|
|
319
|
+
});
|
|
298
320
|
const outboundSessionKey = `direct:${fromAccount}`;
|
|
299
321
|
const msgId = msg.msg_id ?? String(msg.msg_seq ?? '');
|
|
300
322
|
const queueManager = getOutboundQueue(account.accountId);
|
|
@@ -308,24 +330,33 @@ async function handleC2CMessage(params) {
|
|
|
308
330
|
fromAccount: outboundSender,
|
|
309
331
|
ctx,
|
|
310
332
|
mergeOnFlush: account.disableBlockStreaming,
|
|
333
|
+
onFirstSent: cancelSlowReply,
|
|
311
334
|
});
|
|
312
335
|
log.debug(`[${outboundSessionKey}] 出站队列 session 已注册,msgId: ${msgId}`);
|
|
313
336
|
}
|
|
314
337
|
const replyRuntime = buildReplyRuntimeConfig(config, account);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
338
|
+
try {
|
|
339
|
+
await executeReply({
|
|
340
|
+
transport,
|
|
341
|
+
ctx,
|
|
342
|
+
account,
|
|
343
|
+
core,
|
|
344
|
+
config,
|
|
345
|
+
ctxPayload,
|
|
346
|
+
replyRuntime,
|
|
347
|
+
tableMode,
|
|
348
|
+
splitFinalText,
|
|
349
|
+
overflowPolicy: account.overflowPolicy,
|
|
350
|
+
sessionKey: outboundSessionKey,
|
|
351
|
+
appendText: getAppendText(rawBody),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
log.error('executeReply 失败', { error: String(err) });
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
cancelSlowReply();
|
|
359
|
+
}
|
|
329
360
|
log.info(`消息处理完成 <- ${fromAccount}`);
|
|
330
361
|
}
|
|
331
362
|
async function handleGroupMessage(params) {
|
|
@@ -343,7 +374,7 @@ async function handleGroupMessage(params) {
|
|
|
343
374
|
glog.info('跳过机器人自身消息', { groupCode, fromAccount });
|
|
344
375
|
return;
|
|
345
376
|
}
|
|
346
|
-
const { rawBody, isAtBot, medias, mentions } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
377
|
+
const { rawBody, isAtBot, medias, mentions, botUsername, linkUrls } = extractTextFromMsgBody(ctx, msg.msg_body);
|
|
347
378
|
glog.info(`收到群消息 <- group:${groupCode}, from: ${fromAccount}${senderNickname ? `(${senderNickname})` : ''}, msgSeq: ${msg.msg_seq}, isAtBot: ${isAtBot}`);
|
|
348
379
|
const quoteInfo = parseQuoteFromCloudCustomData(msg.cloud_custom_data);
|
|
349
380
|
if (quoteInfo) {
|
|
@@ -448,12 +479,10 @@ async function handleGroupMessage(params) {
|
|
|
448
479
|
const rewrittenBody = rewriteSlashCommand(rawBody, (orig, rewritten) => {
|
|
449
480
|
glog.info(`群命令改写: "${orig}" -> "${rewritten}"`);
|
|
450
481
|
});
|
|
451
|
-
const mentionsContext = mentions && mentions.length > 0
|
|
452
|
-
? `\n[消息中@了以下用户: ${mentions.map(m => `${m.text}(userId: ${m.userId})`).join(', ')}]`
|
|
453
|
-
: '';
|
|
482
|
+
const mentionsContext = mentions && mentions.length > 0 ? `消息中@了以下用户: ${mentions.map(m => `${m.text}(userId: ${m.userId})`).join(', ')}` : null;
|
|
454
483
|
const bodyWithQuote = quoteInfo
|
|
455
|
-
? `${formatQuoteContext(quoteInfo)}\n${rewrittenBody}
|
|
456
|
-
: `${rewrittenBody}
|
|
484
|
+
? `${formatQuoteContext(quoteInfo)}\n${rewrittenBody}`
|
|
485
|
+
: `${rewrittenBody}`;
|
|
457
486
|
glog.debug(`开始处理群消息, 账号: ${account.accountId}, group: ${groupCode}`);
|
|
458
487
|
const historyMedias = getHistoryMedias(groupCode, fromAccount, quoteInfo);
|
|
459
488
|
if (medias.length > 0) {
|
|
@@ -525,9 +554,16 @@ async function handleGroupMessage(params) {
|
|
|
525
554
|
OriginatingChannel: 'yuanbao',
|
|
526
555
|
OriginatingTo: `yuanbao:group:${groupCode}`,
|
|
527
556
|
CommandAuthorized: commandAuthorized,
|
|
557
|
+
WasMentioned: isAtBot,
|
|
558
|
+
BotUsername: botUsername,
|
|
559
|
+
UntrustedContext: [
|
|
560
|
+
botUsername && `BotUsername(self): ${botUsername}`,
|
|
561
|
+
mentionsContext,
|
|
562
|
+
].filter(Boolean),
|
|
528
563
|
...(account.markdownHintEnabled && { GroupSystemPrompt: YUANBAO_MARKDOWN_HINT }),
|
|
529
564
|
...(mediaPaths.length > 0 && { MediaPaths: mediaPaths, MediaPath: mediaPaths[0] }),
|
|
530
565
|
...(mediaTypes.length > 0 && { MediaTypes: mediaTypes, MediaType: mediaTypes[0] }),
|
|
566
|
+
...(linkUrls.length > 0 && { LinkUnderstanding: [...new Set(linkUrls)] }),
|
|
531
567
|
});
|
|
532
568
|
await core.channel.session.recordInboundSession({
|
|
533
569
|
storePath,
|
|
@@ -574,6 +610,20 @@ async function handleGroupMessage(params) {
|
|
|
574
610
|
};
|
|
575
611
|
const outboundGroupSessionKey = `group:${groupCode}`;
|
|
576
612
|
const groupMsgId = msg.msg_id ?? String(msg.msg_seq ?? '');
|
|
613
|
+
const cancelSlowReply = createSlowReplyTimer({
|
|
614
|
+
timeoutMs: account.slowReplyTimeoutMs,
|
|
615
|
+
text: account.slowReplyText,
|
|
616
|
+
send: () => sendYuanbaoGroupMessageBody({
|
|
617
|
+
account,
|
|
618
|
+
groupCode,
|
|
619
|
+
msgBody: buildOutboundMsgBody(prepareOutboundContent(account.slowReplyText, groupCode, getMember(account.accountId))),
|
|
620
|
+
fromAccount: outboundSender,
|
|
621
|
+
refMsgId: msg.msg_id || msg.msg_key || undefined,
|
|
622
|
+
refFromAccount: fromAccount,
|
|
623
|
+
ctx,
|
|
624
|
+
}),
|
|
625
|
+
log: glog,
|
|
626
|
+
});
|
|
577
627
|
const groupQueueManager = getOutboundQueue(account.accountId);
|
|
578
628
|
if (groupQueueManager) {
|
|
579
629
|
groupQueueManager.registerSession(outboundGroupSessionKey, {
|
|
@@ -587,6 +637,7 @@ async function handleGroupMessage(params) {
|
|
|
587
637
|
refFromAccount: fromAccount,
|
|
588
638
|
ctx,
|
|
589
639
|
mergeOnFlush: account.disableBlockStreaming,
|
|
640
|
+
onFirstSent: cancelSlowReply,
|
|
590
641
|
});
|
|
591
642
|
glog.debug(`[${outboundGroupSessionKey}] 群出站队列 session 已注册,msgId: ${groupMsgId}`);
|
|
592
643
|
}
|
|
@@ -609,9 +660,12 @@ async function handleGroupMessage(params) {
|
|
|
609
660
|
});
|
|
610
661
|
}
|
|
611
662
|
catch (err) {
|
|
663
|
+
glog.error('executeReply 失败', { error: String(err) });
|
|
612
664
|
const session = groupQueueManager?.getSession(outboundGroupSessionKey);
|
|
613
665
|
session?.abort();
|
|
614
|
-
|
|
666
|
+
}
|
|
667
|
+
finally {
|
|
668
|
+
cancelSlowReply();
|
|
615
669
|
}
|
|
616
670
|
clearHistoryEntriesIfEnabled({
|
|
617
671
|
historyMap: chatHistories,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk';
|
|
2
2
|
import type { MarkdownTableMode } from 'openclaw/plugin-sdk/config-runtime';
|
|
3
|
-
import type
|
|
3
|
+
import { type ResolvedYuanbaoAccount, type 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';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BotCreateType, } from '../types.js';
|
|
2
|
+
import { DEFAULT_MODEL_RATE_LIMIT_TEXT, DEFAULT_MODEL_BILLING_DEFAULT_TEXT, DEFAULT_MODEL_BILLING_CUSTOM_TEXT, DEFAULT_MODEL_ONE_CLICK_ERROR_TEXT, } from '../config-schema.js';
|
|
1
3
|
import { WS_HEARTBEAT } from '../yuanbao-server/ws/index.js';
|
|
2
4
|
import { createLog } from '../logger.js';
|
|
3
5
|
import { prepareOutboundContent, buildOutboundMsgBody } from './handlers/index.js';
|
|
@@ -151,6 +153,30 @@ export async function sendMsgBodyDirect(params) {
|
|
|
151
153
|
ctx: minCtx,
|
|
152
154
|
});
|
|
153
155
|
}
|
|
156
|
+
const RATE_LIMIT_RE = /rate[_ ]?limit|too many requests|\b429\b/i;
|
|
157
|
+
const BILLING_RE = /billing|credits?|insufficient|payment|\b402\b/i;
|
|
158
|
+
function classifyErrorText(text) {
|
|
159
|
+
if (RATE_LIMIT_RE.test(text))
|
|
160
|
+
return 'rate_limit';
|
|
161
|
+
if (BILLING_RE.test(text))
|
|
162
|
+
return 'billing';
|
|
163
|
+
return 'other';
|
|
164
|
+
}
|
|
165
|
+
function resolveDeliverErrorText(params) {
|
|
166
|
+
const { originalText, botCreateType, config } = params;
|
|
167
|
+
if (botCreateType !== BotCreateType.ONE_CLICK)
|
|
168
|
+
return originalText;
|
|
169
|
+
const model = config?.agents?.defaults?.model;
|
|
170
|
+
const modelId = typeof model === 'string' ? model : model?.primary;
|
|
171
|
+
const isYuanbaoModel = Boolean(modelId?.startsWith('yuanbao'));
|
|
172
|
+
const kind = classifyErrorText(originalText);
|
|
173
|
+
if (kind === 'rate_limit')
|
|
174
|
+
return DEFAULT_MODEL_RATE_LIMIT_TEXT;
|
|
175
|
+
if (kind === 'billing') {
|
|
176
|
+
return isYuanbaoModel ? DEFAULT_MODEL_BILLING_DEFAULT_TEXT : DEFAULT_MODEL_BILLING_CUSTOM_TEXT;
|
|
177
|
+
}
|
|
178
|
+
return isYuanbaoModel ? DEFAULT_MODEL_ONE_CLICK_ERROR_TEXT : originalText;
|
|
179
|
+
}
|
|
154
180
|
export async function executeReply(params) {
|
|
155
181
|
const { transport, ctx, account, core, replyRuntime, splitFinalText, overflowPolicy, ctxPayload, sessionKey, appendText, } = params;
|
|
156
182
|
const rlog = createLog('outbound');
|
|
@@ -190,7 +216,9 @@ export async function executeReply(params) {
|
|
|
190
216
|
rlog.warn(`[deliver][${account.accountId}] 回复已中止,停止处理后续回复块`);
|
|
191
217
|
return;
|
|
192
218
|
}
|
|
193
|
-
rlog.info('[deliver] 收到回复数据', {
|
|
219
|
+
rlog.info('[deliver] 收到回复数据', {
|
|
220
|
+
kind: info.kind, isError: payload.isError, model_output: payload.text,
|
|
221
|
+
});
|
|
194
222
|
if (payload.isReasoning) {
|
|
195
223
|
rlog.info('[deliver] Reasoning', { text: payload.text });
|
|
196
224
|
return;
|
|
@@ -205,7 +233,18 @@ export async function executeReply(params) {
|
|
|
205
233
|
if (info.kind === 'final') {
|
|
206
234
|
hasFinalInfo = true;
|
|
207
235
|
}
|
|
208
|
-
|
|
236
|
+
let text = payload.text ?? '';
|
|
237
|
+
if (payload.isError && text) {
|
|
238
|
+
const replaced = resolveDeliverErrorText({
|
|
239
|
+
originalText: text,
|
|
240
|
+
botCreateType: account.createType,
|
|
241
|
+
config: replyRuntime.config,
|
|
242
|
+
});
|
|
243
|
+
if (replaced !== text) {
|
|
244
|
+
rlog.info('[deliver] 错误文案已替换', { original: text, replaced });
|
|
245
|
+
}
|
|
246
|
+
text = replaced;
|
|
247
|
+
}
|
|
209
248
|
if (session) {
|
|
210
249
|
if (text.trim()) {
|
|
211
250
|
await session.push({ type: 'text', text });
|
|
@@ -54,7 +54,7 @@ function createManager(config) {
|
|
|
54
54
|
existing.abort();
|
|
55
55
|
}
|
|
56
56
|
const onComplete = () => sessions.delete(sessionKey);
|
|
57
|
-
const { chatType, account, target, fromAccount, refMsgId, refFromAccount, ctx, msgId, mergeOnFlush, toAccount, } = options;
|
|
57
|
+
const { chatType, account, target, fromAccount, refMsgId, refFromAccount, ctx, msgId, mergeOnFlush, toAccount, onFirstSent, } = options;
|
|
58
58
|
const heartbeatTarget = (toAccount ?? (chatType === 'c2c' ? target : '')).trim();
|
|
59
59
|
const heartbeatGroupCode = chatType === 'group' ? target : undefined;
|
|
60
60
|
const sendText = async (text) => {
|
|
@@ -149,9 +149,25 @@ function createManager(config) {
|
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
|
+
let firstSentFired = false;
|
|
153
|
+
const fireFirstSent = () => {
|
|
154
|
+
if (!firstSentFired && onFirstSent) {
|
|
155
|
+
firstSentFired = true;
|
|
156
|
+
onFirstSent();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function wrapSend(fn) {
|
|
160
|
+
return (async (...args) => {
|
|
161
|
+
fireFirstSent();
|
|
162
|
+
return fn(...args);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const wrappedSendText = wrapSend(sendText);
|
|
166
|
+
const wrappedSendSticker = wrapSend(sendSticker);
|
|
167
|
+
const wrappedSendMedia = wrapSend(sendMedia);
|
|
152
168
|
let session;
|
|
153
169
|
if (mergeOnFlush) {
|
|
154
|
-
session = createMergeOnFlushSession({ sendText, sendSticker, sendMedia }, msgId, onComplete, log, {
|
|
170
|
+
session = createMergeOnFlushSession({ sendText: wrappedSendText, sendSticker: wrappedSendSticker, sendMedia: wrappedSendMedia }, msgId, onComplete, log, {
|
|
155
171
|
ctx,
|
|
156
172
|
account,
|
|
157
173
|
toAccount: heartbeatTarget,
|
|
@@ -161,7 +177,7 @@ function createManager(config) {
|
|
|
161
177
|
else {
|
|
162
178
|
switch (strategy) {
|
|
163
179
|
case 'immediate':
|
|
164
|
-
session = createImmediateSession({ sendText, sendSticker, sendMedia }, msgId, onComplete, log, {
|
|
180
|
+
session = createImmediateSession({ sendText: wrappedSendText, sendSticker: wrappedSendSticker, sendMedia: wrappedSendMedia }, msgId, onComplete, log, {
|
|
165
181
|
ctx,
|
|
166
182
|
account,
|
|
167
183
|
toAccount: heartbeatTarget,
|
|
@@ -169,7 +185,7 @@ function createManager(config) {
|
|
|
169
185
|
});
|
|
170
186
|
break;
|
|
171
187
|
case 'merge-text':
|
|
172
|
-
session = createMergeTextSession({ sendText, sendSticker, sendMedia }, msgId, sessionKey, onComplete, log, mergeTextOpts, {
|
|
188
|
+
session = createMergeTextSession({ sendText: wrappedSendText, sendSticker: wrappedSendSticker, sendMedia: wrappedSendMedia }, msgId, sessionKey, onComplete, log, mergeTextOpts, {
|
|
173
189
|
ctx,
|
|
174
190
|
account,
|
|
175
191
|
toAccount: heartbeatTarget,
|
package/dist/src/types.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export type YuanbaoAccountConfig = {
|
|
|
27
27
|
requireMention?: boolean;
|
|
28
28
|
fallbackReply?: string;
|
|
29
29
|
markdownHintEnabled?: boolean;
|
|
30
|
+
slowReplyTimeoutMs?: number;
|
|
31
|
+
slowReplyText?: string;
|
|
30
32
|
};
|
|
31
33
|
export type YuanbaoConfig = YuanbaoAccountConfig & {
|
|
32
34
|
accounts?: Record<string, YuanbaoAccountConfig>;
|
|
@@ -56,6 +58,9 @@ export type ResolvedYuanbaoAccount = {
|
|
|
56
58
|
requireMention: boolean;
|
|
57
59
|
fallbackReply?: string;
|
|
58
60
|
markdownHintEnabled: boolean;
|
|
61
|
+
slowReplyTimeoutMs: number;
|
|
62
|
+
slowReplyText: string;
|
|
63
|
+
createType?: BotCreateType;
|
|
59
64
|
config: YuanbaoAccountConfig;
|
|
60
65
|
};
|
|
61
66
|
export type ImImageInfoArrayItem = {
|
|
@@ -87,6 +92,10 @@ export type ImMsgSeq = {
|
|
|
87
92
|
msg_seq?: number;
|
|
88
93
|
msg_id?: string;
|
|
89
94
|
};
|
|
95
|
+
export declare enum BotCreateType {
|
|
96
|
+
ONE_CLICK = 1,
|
|
97
|
+
LINKED = 2
|
|
98
|
+
}
|
|
90
99
|
export declare enum EnumCLawMsgType {
|
|
91
100
|
CLAW_MSG_UNKNOWN = 0,
|
|
92
101
|
CLAW_MSG_GROUP = 1,
|
|
@@ -140,6 +149,7 @@ export type YuanbaoBufferedDeliverPayload = {
|
|
|
140
149
|
mediaUrls?: string[];
|
|
141
150
|
isCompactionNotice: boolean;
|
|
142
151
|
isReasoning: boolean;
|
|
152
|
+
isError?: boolean;
|
|
143
153
|
};
|
|
144
154
|
export type YuanbaoSendMsgRequest = {
|
|
145
155
|
sync_other_machine?: number;
|
package/dist/src/types.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export var BotCreateType;
|
|
2
|
+
(function (BotCreateType) {
|
|
3
|
+
BotCreateType[BotCreateType["ONE_CLICK"] = 1] = "ONE_CLICK";
|
|
4
|
+
BotCreateType[BotCreateType["LINKED"] = 2] = "LINKED";
|
|
5
|
+
})(BotCreateType || (BotCreateType = {}));
|
|
1
6
|
export var EnumCLawMsgType;
|
|
2
7
|
(function (EnumCLawMsgType) {
|
|
3
8
|
EnumCLawMsgType[EnumCLawMsgType["CLAW_MSG_UNKNOWN"] = 0] = "CLAW_MSG_UNKNOWN";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { BotCreateType, type ResolvedYuanbaoAccount } from '../../types.js';
|
|
2
2
|
export type SignTokenData = {
|
|
3
3
|
bot_id: string;
|
|
4
4
|
duration: number;
|
|
5
5
|
product: string;
|
|
6
6
|
source: string;
|
|
7
7
|
token: string;
|
|
8
|
+
create_type?: BotCreateType;
|
|
8
9
|
};
|
|
9
10
|
export type AuthHeaders = {
|
|
10
11
|
'X-ID': string;
|
|
@@ -94,7 +94,7 @@ async function doFetchSignToken(account) {
|
|
|
94
94
|
}
|
|
95
95
|
const result = (await response.json());
|
|
96
96
|
if (result.code === 0) {
|
|
97
|
-
mlog.info(`签票成功: bot_id=${result.data.bot_id}`);
|
|
97
|
+
mlog.info(`签票成功: bot_id=${result.data.bot_id}, create_type=${result.data.create_type}`);
|
|
98
98
|
return result.data;
|
|
99
99
|
}
|
|
100
100
|
if (result.code === RETRYABLE_SIGN_CODE && attempt < SIGN_MAX_RETRIES) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { YuanbaoInboundMessage, YuanbaoMsgBodyElement } from '../../types.js';
|
|
2
|
-
import type { WsSendC2CMessageData, WsSendGroupMessageData, WsSendMessageResponse, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsHeartbeatResponse, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGetGroupMemberListData, WsGetGroupMemberListResponse } from './types.js';
|
|
2
|
+
import type { WsSendC2CMessageData, WsSendGroupMessageData, WsSendMessageResponse, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsHeartbeatResponse, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGetGroupMemberListData, WsGetGroupMemberListResponse, WsSyncInformationData, WsSyncInformationResponse } from './types.js';
|
|
3
3
|
export declare const BIZ_MSG_TYPES: {
|
|
4
4
|
readonly MsgContent: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.MsgContent";
|
|
5
5
|
readonly MsgBodyElement: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.MsgBodyElement";
|
|
@@ -16,6 +16,8 @@ export declare const BIZ_MSG_TYPES: {
|
|
|
16
16
|
readonly SendPrivateHeartbeatRsp: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.SendPrivateHeartbeatRsp";
|
|
17
17
|
readonly SendGroupHeartbeatReq: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.SendGroupHeartbeatReq";
|
|
18
18
|
readonly SendGroupHeartbeatRsp: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.SendGroupHeartbeatRsp";
|
|
19
|
+
readonly SyncInformationReq: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.SyncInformationReq";
|
|
20
|
+
readonly SyncInformationRsp: "trpc.yuanbao.yuanbao_conn.yuanbao_openclaw_proxy.SyncInformationRsp";
|
|
19
21
|
};
|
|
20
22
|
export declare function encodeBizPB(key: string, value: Record<string, unknown>): Uint8Array | null;
|
|
21
23
|
export declare function decodeBizPB(key: string, data: Uint8Array | ArrayBuffer): any;
|
|
@@ -35,3 +37,5 @@ export declare function encodeGetGroupMemberListReq(data: WsGetGroupMemberListDa
|
|
|
35
37
|
export declare function decodeGetGroupMemberListRsp(data: Uint8Array | ArrayBuffer, msgId: string): WsGetGroupMemberListResponse | null;
|
|
36
38
|
export declare function decodeSendPrivateHeartbeatRsp(data: Uint8Array | ArrayBuffer, msgId: string): WsHeartbeatResponse | null;
|
|
37
39
|
export declare function decodeSendGroupHeartbeatRsp(data: Uint8Array | ArrayBuffer, msgId: string): WsHeartbeatResponse | null;
|
|
40
|
+
export declare function encodeSyncInformationReq(data: WsSyncInformationData): Uint8Array | null;
|
|
41
|
+
export declare function decodeSyncInformationRsp(data: Uint8Array | ArrayBuffer, msgId: string): WsSyncInformationResponse | null;
|
|
@@ -25,6 +25,8 @@ export const BIZ_MSG_TYPES = {
|
|
|
25
25
|
SendPrivateHeartbeatRsp: `${PKG}.SendPrivateHeartbeatRsp`,
|
|
26
26
|
SendGroupHeartbeatReq: `${PKG}.SendGroupHeartbeatReq`,
|
|
27
27
|
SendGroupHeartbeatRsp: `${PKG}.SendGroupHeartbeatRsp`,
|
|
28
|
+
SyncInformationReq: `${PKG}.SyncInformationReq`,
|
|
29
|
+
SyncInformationRsp: `${PKG}.SyncInformationRsp`,
|
|
28
30
|
};
|
|
29
31
|
export function encodeBizPB(key, value) {
|
|
30
32
|
try {
|
|
@@ -284,3 +286,21 @@ export function decodeSendGroupHeartbeatRsp(data, msgId) {
|
|
|
284
286
|
message: decoded.msg || '',
|
|
285
287
|
};
|
|
286
288
|
}
|
|
289
|
+
export function encodeSyncInformationReq(data) {
|
|
290
|
+
return encodeBizPB(BIZ_MSG_TYPES.SyncInformationReq, {
|
|
291
|
+
syncType: data.syncType,
|
|
292
|
+
botVersion: data.botVersion,
|
|
293
|
+
pluginVersion: data.pluginVersion,
|
|
294
|
+
...(data.commandData ? { commandData: data.commandData } : {}),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
export function decodeSyncInformationRsp(data, msgId) {
|
|
298
|
+
const decoded = decodeBizPB(BIZ_MSG_TYPES.SyncInformationRsp, data);
|
|
299
|
+
if (!decoded)
|
|
300
|
+
return null;
|
|
301
|
+
return {
|
|
302
|
+
msgId,
|
|
303
|
+
code: decoded.code || 0,
|
|
304
|
+
msg: decoded.msg || '',
|
|
305
|
+
};
|
|
306
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WsClientCallbacks, WsClientConfig, WsClientState, WsConnectionConfig, WsSendMessageResponse, WsSendC2CMessageData, WsSendGroupMessageData, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsHeartbeatResponse, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGetGroupMemberListData, WsGetGroupMemberListResponse } from './types.js';
|
|
1
|
+
import type { WsClientCallbacks, WsClientConfig, WsClientState, WsConnectionConfig, WsSendMessageResponse, WsSendC2CMessageData, WsSendGroupMessageData, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsHeartbeatResponse, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGetGroupMemberListData, WsGetGroupMemberListResponse, WsSyncInformationData, WsSyncInformationResponse } from './types.js';
|
|
2
2
|
export declare const BIZ_CMD: {
|
|
3
3
|
readonly SendC2CMessage: "send_c2c_message";
|
|
4
4
|
readonly SendGroupMessage: "send_group_message";
|
|
@@ -6,6 +6,7 @@ export declare const BIZ_CMD: {
|
|
|
6
6
|
readonly GetGroupMemberList: "get_group_member_list";
|
|
7
7
|
readonly SendPrivateHeartbeat: "send_private_heartbeat";
|
|
8
8
|
readonly SendGroupHeartbeat: "send_group_heartbeat";
|
|
9
|
+
readonly SyncInformation: "sync_information";
|
|
9
10
|
};
|
|
10
11
|
export declare class YuanbaoWsClient {
|
|
11
12
|
private connectionConfig;
|
|
@@ -44,6 +45,7 @@ export declare class YuanbaoWsClient {
|
|
|
44
45
|
getGroupMemberList(data: WsGetGroupMemberListData): Promise<WsGetGroupMemberListResponse>;
|
|
45
46
|
sendPrivateHeartbeat(data: WsSendPrivateHeartbeatData): Promise<WsHeartbeatResponse>;
|
|
46
47
|
sendGroupHeartbeat(data: WsSendGroupHeartbeatData): Promise<WsHeartbeatResponse>;
|
|
48
|
+
syncInformation(data: WsSyncInformationData): Promise<WsSyncInformationResponse>;
|
|
47
49
|
private doConnect;
|
|
48
50
|
private onMessage;
|
|
49
51
|
private handleConnMsg;
|
|
@@ -2,7 +2,7 @@ import WebSocket from 'ws';
|
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { getPluginVersion, getOpenclawVersion, getOperationSystem } from '../../utils/get-env.js';
|
|
4
4
|
import { decodeConnMsg, decodePB, buildAuthBindMsg, buildPingMsg, buildPushAck, buildBusinessConnMsg, PB_MSG_TYPES, CMD_TYPE, CMD, } from './conn-codec.js';
|
|
5
|
-
import { encodeSendC2CMessageReq, encodeSendGroupMessageReq, decodeSendMessageRsp, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, } from './biz-codec.js';
|
|
5
|
+
import { encodeSendC2CMessageReq, encodeSendGroupMessageReq, decodeSendMessageRsp, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, encodeSyncInformationReq, decodeSyncInformationRsp, } from './biz-codec.js';
|
|
6
6
|
import { createLog } from '../../logger.js';
|
|
7
7
|
import { msgBodyDesensitization } from '../../utils.js';
|
|
8
8
|
const DEFAULT_RECONNECT_DELAYS = [1_000, 2_000, 5_000, 10_000, 30_000, 60_000];
|
|
@@ -36,6 +36,7 @@ export const BIZ_CMD = {
|
|
|
36
36
|
GetGroupMemberList: 'get_group_member_list',
|
|
37
37
|
SendPrivateHeartbeat: 'send_private_heartbeat',
|
|
38
38
|
SendGroupHeartbeat: 'send_group_heartbeat',
|
|
39
|
+
SyncInformation: 'sync_information',
|
|
39
40
|
};
|
|
40
41
|
const BIZ_MODULE = 'yuanbao_openclaw_proxy';
|
|
41
42
|
export class YuanbaoWsClient {
|
|
@@ -185,6 +186,13 @@ export class YuanbaoWsClient {
|
|
|
185
186
|
return Promise.reject(new Error('Failed to encode SendGroupHeartbeatReq'));
|
|
186
187
|
return this.sendAndWaitWith(BIZ_CMD.SendGroupHeartbeat, BIZ_MODULE, encoded, decodeSendGroupHeartbeatRsp);
|
|
187
188
|
}
|
|
189
|
+
syncInformation(data) {
|
|
190
|
+
this.log.info('[同步] 发送 SyncInformation 请求', { syncType: data.syncType, botVersion: data.botVersion, pluginVersion: data.pluginVersion });
|
|
191
|
+
const encoded = encodeSyncInformationReq(data);
|
|
192
|
+
if (!encoded)
|
|
193
|
+
return Promise.reject(new Error('Failed to encode SyncInformationReq'));
|
|
194
|
+
return this.sendAndWaitWith(BIZ_CMD.SyncInformation, BIZ_MODULE, encoded, decodeSyncInformationRsp);
|
|
195
|
+
}
|
|
188
196
|
doConnect() {
|
|
189
197
|
if (this.disposed)
|
|
190
198
|
return;
|
|
@@ -5,6 +5,7 @@ import { handleInboundMessage, } from '../../message-handler/index.js';
|
|
|
5
5
|
import { setActiveWsClient } from './runtime.js';
|
|
6
6
|
import { decodeInboundMessage } from './biz-codec.js';
|
|
7
7
|
import { getSignToken, forceRefreshSignToken } from '../api.js';
|
|
8
|
+
import { buildSyncCommandsPayload } from '../../commands/slash-commands/index.js';
|
|
8
9
|
export async function startYuanbaoWsGateway(params) {
|
|
9
10
|
const { account, config, abortSignal, runtime, statusSink } = params;
|
|
10
11
|
const gwlog = createLog('ws');
|
|
@@ -26,6 +27,9 @@ export async function startYuanbaoWsGateway(params) {
|
|
|
26
27
|
wsConnectId: data.connectId,
|
|
27
28
|
lastConnectedAt: Date.now(),
|
|
28
29
|
});
|
|
30
|
+
syncCommandsToServer(client, account.accountId, config).catch((err) => {
|
|
31
|
+
gwlog.warn(`[${account.accountId}] 同步命令列表失败(不影响正常功能)`, { error: String(err) });
|
|
32
|
+
});
|
|
29
33
|
},
|
|
30
34
|
onDispatch: (pushEvent) => {
|
|
31
35
|
gwlog.debug(`[${account.accountId}] WS 推送: cmd=${pushEvent.cmd}, type=${pushEvent.type}`);
|
|
@@ -57,6 +61,9 @@ export async function startYuanbaoWsGateway(params) {
|
|
|
57
61
|
if (tokenData.bot_id) {
|
|
58
62
|
account.botId = tokenData.bot_id;
|
|
59
63
|
}
|
|
64
|
+
if (tokenData.create_type != null) {
|
|
65
|
+
account.createType = tokenData.create_type;
|
|
66
|
+
}
|
|
60
67
|
return {
|
|
61
68
|
bizId: 'ybBot',
|
|
62
69
|
uid,
|
|
@@ -114,7 +121,10 @@ async function resolveWsAuth(account) {
|
|
|
114
121
|
if (tokenData.bot_id) {
|
|
115
122
|
account.botId = tokenData.bot_id;
|
|
116
123
|
}
|
|
117
|
-
|
|
124
|
+
if (tokenData.create_type != null) {
|
|
125
|
+
account.createType = tokenData.create_type;
|
|
126
|
+
}
|
|
127
|
+
mlog.info(`[${account.accountId}] 签票完成 uid=${uid} (bot_id=${tokenData.bot_id}, botId=${account.botId}), create_type=${tokenData.create_type}`);
|
|
118
128
|
return {
|
|
119
129
|
bizId: 'ybBot',
|
|
120
130
|
uid,
|
|
@@ -259,3 +269,30 @@ function handleWsDispatchEvent(params) {
|
|
|
259
269
|
dlog.error(`[${account.accountId}][dispatch] WS ${chatType === 'group' ? 'group ' : ''} message handler failed: ${String(err)}`);
|
|
260
270
|
});
|
|
261
271
|
}
|
|
272
|
+
async function syncCommandsToServer(client, accountId, config) {
|
|
273
|
+
const slog = createLog('ws');
|
|
274
|
+
const payload = await buildSyncCommandsPayload(config);
|
|
275
|
+
slog.info(`[${accountId}] 同步命令列表`, {
|
|
276
|
+
botVersion: payload.botVersion,
|
|
277
|
+
pluginVersion: payload.pluginVersion,
|
|
278
|
+
botCommands: payload.commandData.botCommands.length,
|
|
279
|
+
pluginCommands: payload.commandData.pluginCommands.length,
|
|
280
|
+
});
|
|
281
|
+
slog.info(`[${accountId}] SyncInformationReq 请求内容:`, {
|
|
282
|
+
sync_type: payload.syncType,
|
|
283
|
+
bot_version: payload.botVersion,
|
|
284
|
+
plugin_version: payload.pluginVersion,
|
|
285
|
+
command_data: {
|
|
286
|
+
bot_commands: payload.commandData.botCommands,
|
|
287
|
+
plugin_commands: payload.commandData.pluginCommands,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
const rsp = await client.syncInformation(payload);
|
|
291
|
+
slog.info(`[${accountId}] SyncInformationRsp 响应内容:`, { code: rsp.code, msg: rsp.msg });
|
|
292
|
+
if (rsp.code !== 0) {
|
|
293
|
+
slog.warn(`[${accountId}] 同步命令列表返回非零码: code=${rsp.code}, msg=${rsp.msg}`);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
slog.info(`[${accountId}] 同步命令列表成功`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { encodePB, decodePB, encodeConnMsg, decodeConnMsg, buildAuthBindMsg, buildPingMsg, buildPushAck, buildBusinessConnMsg, createHead, nextSeqNo, PB_MSG_TYPES, CMD_TYPE, CMD, MODULE, } from './conn-codec.js';
|
|
2
2
|
export type { PBHead, PBConnMsg } from './conn-codec.js';
|
|
3
|
-
export { encodeBizPB, decodeBizPB, toProtoMsgBody, fromProtoMsgBody, encodeSendC2CMessageReq, encodeSendGroupMessageReq, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeInboundMessage, decodeSendC2CMessageRsp, decodeSendGroupMessageRsp, decodeSendMessageRsp, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, BIZ_MSG_TYPES, } from './biz-codec.js';
|
|
3
|
+
export { encodeBizPB, decodeBizPB, toProtoMsgBody, fromProtoMsgBody, encodeSendC2CMessageReq, encodeSendGroupMessageReq, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeInboundMessage, decodeSendC2CMessageRsp, decodeSendGroupMessageRsp, decodeSendMessageRsp, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, encodeSyncInformationReq, decodeSyncInformationRsp, BIZ_MSG_TYPES, } from './biz-codec.js';
|
|
4
4
|
export { YuanbaoWsClient, BIZ_CMD } from './client.js';
|
|
5
5
|
export { startYuanbaoWsGateway, wsPushToInboundMessage } from './gateway.js';
|
|
6
6
|
export type { StartWsGatewayParams } from './gateway.js';
|
|
7
7
|
export { setActiveWsClient, getActiveWsClient, getAllActiveWsClients } from './runtime.js';
|
|
8
8
|
export { WS_HEARTBEAT } from './types.js';
|
|
9
|
-
export type { WsConnectionConfig, WsClientConfig, WsClientState, WsAuthBindResult, WsClientCallbacks, WsPushEvent, WsSendC2CMessageData, WsSendGroupMessageData, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsOutboundMessageData, WsSendMessageResponse, WsHeartbeatResponse, WsHeartbeatValue, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGroupInfo, WsGetGroupMemberListData, WsGetGroupMemberListResponse, WsGroupMember, } from './types.js';
|
|
9
|
+
export type { WsConnectionConfig, WsClientConfig, WsClientState, WsAuthBindResult, WsClientCallbacks, WsPushEvent, WsSendC2CMessageData, WsSendGroupMessageData, WsSendPrivateHeartbeatData, WsSendGroupHeartbeatData, WsOutboundMessageData, WsSendMessageResponse, WsHeartbeatResponse, WsHeartbeatValue, WsQueryGroupInfoData, WsQueryGroupInfoResponse, WsGroupInfo, WsGetGroupMemberListData, WsGetGroupMemberListResponse, WsGroupMember, WsSyncInformationData, WsSyncInformationResponse, WsSyncCommand, WsSyncCommandsData, } from './types.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { encodePB, decodePB, encodeConnMsg, decodeConnMsg, buildAuthBindMsg, buildPingMsg, buildPushAck, buildBusinessConnMsg, createHead, nextSeqNo, PB_MSG_TYPES, CMD_TYPE, CMD, MODULE, } from './conn-codec.js';
|
|
2
|
-
export { encodeBizPB, decodeBizPB, toProtoMsgBody, fromProtoMsgBody, encodeSendC2CMessageReq, encodeSendGroupMessageReq, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeInboundMessage, decodeSendC2CMessageRsp, decodeSendGroupMessageRsp, decodeSendMessageRsp, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, BIZ_MSG_TYPES, } from './biz-codec.js';
|
|
2
|
+
export { encodeBizPB, decodeBizPB, toProtoMsgBody, fromProtoMsgBody, encodeSendC2CMessageReq, encodeSendGroupMessageReq, encodeSendPrivateHeartbeatReq, encodeSendGroupHeartbeatReq, decodeInboundMessage, decodeSendC2CMessageRsp, decodeSendGroupMessageRsp, decodeSendMessageRsp, decodeSendPrivateHeartbeatRsp, decodeSendGroupHeartbeatRsp, encodeQueryGroupInfoReq, decodeQueryGroupInfoRsp, encodeGetGroupMemberListReq, decodeGetGroupMemberListRsp, encodeSyncInformationReq, decodeSyncInformationRsp, BIZ_MSG_TYPES, } from './biz-codec.js';
|
|
3
3
|
export { YuanbaoWsClient, BIZ_CMD } from './client.js';
|
|
4
4
|
export { startYuanbaoWsGateway, wsPushToInboundMessage } from './gateway.js';
|
|
5
5
|
export { setActiveWsClient, getActiveWsClient, getAllActiveWsClients } from './runtime.js';
|
|
@@ -470,6 +470,75 @@
|
|
|
470
470
|
"id": 2
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
+
},
|
|
474
|
+
"SyncInformationType": {
|
|
475
|
+
"values": {
|
|
476
|
+
"SYNC_INFORMATION_TYPE_UNSPECIFIED": 0,
|
|
477
|
+
"SYNC_INFORMATION_TYPE_COMMANDS": 1
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
"Command": {
|
|
481
|
+
"fields": {
|
|
482
|
+
"name": {
|
|
483
|
+
"type": "string",
|
|
484
|
+
"id": 1
|
|
485
|
+
},
|
|
486
|
+
"description": {
|
|
487
|
+
"type": "string",
|
|
488
|
+
"id": 2
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
"SyncCommandsData": {
|
|
493
|
+
"fields": {
|
|
494
|
+
"botCommands": {
|
|
495
|
+
"rule": "repeated",
|
|
496
|
+
"type": "Command",
|
|
497
|
+
"id": 1
|
|
498
|
+
},
|
|
499
|
+
"pluginCommands": {
|
|
500
|
+
"rule": "repeated",
|
|
501
|
+
"type": "Command",
|
|
502
|
+
"id": 2
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
"SyncInformationReq": {
|
|
507
|
+
"oneofs": {
|
|
508
|
+
"data": {
|
|
509
|
+
"oneof": ["commandData"]
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
"fields": {
|
|
513
|
+
"syncType": {
|
|
514
|
+
"type": "SyncInformationType",
|
|
515
|
+
"id": 1
|
|
516
|
+
},
|
|
517
|
+
"botVersion": {
|
|
518
|
+
"type": "string",
|
|
519
|
+
"id": 2
|
|
520
|
+
},
|
|
521
|
+
"pluginVersion": {
|
|
522
|
+
"type": "string",
|
|
523
|
+
"id": 3
|
|
524
|
+
},
|
|
525
|
+
"commandData": {
|
|
526
|
+
"type": "SyncCommandsData",
|
|
527
|
+
"id": 11
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
"SyncInformationRsp": {
|
|
532
|
+
"fields": {
|
|
533
|
+
"code": {
|
|
534
|
+
"type": "int32",
|
|
535
|
+
"id": 1
|
|
536
|
+
},
|
|
537
|
+
"msg": {
|
|
538
|
+
"type": "string",
|
|
539
|
+
"id": 2
|
|
540
|
+
}
|
|
541
|
+
}
|
|
473
542
|
}
|
|
474
543
|
}
|
|
475
544
|
}
|
|
@@ -121,3 +121,22 @@ export type WsHeartbeatResponse = {
|
|
|
121
121
|
msg?: string;
|
|
122
122
|
message?: string;
|
|
123
123
|
};
|
|
124
|
+
export type WsSyncCommand = {
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
};
|
|
128
|
+
export type WsSyncCommandsData = {
|
|
129
|
+
botCommands: WsSyncCommand[];
|
|
130
|
+
pluginCommands: WsSyncCommand[];
|
|
131
|
+
};
|
|
132
|
+
export type WsSyncInformationData = {
|
|
133
|
+
syncType: number;
|
|
134
|
+
botVersion: string;
|
|
135
|
+
pluginVersion: string;
|
|
136
|
+
commandData?: WsSyncCommandsData;
|
|
137
|
+
};
|
|
138
|
+
export type WsSyncInformationResponse = {
|
|
139
|
+
msgId: string;
|
|
140
|
+
code: number;
|
|
141
|
+
msg: string;
|
|
142
|
+
};
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { createLog } from '../../logger.js';
|
|
2
|
-
export function buildAtUserMsgBodyItem(userId, senderNickname) {
|
|
3
|
-
return {
|
|
4
|
-
msg_type: 'TIMCustomElem',
|
|
5
|
-
msg_content: {
|
|
6
|
-
data: JSON.stringify({ elem_type: 1002, text: `@${senderNickname ?? ''}`, user_id: userId }),
|
|
7
|
-
},
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
const FALLBACK_TEXT = '[custom]';
|
|
11
|
-
export const customHandler = {
|
|
12
|
-
msgType: 'TIMCustomElem',
|
|
13
|
-
extract(ctx, elem, resData) {
|
|
14
|
-
if (elem.msg_content?.data) {
|
|
15
|
-
try {
|
|
16
|
-
const customContent = JSON.parse(elem.msg_content?.data);
|
|
17
|
-
if (customContent?.elem_type !== 1002) {
|
|
18
|
-
return FALLBACK_TEXT;
|
|
19
|
-
}
|
|
20
|
-
const { botId } = ctx.account;
|
|
21
|
-
const isAtBotSelf = customContent?.user_id === botId;
|
|
22
|
-
if (!resData.isAtBot) {
|
|
23
|
-
resData.isAtBot = isAtBotSelf;
|
|
24
|
-
}
|
|
25
|
-
createLog('custom').info('@消息', { text: customContent?.text, userId: customContent?.user_id, isAtBot: resData.isAtBot });
|
|
26
|
-
if (!isAtBotSelf && customContent?.user_id) {
|
|
27
|
-
resData.mentions.push({
|
|
28
|
-
userId: customContent.user_id,
|
|
29
|
-
text: customContent.text || '',
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
const result = !isAtBotSelf && customContent.text ? customContent.text : undefined;
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
catch { }
|
|
36
|
-
}
|
|
37
|
-
return FALLBACK_TEXT;
|
|
38
|
-
},
|
|
39
|
-
buildMsgBody(data) {
|
|
40
|
-
const customData = typeof data.data === 'string'
|
|
41
|
-
? data.data
|
|
42
|
-
: JSON.stringify(data.data);
|
|
43
|
-
return [
|
|
44
|
-
{
|
|
45
|
-
msg_type: 'TIMCustomElem',
|
|
46
|
-
msg_content: { data: customData },
|
|
47
|
-
},
|
|
48
|
-
];
|
|
49
|
-
},
|
|
50
|
-
};
|