@zhin.js/core 1.0.24 → 1.0.26
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 +22 -0
- package/README.md +84 -342
- package/lib/adapter.d.ts +45 -1
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +182 -1
- package/lib/adapter.js.map +1 -1
- package/lib/ai/agent.d.ts +126 -0
- package/lib/ai/agent.d.ts.map +1 -0
- package/lib/ai/agent.js +645 -0
- package/lib/ai/agent.js.map +1 -0
- package/lib/ai/context-manager.d.ts +213 -0
- package/lib/ai/context-manager.d.ts.map +1 -0
- package/lib/ai/context-manager.js +313 -0
- package/lib/ai/context-manager.js.map +1 -0
- package/lib/ai/conversation-memory.d.ts +181 -0
- package/lib/ai/conversation-memory.d.ts.map +1 -0
- package/lib/ai/conversation-memory.js +581 -0
- package/lib/ai/conversation-memory.js.map +1 -0
- package/lib/ai/follow-up.d.ts +131 -0
- package/lib/ai/follow-up.d.ts.map +1 -0
- package/lib/ai/follow-up.js +265 -0
- package/lib/ai/follow-up.js.map +1 -0
- package/lib/ai/index.d.ts +29 -0
- package/lib/ai/index.d.ts.map +1 -0
- package/lib/ai/index.js +34 -0
- package/lib/ai/index.js.map +1 -0
- package/lib/ai/init.d.ts +30 -0
- package/lib/ai/init.d.ts.map +1 -0
- package/lib/ai/init.js +424 -0
- package/lib/ai/init.js.map +1 -0
- package/lib/ai/output.d.ts +93 -0
- package/lib/ai/output.d.ts.map +1 -0
- package/lib/ai/output.js +176 -0
- package/lib/ai/output.js.map +1 -0
- package/lib/ai/providers/anthropic.d.ts +23 -0
- package/lib/ai/providers/anthropic.d.ts.map +1 -0
- package/lib/ai/providers/anthropic.js +322 -0
- package/lib/ai/providers/anthropic.js.map +1 -0
- package/lib/ai/providers/base.d.ts +43 -0
- package/lib/ai/providers/base.d.ts.map +1 -0
- package/lib/ai/providers/base.js +135 -0
- package/lib/ai/providers/base.js.map +1 -0
- package/lib/ai/providers/index.d.ts +12 -0
- package/lib/ai/providers/index.d.ts.map +1 -0
- package/lib/ai/providers/index.js +9 -0
- package/lib/ai/providers/index.js.map +1 -0
- package/lib/ai/providers/ollama.d.ts +25 -0
- package/lib/ai/providers/ollama.d.ts.map +1 -0
- package/lib/ai/providers/ollama.js +243 -0
- package/lib/ai/providers/ollama.js.map +1 -0
- package/lib/ai/providers/openai.d.ts +46 -0
- package/lib/ai/providers/openai.d.ts.map +1 -0
- package/lib/ai/providers/openai.js +132 -0
- package/lib/ai/providers/openai.js.map +1 -0
- package/lib/ai/rate-limiter.d.ts +38 -0
- package/lib/ai/rate-limiter.d.ts.map +1 -0
- package/lib/ai/rate-limiter.js +86 -0
- package/lib/ai/rate-limiter.js.map +1 -0
- package/lib/ai/service.d.ts +81 -0
- package/lib/ai/service.d.ts.map +1 -0
- package/lib/ai/service.js +274 -0
- package/lib/ai/service.js.map +1 -0
- package/lib/ai/session.d.ts +186 -0
- package/lib/ai/session.d.ts.map +1 -0
- package/lib/ai/session.js +443 -0
- package/lib/ai/session.js.map +1 -0
- package/lib/ai/tone-detector.d.ts +19 -0
- package/lib/ai/tone-detector.d.ts.map +1 -0
- package/lib/ai/tone-detector.js +72 -0
- package/lib/ai/tone-detector.js.map +1 -0
- package/lib/ai/tools.d.ts +45 -0
- package/lib/ai/tools.d.ts.map +1 -0
- package/lib/ai/tools.js +206 -0
- package/lib/ai/tools.js.map +1 -0
- package/lib/ai/types.d.ts +264 -0
- package/lib/ai/types.d.ts.map +1 -0
- package/lib/ai/types.js +6 -0
- package/lib/ai/types.js.map +1 -0
- package/lib/ai/user-profile.d.ts +56 -0
- package/lib/ai/user-profile.d.ts.map +1 -0
- package/lib/ai/user-profile.js +130 -0
- package/lib/ai/user-profile.js.map +1 -0
- package/lib/ai/zhin-agent.d.ts +165 -0
- package/lib/ai/zhin-agent.d.ts.map +1 -0
- package/lib/ai/zhin-agent.js +707 -0
- package/lib/ai/zhin-agent.js.map +1 -0
- package/lib/built/adapter-process.d.ts +4 -0
- package/lib/built/adapter-process.d.ts.map +1 -1
- package/lib/built/adapter-process.js +94 -0
- package/lib/built/adapter-process.js.map +1 -1
- package/lib/built/ai-trigger.d.ts +89 -0
- package/lib/built/ai-trigger.d.ts.map +1 -0
- package/lib/built/ai-trigger.js +166 -0
- package/lib/built/ai-trigger.js.map +1 -0
- package/lib/built/command.d.ts +33 -17
- package/lib/built/command.d.ts.map +1 -1
- package/lib/built/command.js +71 -44
- package/lib/built/command.js.map +1 -1
- package/lib/built/component.d.ts +42 -15
- package/lib/built/component.d.ts.map +1 -1
- package/lib/built/component.js +84 -52
- package/lib/built/component.js.map +1 -1
- package/lib/built/config.d.ts +54 -5
- package/lib/built/config.d.ts.map +1 -1
- package/lib/built/config.js +76 -10
- package/lib/built/config.js.map +1 -1
- package/lib/built/cron.d.ts +41 -18
- package/lib/built/cron.d.ts.map +1 -1
- package/lib/built/cron.js +106 -63
- package/lib/built/cron.js.map +1 -1
- package/lib/built/database.d.ts +55 -6
- package/lib/built/database.d.ts.map +1 -1
- package/lib/built/database.js +93 -22
- package/lib/built/database.js.map +1 -1
- package/lib/built/dispatcher.d.ts +118 -0
- package/lib/built/dispatcher.d.ts.map +1 -0
- package/lib/built/dispatcher.js +196 -0
- package/lib/built/dispatcher.js.map +1 -0
- package/lib/built/permission.d.ts +45 -5
- package/lib/built/permission.d.ts.map +1 -1
- package/lib/built/permission.js +56 -11
- package/lib/built/permission.js.map +1 -1
- package/lib/built/skill.d.ts +117 -0
- package/lib/built/skill.d.ts.map +1 -0
- package/lib/built/skill.js +191 -0
- package/lib/built/skill.js.map +1 -0
- package/lib/built/tool.d.ts +188 -0
- package/lib/built/tool.d.ts.map +1 -0
- package/lib/built/tool.js +749 -0
- package/lib/built/tool.js.map +1 -0
- package/lib/feature.d.ts +75 -0
- package/lib/feature.d.ts.map +1 -0
- package/lib/feature.js +69 -0
- package/lib/feature.js.map +1 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -1
- package/lib/plugin.d.ts +53 -18
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +301 -31
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +248 -9
- package/lib/types.d.ts.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +38 -12
- package/lib/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/adapter.ts +206 -2
- package/src/ai/agent.ts +772 -0
- package/src/ai/context-manager.ts +440 -0
- package/src/ai/conversation-memory.ts +774 -0
- package/src/ai/follow-up.ts +357 -0
- package/src/ai/index.ts +128 -0
- package/src/ai/init.ts +502 -0
- package/src/ai/output.ts +261 -0
- package/src/ai/providers/anthropic.ts +375 -0
- package/src/ai/providers/base.ts +173 -0
- package/src/ai/providers/index.ts +13 -0
- package/src/ai/providers/ollama.ts +292 -0
- package/src/ai/providers/openai.ts +167 -0
- package/src/ai/rate-limiter.ts +129 -0
- package/src/ai/service.ts +319 -0
- package/src/ai/session.ts +544 -0
- package/src/ai/tone-detector.ts +89 -0
- package/src/ai/tools.ts +218 -0
- package/src/ai/types.ts +296 -0
- package/src/ai/user-profile.ts +181 -0
- package/src/ai/zhin-agent.ts +845 -0
- package/src/built/adapter-process.ts +99 -0
- package/src/built/ai-trigger.ts +259 -0
- package/src/built/command.ts +75 -69
- package/src/built/component.ts +94 -76
- package/src/built/config.ts +238 -128
- package/src/built/cron.ts +117 -101
- package/src/built/database.ts +128 -33
- package/src/built/dispatcher.ts +332 -0
- package/src/built/permission.ts +146 -54
- package/src/built/skill.ts +280 -0
- package/src/built/tool.ts +928 -0
- package/src/feature.ts +113 -0
- package/src/index.ts +11 -0
- package/src/plugin.ts +359 -69
- package/src/types.ts +306 -11
- package/src/utils.ts +37 -13
- package/tests/adapter.test.ts +153 -1
- package/tests/ai/agent.test.ts +614 -0
- package/tests/ai/ai-trigger.test.ts +368 -0
- package/tests/ai/context-manager.test.ts +413 -0
- package/tests/ai/conversation-memory.test.ts +128 -0
- package/tests/ai/follow-up.test.ts +175 -0
- package/tests/ai/integration.test.ts +584 -0
- package/tests/ai/output.test.ts +128 -0
- package/tests/ai/providers.integration.test.ts +227 -0
- package/tests/ai/rate-limiter.test.ts +108 -0
- package/tests/ai/session.test.ts +375 -0
- package/tests/ai/setup.ts +308 -0
- package/tests/ai/tone-detector.test.ts +80 -0
- package/tests/ai/tool.test.ts +800 -0
- package/tests/ai/tools-builtin.test.ts +346 -0
- package/tests/ai/user-profile.test.ts +73 -0
- package/tests/ai/zhin-agent.test.ts +177 -0
- package/tests/component-new.test.ts +17 -6
- package/tests/config.test.ts +46 -0
- package/tests/cron.test.ts +94 -5
- package/tests/dispatcher.test.ts +146 -0
- package/tests/feature.test.ts +145 -0
- package/tests/features-builtin.test.ts +191 -0
- package/tests/plugin.test.ts +88 -14
- package/tests/skill-feature.test.ts +179 -0
- package/tests/tool-feature.test.ts +254 -0
|
@@ -65,10 +65,109 @@ export class ProcessBot implements Bot<{},{content:string,ts:number}>{
|
|
|
65
65
|
export class ProcessAdapter extends Adapter<ProcessBot>{
|
|
66
66
|
constructor(plugin: Plugin) {
|
|
67
67
|
super(plugin, 'process', [{}]);
|
|
68
|
+
|
|
69
|
+
// 注册适配器提供的 AI 工具
|
|
70
|
+
this.registerDefaultTools();
|
|
71
|
+
this.registerProcessTools();
|
|
68
72
|
}
|
|
73
|
+
|
|
69
74
|
createBot(config: Adapter.BotConfig<ProcessBot>): ProcessBot {
|
|
70
75
|
return new ProcessBot(this, config);
|
|
71
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 注册 Process 适配器特有的工具
|
|
80
|
+
*/
|
|
81
|
+
private registerProcessTools(): void {
|
|
82
|
+
// 获取进程信息
|
|
83
|
+
this.addTool({
|
|
84
|
+
name: 'process_get_info',
|
|
85
|
+
description: '获取当前进程的详细信息,包括 PID、内存使用、运行时间等',
|
|
86
|
+
parameters: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {},
|
|
89
|
+
},
|
|
90
|
+
execute: async () => {
|
|
91
|
+
const memUsage = process.memoryUsage();
|
|
92
|
+
return {
|
|
93
|
+
pid: process.pid,
|
|
94
|
+
title: process.title,
|
|
95
|
+
uptime: process.uptime(),
|
|
96
|
+
platform: process.platform,
|
|
97
|
+
arch: process.arch,
|
|
98
|
+
nodeVersion: process.version,
|
|
99
|
+
memory: {
|
|
100
|
+
rss: Math.round(memUsage.rss / 1024 / 1024) + ' MB',
|
|
101
|
+
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024) + ' MB',
|
|
102
|
+
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024) + ' MB',
|
|
103
|
+
},
|
|
104
|
+
cwd: process.cwd(),
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 获取环境变量
|
|
110
|
+
this.addTool({
|
|
111
|
+
name: 'process_get_env',
|
|
112
|
+
description: '获取指定的环境变量值',
|
|
113
|
+
parameters: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
name: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: '环境变量名称',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
required: ['name'],
|
|
122
|
+
},
|
|
123
|
+
execute: async (args) => {
|
|
124
|
+
const { name } = args;
|
|
125
|
+
// 安全起见,不返回敏感环境变量
|
|
126
|
+
const sensitive = ['PASSWORD', 'SECRET', 'TOKEN', 'KEY', 'CREDENTIAL'];
|
|
127
|
+
if (sensitive.some(s => name.toUpperCase().includes(s))) {
|
|
128
|
+
return { error: '无法访问敏感环境变量' };
|
|
129
|
+
}
|
|
130
|
+
return { name, value: process.env[name] || null };
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// 输出到控制台
|
|
135
|
+
this.addTool({
|
|
136
|
+
name: 'process_console_log',
|
|
137
|
+
description: '向控制台输出信息',
|
|
138
|
+
parameters: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
message: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
description: '要输出的消息',
|
|
144
|
+
},
|
|
145
|
+
level: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
description: '日志级别',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
required: ['message'],
|
|
151
|
+
},
|
|
152
|
+
execute: async (args) => {
|
|
153
|
+
const { message, level = 'info' } = args;
|
|
154
|
+
switch (level) {
|
|
155
|
+
case 'warn':
|
|
156
|
+
console.warn(message);
|
|
157
|
+
break;
|
|
158
|
+
case 'error':
|
|
159
|
+
console.error(message);
|
|
160
|
+
break;
|
|
161
|
+
case 'debug':
|
|
162
|
+
console.debug(message);
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
console.log(message);
|
|
166
|
+
}
|
|
167
|
+
return { success: true, message: `已输出: ${message}` };
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
72
171
|
}
|
|
73
172
|
declare module '../adapter.js'{
|
|
74
173
|
interface Adapters{
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Trigger 工具函数
|
|
3
|
+
* 提供 AI 触发相关的工具函数,可在插件中直接使用
|
|
4
|
+
*
|
|
5
|
+
* 触发方式:
|
|
6
|
+
* 1. @机器人 - 在群聊中 @ 机器人触发
|
|
7
|
+
* 2. 前缀触发 - 使用配置的前缀(如 # 或 AI:)
|
|
8
|
+
* 3. 私聊直接对话 - 私聊时直接对话
|
|
9
|
+
* 4. 关键词触发 - 包含特定关键词时触发
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Message } from "../message.js";
|
|
13
|
+
import type {
|
|
14
|
+
MessageElement,
|
|
15
|
+
ToolScope,
|
|
16
|
+
ToolPermissionLevel,
|
|
17
|
+
MaybePromise,
|
|
18
|
+
} from "../types.js";
|
|
19
|
+
import { segment } from "../utils.js";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// 类型定义
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* AI 触发配置
|
|
27
|
+
*/
|
|
28
|
+
export interface AITriggerConfig {
|
|
29
|
+
/** 是否启用(默认 true) */
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
|
|
32
|
+
/** 触发前缀列表(默认 ['#', 'AI:']) */
|
|
33
|
+
prefixes?: string[];
|
|
34
|
+
|
|
35
|
+
/** 是否响应 @ 机器人(默认 true) */
|
|
36
|
+
respondToAt?: boolean;
|
|
37
|
+
|
|
38
|
+
/** 是否响应私聊(默认 true) */
|
|
39
|
+
respondToPrivate?: boolean;
|
|
40
|
+
|
|
41
|
+
/** 触发关键词(可选) */
|
|
42
|
+
keywords?: string[];
|
|
43
|
+
|
|
44
|
+
/** 忽略的前缀(命令前缀,避免与命令冲突) */
|
|
45
|
+
ignorePrefixes?: string[];
|
|
46
|
+
|
|
47
|
+
/** 超时时间(毫秒,默认 60000) */
|
|
48
|
+
timeout?: number;
|
|
49
|
+
|
|
50
|
+
/** 思考中提示(可选) */
|
|
51
|
+
thinkingMessage?: string;
|
|
52
|
+
|
|
53
|
+
/** 错误消息模板 */
|
|
54
|
+
errorTemplate?: string;
|
|
55
|
+
|
|
56
|
+
/** Zhin 拥有者 ID 列表 */
|
|
57
|
+
owners?: string[];
|
|
58
|
+
|
|
59
|
+
/** 机器人管理员 ID 列表 */
|
|
60
|
+
botAdmins?: string[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* AI 触发检查结果
|
|
65
|
+
*/
|
|
66
|
+
export interface AITriggerResult {
|
|
67
|
+
triggered: boolean;
|
|
68
|
+
content: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 发送者权限信息
|
|
73
|
+
*/
|
|
74
|
+
export interface SenderPermissions {
|
|
75
|
+
scope: ToolScope;
|
|
76
|
+
isGroupAdmin: boolean;
|
|
77
|
+
isGroupOwner: boolean;
|
|
78
|
+
isBotAdmin: boolean;
|
|
79
|
+
isOwner: boolean;
|
|
80
|
+
permissionLevel: ToolPermissionLevel;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// 默认配置
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
export const DEFAULT_AI_TRIGGER_CONFIG: Required<AITriggerConfig> = {
|
|
88
|
+
enabled: true,
|
|
89
|
+
prefixes: ['#', 'AI:', 'ai:'],
|
|
90
|
+
respondToAt: true,
|
|
91
|
+
respondToPrivate: true,
|
|
92
|
+
keywords: [],
|
|
93
|
+
ignorePrefixes: ['/', '!', '!'],
|
|
94
|
+
timeout: 60000,
|
|
95
|
+
thinkingMessage: '',
|
|
96
|
+
errorTemplate: '❌ AI 处理失败: {error}',
|
|
97
|
+
owners: [],
|
|
98
|
+
botAdmins: [],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// 工具函数
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 检查消息是否 @ 了机器人
|
|
107
|
+
*/
|
|
108
|
+
export function isAtBot<T extends object>(message: Message<T>): boolean {
|
|
109
|
+
const botId = String(message.$bot);
|
|
110
|
+
return message.$content.some(seg => {
|
|
111
|
+
return String(seg.data?.qq) === botId || String(seg.data?.user_id) === botId;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 提取消息文本内容(保留富媒体信息)
|
|
117
|
+
* 使用 segment.toString 将 MessageElement 转为 XML 格式
|
|
118
|
+
*/
|
|
119
|
+
export function extractTextContent<T extends object>(message: Message<T>): string {
|
|
120
|
+
return segment.toString(message.$content);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 解析 AI 回复中的富媒体内容
|
|
125
|
+
* 将字符串中的 XML-like 标签解析为 MessageElement
|
|
126
|
+
*/
|
|
127
|
+
export function parseRichMediaContent(content: string): MessageElement[] {
|
|
128
|
+
try {
|
|
129
|
+
const parsed = segment.from(content);
|
|
130
|
+
const elements = Array.isArray(parsed) ? parsed : [parsed];
|
|
131
|
+
return elements.map(el => {
|
|
132
|
+
if (typeof el === 'string') {
|
|
133
|
+
return { type: 'text', data: { text: el } };
|
|
134
|
+
}
|
|
135
|
+
return el as MessageElement;
|
|
136
|
+
});
|
|
137
|
+
} catch (error) {
|
|
138
|
+
return [{ type: 'text', data: { text: content } }];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 移除 @ 机器人的部分
|
|
144
|
+
*/
|
|
145
|
+
export function removeAtBot<T extends object>(message: Message<T>): MessageElement[] {
|
|
146
|
+
const botId = String(message.$bot);
|
|
147
|
+
return message.$content.filter(seg => {
|
|
148
|
+
const { type, data } = seg;
|
|
149
|
+
if (type !== "at") return true; // 非 at 段全部保留
|
|
150
|
+
const userId = String(data?.user_id || data?.qq);
|
|
151
|
+
return userId !== botId; // 只移除 @机器人 的段,保留 @其他人
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 从消息中推断发送者的权限级别
|
|
157
|
+
*/
|
|
158
|
+
export function inferSenderPermissions<T extends object>(
|
|
159
|
+
message: Message<T>,
|
|
160
|
+
config: AITriggerConfig
|
|
161
|
+
): SenderPermissions {
|
|
162
|
+
const scope: ToolScope = message.$channel?.type as ToolScope || 'private';
|
|
163
|
+
const senderId = message.$sender.id;
|
|
164
|
+
const senderPermissions = message.$sender.permissions || [];
|
|
165
|
+
|
|
166
|
+
const owners = config.owners || [];
|
|
167
|
+
const isOwner = owners.includes(senderId);
|
|
168
|
+
|
|
169
|
+
const botAdmins = config.botAdmins || [];
|
|
170
|
+
const isBotAdmin = isOwner || botAdmins.includes(senderId);
|
|
171
|
+
|
|
172
|
+
const isGroupOwner = senderPermissions.includes('owner') ||
|
|
173
|
+
senderPermissions.includes('group_owner') ||
|
|
174
|
+
(message as any).$sender?.role === 'owner';
|
|
175
|
+
|
|
176
|
+
const isGroupAdmin = isGroupOwner ||
|
|
177
|
+
senderPermissions.includes('admin') ||
|
|
178
|
+
senderPermissions.includes('group_admin') ||
|
|
179
|
+
(message as any).$sender?.role === 'admin';
|
|
180
|
+
|
|
181
|
+
let permissionLevel: ToolPermissionLevel = 'user';
|
|
182
|
+
if (isOwner) permissionLevel = 'owner';
|
|
183
|
+
else if (isBotAdmin) permissionLevel = 'bot_admin';
|
|
184
|
+
else if (isGroupOwner) permissionLevel = 'group_owner';
|
|
185
|
+
else if (isGroupAdmin) permissionLevel = 'group_admin';
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
scope,
|
|
189
|
+
isGroupAdmin,
|
|
190
|
+
isGroupOwner,
|
|
191
|
+
isBotAdmin,
|
|
192
|
+
isOwner,
|
|
193
|
+
permissionLevel,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 检查消息是否应该触发 AI
|
|
199
|
+
*/
|
|
200
|
+
export function shouldTriggerAI<T extends object>(
|
|
201
|
+
message: Message<T>,
|
|
202
|
+
config: AITriggerConfig
|
|
203
|
+
): AITriggerResult {
|
|
204
|
+
const fullConfig = { ...DEFAULT_AI_TRIGGER_CONFIG, ...config };
|
|
205
|
+
|
|
206
|
+
if (!fullConfig.enabled) {
|
|
207
|
+
return { triggered: false, content: '' };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const text = extractTextContent(message);
|
|
211
|
+
|
|
212
|
+
// 检查忽略前缀
|
|
213
|
+
for (const prefix of fullConfig.ignorePrefixes) {
|
|
214
|
+
if (text.startsWith(prefix)) {
|
|
215
|
+
return { triggered: false, content: '' };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 1. 检查前缀触发
|
|
220
|
+
for (const prefix of fullConfig.prefixes) {
|
|
221
|
+
if (text.startsWith(prefix)) {
|
|
222
|
+
return { triggered: true, content: text.slice(prefix.length).trim() };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 2. 检查 @ 触发
|
|
227
|
+
if (fullConfig.respondToAt && isAtBot(message)) {
|
|
228
|
+
const content = removeAtBot(message);
|
|
229
|
+
if (content.length) {
|
|
230
|
+
return { triggered: true, content: segment.toString(content) };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 3. 检查私聊触发
|
|
235
|
+
if (fullConfig.respondToPrivate && message.$channel?.type === 'private') {
|
|
236
|
+
if (text.trim()) {
|
|
237
|
+
return { triggered: true, content: text.trim() };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 4. 检查关键词触发
|
|
242
|
+
if (fullConfig.keywords.length > 0) {
|
|
243
|
+
const lowerText = text.toLowerCase();
|
|
244
|
+
for (const keyword of fullConfig.keywords) {
|
|
245
|
+
if (lowerText.includes(keyword.toLowerCase())) {
|
|
246
|
+
return { triggered: true, content: text };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { triggered: false, content: '' };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 合并配置
|
|
256
|
+
*/
|
|
257
|
+
export function mergeAITriggerConfig(config: AITriggerConfig): Required<AITriggerConfig> {
|
|
258
|
+
return { ...DEFAULT_AI_TRIGGER_CONFIG, ...config };
|
|
259
|
+
}
|
package/src/built/command.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* CommandFeature
|
|
3
|
+
* 管理所有插件注册的命令,继承自 Feature 抽象类
|
|
4
4
|
*/
|
|
5
|
+
import { Feature, FeatureJSON } from "../feature.js";
|
|
5
6
|
import { MessageCommand } from "../command.js";
|
|
6
7
|
import { Message } from "../message.js";
|
|
7
|
-
import {
|
|
8
|
+
import { Plugin, getPlugin } from "../plugin.js";
|
|
8
9
|
import type { RegisteredAdapter, AdapterMessage } from "../types.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -20,83 +21,88 @@ declare module "../plugin.js" {
|
|
|
20
21
|
namespace Plugin {
|
|
21
22
|
interface Extensions extends CommandContextExtensions {}
|
|
22
23
|
interface Contexts {
|
|
23
|
-
command:
|
|
24
|
+
command: CommandFeature;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
|
-
*
|
|
30
|
+
* 命令服务 Feature
|
|
30
31
|
*/
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
readonly
|
|
32
|
+
export class CommandFeature extends Feature<MessageCommand<RegisteredAdapter>> {
|
|
33
|
+
readonly name = 'command' as const;
|
|
34
|
+
readonly icon = 'Terminal';
|
|
35
|
+
readonly desc = '命令';
|
|
36
|
+
|
|
34
37
|
/** 按 pattern 索引 */
|
|
35
|
-
readonly byName
|
|
36
|
-
/** 添加命令 */
|
|
37
|
-
add(command: MessageCommand<RegisteredAdapter>, pluginName: string): () => void;
|
|
38
|
-
/** 移除命令 */
|
|
39
|
-
remove(command: MessageCommand<RegisteredAdapter>): boolean;
|
|
40
|
-
/** 按名称获取 */
|
|
41
|
-
get(pattern: string): MessageCommand<RegisteredAdapter> | undefined;
|
|
42
|
-
/** 处理消息 */
|
|
43
|
-
handle(message: Message<AdapterMessage<RegisteredAdapter>>, plugin: Plugin): Promise<any>;
|
|
44
|
-
}
|
|
38
|
+
readonly byName = new Map<string, MessageCommand<RegisteredAdapter>>();
|
|
45
39
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
get(pattern) {
|
|
77
|
-
return byName.get(pattern);
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
async handle(message, plugin) {
|
|
81
|
-
for (const command of items) {
|
|
82
|
-
const result = await command.handle(message, plugin);
|
|
83
|
-
if (result) return result;
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
40
|
+
/**
|
|
41
|
+
* 添加命令
|
|
42
|
+
*/
|
|
43
|
+
add(command: MessageCommand<RegisteredAdapter>, pluginName: string): () => void {
|
|
44
|
+
this.byName.set(command.pattern, command);
|
|
45
|
+
return super.add(command, pluginName);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 移除命令
|
|
50
|
+
*/
|
|
51
|
+
remove(command: MessageCommand<RegisteredAdapter>): boolean {
|
|
52
|
+
this.byName.delete(command.pattern);
|
|
53
|
+
return super.remove(command);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 按 pattern 获取命令
|
|
58
|
+
*/
|
|
59
|
+
get(pattern: string): MessageCommand<RegisteredAdapter> | undefined {
|
|
60
|
+
return this.byName.get(pattern);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 处理消息,依次尝试匹配命令
|
|
65
|
+
*/
|
|
66
|
+
async handle(message: Message<AdapterMessage<RegisteredAdapter>>, plugin: Plugin): Promise<any> {
|
|
67
|
+
for (const command of this.items) {
|
|
68
|
+
const result = await command.handle(message, plugin);
|
|
69
|
+
if (result) return result;
|
|
86
70
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 序列化为 JSON
|
|
76
|
+
*/
|
|
77
|
+
toJSON(pluginName?: string): FeatureJSON {
|
|
78
|
+
const list = pluginName ? this.getByPlugin(pluginName) : this.items;
|
|
79
|
+
return {
|
|
80
|
+
name: this.name,
|
|
81
|
+
icon: this.icon,
|
|
82
|
+
desc: this.desc,
|
|
83
|
+
count: list.length,
|
|
84
|
+
items: list.map(c => ({
|
|
85
|
+
name: c.pattern,
|
|
86
|
+
desc: c.helpInfo?.desc,
|
|
87
|
+
usage: c.helpInfo?.usage,
|
|
88
|
+
examples: c.helpInfo?.examples,
|
|
89
|
+
})),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 提供给 Plugin.prototype 的扩展方法
|
|
95
|
+
*/
|
|
96
|
+
get extensions() {
|
|
97
|
+
const feature = this;
|
|
98
|
+
return {
|
|
94
99
|
addCommand<T extends RegisteredAdapter>(command: MessageCommand<T>) {
|
|
95
100
|
const plugin = getPlugin();
|
|
96
|
-
const dispose =
|
|
101
|
+
const dispose = feature.add(command as MessageCommand<RegisteredAdapter>, plugin.name);
|
|
102
|
+
plugin.recordFeatureContribution(feature.name, command.pattern);
|
|
97
103
|
plugin.onDispose(dispose);
|
|
98
104
|
return dispose;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
102
108
|
}
|