@chbo297/infoflow 2026.5.4 → 2026.5.5
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 +49 -1
- package/dist/src/cli.js +157 -0
- package/package.json +13 -3
- package/scripts/deploy.sh +10 -196
- package/scripts/lib/deploy-common.sh +157 -0
- package/.claude/settings.local.json +0 -9
- package/CHANGELOG.md +0 -147
- package/index.ts +0 -23
- package/src/accounts.ts +0 -170
- package/src/actions.ts +0 -458
- package/src/bot.ts +0 -1235
- package/src/channel.ts +0 -443
- package/src/infoflow-req-parse.ts +0 -488
- package/src/infoflow-sdk.d.ts +0 -12
- package/src/logging.ts +0 -123
- package/src/markdown-local-images.ts +0 -75
- package/src/media.ts +0 -405
- package/src/monitor.ts +0 -188
- package/src/reply-dispatcher.ts +0 -364
- package/src/runtime.ts +0 -14
- package/src/send.ts +0 -986
- package/src/sent-message-store.ts +0 -267
- package/src/targets.ts +0 -109
- package/src/types.ts +0 -242
- package/src/ws-receiver.ts +0 -482
- package/tsconfig.build.json +0 -6
package/CHANGELOG.md
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 2026.3.17
|
|
4
|
-
|
|
5
|
-
### 修复与优化
|
|
6
|
-
|
|
7
|
-
#### follow-up(跟进回复)策略
|
|
8
|
-
|
|
9
|
-
- **他人被 @ 时仅记录不派发**:在 follow-up 时间窗口内,若新消息 @ 了其他人或机器人(而非本 bot),则仅写入会话历史、不派发 LLM,避免误判为对机器人的追问。
|
|
10
|
-
- **LLM 可见 body 含 robotid**:群消息中 @ 提及会以「@名称 (robotid:N)」形式呈现给模型,便于区分不同机器人并做出更准确的回复判断。
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 2026.3.15
|
|
15
|
-
|
|
16
|
-
### 新功能
|
|
17
|
-
|
|
18
|
-
#### watchRegex 支持数组
|
|
19
|
-
|
|
20
|
-
- `watchRegex` 可配置为字符串或字符串数组,支持多条正则;命中任一条即触发回复判断。
|
|
21
|
-
|
|
22
|
-
#### Markdown 本地图片
|
|
23
|
-
|
|
24
|
-
- 回复内容中的本地图片 URL(`/`、`./`、`file://` 等)会解析并转为 base64,以如流图片消息形式发送,避免链接不可访问。
|
|
25
|
-
|
|
26
|
-
### 优化
|
|
27
|
-
|
|
28
|
-
#### follow-up(跟进回复)判定逻辑
|
|
29
|
-
|
|
30
|
-
- 机器人更智能地判断是否在跟进窗口内回复——
|
|
31
|
-
- **提高回复倾向**:当发言者表现出期望得到回应的意愿(例如追问、延续同一话题)时,增加机器人回复的可能性;
|
|
32
|
-
- **降低回复倾向**:当发言者明确禁止机器人回复(例如「不用回了」「别回复」等)时,降低机器人回复的可能性。
|
|
33
|
-
|
|
34
|
-
### Bug 修复
|
|
35
|
-
|
|
36
|
-
- 修复消息分片发送时顺序错乱的问题。
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 2026.3.10
|
|
41
|
-
|
|
42
|
-
### Bug 修复
|
|
43
|
-
|
|
44
|
-
- 修复来自 bot 的消息处理逻辑(避免对机器人自身消息错误响应)。
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## 2026.3.8
|
|
49
|
-
|
|
50
|
-
### 新功能
|
|
51
|
-
|
|
52
|
-
#### watchRegex(正则匹配群消息)
|
|
53
|
-
|
|
54
|
-
- 支持 `watchRegex` 配置:按正则表达式匹配群内聊天内容,命中时触发机器人参与并回复
|
|
55
|
-
- 可在顶层、账号级别或按群(`groups.<groupId>.watchRegex`)单独配置
|
|
56
|
-
- 需配合 `replyMode` 为 `mention-and-watch` 或 `proactive` 使用
|
|
57
|
-
|
|
58
|
-
#### 撤回消息
|
|
59
|
-
|
|
60
|
-
- 支持私聊消息撤回能力,需在配置中填写如流企业后台的 `appAgentId`(应用 ID)
|
|
61
|
-
|
|
62
|
-
### Bug 修复
|
|
63
|
-
|
|
64
|
-
- 修复了一些消息回复失败的问题
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## 2026.2.28
|
|
69
|
-
|
|
70
|
-
### 新功能
|
|
71
|
-
|
|
72
|
-
#### 助手模式与 @mention 能力
|
|
73
|
-
|
|
74
|
-
- 支持 `watchMentions` 配置:当群内有人 @了 watchMentions 列表中的用户时,机器人作为该用户的助手自动判断是否代为回复
|
|
75
|
-
- 支持识别群消息中的 @mention(人类用户和机器人),并在回复中正确生成 @mention 内容
|
|
76
|
-
- 新增 `at-agent` 消息类型,支持在回复中 @其他机器人
|
|
77
|
-
|
|
78
|
-
#### Follow-Up 跟进回复
|
|
79
|
-
|
|
80
|
-
- 支持 `followUp` 配置(默认开启):机器人回复后,在 `followUpWindow`(默认 300 秒)时间窗口内,即使未被 @,也会判断后续消息是否是对之前话题的延续并决定是否回复
|
|
81
|
-
- 通过内存 Map 跟踪每个群的最后回复时间
|
|
82
|
-
|
|
83
|
-
#### 回复模式系统(replyMode)
|
|
84
|
-
|
|
85
|
-
新增 5 种群聊回复模式,支持按群独立配置:
|
|
86
|
-
|
|
87
|
-
| 模式 | 行为 |
|
|
88
|
-
| --- | --- |
|
|
89
|
-
| `ignore` | 直接丢弃,不记录、不思考、不回复 |
|
|
90
|
-
| `record` | 仅记录到会话,不思考、不回复 |
|
|
91
|
-
| `mention-only` | 仅在机器人被直接 @ 时回复 |
|
|
92
|
-
| `mention-and-watch`(默认) | 机器人被 @、或 watchMentions 中的用户被 @、或处于 follow-up 窗口内时回复 |
|
|
93
|
-
| `proactive` | 始终思考并判断是否回复 |
|
|
94
|
-
|
|
95
|
-
#### 消息动作(Actions)
|
|
96
|
-
|
|
97
|
-
- 新增 `ChannelMessageActionAdapter` 实现,支持 LLM Agent 通过 `send` action 主动发送消息
|
|
98
|
-
- 支持 `atAll`、`mentionUserIds`、`media` 等参数
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
### 重构
|
|
103
|
-
|
|
104
|
-
#### 日志系统统一
|
|
105
|
-
|
|
106
|
-
- 移除 `logInfoflowApiError`,新增统一的 `logVerbose()` 函数
|
|
107
|
-
- 全项目统一使用 `logVerbose()` 替代分散的 verbose 日志判断逻辑
|
|
108
|
-
- 统一使用 `formatInfoflowError()` 格式化错误信息
|
|
109
|
-
- 改进多处错误日志的上下文信息
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
### ⚠️ 不兼容变更
|
|
114
|
-
|
|
115
|
-
#### openclaw.plugin.json 配置格式变更
|
|
116
|
-
|
|
117
|
-
群聊配置部分有较大变更,**不兼容以前的格式**。主要变更:
|
|
118
|
-
|
|
119
|
-
- 新增顶层 `replyMode`、`followUp`、`followUpWindow`、`watchMentions` 字段
|
|
120
|
-
- 新增 `groups` 对象,支持按群 ID 独立配置(`replyMode`、`watchMentions`、`followUp`、`followUpWindow`、`systemPrompt`)
|
|
121
|
-
- 新增 `defaultAccount` 字段
|
|
122
|
-
- `accounts` 下每个账号也可嵌套独自的 `groups` 配置
|
|
123
|
-
- 配置解析优先级:群级别 → 账号级别 → 顶层默认值
|
|
124
|
-
|
|
125
|
-
旧的 `requireMention` 字段仍可用(内部自动映射到新的 replyMode),但建议迁移到新的 `replyMode` 配置。
|
|
126
|
-
|
|
127
|
-
新配置示例:
|
|
128
|
-
|
|
129
|
-
```json5
|
|
130
|
-
{
|
|
131
|
-
channels: {
|
|
132
|
-
infoflow: {
|
|
133
|
-
replyMode: "mention-and-watch",
|
|
134
|
-
followUp: true,
|
|
135
|
-
followUpWindow: 300,
|
|
136
|
-
watchMentions: ["alice01", "bob02"],
|
|
137
|
-
groups: {
|
|
138
|
-
"12345": {
|
|
139
|
-
replyMode: "proactive",
|
|
140
|
-
systemPrompt: "你是项目 X 的专家...",
|
|
141
|
-
watchMentions: ["alice01"],
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
}
|
|
147
|
-
```
|
package/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { infoflowPlugin } from "./src/channel.js";
|
|
3
|
-
import { handleInfoflowWebhookRequest } from "./src/monitor.js";
|
|
4
|
-
import { setInfoflowRuntime } from "./src/runtime.js";
|
|
5
|
-
|
|
6
|
-
const plugin = {
|
|
7
|
-
id: "infoflow",
|
|
8
|
-
name: "Infoflow",
|
|
9
|
-
description: "OpenClaw Infoflow channel plugin",
|
|
10
|
-
configSchema: emptyPluginConfigSchema(),
|
|
11
|
-
register(api: OpenClawPluginApi) {
|
|
12
|
-
setInfoflowRuntime(api.runtime);
|
|
13
|
-
api.registerChannel({ plugin: infoflowPlugin });
|
|
14
|
-
api.registerHttpRoute({
|
|
15
|
-
path: "/webhook/infoflow",
|
|
16
|
-
auth: "plugin",
|
|
17
|
-
match: "exact",
|
|
18
|
-
handler: handleInfoflowWebhookRequest,
|
|
19
|
-
});
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export default plugin;
|
package/src/accounts.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infoflow account resolution and configuration helpers.
|
|
3
|
-
* Handles multi-account support with config merging.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
7
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/core";
|
|
8
|
-
import type {
|
|
9
|
-
InfoflowAccountConfig,
|
|
10
|
-
InfoflowConnectionMode,
|
|
11
|
-
ResolvedInfoflowAccount,
|
|
12
|
-
} from "./types.js";
|
|
13
|
-
|
|
14
|
-
const DEFAULT_INFOFLOW_WS_GATEWAY = "infoflow-open-gateway.weiyun.baidu.com";
|
|
15
|
-
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Config Access Helpers
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Get the raw Infoflow channel section from config.
|
|
22
|
-
*/
|
|
23
|
-
export function getChannelSection(cfg: OpenClawConfig): InfoflowAccountConfig | undefined {
|
|
24
|
-
return cfg.channels?.["infoflow"] as InfoflowAccountConfig | undefined;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Account ID Resolution
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* List all configured Infoflow account IDs.
|
|
33
|
-
* Returns [DEFAULT_ACCOUNT_ID] if no accounts are configured (backward compatibility).
|
|
34
|
-
*/
|
|
35
|
-
export function listInfoflowAccountIds(cfg: OpenClawConfig): string[] {
|
|
36
|
-
const accounts = getChannelSection(cfg)?.accounts;
|
|
37
|
-
if (!accounts || typeof accounts !== "object") {
|
|
38
|
-
return [DEFAULT_ACCOUNT_ID];
|
|
39
|
-
}
|
|
40
|
-
const ids = Object.keys(accounts).filter(Boolean);
|
|
41
|
-
return ids.length === 0 ? [DEFAULT_ACCOUNT_ID] : ids.toSorted((a, b) => a.localeCompare(b));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Resolve the default account ID for Infoflow.
|
|
46
|
-
*/
|
|
47
|
-
export function resolveDefaultInfoflowAccountId(cfg: OpenClawConfig): string {
|
|
48
|
-
const channel = getChannelSection(cfg);
|
|
49
|
-
if (channel?.defaultAccount?.trim()) {
|
|
50
|
-
return channel.defaultAccount.trim();
|
|
51
|
-
}
|
|
52
|
-
const ids = listInfoflowAccountIds(cfg);
|
|
53
|
-
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
54
|
-
return DEFAULT_ACCOUNT_ID;
|
|
55
|
-
}
|
|
56
|
-
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
// Config Merging
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Merge top-level Infoflow config with account-specific overrides.
|
|
65
|
-
* Account fields override base fields.
|
|
66
|
-
*/
|
|
67
|
-
function mergeInfoflowAccountConfig(
|
|
68
|
-
cfg: OpenClawConfig,
|
|
69
|
-
accountId: string,
|
|
70
|
-
): {
|
|
71
|
-
apiHost: string;
|
|
72
|
-
connectionMode?: InfoflowConnectionMode;
|
|
73
|
-
wsGateway?: string;
|
|
74
|
-
wsConnectDomain?: string;
|
|
75
|
-
checkToken: string;
|
|
76
|
-
encodingAESKey: string;
|
|
77
|
-
appKey: string;
|
|
78
|
-
appSecret: string;
|
|
79
|
-
enabled?: boolean;
|
|
80
|
-
name?: string;
|
|
81
|
-
robotName?: string;
|
|
82
|
-
robotId?: string;
|
|
83
|
-
requireMention?: boolean;
|
|
84
|
-
watchMentions?: string[];
|
|
85
|
-
watchRegex?: string | string[];
|
|
86
|
-
appAgentId?: number;
|
|
87
|
-
} {
|
|
88
|
-
const raw = getChannelSection(cfg) ?? {};
|
|
89
|
-
const { accounts: _ignored, defaultAccount: _ignored2, ...base } = raw;
|
|
90
|
-
const account = raw.accounts?.[accountId] ?? {};
|
|
91
|
-
return { ...base, ...account } as {
|
|
92
|
-
apiHost: string;
|
|
93
|
-
connectionMode?: InfoflowConnectionMode;
|
|
94
|
-
wsGateway?: string;
|
|
95
|
-
wsConnectDomain?: string;
|
|
96
|
-
checkToken: string;
|
|
97
|
-
encodingAESKey: string;
|
|
98
|
-
appKey: string;
|
|
99
|
-
appSecret: string;
|
|
100
|
-
enabled?: boolean;
|
|
101
|
-
name?: string;
|
|
102
|
-
robotName?: string;
|
|
103
|
-
robotId?: string;
|
|
104
|
-
requireMention?: boolean;
|
|
105
|
-
watchMentions?: string[];
|
|
106
|
-
watchRegex?: string | string[];
|
|
107
|
-
appAgentId?: number;
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function normalizeWatchRegex(v: string | string[] | undefined): string[] {
|
|
112
|
-
if (v == null) return [];
|
|
113
|
-
return Array.isArray(v) ? v : [v];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
// Account Resolution
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Resolve a complete Infoflow account with merged config.
|
|
122
|
-
*/
|
|
123
|
-
export function resolveInfoflowAccount(params: {
|
|
124
|
-
cfg: OpenClawConfig;
|
|
125
|
-
accountId?: string | null;
|
|
126
|
-
}): ResolvedInfoflowAccount {
|
|
127
|
-
const accountId = normalizeAccountId(params.accountId);
|
|
128
|
-
const baseEnabled = getChannelSection(params.cfg)?.enabled !== false;
|
|
129
|
-
const merged = mergeInfoflowAccountConfig(params.cfg, accountId);
|
|
130
|
-
const accountEnabled = merged.enabled !== false;
|
|
131
|
-
const enabled = baseEnabled && accountEnabled;
|
|
132
|
-
const apiHost = merged.apiHost ?? "";
|
|
133
|
-
const checkToken = merged.checkToken ?? "";
|
|
134
|
-
const encodingAESKey = merged.encodingAESKey ?? "";
|
|
135
|
-
const appKey = merged.appKey ?? "";
|
|
136
|
-
const appSecret = merged.appSecret ?? "";
|
|
137
|
-
const effectiveConnectionMode: InfoflowConnectionMode =
|
|
138
|
-
merged.connectionMode ?? "webhook";
|
|
139
|
-
const wsGateway = merged.wsGateway?.trim() || DEFAULT_INFOFLOW_WS_GATEWAY;
|
|
140
|
-
const wsConnectDomain = merged.wsConnectDomain?.trim() || undefined;
|
|
141
|
-
const configured =
|
|
142
|
-
effectiveConnectionMode === "websocket"
|
|
143
|
-
? Boolean(appKey) && Boolean(appSecret)
|
|
144
|
-
: Boolean(checkToken) && Boolean(encodingAESKey) && Boolean(appKey) && Boolean(appSecret);
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
accountId,
|
|
148
|
-
name: merged.name?.trim() || undefined,
|
|
149
|
-
enabled,
|
|
150
|
-
configured,
|
|
151
|
-
config: {
|
|
152
|
-
enabled: merged.enabled,
|
|
153
|
-
name: merged.name,
|
|
154
|
-
apiHost,
|
|
155
|
-
connectionMode: effectiveConnectionMode,
|
|
156
|
-
wsGateway,
|
|
157
|
-
wsConnectDomain,
|
|
158
|
-
checkToken,
|
|
159
|
-
encodingAESKey,
|
|
160
|
-
appKey,
|
|
161
|
-
appSecret,
|
|
162
|
-
robotName: merged.robotName?.trim() || undefined,
|
|
163
|
-
robotId: merged.robotId?.trim() || undefined,
|
|
164
|
-
requireMention: merged.requireMention,
|
|
165
|
-
watchMentions: merged.watchMentions,
|
|
166
|
-
watchRegex: normalizeWatchRegex(merged.watchRegex),
|
|
167
|
-
appAgentId: merged.appAgentId,
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
}
|