@zhin.js/core 1.0.45 → 1.0.46
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/CHANGELOG.md +13 -0
- package/lib/adapter.d.ts +3 -13
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +3 -63
- package/lib/adapter.js.map +1 -1
- package/lib/built/common-adapter-tools.d.ts +10 -14
- package/lib/built/common-adapter-tools.d.ts.map +1 -1
- package/lib/built/common-adapter-tools.js +47 -21
- package/lib/built/common-adapter-tools.js.map +1 -1
- package/lib/built/message-filter.d.ts +175 -0
- package/lib/built/message-filter.d.ts.map +1 -0
- package/lib/built/message-filter.js +236 -0
- package/lib/built/message-filter.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +6 -6
- package/src/adapter.ts +3 -81
- package/src/built/common-adapter-tools.ts +54 -25
- package/src/built/message-filter.ts +390 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageFilterFeature — 消息过滤引擎
|
|
3
|
+
*
|
|
4
|
+
* 设计理念:
|
|
5
|
+
* 将过滤规则视为 Feature Item,与命令 (CommandFeature)、权限 (PermissionFeature) 同构,
|
|
6
|
+
* 遵循框架的 add/remove/extensions/toJSON 范式,支持插件级 CRUD 和生命周期自动回收。
|
|
7
|
+
*
|
|
8
|
+
* 核心特性:
|
|
9
|
+
* - 基于优先级的规则引擎(first-match-wins,类似防火墙规则)
|
|
10
|
+
* - 多维匹配:scope / adapter / bot / channel / sender
|
|
11
|
+
* - 支持精确匹配、通配符 `*`、正则 `/pattern/`
|
|
12
|
+
* - 通过 Dispatcher Guardrail 集成,在消息调度第一阶段拦截
|
|
13
|
+
* - 插件通过 `addFilterRule()` 动态注册规则,卸载时自动清理
|
|
14
|
+
*/
|
|
15
|
+
import { Feature } from '../feature.js';
|
|
16
|
+
import { getPlugin } from '../plugin.js';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// 规则工厂
|
|
19
|
+
// ============================================================================
|
|
20
|
+
export var FilterRules;
|
|
21
|
+
(function (FilterRules) {
|
|
22
|
+
/** 创建拒绝规则 */
|
|
23
|
+
function deny(name, conditions) {
|
|
24
|
+
return { ...conditions, name, action: 'deny' };
|
|
25
|
+
}
|
|
26
|
+
FilterRules.deny = deny;
|
|
27
|
+
/** 创建放行规则 */
|
|
28
|
+
function allow(name, conditions) {
|
|
29
|
+
return { ...conditions, name, action: 'allow' };
|
|
30
|
+
}
|
|
31
|
+
FilterRules.allow = allow;
|
|
32
|
+
/**
|
|
33
|
+
* 创建黑名单规则组
|
|
34
|
+
* @returns 一条 deny 规则
|
|
35
|
+
*/
|
|
36
|
+
function blacklist(scope, ids, name) {
|
|
37
|
+
return {
|
|
38
|
+
name: name ?? `${scope}-blacklist`,
|
|
39
|
+
action: 'deny',
|
|
40
|
+
scopes: [scope],
|
|
41
|
+
channels: ids,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
FilterRules.blacklist = blacklist;
|
|
45
|
+
/**
|
|
46
|
+
* 创建白名单规则组
|
|
47
|
+
* @returns [allow 规则, deny-catch-all 规则]
|
|
48
|
+
*/
|
|
49
|
+
function whitelist(scope, ids, name) {
|
|
50
|
+
const baseName = name ?? `${scope}-whitelist`;
|
|
51
|
+
return [
|
|
52
|
+
{ name: baseName, action: 'allow', scopes: [scope], channels: ids, priority: 1 },
|
|
53
|
+
{ name: `${baseName}-deny-rest`, action: 'deny', scopes: [scope], priority: -100 },
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
FilterRules.whitelist = whitelist;
|
|
57
|
+
})(FilterRules || (FilterRules = {}));
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Feature 实现
|
|
60
|
+
// ============================================================================
|
|
61
|
+
export class MessageFilterFeature extends Feature {
|
|
62
|
+
name = 'message-filter';
|
|
63
|
+
icon = 'Filter';
|
|
64
|
+
desc = '消息过滤';
|
|
65
|
+
/** 按规则名称索引 */
|
|
66
|
+
byName = new Map();
|
|
67
|
+
#defaultPolicy = 'allow';
|
|
68
|
+
#sortedCache = null;
|
|
69
|
+
constructor(config) {
|
|
70
|
+
super();
|
|
71
|
+
if (config)
|
|
72
|
+
this.#loadConfig(config);
|
|
73
|
+
}
|
|
74
|
+
// ---- 公共 API ----
|
|
75
|
+
/** 默认策略 */
|
|
76
|
+
get defaultPolicy() { return this.#defaultPolicy; }
|
|
77
|
+
set defaultPolicy(policy) { this.#defaultPolicy = policy; }
|
|
78
|
+
/** 获取按优先级排序的启用规则列表(带缓存) */
|
|
79
|
+
get sortedRules() {
|
|
80
|
+
if (!this.#sortedCache) {
|
|
81
|
+
this.#sortedCache = [...this.items]
|
|
82
|
+
.filter(r => r.enabled !== false)
|
|
83
|
+
.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
84
|
+
}
|
|
85
|
+
return this.#sortedCache;
|
|
86
|
+
}
|
|
87
|
+
/** 按名称查询规则 */
|
|
88
|
+
getRule(name) {
|
|
89
|
+
return this.byName.get(name);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 检测消息是否应该被处理
|
|
93
|
+
*
|
|
94
|
+
* 遍历按优先级排序的规则列表,返回第一个匹配的规则结果。
|
|
95
|
+
* 若无规则匹配,按默认策略决定。
|
|
96
|
+
*/
|
|
97
|
+
test(message) {
|
|
98
|
+
for (const rule of this.sortedRules) {
|
|
99
|
+
if (this.#matchRule(rule, message)) {
|
|
100
|
+
return {
|
|
101
|
+
allowed: rule.action === 'allow',
|
|
102
|
+
matchedRule: rule.name,
|
|
103
|
+
reason: `matched rule "${rule.name}" → ${rule.action}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
allowed: this.#defaultPolicy === 'allow',
|
|
109
|
+
matchedRule: null,
|
|
110
|
+
reason: `no rule matched → default policy: ${this.#defaultPolicy}`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ---- Feature 重写 ----
|
|
114
|
+
/** 添加规则,维护 byName 索引 */
|
|
115
|
+
add(rule, pluginName) {
|
|
116
|
+
this.byName.set(rule.name, rule);
|
|
117
|
+
this.#sortedCache = null;
|
|
118
|
+
return super.add(rule, pluginName);
|
|
119
|
+
}
|
|
120
|
+
/** 移除规则,清理 byName 索引 */
|
|
121
|
+
remove(rule, pluginName) {
|
|
122
|
+
this.byName.delete(rule.name);
|
|
123
|
+
this.#sortedCache = null;
|
|
124
|
+
return super.remove(rule, pluginName);
|
|
125
|
+
}
|
|
126
|
+
/** 序列化为 JSON(供 Web 控制台展示) */
|
|
127
|
+
toJSON(pluginName) {
|
|
128
|
+
const list = pluginName ? this.getByPlugin(pluginName) : this.items;
|
|
129
|
+
return {
|
|
130
|
+
name: this.name,
|
|
131
|
+
icon: this.icon,
|
|
132
|
+
desc: this.desc,
|
|
133
|
+
count: list.length,
|
|
134
|
+
items: list.map(r => ({
|
|
135
|
+
name: r.name,
|
|
136
|
+
description: r.description,
|
|
137
|
+
action: r.action,
|
|
138
|
+
priority: r.priority ?? 0,
|
|
139
|
+
enabled: r.enabled !== false,
|
|
140
|
+
scopes: r.scopes,
|
|
141
|
+
adapters: r.adapters?.map(p => p instanceof RegExp ? p.source : p),
|
|
142
|
+
bots: r.bots?.map(p => p instanceof RegExp ? p.source : p),
|
|
143
|
+
channels: r.channels?.map(p => p instanceof RegExp ? p.source : p),
|
|
144
|
+
senders: r.senders?.map(p => p instanceof RegExp ? p.source : p),
|
|
145
|
+
})),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** 插件扩展方法 */
|
|
149
|
+
get extensions() {
|
|
150
|
+
const feature = this;
|
|
151
|
+
return {
|
|
152
|
+
addFilterRule(rule) {
|
|
153
|
+
const plugin = getPlugin();
|
|
154
|
+
const dispose = feature.add(rule, plugin.name);
|
|
155
|
+
plugin.recordFeatureContribution(feature.name, rule.name);
|
|
156
|
+
plugin.onDispose(dispose);
|
|
157
|
+
return dispose;
|
|
158
|
+
},
|
|
159
|
+
testFilter(message) {
|
|
160
|
+
return feature.test(message);
|
|
161
|
+
},
|
|
162
|
+
setDefaultFilterPolicy(policy) {
|
|
163
|
+
feature.defaultPolicy = policy;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// 配置加载
|
|
169
|
+
// ============================================================================
|
|
170
|
+
#loadConfig(config) {
|
|
171
|
+
if (config.default_policy) {
|
|
172
|
+
this.#defaultPolicy = config.default_policy;
|
|
173
|
+
}
|
|
174
|
+
if (config.rules) {
|
|
175
|
+
for (const rc of config.rules) {
|
|
176
|
+
this.add(this.#parseRuleConfig(rc), '__config__');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// 规则匹配
|
|
182
|
+
// ============================================================================
|
|
183
|
+
/** 解析配置中的规则字符串为 FilterPattern */
|
|
184
|
+
#parseRuleConfig(rc) {
|
|
185
|
+
return {
|
|
186
|
+
name: rc.name,
|
|
187
|
+
description: rc.description,
|
|
188
|
+
action: rc.action,
|
|
189
|
+
priority: rc.priority,
|
|
190
|
+
enabled: rc.enabled,
|
|
191
|
+
scopes: rc.scopes,
|
|
192
|
+
adapters: rc.adapters?.map(p => this.#parsePattern(p)),
|
|
193
|
+
bots: rc.bots?.map(p => this.#parsePattern(p)),
|
|
194
|
+
channels: rc.channels?.map(p => this.#parsePattern(p)),
|
|
195
|
+
senders: rc.senders?.map(p => this.#parsePattern(p)),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/** 解析单个 pattern:"/regex/flags" → RegExp,其余为精确字符串 */
|
|
199
|
+
#parsePattern(pattern) {
|
|
200
|
+
const match = pattern.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
201
|
+
if (match) {
|
|
202
|
+
return new RegExp(match[1], match[2]);
|
|
203
|
+
}
|
|
204
|
+
return pattern;
|
|
205
|
+
}
|
|
206
|
+
/** 检查规则是否匹配消息(所有条件 AND,条件内 OR) */
|
|
207
|
+
#matchRule(rule, message) {
|
|
208
|
+
if (rule.scopes?.length && !rule.scopes.includes(message.$channel.type)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
if (rule.adapters?.length && !this.#matchAny(rule.adapters, String(message.$adapter))) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (rule.bots?.length && !this.#matchAny(rule.bots, String(message.$bot))) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
if (rule.channels?.length && !this.#matchAny(rule.channels, message.$channel.id)) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
if (rule.senders?.length && !this.#matchAny(rule.senders, message.$sender.id)) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
/** 检查值是否与任一 pattern 匹配 */
|
|
226
|
+
#matchAny(patterns, value) {
|
|
227
|
+
return patterns.some(p => {
|
|
228
|
+
if (p instanceof RegExp)
|
|
229
|
+
return p.test(value);
|
|
230
|
+
if (p === '*')
|
|
231
|
+
return true;
|
|
232
|
+
return p === value;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=message-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-filter.js","sourceRoot":"","sources":["../../src/built/message-filter.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './built/permission.js';
|
|
|
15
15
|
export * from './built/adapter-process.js';
|
|
16
16
|
export * from './built/component.js';
|
|
17
17
|
export * from './built/database.js';
|
|
18
|
+
export * from './built/message-filter.js';
|
|
18
19
|
export * from './built/tool.js';
|
|
19
20
|
export * from './built/ai-trigger.js';
|
|
20
21
|
export * from './built/dispatcher.js';
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAE3B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAEhC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAE3B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAEhC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AAEzC,cAAc,iBAAiB,CAAA;AAE/B,cAAc,uBAAuB,CAAA;AAErC,cAAc,uBAAuB,CAAA;AAErC,cAAc,kBAAkB,CAAA;AAEhC,cAAc,2BAA2B,CAAA;AAEzC,cAAc,iCAAiC,CAAA;AAE/C,cAAc,eAAe,CAAA;AAE7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAE/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAExC,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA"}
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export * from './built/permission.js';
|
|
|
18
18
|
export * from './built/adapter-process.js';
|
|
19
19
|
export * from './built/component.js';
|
|
20
20
|
export * from './built/database.js';
|
|
21
|
+
export * from './built/message-filter.js';
|
|
21
22
|
// Tool Service (纯工具,无副作用)
|
|
22
23
|
export * from './built/tool.js';
|
|
23
24
|
// AI Trigger Service (纯工具,无副作用)
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,SAAS;AACT,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,oBAAoB;AACpB,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,0BAA0B;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,gCAAgC;AAChC,cAAc,uBAAuB,CAAA;AACrC,4BAA4B;AAC5B,cAAc,uBAAuB,CAAA;AACrC,qBAAqB;AACrB,cAAc,kBAAkB,CAAA;AAChC,sBAAsB;AACtB,cAAc,2BAA2B,CAAA;AACzC,yDAAyD;AACzD,cAAc,iCAAiC,CAAA;AAC/C,kCAAkC;AAClC,cAAc,eAAe,CAAA;AAE7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA,CAAE,WAAW;AACxC,cAAc,WAAW,CAAA;AACzB,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,yCAAyC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,SAAS;AACT,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,oBAAoB;AACpB,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,0BAA0B;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,gCAAgC;AAChC,cAAc,uBAAuB,CAAA;AACrC,4BAA4B;AAC5B,cAAc,uBAAuB,CAAA;AACrC,qBAAqB;AACrB,cAAc,kBAAkB,CAAA;AAChC,sBAAsB;AACtB,cAAc,2BAA2B,CAAA;AACzC,yDAAyD;AACzD,cAAc,iCAAiC,CAAA;AAC/C,kCAAkC;AAClC,cAAc,eAAe,CAAA;AAE7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA,CAAE,WAAW;AACxC,cAAc,WAAW,CAAA;AACzB,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,yCAAyC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "Zhin机器人核心框架",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"segment-matcher": "^1.0.5",
|
|
38
38
|
"smol-toml": "^1.6.0",
|
|
39
39
|
"yaml": "^2.3.4",
|
|
40
|
-
"@zhin.js/
|
|
41
|
-
"@zhin.js/database": "1.0.
|
|
42
|
-
"@zhin.js/
|
|
43
|
-
"@zhin.js/logger": "0.1.
|
|
44
|
-
"@zhin.js/schema": "1.0.
|
|
40
|
+
"@zhin.js/kernel": "0.0.7",
|
|
41
|
+
"@zhin.js/database": "1.0.33",
|
|
42
|
+
"@zhin.js/ai": "1.0.7",
|
|
43
|
+
"@zhin.js/logger": "0.1.30",
|
|
44
|
+
"@zhin.js/schema": "1.0.30"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^24.3.0",
|
package/src/adapter.ts
CHANGED
|
@@ -6,25 +6,12 @@ import { BeforeSendHandler, SendOptions, Tool, ToolContext, ToolScope } from "./
|
|
|
6
6
|
import { segment } from "./utils.js";
|
|
7
7
|
import { ZhinTool, isZhinTool, type ToolInput } from "./built/tool.js";
|
|
8
8
|
import type { Skill, SkillFeature } from "./built/skill.js";
|
|
9
|
-
import {
|
|
10
|
-
type IGroupManagement,
|
|
11
|
-
GROUP_METHOD_SPECS,
|
|
12
|
-
GROUP_MANAGEMENT_SKILL_DESCRIPTION,
|
|
13
|
-
GROUP_MANAGEMENT_SKILL_TAGS,
|
|
14
|
-
GROUP_MANAGEMENT_SKILL_KEYWORDS,
|
|
15
|
-
buildMethodArgs,
|
|
16
|
-
} from "./built/common-adapter-tools.js";
|
|
17
9
|
/**
|
|
18
|
-
* Adapter类:适配器抽象,管理多平台Bot实例。
|
|
10
|
+
* Adapter 类:适配器抽象,管理多平台 Bot 实例。
|
|
19
11
|
* 负责根据配置启动/关闭各平台机器人,统一异常处理。
|
|
20
12
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* 群管理能力:
|
|
24
|
-
* Adapter 基类声明了 IGroupManagement 中的可选方法规范,
|
|
25
|
-
* 具体适配器(ICQQ/Discord/Telegram 等)选择性覆写。
|
|
26
|
-
* 调用 registerGroupManagementSkill() 后,基类自动检测
|
|
27
|
-
* 哪些方法已被实现,生成对应的 Tool 并注册为 "群聊管理" Skill。
|
|
13
|
+
* 适配器可提供 AI 工具并声明 Skill。群管等能力由各 IM 平台在子类中
|
|
14
|
+
* 自行实现方法并注册 Tool(如使用 createGroupManagementTools)+ declareSkill。
|
|
28
15
|
*/
|
|
29
16
|
export abstract class Adapter<R extends Bot = Bot> extends EventEmitter<Adapter.Lifecycle> {
|
|
30
17
|
/** 当前适配器下所有Bot实例,key为bot名称 */
|
|
@@ -101,8 +88,6 @@ export abstract class Adapter<R extends Bot = Bot> extends EventEmitter<Adapter.
|
|
|
101
88
|
this.bots.set(bot.$id, bot);
|
|
102
89
|
}
|
|
103
90
|
this.logger.debug(`adapter ${this.name} started`);
|
|
104
|
-
|
|
105
|
-
this._autoDetectGroupManagement();
|
|
106
91
|
}
|
|
107
92
|
/**
|
|
108
93
|
* 停止适配器,断开并移除所有Bot实例
|
|
@@ -322,69 +307,6 @@ export abstract class Adapter<R extends Bot = Bot> extends EventEmitter<Adapter.
|
|
|
322
307
|
});
|
|
323
308
|
}
|
|
324
309
|
|
|
325
|
-
// ==========================================================================
|
|
326
|
-
// 群管理自动检测 — start() 结束时自动执行
|
|
327
|
-
// ==========================================================================
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* 遍历 GROUP_METHOD_SPECS,检测子类是否覆写了对应方法。
|
|
331
|
-
* 对每个已实现的方法生成 Tool 并注册,最后聚合为 "群聊管理" Skill。
|
|
332
|
-
*/
|
|
333
|
-
private _autoDetectGroupManagement(): void {
|
|
334
|
-
const self = this as unknown as IGroupManagement;
|
|
335
|
-
const prefix = this.name as string;
|
|
336
|
-
const generatedTools: Tool[] = [];
|
|
337
|
-
|
|
338
|
-
for (const spec of GROUP_METHOD_SPECS) {
|
|
339
|
-
const fn = self[spec.method];
|
|
340
|
-
if (typeof fn !== 'function') continue;
|
|
341
|
-
|
|
342
|
-
const boundFn: (...args: any[]) => Promise<any> = fn.bind(self);
|
|
343
|
-
|
|
344
|
-
const properties: Record<string, any> = {
|
|
345
|
-
bot_id: { type: 'string', description: 'Bot ID', contextKey: 'botId' },
|
|
346
|
-
scene_id: { type: 'string', description: '群/服务器 ID', contextKey: 'sceneId' },
|
|
347
|
-
};
|
|
348
|
-
const required: string[] = ['bot_id', 'scene_id'];
|
|
349
|
-
|
|
350
|
-
for (const [name, schema] of Object.entries(spec.extraParams)) {
|
|
351
|
-
properties[name] = schema;
|
|
352
|
-
}
|
|
353
|
-
if (spec.extraRequired) {
|
|
354
|
-
required.push(...spec.extraRequired);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const tool: Tool = {
|
|
358
|
-
name: `${prefix}_${spec.toolSuffix}`,
|
|
359
|
-
description: `${spec.description} (${prefix})`,
|
|
360
|
-
parameters: { type: 'object' as const, properties, required },
|
|
361
|
-
execute: async (args: Record<string, any>) => {
|
|
362
|
-
const { bot_id, scene_id, ...rest } = args;
|
|
363
|
-
return boundFn(...buildMethodArgs(spec.method, bot_id, scene_id, rest));
|
|
364
|
-
},
|
|
365
|
-
tags: ['group', 'management', prefix],
|
|
366
|
-
keywords: spec.keywords,
|
|
367
|
-
permissionLevel: spec.permissionLevel,
|
|
368
|
-
scopes: ['group', 'channel'] as ToolScope[],
|
|
369
|
-
preExecutable: spec.preExecutable,
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
this.addTool(tool);
|
|
373
|
-
generatedTools.push(tool);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (generatedTools.length === 0) return;
|
|
377
|
-
|
|
378
|
-
this.declareSkill({
|
|
379
|
-
description: GROUP_MANAGEMENT_SKILL_DESCRIPTION,
|
|
380
|
-
keywords: GROUP_MANAGEMENT_SKILL_KEYWORDS,
|
|
381
|
-
tags: GROUP_MANAGEMENT_SKILL_TAGS,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
this.logger.debug(
|
|
385
|
-
`自动检测到 ${generatedTools.length} 个群管理方法 → 已注册 Skill`,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
310
|
}
|
|
389
311
|
export interface Adapters {}
|
|
390
312
|
export namespace Adapter {
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Group Management
|
|
2
|
+
* Group Management — 方法规范与 Tool 工厂
|
|
3
3
|
*
|
|
4
4
|
* 设计理念:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 具体适配器(ICQQ/Discord/Telegram 等)选择性覆写。
|
|
8
|
-
* Adapter.start() 自动检测哪些方法已被子类实现,
|
|
9
|
-
* 生成对应的 Tool 并注册为 "群聊管理" Skill。
|
|
5
|
+
* 各 IM 平台在适配器内自行实现群管方法(kickMember、muteMember 等),
|
|
6
|
+
* 并自行注册 Tool 与 declareSkill,保障平台特性与描述一致。
|
|
10
7
|
*
|
|
11
|
-
*
|
|
8
|
+
* 使用方式(在各适配器 start 或注册方法中):
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* }
|
|
10
|
+
* import { createGroupManagementTools, GROUP_MANAGEMENT_SKILL_KEYWORDS, GROUP_MANAGEMENT_SKILL_TAGS } from 'zhin.js';
|
|
11
|
+
* const tools = createGroupManagementTools(this, this.name);
|
|
12
|
+
* tools.forEach(t => this.addTool(t));
|
|
13
|
+
* this.declareSkill({ description: '本平台群管说明...', keywords: [...], tags: [...] });
|
|
18
14
|
*/
|
|
19
15
|
|
|
20
|
-
import type { ToolPermissionLevel } from '../types.js';
|
|
16
|
+
import type { Tool, ToolPermissionLevel, ToolScope } from '../types.js';
|
|
21
17
|
|
|
22
18
|
// ============================================================================
|
|
23
19
|
// Adapter 群管理方法规范
|
|
@@ -168,25 +164,58 @@ export const GROUP_METHOD_SPECS: GroupMethodSpec[] = [
|
|
|
168
164
|
];
|
|
169
165
|
|
|
170
166
|
// ============================================================================
|
|
171
|
-
// Skill
|
|
167
|
+
// Skill 常量(各适配器 declareSkill 时可复用 keywords/tags)
|
|
172
168
|
// ============================================================================
|
|
173
169
|
|
|
174
|
-
export const GROUP_MANAGEMENT_SKILL_DESCRIPTION =
|
|
175
|
-
'群聊管理能力:在 IM 系统中对群/服务器进行管理,包括踢人、禁言、封禁、' +
|
|
176
|
-
'设置管理员、修改群名、查看成员列表等操作。具体可用的操作取决于平台和 Bot 权限。\n\n' +
|
|
177
|
-
'使用指南:\n' +
|
|
178
|
-
'1. 用户提供昵称/名片而非 ID 时,必须先调用 list_members 查询成员列表,从返回结果中匹配目标用户的 user_id,再执行后续操作\n' +
|
|
179
|
-
'2. 禁言(mute_member)适用场景:违规发言、刷屏、骚扰他人等;传 duration=0 可解除禁言\n' +
|
|
180
|
-
'3. 设置/取消管理员(set_admin)需要群主权限,普通管理员无法操作;enable=false 为取消管理员\n' +
|
|
181
|
-
'4. 踢人(kick_member)是将成员移出群聊,封禁(ban_member)是永久拉黑,两者不同\n' +
|
|
182
|
-
'5. 操作前应确认目标用户正确,避免误操作';
|
|
183
|
-
|
|
184
170
|
export const GROUP_MANAGEMENT_SKILL_TAGS = ['group', 'management', 'im', 'admin'];
|
|
185
171
|
export const GROUP_MANAGEMENT_SKILL_KEYWORDS = [
|
|
186
172
|
'群管理', '踢人', '禁言', '封禁', '管理员', '群名',
|
|
187
173
|
'成员', 'kick', 'mute', 'ban', 'admin', 'members',
|
|
188
174
|
];
|
|
189
175
|
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// 工厂:根据已实现的方法为指定适配器生成群管 Tool 列表(各平台自行调用并 addTool)
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
export function createGroupManagementTools(
|
|
181
|
+
adapter: IGroupManagement,
|
|
182
|
+
prefix: string,
|
|
183
|
+
): Tool[] {
|
|
184
|
+
const tools: Tool[] = [];
|
|
185
|
+
for (const spec of GROUP_METHOD_SPECS) {
|
|
186
|
+
const fn = adapter[spec.method];
|
|
187
|
+
if (typeof fn !== 'function') continue;
|
|
188
|
+
|
|
189
|
+
const properties: Record<string, any> = {
|
|
190
|
+
bot_id: { type: 'string', description: 'Bot ID', contextKey: 'botId' },
|
|
191
|
+
scene_id: { type: 'string', description: '群/服务器 ID', contextKey: 'sceneId' },
|
|
192
|
+
};
|
|
193
|
+
const required: string[] = ['bot_id', 'scene_id'];
|
|
194
|
+
for (const [name, schema] of Object.entries(spec.extraParams)) {
|
|
195
|
+
properties[name] = schema;
|
|
196
|
+
}
|
|
197
|
+
if (spec.extraRequired) required.push(...spec.extraRequired);
|
|
198
|
+
|
|
199
|
+
const boundFn = fn.bind(adapter);
|
|
200
|
+
tools.push({
|
|
201
|
+
name: `${prefix}_${spec.toolSuffix}`,
|
|
202
|
+
description: `${spec.description} (${prefix})`,
|
|
203
|
+
parameters: { type: 'object' as const, properties, required },
|
|
204
|
+
execute: async (args: Record<string, any>) => {
|
|
205
|
+
const { bot_id, scene_id, ...rest } = args;
|
|
206
|
+
const methodArgs = buildMethodArgs(spec.method, bot_id, scene_id, rest);
|
|
207
|
+
return (boundFn as (...a: any[]) => Promise<any>).apply(adapter, methodArgs);
|
|
208
|
+
},
|
|
209
|
+
tags: ['group', 'management', prefix],
|
|
210
|
+
keywords: spec.keywords,
|
|
211
|
+
permissionLevel: spec.permissionLevel,
|
|
212
|
+
scopes: ['group', 'channel'] as ToolScope[],
|
|
213
|
+
preExecutable: spec.preExecutable,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return tools;
|
|
217
|
+
}
|
|
218
|
+
|
|
190
219
|
// ============================================================================
|
|
191
220
|
// 参数映射(method → 有序参数列表)
|
|
192
221
|
// ============================================================================
|
|
@@ -208,6 +237,6 @@ export function buildMethodArgs(
|
|
|
208
237
|
case 'setGroupName': return [botId, sceneId, rest.name];
|
|
209
238
|
case 'muteAll': return [botId, sceneId, rest.enable ?? true];
|
|
210
239
|
case 'getGroupInfo': return [botId, sceneId];
|
|
211
|
-
default: return [botId, sceneId, ...Object.values(rest)];
|
|
240
|
+
default: return [botId, sceneId, ...(Object.values(rest) as any[])];
|
|
212
241
|
}
|
|
213
242
|
}
|