koishi-plugin-openai-compatible 1.0.7 → 1.0.9

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/lib/index.js CHANGED
@@ -1,157 +1,681 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inject = exports.name = void 0;
4
- exports.apply = apply;
5
- const api_1 = require("./api");
6
- const emotion_1 = require("./emotion");
7
- const storage_1 = require("./storage");
8
- exports.name = 'openai-compatible';
9
- exports.inject = {
10
- required: [],
11
- optional: [],
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
12
15
  };
13
- function apply(ctx, config) {
14
- // 导出配置 Schema
15
- ctx.i18n.define('zh', require('./locales/zh'));
16
- // 初始化存储
17
- const storage = new storage_1.Storage();
18
- // 初始化 API 客户端
19
- const api = new api_1.OpenAICompatibleAPI(config.endpoint, config.apiKey);
20
- // 初始化情绪分析器
21
- let emotionAnalyzer = null;
22
- if (config.enableEmotion) {
23
- const emotionApi = new api_1.OpenAICompatibleAPI(config.emotionEndpoint, config.emotionApiKey);
24
- emotionAnalyzer = new emotion_1.EmotionAnalyzer(emotionApi, config);
25
- }
26
- // 注册命令
27
- ctx.command(config.commandName, { authority: 1 })
28
- .alias('chat')
29
- .action(async ({ session }, content) => {
30
- if (!session)
31
- return;
32
- const userId = session.userId;
33
- if (!userId)
34
- return;
35
- // 检查黑名单
36
- if (config.blacklist.includes(userId)) {
37
- if (config.enableMessage) {
38
- await session.send('您在黑名单中,无法使用此功能。');
39
- }
40
- return;
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.EmotionAnalyzer = exports.Config = exports.OpenAICompatible = void 0;
18
+ const types_1 = require("./types");
19
+ Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return types_1.Config; } });
20
+ // 冷却时间管理器
21
+ class CooldownManager {
22
+ constructor(cooldown) {
23
+ this.cooldown = cooldown;
24
+ this.cooldowns = new Map();
25
+ }
26
+ check(userId) {
27
+ if (this.cooldown <= 0)
28
+ return { available: true, remaining: 0 };
29
+ const lastTime = this.cooldowns.get(userId);
30
+ const now = Date.now();
31
+ if (!lastTime) {
32
+ this.cooldowns.set(userId, now);
33
+ return { available: true, remaining: 0 };
41
34
  }
42
- // 检查冷却时间
43
- if (storage.isOnCooldown(userId, config.cooldown)) {
44
- const data = storage.getCooldown(userId);
45
- const remaining = config.cooldown - (Date.now() - data.lastRequestTime);
46
- const remainingSeconds = Math.ceil(remaining / 1000);
47
- if (config.enableMessage) {
48
- await session.send(`请求过于频繁,请等待 ${remainingSeconds} 秒后再试。`);
49
- }
50
- return;
35
+ const elapsed = (now - lastTime) / 1000;
36
+ const remaining = this.cooldown - elapsed;
37
+ if (remaining > 0) {
38
+ return { available: false, remaining: Math.ceil(remaining) };
51
39
  }
52
- // 获取用户输入
53
- if (!content || content.trim() === '') {
54
- if (config.enableMessage) {
55
- await session.send('请输入要聊天的内容。');
40
+ this.cooldowns.set(userId, now);
41
+ return { available: true, remaining: 0 };
42
+ }
43
+ reset(userId) {
44
+ this.cooldowns.delete(userId);
45
+ }
46
+ }
47
+ // OpenAI兼容客户端
48
+ class OpenAICompatibleClient {
49
+ constructor(config, logger, isEmotion = false) {
50
+ this.config = config;
51
+ this.logger = logger.extend(isEmotion ? 'emotion-client' : 'client');
52
+ // 如果是情绪分析客户端,使用情绪分析配置(如果提供)
53
+ if (isEmotion && config.emotionAnalysis.enabled) {
54
+ this.endpoint = config.emotionAnalysis.endpoint || config.endpoint;
55
+ this.endpoint = this.endpoint.replace(/\/$/, '');
56
+ this.apiKey = config.emotionAnalysis.apiKey || config.apiKey;
57
+ this.timeout = config.emotionTimeout;
58
+ }
59
+ else {
60
+ this.endpoint = config.endpoint.replace(/\/$/, '');
61
+ this.apiKey = config.apiKey;
62
+ this.timeout = config.timeout;
63
+ }
64
+ this.maxRetries = config.maxRetries;
65
+ }
66
+ async createChatCompletion(messages, config, isEmotion = false) {
67
+ var _a, _b, _c, _d;
68
+ const url = `${this.endpoint}/chat/completions`;
69
+ const headers = {
70
+ 'Content-Type': 'application/json',
71
+ 'Authorization': `Bearer ${this.apiKey}`,
72
+ };
73
+ // 根据是否为情绪分析设置不同的参数
74
+ let requestConfig;
75
+ if (isEmotion) {
76
+ requestConfig = {
77
+ model: ((_a = config === null || config === void 0 ? void 0 : config.emotionAnalysis) === null || _a === void 0 ? void 0 : _a.model) || this.config.emotionAnalysis.model,
78
+ messages,
79
+ max_tokens: (config === null || config === void 0 ? void 0 : config.emotionMaxTokens) || this.config.emotionMaxTokens,
80
+ temperature: (config === null || config === void 0 ? void 0 : config.emotionTemperature) || this.config.emotionTemperature,
81
+ top_p: 1,
82
+ presence_penalty: 0,
83
+ frequency_penalty: 0,
84
+ stream: false,
85
+ };
86
+ }
87
+ else {
88
+ requestConfig = {
89
+ model: (config === null || config === void 0 ? void 0 : config.model) || this.config.model,
90
+ messages,
91
+ max_tokens: (config === null || config === void 0 ? void 0 : config.maxTokens) || this.config.maxTokens,
92
+ temperature: (config === null || config === void 0 ? void 0 : config.temperature) || this.config.temperature,
93
+ top_p: (config === null || config === void 0 ? void 0 : config.topP) || this.config.topP,
94
+ presence_penalty: (config === null || config === void 0 ? void 0 : config.presencePenalty) || this.config.presencePenalty,
95
+ frequency_penalty: (config === null || config === void 0 ? void 0 : config.frequencyPenalty) || this.config.frequencyPenalty,
96
+ stream: false,
97
+ };
98
+ }
99
+ let lastError = null;
100
+ for (let i = 0; i <= this.maxRetries; i++) {
101
+ try {
102
+ const controller = new AbortController();
103
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
104
+ const response = await fetch(url, {
105
+ method: 'POST',
106
+ headers,
107
+ body: JSON.stringify(requestConfig),
108
+ signal: controller.signal,
109
+ });
110
+ clearTimeout(timeoutId);
111
+ if (!response.ok) {
112
+ const errorText = await response.text();
113
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
114
+ }
115
+ const data = await response.json();
116
+ if (data.error) {
117
+ throw new Error(data.error.message || 'Unknown API error');
118
+ }
119
+ return ((_d = (_c = (_b = data.choices[0]) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.trim()) || '';
120
+ }
121
+ catch (error) {
122
+ lastError = error;
123
+ this.logger.warn(`Request failed (attempt ${i + 1}/${this.maxRetries + 1}):`, error.message);
124
+ if (i < this.maxRetries) {
125
+ // 指数退避
126
+ await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
127
+ }
56
128
  }
57
- return;
129
+ }
130
+ if (lastError) {
131
+ throw lastError;
132
+ }
133
+ else {
134
+ throw new Error('Unknown error occurred');
135
+ }
136
+ }
137
+ async testConnection() {
138
+ try {
139
+ const url = `${this.endpoint}/models`;
140
+ const controller = new AbortController();
141
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
142
+ const response = await fetch(url, {
143
+ headers: {
144
+ 'Authorization': `Bearer ${this.apiKey}`,
145
+ },
146
+ signal: controller.signal,
147
+ });
148
+ clearTimeout(timeoutId);
149
+ return response.ok;
150
+ }
151
+ catch (error) {
152
+ this.logger.error('Connection test failed:', error.message);
153
+ return false;
154
+ }
155
+ }
156
+ }
157
+ // 情绪分析器
158
+ class EmotionAnalyzer {
159
+ constructor(config, logger) {
160
+ this.config = config;
161
+ this.logger = logger.extend('emotion-analyzer');
162
+ this.client = new OpenAICompatibleClient(config, logger, true);
163
+ this.emotionEmojis = { ...config.emotionEmojis };
164
+ this.showEmotionImage = config.showEmotionImage;
165
+ this.imageAsMarkdown = config.imageAsMarkdown;
166
+ }
167
+ async analyze(text) {
168
+ if (!this.config.emotionAnalysis.enabled || !text.trim()) {
169
+ const neutralConfig = this.emotionEmojis['neutral'] || { text: '😐', image: '' };
170
+ return {
171
+ emotion: 'neutral',
172
+ emoji: neutralConfig.text,
173
+ imageUrl: neutralConfig.image,
174
+ displayText: neutralConfig.text,
175
+ success: true,
176
+ };
58
177
  }
59
178
  try {
60
- // 构建消息列表
61
179
  const messages = [
62
- { role: 'system', content: config.systemPrompt },
63
- { role: 'user', content: content },
180
+ {
181
+ role: 'system',
182
+ content: '你是一个情绪分析专家,只能返回单个情绪类型单词,不要解释,不要额外文本。',
183
+ },
184
+ {
185
+ role: 'user',
186
+ content: `${this.config.emotionAnalysis.prompt}\n\n文本:${text}`,
187
+ },
64
188
  ];
65
- // 调用 AI
66
- const request = {
67
- model: config.model,
68
- messages,
69
- temperature: config.temperature,
70
- max_tokens: config.maxTokens,
71
- top_p: config.topP,
72
- frequency_penalty: config.frequencyPenalty,
73
- presence_penalty: config.presencePenalty,
74
- };
75
- const response = await api.chatCompletion(request);
76
- let finalResponse = response;
77
- // 如果启用了情绪分析,则分析 AI 输出的内容
78
- if (config.enableEmotion && emotionAnalyzer) {
79
- const emotion = await emotionAnalyzer.analyzeEmotion(response);
80
- const emotionImage = emotionAnalyzer.getEmotionImage(emotion);
81
- if (emotion && emotionImage) {
82
- finalResponse = `${response}\n\n${emotionImage}`;
189
+ const response = await this.client.createChatCompletion(messages, this.config, true);
190
+ // 清理和标准化情绪类型
191
+ const emotion = this.normalizeEmotion(response);
192
+ // 获取对应的表情配置
193
+ const emojiConfig = this.getEmojiConfig(emotion);
194
+ // 构建显示文本
195
+ let displayText = emojiConfig.text;
196
+ // 如果有图片链接且启用了图片显示
197
+ if (emojiConfig.image && this.showEmotionImage) {
198
+ if (this.imageAsMarkdown) {
199
+ // 使用Markdown格式
200
+ displayText = `![${emotion}](${emojiConfig.image})`;
201
+ }
202
+ else {
203
+ // 使用CQ码格式(适用于QQ平台)
204
+ displayText = `[CQ:image,file=${emojiConfig.image}]`;
83
205
  }
84
206
  }
85
- // 发送回复
86
- await session.send(finalResponse);
87
- // 更新冷却时间
88
- storage.setCooldown(userId, { lastRequestTime: Date.now() });
207
+ this.logger.debug(`Analyzed emotion: ${emotion} -> ${displayText}`);
208
+ return {
209
+ emotion,
210
+ emoji: emojiConfig.text,
211
+ imageUrl: emojiConfig.image,
212
+ displayText,
213
+ success: true,
214
+ };
89
215
  }
90
216
  catch (error) {
91
- console.error('AI 请求失败:', error);
92
- if (config.enableMessage) {
93
- await session.send(`请求失败: ${error instanceof Error ? error.message : '未知错误'}`);
94
- }
95
- }
96
- });
97
- // 黑名单管理命令
98
- ctx.command(`${config.commandName}.admin`)
99
- .subcommand('.add <userId:string>', '添加黑名单')
100
- .action(async ({ session }, userId) => {
101
- if (!session)
102
- return;
103
- config.blacklist.push(userId);
104
- await session.send(`已将用户 ${userId} 添加到黑名单。`);
105
- });
106
- ctx.command(`${config.commandName}.admin`)
107
- .subcommand('.remove <userId:string>', '移除黑名单')
108
- .action(async ({ session }, userId) => {
109
- if (!session)
110
- return;
111
- const index = config.blacklist.indexOf(userId);
112
- if (index > -1) {
113
- config.blacklist.splice(index, 1);
114
- await session.send(`已将用户 ${userId} 从黑名单中移除。`);
217
+ this.logger.error('Emotion analysis failed:', error);
218
+ const neutralConfig = this.emotionEmojis['neutral'] || { text: '😐', image: '' };
219
+ return {
220
+ emotion: 'neutral',
221
+ emoji: neutralConfig.text,
222
+ imageUrl: neutralConfig.image,
223
+ displayText: neutralConfig.text,
224
+ success: false,
225
+ error: error.message,
226
+ };
115
227
  }
116
- else {
117
- await session.send(`用户 ${userId} 不在黑名单中。`);
228
+ }
229
+ normalizeEmotion(text) {
230
+ if (!text)
231
+ return 'neutral';
232
+ // 清理文本
233
+ const cleaned = text.toLowerCase().trim();
234
+ // 常见的情绪类型映射
235
+ const emotionMap = {
236
+ 'happy': 'happy',
237
+ 'happiness': 'happy',
238
+ 'joy': 'happy',
239
+ 'joyful': 'happy',
240
+ 'glad': 'happy',
241
+ 'sad': 'sad',
242
+ 'sadness': 'sad',
243
+ 'unhappy': 'sad',
244
+ 'depressed': 'sad',
245
+ 'angry': 'angry',
246
+ 'anger': 'angry',
247
+ 'mad': 'angry',
248
+ 'furious': 'angry',
249
+ 'neutral': 'neutral',
250
+ 'normal': 'neutral',
251
+ 'default': 'neutral',
252
+ 'surprised': 'surprised',
253
+ 'surprise': 'surprised',
254
+ 'shocked': 'surprised',
255
+ 'fearful': 'fearful',
256
+ 'fear': 'fearful',
257
+ 'scared': 'fearful',
258
+ 'afraid': 'fearful',
259
+ 'disgusted': 'disgusted',
260
+ 'disgust': 'disgusted',
261
+ 'excited': 'excited',
262
+ 'excitement': 'excited',
263
+ 'calm': 'calm',
264
+ 'calmness': 'calm',
265
+ 'peaceful': 'calm',
266
+ 'confused': 'confused',
267
+ 'confusion': 'confused',
268
+ 'puzzled': 'confused',
269
+ };
270
+ // 检查是否包含已知情绪关键词
271
+ for (const [key, emotion] of Object.entries(emotionMap)) {
272
+ if (cleaned.includes(key) || cleaned === key) {
273
+ return emotion;
274
+ }
118
275
  }
119
- });
120
- ctx.command(`${config.commandName}.admin`)
121
- .subcommand('.list', '查看黑名单')
122
- .action(async ({ session }) => {
123
- if (!session)
124
- return;
125
- if (config.blacklist.length === 0) {
126
- await session.send('黑名单为空。');
276
+ // 如果没有匹配,尝试提取第一个单词
277
+ const firstWord = cleaned.split(/[,\s\.]+/)[0];
278
+ if (firstWord && firstWord.length > 1) {
279
+ return firstWord;
127
280
  }
128
- else {
129
- await session.send(`黑名单列表:\n${config.blacklist.join('\n')}`);
130
- }
131
- });
132
- // 冷却时间管理命令
133
- ctx.command(`${config.commandName}.admin`)
134
- .subcommand('.clearcooldown <userId:string>', '清除用户冷却')
135
- .action(async ({ session }, userId) => {
136
- if (!session)
137
- return;
138
- storage.deleteCooldown(userId);
139
- await session.send(`已清除用户 ${userId} 的冷却时间。`);
140
- });
141
- // 配置信息查看命令
142
- ctx.command(`${config.commandName}.admin`)
143
- .subcommand('.status', '查看插件状态')
144
- .action(async ({ session }) => {
145
- if (!session)
146
- return;
147
- const status = [
148
- `模型: ${config.model}`,
149
- `端点: ${config.endpoint}`,
150
- `情绪分析: ${config.enableEmotion ? '已启用' : '未启用'}`,
151
- `冷却时间: ${config.cooldown / 1000} 秒`,
152
- `黑名单数量: ${config.blacklist.length}`,
153
- ];
154
- await session.send(`插件状态:\n${status.join('\n')}`);
155
- });
156
- ctx.logger.info('koishi-plugin-openai-compatible 插件已加载');
281
+ return 'neutral';
282
+ }
283
+ getEmojiConfig(emotion) {
284
+ // 首先尝试精确匹配
285
+ const exactMatch = this.emotionEmojis[emotion];
286
+ if (exactMatch)
287
+ return exactMatch;
288
+ // 尝试小写匹配
289
+ const lowerMatch = this.emotionEmojis[emotion.toLowerCase()];
290
+ if (lowerMatch)
291
+ return lowerMatch;
292
+ // 默认返回中性表情
293
+ return this.emotionEmojis['neutral'] || { text: '😐', image: '' };
294
+ }
295
+ setEmotionEmojis(emojis) {
296
+ this.emotionEmojis = { ...this.emotionEmojis, ...emojis };
297
+ }
298
+ getEmotionEmojis() {
299
+ return { ...this.emotionEmojis };
300
+ }
301
+ getEmojiConfigForEmotion(emotion) {
302
+ return this.getEmojiConfig(emotion);
303
+ }
304
+ updateDisplaySettings(showImage, useMarkdown) {
305
+ this.showEmotionImage = showImage;
306
+ this.imageAsMarkdown = useMarkdown;
307
+ }
308
+ }
309
+ exports.EmotionAnalyzer = EmotionAnalyzer;
310
+ // 主插件类
311
+ class OpenAICompatible {
312
+ constructor(ctx, config) {
313
+ this.ctx = ctx;
314
+ this.config = config;
315
+ this.logger = ctx.logger('openai-compatible');
316
+ this.client = new OpenAICompatibleClient(config, this.logger);
317
+ this.emotionAnalyzer = new EmotionAnalyzer(config, this.logger);
318
+ this.cooldownManager = new CooldownManager(config.cooldown);
319
+ this.registerCommand();
320
+ this.registerService();
321
+ }
322
+ registerService() {
323
+ this.ctx.openai = this;
324
+ this.ctx.on('dispose', () => {
325
+ this.ctx.openai = undefined;
326
+ });
327
+ }
328
+ registerCommand() {
329
+ const { showError } = this.config;
330
+ this.ctx.command('ai <message:text>', '与AI对话')
331
+ .option('model', '-m <model:string>', { fallback: this.config.model })
332
+ .option('temperature', '-t <temp:number>', { fallback: this.config.temperature })
333
+ .option('system', '-s <systemPrompt:string>', { fallback: this.config.systemPrompt })
334
+ .option('noEmotion', '-n')
335
+ .option('noImage', '-i')
336
+ .action(async ({ session, options }, message) => {
337
+ if (!message)
338
+ return '请输入消息内容。';
339
+ const userId = (session === null || session === void 0 ? void 0 : session.userId) || 'unknown';
340
+ // 检查黑名单
341
+ if (this.config.blacklist.includes(userId)) {
342
+ return showError ? '您已被加入黑名单,无法使用此功能。' : '';
343
+ }
344
+ // 检查冷却时间
345
+ const cooldownCheck = this.cooldownManager.check(userId);
346
+ if (!cooldownCheck.available) {
347
+ return showError ? `冷却时间剩余 ${cooldownCheck.remaining} 秒。` : '';
348
+ }
349
+ try {
350
+ if (session) {
351
+ await session.send('正在思考中...');
352
+ }
353
+ // 构建消息
354
+ const messages = [];
355
+ // 添加系统提示
356
+ const systemContent = (options === null || options === void 0 ? void 0 : options.system) || this.config.systemPrompt;
357
+ if (systemContent) {
358
+ messages.push({
359
+ role: 'system',
360
+ content: systemContent,
361
+ });
362
+ }
363
+ // 添加用户消息和提示词
364
+ let userMessage = message;
365
+ if (this.config.prompt) {
366
+ userMessage = `${this.config.prompt}\n\n${message}`;
367
+ }
368
+ messages.push({
369
+ role: 'user',
370
+ content: userMessage,
371
+ });
372
+ // 调用API获取主回复
373
+ const mainResponse = await this.client.createChatCompletion(messages, {
374
+ model: options === null || options === void 0 ? void 0 : options.model,
375
+ temperature: options === null || options === void 0 ? void 0 : options.temperature,
376
+ });
377
+ if (!mainResponse) {
378
+ return 'AI没有返回任何内容。';
379
+ }
380
+ // 情绪分析(如果不禁用)
381
+ let emotionResult = null;
382
+ if (this.config.emotionAnalysis.enabled && !(options === null || options === void 0 ? void 0 : options.noEmotion)) {
383
+ try {
384
+ // 临时修改显示设置(如果指定了禁用图片)
385
+ const showImage = !(options === null || options === void 0 ? void 0 : options.noImage) && this.config.showEmotionImage;
386
+ this.emotionAnalyzer.updateDisplaySettings(showImage, this.config.imageAsMarkdown);
387
+ // 并行处理情绪分析,设置超时
388
+ const emotionPromise = this.emotionAnalyzer.analyze(mainResponse);
389
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('情绪分析超时')), this.config.emotionTimeout));
390
+ emotionResult = await Promise.race([emotionPromise, timeoutPromise]);
391
+ }
392
+ catch (error) {
393
+ this.logger.warn('Emotion analysis skipped or failed:', error.message);
394
+ emotionResult = {
395
+ emotion: 'neutral',
396
+ emoji: '😐',
397
+ displayText: '😐',
398
+ success: false,
399
+ error: error.message,
400
+ };
401
+ }
402
+ }
403
+ // 构建最终回复
404
+ let finalResponse = mainResponse;
405
+ if (emotionResult === null || emotionResult === void 0 ? void 0 : emotionResult.success) {
406
+ // 添加情绪显示
407
+ finalResponse += `\n\n${emotionResult.displayText} (情绪: ${emotionResult.emotion})`;
408
+ }
409
+ else if (emotionResult && this.config.showError) {
410
+ // 情绪分析失败但启用了错误提示
411
+ this.logger.debug('Emotion analysis failed, but main response sent');
412
+ }
413
+ return finalResponse;
414
+ }
415
+ catch (error) {
416
+ this.logger.error('AI request failed:', error);
417
+ return showError ? `请求失败:${error.message}` : '';
418
+ }
419
+ });
420
+ // 添加测试命令
421
+ this.ctx.command('ai.test', '测试AI连接')
422
+ .option('emotion', '-e')
423
+ .option('image', '-i')
424
+ .action(async ({ session, options }) => {
425
+ try {
426
+ if (session) {
427
+ await session.send('正在测试连接...');
428
+ }
429
+ if ((options === null || options === void 0 ? void 0 : options.emotion) && this.config.emotionAnalysis.enabled) {
430
+ const connected = await this.client.testConnection();
431
+ if (connected) {
432
+ // 测试情绪分析
433
+ const testText = '我今天非常开心!';
434
+ const emotionResult = await this.emotionAnalyzer.analyze(testText);
435
+ let resultText = `连接测试成功!\n情绪分析测试:\n文本:"${testText}"\n分析结果:${emotionResult.emotion} ${emotionResult.displayText}`;
436
+ if (emotionResult.imageUrl) {
437
+ resultText += `\n图片链接:${emotionResult.imageUrl}`;
438
+ }
439
+ return resultText;
440
+ }
441
+ else {
442
+ return '连接测试失败!';
443
+ }
444
+ }
445
+ else {
446
+ const connected = await this.client.testConnection();
447
+ return connected ? '连接测试成功!' : '连接测试失败!';
448
+ }
449
+ }
450
+ catch (error) {
451
+ return showError ? `测试失败:${error.message}` : '';
452
+ }
453
+ });
454
+ // 添加重置冷却命令
455
+ this.ctx.command('ai.reset <userId:string>', '重置用户冷却时间')
456
+ .action(({ session }, userId) => {
457
+ if (!userId && session) {
458
+ const sessionUserId = session.userId;
459
+ if (sessionUserId) {
460
+ this.cooldownManager.reset(sessionUserId);
461
+ return '已重置您的冷却时间。';
462
+ }
463
+ else {
464
+ return '无法获取您的用户ID。';
465
+ }
466
+ }
467
+ else if (userId) {
468
+ this.cooldownManager.reset(userId);
469
+ return `已重置用户 ${userId} 的冷却时间。`;
470
+ }
471
+ else {
472
+ return '请提供用户ID。';
473
+ }
474
+ });
475
+ // 添加情绪表情管理命令
476
+ this.ctx.command('ai.emotion', '情绪分析管理')
477
+ .subcommand('.test <text:text>', '测试情绪分析')
478
+ .option('image', '-i')
479
+ .action(async ({ session, options }, text) => {
480
+ if (!this.config.emotionAnalysis.enabled) {
481
+ return '情绪分析功能未启用。';
482
+ }
483
+ if (!text) {
484
+ return '请输入要分析的文本。';
485
+ }
486
+ try {
487
+ if (session) {
488
+ await session.send('正在分析情绪...');
489
+ }
490
+ // 临时修改显示设置
491
+ const showImage = (options === null || options === void 0 ? void 0 : options.image) ? this.config.showEmotionImage : false;
492
+ this.emotionAnalyzer.updateDisplaySettings(showImage, this.config.imageAsMarkdown);
493
+ const result = await this.emotionAnalyzer.analyze(text);
494
+ let responseText = `文本:"${text}"\n情绪分析结果:${result.emotion} ${result.displayText}`;
495
+ if (result.imageUrl) {
496
+ responseText += `\n图片链接:${result.imageUrl}`;
497
+ }
498
+ if (result.success) {
499
+ return responseText;
500
+ }
501
+ else {
502
+ return `情绪分析失败:${result.error}\n${responseText}`;
503
+ }
504
+ }
505
+ catch (error) {
506
+ return showError ? `情绪分析失败:${error.message}` : '';
507
+ }
508
+ })
509
+ .subcommand('.list', '查看情绪表情映射')
510
+ .action(() => {
511
+ const emojis = this.emotionAnalyzer.getEmotionEmojis();
512
+ const list = Object.entries(emojis)
513
+ .map(([emotion, config]) => {
514
+ const imageInfo = config.image ? `\n 图片链接: ${config.image}` : '';
515
+ return `${emotion}: ${config.text}${imageInfo}`;
516
+ })
517
+ .join('\n\n');
518
+ return `情绪表情映射:\n${list}`;
519
+ })
520
+ .subcommand('.get <emotion:string>', '获取情绪表情配置')
521
+ .action(({ session }, emotion) => {
522
+ if (!emotion) {
523
+ return '请提供情绪类型。';
524
+ }
525
+ const config = this.emotionAnalyzer.getEmojiConfigForEmotion(emotion);
526
+ let response = `${emotion}: ${config.text}`;
527
+ if (config.image) {
528
+ response += `\n图片链接: ${config.image}`;
529
+ if (this.config.showEmotionImage) {
530
+ if (this.config.imageAsMarkdown) {
531
+ response += `\n显示为: ![${emotion}](${config.image})`;
532
+ }
533
+ else {
534
+ response += `\n显示为: [CQ:image,file=${config.image}]`;
535
+ }
536
+ }
537
+ }
538
+ else {
539
+ response += '\n未配置图片链接';
540
+ }
541
+ return response;
542
+ })
543
+ .subcommand('.set <emotion:string> <text:string> [image:string]', '设置情绪表情配置', { authority: 3 })
544
+ .action(({ session }, emotion, text, image) => {
545
+ if (!emotion || !text) {
546
+ return '请提供情绪类型和文本表情。';
547
+ }
548
+ const newConfig = {
549
+ text,
550
+ image: image || '',
551
+ };
552
+ const newEmojis = { [emotion]: newConfig };
553
+ this.emotionAnalyzer.setEmotionEmojis(newEmojis);
554
+ // 更新配置(内存中)
555
+ this.config.emotionEmojis = this.emotionAnalyzer.getEmotionEmojis();
556
+ let response = `已设置 ${emotion} 的配置:\n文本表情: ${text}`;
557
+ if (image) {
558
+ response += `\n图片链接: ${image}`;
559
+ }
560
+ return response;
561
+ })
562
+ .subcommand('.image <enabled:boolean>', '启用/禁用情绪图片显示', { authority: 3 })
563
+ .action(({ session }, enabled) => {
564
+ if (enabled === undefined) {
565
+ return `当前情绪图片显示状态: ${this.config.showEmotionImage ? '启用' : '禁用'}`;
566
+ }
567
+ this.config.showEmotionImage = enabled;
568
+ this.emotionAnalyzer.updateDisplaySettings(enabled, this.config.imageAsMarkdown);
569
+ return `已${enabled ? '启用' : '禁用'}情绪图片显示`;
570
+ })
571
+ .subcommand('.format <format:string>', '设置图片显示格式(markdown/cq)', { authority: 3 })
572
+ .action(({ session }, format) => {
573
+ if (!format || !['markdown', 'cq'].includes(format.toLowerCase())) {
574
+ return '请指定格式:markdown 或 cq';
575
+ }
576
+ const useMarkdown = format.toLowerCase() === 'markdown';
577
+ this.config.imageAsMarkdown = useMarkdown;
578
+ this.emotionAnalyzer.updateDisplaySettings(this.config.showEmotionImage, useMarkdown);
579
+ return `已设置图片显示格式为: ${format}`;
580
+ });
581
+ }
582
+ // 公共API方法
583
+ async chat(message, options) {
584
+ var _a, _b;
585
+ const userId = (options === null || options === void 0 ? void 0 : options.userId) || 'anonymous';
586
+ // 检查黑名单
587
+ if (this.config.blacklist.includes(userId)) {
588
+ throw new Error('User is blacklisted');
589
+ }
590
+ // 检查冷却时间
591
+ const cooldownCheck = this.cooldownManager.check(userId);
592
+ if (!cooldownCheck.available) {
593
+ throw new Error(`Cooldown: ${cooldownCheck.remaining}s remaining`);
594
+ }
595
+ // 构建消息
596
+ const messages = [];
597
+ if ((options === null || options === void 0 ? void 0 : options.systemPrompt) || this.config.systemPrompt) {
598
+ messages.push({
599
+ role: 'system',
600
+ content: (options === null || options === void 0 ? void 0 : options.systemPrompt) || this.config.systemPrompt,
601
+ });
602
+ }
603
+ let userMessage = message;
604
+ if (this.config.prompt) {
605
+ userMessage = `${this.config.prompt}\n\n${message}`;
606
+ }
607
+ messages.push({
608
+ role: 'user',
609
+ content: userMessage,
610
+ });
611
+ // 获取主回复
612
+ const mainResponse = await this.client.createChatCompletion(messages, {
613
+ model: options === null || options === void 0 ? void 0 : options.model,
614
+ temperature: options === null || options === void 0 ? void 0 : options.temperature,
615
+ });
616
+ // 情绪分析
617
+ let emotionResult = null;
618
+ const enableEmotion = (_a = options === null || options === void 0 ? void 0 : options.enableEmotion) !== null && _a !== void 0 ? _a : this.config.emotionAnalysis.enabled;
619
+ if (enableEmotion) {
620
+ try {
621
+ // 临时修改显示设置
622
+ const showImage = (_b = options === null || options === void 0 ? void 0 : options.showImage) !== null && _b !== void 0 ? _b : this.config.showEmotionImage;
623
+ this.emotionAnalyzer.updateDisplaySettings(showImage, this.config.imageAsMarkdown);
624
+ emotionResult = await this.emotionAnalyzer.analyze(mainResponse);
625
+ }
626
+ catch (error) {
627
+ this.logger.warn('Emotion analysis failed in API call:', error.message);
628
+ }
629
+ }
630
+ // 构建结果
631
+ const result = {
632
+ text: mainResponse,
633
+ };
634
+ if (emotionResult) {
635
+ result.emotion = emotionResult.emotion;
636
+ result.emoji = emotionResult.emoji;
637
+ result.imageUrl = emotionResult.imageUrl;
638
+ result.displayText = emotionResult.displayText;
639
+ result.emotionSuccess = emotionResult.success;
640
+ }
641
+ return result;
642
+ }
643
+ async analyzeEmotion(text, showImage) {
644
+ // 临时修改显示设置
645
+ const displayImage = showImage !== null && showImage !== void 0 ? showImage : this.config.showEmotionImage;
646
+ this.emotionAnalyzer.updateDisplaySettings(displayImage, this.config.imageAsMarkdown);
647
+ return this.emotionAnalyzer.analyze(text);
648
+ }
649
+ async test() {
650
+ return this.client.testConnection();
651
+ }
652
+ // 获取情绪分析器实例
653
+ getEmotionAnalyzer() {
654
+ return this.emotionAnalyzer;
655
+ }
656
+ // 更新配置(供运行时调整)
657
+ updateConfig(newConfig) {
658
+ var _a, _b;
659
+ // 更新相关配置
660
+ if (newConfig.emotionEmojis) {
661
+ this.emotionAnalyzer.setEmotionEmojis(newConfig.emotionEmojis);
662
+ this.config.emotionEmojis = { ...this.config.emotionEmojis, ...newConfig.emotionEmojis };
663
+ }
664
+ if (newConfig.showEmotionImage !== undefined || newConfig.imageAsMarkdown !== undefined) {
665
+ const showImage = (_a = newConfig.showEmotionImage) !== null && _a !== void 0 ? _a : this.config.showEmotionImage;
666
+ const useMarkdown = (_b = newConfig.imageAsMarkdown) !== null && _b !== void 0 ? _b : this.config.imageAsMarkdown;
667
+ this.emotionAnalyzer.updateDisplaySettings(showImage, useMarkdown);
668
+ if (newConfig.showEmotionImage !== undefined) {
669
+ this.config.showEmotionImage = newConfig.showEmotionImage;
670
+ }
671
+ if (newConfig.imageAsMarkdown !== undefined) {
672
+ this.config.imageAsMarkdown = newConfig.imageAsMarkdown;
673
+ }
674
+ }
675
+ }
157
676
  }
677
+ exports.OpenAICompatible = OpenAICompatible;
678
+ __exportStar(require("./types"), exports);
679
+ exports.default = (ctx, config) => {
680
+ return new OpenAICompatible(ctx, config);
681
+ };