@anyul/koishi-plugin-rss 5.3.2 → 5.3.4

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.
Files changed (38) hide show
  1. package/lib/commands/index.js +0 -2
  2. package/lib/commands/subscription-management.js +0 -2
  3. package/lib/commands/utils.d.ts +0 -5
  4. package/lib/commands/utils.js +0 -16
  5. package/lib/config.js +0 -8
  6. package/lib/core/feeder-arg.js +3 -38
  7. package/lib/core/feeder-runtime.js +0 -5
  8. package/lib/core/feeder.js +0 -10
  9. package/lib/core/item-processor-runtime.d.ts +4 -2
  10. package/lib/core/item-processor-runtime.js +4 -12
  11. package/lib/core/notification-queue-sender.js +0 -5
  12. package/lib/core/notification-queue.js +0 -7
  13. package/lib/core/telegram-video-restore.js +3 -8
  14. package/lib/index.js +3 -11
  15. package/lib/tsconfig.tsbuildinfo +1 -1
  16. package/lib/types.d.ts +0 -9
  17. package/lib/utils/common.d.ts +6 -0
  18. package/lib/utils/common.js +15 -25
  19. package/lib/utils/error-handler.d.ts +49 -0
  20. package/lib/utils/error-handler.js +111 -1
  21. package/lib/utils/fetcher.js +11 -37
  22. package/lib/utils/logger.d.ts +6 -0
  23. package/lib/utils/logger.js +82 -14
  24. package/lib/utils/proxy.d.ts +37 -0
  25. package/lib/utils/proxy.js +60 -0
  26. package/package.json +1 -1
  27. package/lib/commands/error-handler.d.ts +0 -53
  28. package/lib/commands/error-handler.js +0 -120
  29. package/lib/commands/rss-subscribe.d.ts +0 -11
  30. package/lib/commands/rss-subscribe.js +0 -323
  31. package/lib/core/feeder-old.d.ts +0 -15
  32. package/lib/core/feeder-old.js +0 -403
  33. package/lib/utils/error-tracker.d.ts +0 -127
  34. package/lib/utils/error-tracker.js +0 -352
  35. package/lib/utils/metrics.d.ts +0 -184
  36. package/lib/utils/metrics.js +0 -401
  37. package/lib/utils/structured-logger.d.ts +0 -135
  38. package/lib/utils/structured-logger.js +0 -248
@@ -5,7 +5,7 @@
5
5
  * 将技术性错误转换为用户友好的提示信息
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.ErrorType = void 0;
8
+ exports.CommandError = exports.CommandErrorType = exports.ErrorType = void 0;
9
9
  exports.getErrorType = getErrorType;
10
10
  exports.getFriendlyErrorMessage = getFriendlyErrorMessage;
11
11
  exports.createError = createError;
@@ -14,6 +14,12 @@ exports.isNetworkError = isNetworkError;
14
14
  exports.isParseError = isParseError;
15
15
  exports.isPermissionError = isPermissionError;
16
16
  exports.isRetryable = isRetryable;
17
+ exports.executeCommand = executeCommand;
18
+ exports.permissionDenied = permissionDenied;
19
+ exports.invalidArgument = invalidArgument;
20
+ exports.notFound = notFound;
21
+ exports.alreadyExists = alreadyExists;
22
+ const logger_1 = require("./logger");
17
23
  /**
18
24
  * 错误类型枚举
19
25
  */
@@ -276,3 +282,107 @@ function isRetryable(error) {
276
282
  // 其他错误不建议重试
277
283
  return false;
278
284
  }
285
+ /**
286
+ * 命令错误类型
287
+ */
288
+ var CommandErrorType;
289
+ (function (CommandErrorType) {
290
+ CommandErrorType["PERMISSION_DENIED"] = "PERMISSION_DENIED";
291
+ CommandErrorType["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
292
+ CommandErrorType["NOT_FOUND"] = "NOT_FOUND";
293
+ CommandErrorType["ALREADY_EXISTS"] = "ALREADY_EXISTS";
294
+ CommandErrorType["NETWORK_ERROR"] = "NETWORK_ERROR";
295
+ CommandErrorType["INTERNAL_ERROR"] = "INTERNAL_ERROR";
296
+ })(CommandErrorType || (exports.CommandErrorType = CommandErrorType = {}));
297
+ /**
298
+ * 命令错误类
299
+ */
300
+ class CommandError extends Error {
301
+ type;
302
+ details;
303
+ constructor(type, message, details) {
304
+ super(message);
305
+ this.type = type;
306
+ this.details = details;
307
+ this.name = 'CommandError';
308
+ }
309
+ }
310
+ exports.CommandError = CommandError;
311
+ /**
312
+ * 包装命令执行,提供统一的错误处理
313
+ */
314
+ async function executeCommand(ctx, config, operationName, handler) {
315
+ try {
316
+ return await handler();
317
+ }
318
+ catch (error) {
319
+ // 如果是 CommandError,使用自定义消息
320
+ if (error instanceof CommandError) {
321
+ logCommandError(config, operationName, error);
322
+ return formatCommandError(error);
323
+ }
324
+ // 其他错误使用友好错误消息
325
+ logCommandError(config, operationName, error);
326
+ const friendlyMessage = getFriendlyErrorMessage(error, operationName);
327
+ return `${operationName}失败: ${friendlyMessage}`;
328
+ }
329
+ }
330
+ /**
331
+ * 记录命令错误
332
+ */
333
+ function logCommandError(config, operation, error) {
334
+ const normalizedError = normalizeError(error, `${operation} failed`);
335
+ const context = {
336
+ command: operation,
337
+ };
338
+ if (error instanceof CommandError) {
339
+ context.commandErrorType = error.type;
340
+ }
341
+ if (normalizedError && typeof normalizedError.code === 'string') {
342
+ context.errorCode = normalizedError.code;
343
+ }
344
+ (0, logger_1.debugError)(config, normalizedError, `${operation} error`, context);
345
+ }
346
+ /**
347
+ * 格式化命令错误消息
348
+ */
349
+ function formatCommandError(error) {
350
+ switch (error.type) {
351
+ case CommandErrorType.PERMISSION_DENIED:
352
+ return error.message;
353
+ case CommandErrorType.INVALID_ARGUMENT:
354
+ return `参数错误: ${error.message}`;
355
+ case CommandErrorType.NOT_FOUND:
356
+ return `未找到: ${error.message}`;
357
+ case CommandErrorType.ALREADY_EXISTS:
358
+ return `已存在: ${error.message}`;
359
+ case CommandErrorType.NETWORK_ERROR:
360
+ return `网络错误: ${error.message}`;
361
+ default:
362
+ return error.message || '操作失败,请稍后重试';
363
+ }
364
+ }
365
+ /**
366
+ * 创建权限检查错误
367
+ */
368
+ function permissionDenied(customMessage) {
369
+ return new CommandError(CommandErrorType.PERMISSION_DENIED, customMessage || '权限不足');
370
+ }
371
+ /**
372
+ * 创建参数错误
373
+ */
374
+ function invalidArgument(message) {
375
+ return new CommandError(CommandErrorType.INVALID_ARGUMENT, message);
376
+ }
377
+ /**
378
+ * 创建未找到错误
379
+ */
380
+ function notFound(resource) {
381
+ return new CommandError(CommandErrorType.NOT_FOUND, resource);
382
+ }
383
+ /**
384
+ * 创建已存在错误
385
+ */
386
+ function alreadyExists(resource) {
387
+ return new CommandError(CommandErrorType.ALREADY_EXISTS, resource);
388
+ }
@@ -5,11 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createHttpFunction = exports.RequestManager = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
- const https_proxy_agent_1 = require("https-proxy-agent");
9
8
  const error_handler_1 = require("./error-handler");
10
- const error_tracker_1 = require("./error-tracker");
11
9
  const logger_1 = require("./logger");
12
10
  const common_1 = require("./common");
11
+ const proxy_1 = require("./proxy");
13
12
  const security_1 = require("./security");
14
13
  // 简化版 RequestManager,仅用于普通 API 请求,大文件下载建议绕过
15
14
  class RequestManager {
@@ -91,12 +90,6 @@ const createHttpFunction = (ctx, config, requestManager) => {
91
90
  requestDebug(`URL 安全验证失败: ${normalizedError.message}`, 'security', 'error', {
92
91
  stage: 'security-validation',
93
92
  });
94
- (0, error_tracker_1.trackError)(normalizedError, {
95
- url,
96
- requestType,
97
- isHeavyRequest,
98
- stage: 'security-validation',
99
- });
100
93
  throw error;
101
94
  }
102
95
  throw error;
@@ -114,32 +107,20 @@ const createHttpFunction = (ctx, config, requestManager) => {
114
107
  // 允许传入自定义 proxyAgent
115
108
  ...(arg.proxyAgent?.enabled ? {} : {})
116
109
  };
117
- // 代理配置
118
- let currentProxyAgent = arg?.proxyAgent;
119
- // 防御性:如果 arg 中没有有效的代理配置,尝试使用全局配置(作为安全网)
120
- if (!currentProxyAgent || currentProxyAgent.enabled === undefined || currentProxyAgent.enabled === null) {
121
- if (config.net?.proxyAgent?.enabled) {
122
- currentProxyAgent = {
123
- enabled: true,
124
- protocol: config.net.proxyAgent.protocol,
125
- host: config.net.proxyAgent.host,
126
- port: config.net.proxyAgent.port,
127
- auth: config.net.proxyAgent.auth?.enabled ? config.net.proxyAgent.auth : undefined
128
- };
129
- requestDebug(`[DEBUG_PROXY] fetcher 使用防御性全局代理`, 'request', 'details', {
130
- proxyEnabled: true,
131
- proxyUrl: config.net.proxyAgent.host
132
- ? `${config.net.proxyAgent.protocol}://${config.net.proxyAgent.host}:${config.net.proxyAgent.port}`
133
- : '',
134
- });
135
- }
136
- }
110
+ // 代理配置:复用 utils/proxy.ts 的合并 + 构造 helper(CLAUDE.md:346 规范)
111
+ // mergeProxyAgent 已处理“arg 缺失回落全局 / arg 不完整用全局补全”的防御性逻辑
112
+ const currentProxyAgent = (0, proxy_1.mergeProxyAgent)(arg?.proxyAgent, config.net?.proxyAgent, config);
137
113
  const proxyEnabled = Boolean(currentProxyAgent?.enabled);
138
114
  let proxyUrl = '';
139
115
  if (proxyEnabled && currentProxyAgent.host) {
140
116
  proxyUrl = `${currentProxyAgent.protocol}://${currentProxyAgent.host}:${currentProxyAgent.port}`;
141
- const agent = new https_proxy_agent_1.HttpsProxyAgent(proxyUrl);
142
- configObj.httpsAgent = agent;
117
+ requestDebug(`[DEBUG_PROXY] fetcher 使用代理`, 'request', 'details', {
118
+ proxyEnabled: true,
119
+ proxyUrl,
120
+ });
121
+ // 用合并后的片段构造 axios 代理配置,与 ai-client / search-providers 同源
122
+ const proxyConfig = (0, proxy_1.buildAxiosProxyConfig)({ net: { proxyAgent: currentProxyAgent } });
123
+ configObj.httpsAgent = proxyConfig.httpsAgent;
143
124
  configObj.proxy = false; // 禁用 axios 原生 proxy
144
125
  }
145
126
  // 合并外部配置
@@ -190,13 +171,6 @@ const createHttpFunction = (ctx, config, requestManager) => {
190
171
  status,
191
172
  retriesRemaining: 0,
192
173
  });
193
- (0, error_tracker_1.trackError)(normalizedError, {
194
- url,
195
- requestType,
196
- isHeavyRequest,
197
- status,
198
- ...requestContext,
199
- });
200
174
  throw normalizedError;
201
175
  };
202
176
  if (isHeavyRequest) {
@@ -2,6 +2,12 @@ import { Logger } from 'koishi';
2
2
  import { Config } from '../types';
3
3
  declare const logger: Logger;
4
4
  export type DebugLogType = "disable" | "error" | "info" | "details";
5
+ export declare function applyDebugLevel(config: Config): void;
6
+ /**
7
+ * 按 config.debug 判定某级是否应输出。
8
+ * 在 debug() 中作为性能预检门控(避免对注定被丢弃的日志做脱敏/格式化),
9
+ * 与 applyDebugLevel 同步到 Koishi levels 的语义保持一致;最终输出与否仍由 reggol levels 决定。
10
+ */
5
11
  export declare function shouldLog(config: Config, type: DebugLogType): boolean;
6
12
  /**
7
13
  * 增强的调试日志函数
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.logger = void 0;
4
+ exports.applyDebugLevel = applyDebugLevel;
4
5
  exports.shouldLog = shouldLog;
5
6
  exports.debug = debug;
6
7
  exports.debugError = debugError;
@@ -10,6 +11,47 @@ const koishi_1 = require("koishi");
10
11
  const types_1 = require("../types");
11
12
  const logger = new koishi_1.Logger('rss-owl');
12
13
  exports.logger = logger;
14
+ /**
15
+ * 把 config.debug 映射到 Koishi/reggol 的原生日志级别数值。
16
+ *
17
+ * reggol 级别(见 reggol/index.d.ts):
18
+ * SILENT=0, SUCCESS/ERROR=1, INFO/WARN=2, DEBUG=3
19
+ *
20
+ * 本插件的 debug 字段语义:
21
+ * disable → 0(SILENT,什么也不输出)
22
+ * error → 1(仅 error)
23
+ * info → 2(error + info/warn)
24
+ * details → 3(含 debug 全量)
25
+ */
26
+ function debugLevelToReggol(debug) {
27
+ switch (debug) {
28
+ case 'error': return koishi_1.Logger.ERROR;
29
+ case 'info': return koishi_1.Logger.INFO;
30
+ case 'details': return koishi_1.Logger.DEBUG;
31
+ default: return koishi_1.Logger.SILENT;
32
+ }
33
+ }
34
+ /**
35
+ * 把 config.debug 同步到 Koishi 原生日志分级 `Logger.levels['rss-owl']`。
36
+ *
37
+ * 这样本插件的日志可见性既受自身 debug 字段控制,也能被 Koishi 全局 levels
38
+ * (WebUI 的 logger 插件、配置文件中的 levels.base / levels['rss-owl'])覆盖/调节。
39
+ * 应在 apply() 启动时调用一次;值未变化时不重复写入。
40
+ */
41
+ let lastAppliedLevel;
42
+ function applyDebugLevel(config) {
43
+ const target = debugLevelToReggol(config.debug);
44
+ if (lastAppliedLevel === target)
45
+ return;
46
+ lastAppliedLevel = target;
47
+ koishi_1.Logger.levels = koishi_1.Logger.levels || { base: 2 };
48
+ koishi_1.Logger.levels['rss-owl'] = target;
49
+ }
50
+ /**
51
+ * 按 config.debug 判定某级是否应输出。
52
+ * 在 debug() 中作为性能预检门控(避免对注定被丢弃的日志做脱敏/格式化),
53
+ * 与 applyDebugLevel 同步到 Koishi levels 的语义保持一致;最终输出与否仍由 reggol levels 决定。
54
+ */
13
55
  function shouldLog(config, type) {
14
56
  const typeLevel = types_1.debugLevel.findIndex(i => i === type);
15
57
  if (typeLevel < 1)
@@ -19,6 +61,22 @@ function shouldLog(config, type) {
19
61
  return false;
20
62
  return typeLevel <= configLevel;
21
63
  }
64
+ /**
65
+ * 子命名空间 logger 缓存,避免每条日志都 extend。
66
+ * 用 logger.extend('feeder') 产出 rss-owl:feeder,命名空间天然带模块名,
67
+ * 取代此前 formatTextLog 里手工拼 [name] 前缀的做法(与 Koishi Logger 命名空间重复)。
68
+ */
69
+ const subLoggers = new Map();
70
+ function getSubLogger(name) {
71
+ if (!name)
72
+ return logger;
73
+ let sub = subLoggers.get(name);
74
+ if (!sub) {
75
+ sub = logger.extend(name);
76
+ subLoggers.set(name, sub);
77
+ }
78
+ return sub;
79
+ }
22
80
  /**
23
81
  * 敏感信息模式定义
24
82
  */
@@ -129,12 +187,21 @@ function sanitizeObject(obj, depth = 0) {
129
187
  }
130
188
  return obj;
131
189
  }
132
- function emitLog(type, content) {
133
- if (type === 'error') {
134
- logger.error(content);
135
- return;
136
- }
137
- logger.info(content);
190
+ /**
191
+ * Koishi 原生日志级别输出。
192
+ *
193
+ * 关键纠错:'details' 之前被错压成 logger.info(),现归到 logger.debug(),
194
+ * 让 reggol 的 levels(SILENT=0 / ERROR=1 / INFO=2 / DEBUG=3)能正确分级过滤。
195
+ * 模块名 name 通过子命名空间 logger.extend(name) 体现(如 rss-owl:feeder),
196
+ * 不再手工拼 [name] 前缀。
197
+ */
198
+ function emitLog(type, content, name = '') {
199
+ const target = getSubLogger(name);
200
+ if (type === 'error')
201
+ return target.error(content);
202
+ if (type === 'info')
203
+ return target.info(content);
204
+ return target.debug(content); // 'details'
138
205
  }
139
206
  function filterContextFields(context, contextFields) {
140
207
  if (!contextFields?.length) {
@@ -163,12 +230,10 @@ function formatContextValue(value) {
163
230
  return String(value);
164
231
  }
165
232
  function formatTextLog(message, name, context, loggingConfig) {
166
- const parts = [];
167
- if (loggingConfig?.includeModule !== false && name) {
168
- parts.push(`[${name}]`);
169
- }
170
- parts.push(message);
171
- const textOutput = parts.filter(Boolean).join(' ').trim();
233
+ // 模块名不再手工拼前缀:已由子命名空间 logger(rss-owl:<name>)体现,
234
+ // 避免与 Koishi Logger 命名空间重复输出。
235
+ void name;
236
+ const textOutput = message.trim();
172
237
  if (!context || Object.keys(context).length === 0) {
173
238
  return textOutput;
174
239
  }
@@ -192,6 +257,9 @@ function formatTextLog(message, name, context, loggingConfig) {
192
257
  * @param context - 额外的上下文信息
193
258
  */
194
259
  function debug(config, message, name = '', type = 'details', context) {
260
+ // 性能预检:按 config.debug 快速跳过注定被 Koishi levels 丢弃的日志,
261
+ // 避免无谓的脱敏/格式化开销。最终是否真正输出由 reggol levels 决定
262
+ // (applyDebugLevel 已把 config.debug 同步到 Logger.levels['rss-owl'])。
195
263
  if (!shouldLog(config, type))
196
264
  return;
197
265
  // 检查是否启用日志脱敏(默认启用)
@@ -258,10 +326,10 @@ function debug(config, message, name = '', type = 'details', context) {
258
326
  }
259
327
  }
260
328
  // 输出结构化日志
261
- emitLog(type, JSON.stringify(logEntry));
329
+ emitLog(type, JSON.stringify(logEntry), name);
262
330
  }
263
331
  else {
264
- emitLog(type, formatTextLog(formattedMessage, name, context, loggingConfig));
332
+ emitLog(type, formatTextLog(formattedMessage, name, context, loggingConfig), name);
265
333
  }
266
334
  }
267
335
  /**
@@ -1,3 +1,40 @@
1
1
  import type { AxiosRequestConfig } from 'axios';
2
2
  import type { Config } from '../types';
3
+ /**
4
+ * 代理配置片段(订阅级 / 全局级通用形状)。
5
+ */
6
+ export interface ProxyAgentShape {
7
+ enabled?: boolean;
8
+ protocol?: string;
9
+ host?: string;
10
+ port?: number | string;
11
+ auth?: {
12
+ enabled?: boolean;
13
+ user?: string;
14
+ pass?: string;
15
+ } & Record<string, any>;
16
+ [key: string]: any;
17
+ }
18
+ /**
19
+ * 合并订阅级代理配置 (argProxy) 与全局代理配置 (configProxy)。
20
+ *
21
+ * 优先级:
22
+ * 1. argProxy.enabled === false → 显式禁用
23
+ * 2. argProxy.enabled === true 且有 host → 用订阅配置
24
+ * 3. argProxy 缺失 / 未设置 enabled → 回落全局配置
25
+ * 4. argProxy.enabled === true 但缺 host → 用全局补全
26
+ * 5. 其余 → 禁用
27
+ *
28
+ * 返回值与 `config.net.proxyAgent` 同构,可直接喂给 `buildAxiosProxyConfig`。
29
+ *
30
+ * 本函数原位于 `core/feeder-arg.ts`,提升到此处以统一代理 helper 归属,
31
+ * 供 fetcher / feeder 等模块复用,避免重复实现合并逻辑。
32
+ */
33
+ export declare function mergeProxyAgent(argProxy: any, configProxy: any, config: Config): ProxyAgentShape;
34
+ /**
35
+ * 构造 axios 的代理配置(httpsAgent + 关闭 axios 原生 proxy)。
36
+ *
37
+ * 接受完整的 Config(读取 config.net.proxyAgent)。对于需要按订阅合并代理的场景,
38
+ * 先用 `mergeProxyAgent` 得到统一片段,再包成 `{ net: { proxyAgent } }` 传入。
39
+ */
3
40
  export declare function buildAxiosProxyConfig(config: Config): Pick<AxiosRequestConfig, 'httpsAgent' | 'proxy'>;
@@ -1,7 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeProxyAgent = mergeProxyAgent;
3
4
  exports.buildAxiosProxyConfig = buildAxiosProxyConfig;
4
5
  const https_proxy_agent_1 = require("https-proxy-agent");
6
+ const logger_1 = require("./logger");
7
+ /**
8
+ * 合并订阅级代理配置 (argProxy) 与全局代理配置 (configProxy)。
9
+ *
10
+ * 优先级:
11
+ * 1. argProxy.enabled === false → 显式禁用
12
+ * 2. argProxy.enabled === true 且有 host → 用订阅配置
13
+ * 3. argProxy 缺失 / 未设置 enabled → 回落全局配置
14
+ * 4. argProxy.enabled === true 但缺 host → 用全局补全
15
+ * 5. 其余 → 禁用
16
+ *
17
+ * 返回值与 `config.net.proxyAgent` 同构,可直接喂给 `buildAxiosProxyConfig`。
18
+ *
19
+ * 本函数原位于 `core/feeder-arg.ts`,提升到此处以统一代理 helper 归属,
20
+ * 供 fetcher / feeder 等模块复用,避免重复实现合并逻辑。
21
+ */
22
+ function mergeProxyAgent(argProxy, configProxy, config) {
23
+ (0, logger_1.debug)(config, `合并代理配置 - argProxy: ${JSON.stringify(argProxy)}, configProxy.enabled: ${configProxy?.enabled}`, 'proxy merge debug', 'details');
24
+ if (argProxy?.enabled === false) {
25
+ (0, logger_1.debug)(config, '订阅明确禁用代理', 'proxy merge', 'details');
26
+ return { enabled: false };
27
+ }
28
+ if (argProxy?.enabled === true && argProxy?.host) {
29
+ (0, logger_1.debug)(config, '使用订阅的代理配置', 'proxy merge', 'details');
30
+ return argProxy;
31
+ }
32
+ const shouldUseConfigProxy = !argProxy || Object.keys(argProxy || {}).length === 0 || argProxy?.enabled === undefined || argProxy?.enabled === null;
33
+ if (shouldUseConfigProxy) {
34
+ if (configProxy?.enabled) {
35
+ const result = {
36
+ enabled: true,
37
+ protocol: configProxy.protocol,
38
+ host: configProxy.host,
39
+ port: configProxy.port,
40
+ auth: configProxy.auth?.enabled ? configProxy.auth : undefined,
41
+ };
42
+ (0, logger_1.debug)(config, `使用全局代理: ${result.protocol}://${result.host}:${result.port}`, 'proxy merge', 'info');
43
+ return result;
44
+ }
45
+ (0, logger_1.debug)(config, '全局代理未启用', 'proxy merge', 'details');
46
+ }
47
+ if (argProxy?.enabled === true && !argProxy?.host) {
48
+ const result = {
49
+ ...configProxy,
50
+ ...argProxy,
51
+ auth: configProxy?.auth?.enabled ? configProxy.auth : undefined,
52
+ };
53
+ (0, logger_1.debug)(config, '订阅代理配置不完整,补充全局配置', 'proxy merge', 'details');
54
+ return result;
55
+ }
56
+ (0, logger_1.debug)(config, '代理未配置,使用默认(禁用)', 'proxy merge', 'details');
57
+ return { enabled: false };
58
+ }
59
+ /**
60
+ * 构造 axios 的代理配置(httpsAgent + 关闭 axios 原生 proxy)。
61
+ *
62
+ * 接受完整的 Config(读取 config.net.proxyAgent)。对于需要按订阅合并代理的场景,
63
+ * 先用 `mergeProxyAgent` 得到统一片段,再包成 `{ net: { proxyAgent } }` 传入。
64
+ */
5
65
  function buildAxiosProxyConfig(config) {
6
66
  if (!config.net?.proxyAgent?.enabled) {
7
67
  return {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@anyul/koishi-plugin-rss",
3
3
  "description": "Koishi RSS订阅器,支持多种RSS源、图片渲染、AI摘要等高级功能",
4
- "version": "5.3.2",
4
+ "version": "5.3.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "author": "Anyuluo <anyul@email.com>",
@@ -1,53 +0,0 @@
1
- /**
2
- * 命令错误处理中间件
3
- * 提供统一的错误处理和用户友好的错误消息
4
- */
5
- import { Context } from 'koishi';
6
- import { Config } from '../types';
7
- /**
8
- * 命令执行结果
9
- */
10
- export interface CommandResult {
11
- success: boolean;
12
- message: string;
13
- error?: any;
14
- }
15
- /**
16
- * 命令错误类型
17
- */
18
- export declare enum CommandErrorType {
19
- PERMISSION_DENIED = "PERMISSION_DENIED",
20
- INVALID_ARGUMENT = "INVALID_ARGUMENT",
21
- NOT_FOUND = "NOT_FOUND",
22
- ALREADY_EXISTS = "ALREADY_EXISTS",
23
- NETWORK_ERROR = "NETWORK_ERROR",
24
- INTERNAL_ERROR = "INTERNAL_ERROR"
25
- }
26
- /**
27
- * 命令错误类
28
- */
29
- export declare class CommandError extends Error {
30
- type: CommandErrorType;
31
- details?: any;
32
- constructor(type: CommandErrorType, message: string, details?: any);
33
- }
34
- /**
35
- * 包装命令执行,提供统一的错误处理
36
- */
37
- export declare function executeCommand(ctx: Context, config: Config, operationName: string, handler: () => Promise<string>): Promise<string>;
38
- /**
39
- * 创建权限检查错误
40
- */
41
- export declare function permissionDenied(customMessage?: string): CommandError;
42
- /**
43
- * 创建参数错误
44
- */
45
- export declare function invalidArgument(message: string): CommandError;
46
- /**
47
- * 创建未找到错误
48
- */
49
- export declare function notFound(resource: string): CommandError;
50
- /**
51
- * 创建已存在错误
52
- */
53
- export declare function alreadyExists(resource: string): CommandError;
@@ -1,120 +0,0 @@
1
- "use strict";
2
- /**
3
- * 命令错误处理中间件
4
- * 提供统一的错误处理和用户友好的错误消息
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.CommandError = exports.CommandErrorType = void 0;
8
- exports.executeCommand = executeCommand;
9
- exports.permissionDenied = permissionDenied;
10
- exports.invalidArgument = invalidArgument;
11
- exports.notFound = notFound;
12
- exports.alreadyExists = alreadyExists;
13
- const error_handler_1 = require("../utils/error-handler");
14
- const error_tracker_1 = require("../utils/error-tracker");
15
- const logger_1 = require("../utils/logger");
16
- /**
17
- * 命令错误类型
18
- */
19
- var CommandErrorType;
20
- (function (CommandErrorType) {
21
- CommandErrorType["PERMISSION_DENIED"] = "PERMISSION_DENIED";
22
- CommandErrorType["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
23
- CommandErrorType["NOT_FOUND"] = "NOT_FOUND";
24
- CommandErrorType["ALREADY_EXISTS"] = "ALREADY_EXISTS";
25
- CommandErrorType["NETWORK_ERROR"] = "NETWORK_ERROR";
26
- CommandErrorType["INTERNAL_ERROR"] = "INTERNAL_ERROR";
27
- })(CommandErrorType || (exports.CommandErrorType = CommandErrorType = {}));
28
- /**
29
- * 命令错误类
30
- */
31
- class CommandError extends Error {
32
- type;
33
- details;
34
- constructor(type, message, details) {
35
- super(message);
36
- this.type = type;
37
- this.details = details;
38
- this.name = 'CommandError';
39
- }
40
- }
41
- exports.CommandError = CommandError;
42
- /**
43
- * 包装命令执行,提供统一的错误处理
44
- */
45
- async function executeCommand(ctx, config, operationName, handler) {
46
- try {
47
- return await handler();
48
- }
49
- catch (error) {
50
- // 如果是 CommandError,使用自定义消息
51
- if (error instanceof CommandError) {
52
- logError(config, operationName, error);
53
- return formatCommandError(error);
54
- }
55
- // 其他错误使用友好错误消息
56
- logError(config, operationName, error);
57
- const friendlyMessage = (0, error_handler_1.getFriendlyErrorMessage)(error, operationName);
58
- return `${operationName}失败: ${friendlyMessage}`;
59
- }
60
- }
61
- /**
62
- * 记录命令错误
63
- */
64
- function logError(config, operation, error) {
65
- const normalizedError = (0, error_handler_1.normalizeError)(error, `${operation} failed`);
66
- const context = {
67
- command: operation,
68
- };
69
- if (error instanceof CommandError) {
70
- context.commandErrorType = error.type;
71
- }
72
- if (normalizedError && typeof normalizedError.code === 'string') {
73
- context.errorCode = normalizedError.code;
74
- }
75
- (0, logger_1.debugError)(config, normalizedError, `${operation} error`, context);
76
- (0, error_tracker_1.trackError)(normalizedError, context);
77
- }
78
- /**
79
- * 格式化命令错误消息
80
- */
81
- function formatCommandError(error) {
82
- switch (error.type) {
83
- case CommandErrorType.PERMISSION_DENIED:
84
- return error.message;
85
- case CommandErrorType.INVALID_ARGUMENT:
86
- return `参数错误: ${error.message}`;
87
- case CommandErrorType.NOT_FOUND:
88
- return `未找到: ${error.message}`;
89
- case CommandErrorType.ALREADY_EXISTS:
90
- return `已存在: ${error.message}`;
91
- case CommandErrorType.NETWORK_ERROR:
92
- return `网络错误: ${error.message}`;
93
- default:
94
- return error.message || '操作失败,请稍后重试';
95
- }
96
- }
97
- /**
98
- * 创建权限检查错误
99
- */
100
- function permissionDenied(customMessage) {
101
- return new CommandError(CommandErrorType.PERMISSION_DENIED, customMessage || '权限不足');
102
- }
103
- /**
104
- * 创建参数错误
105
- */
106
- function invalidArgument(message) {
107
- return new CommandError(CommandErrorType.INVALID_ARGUMENT, message);
108
- }
109
- /**
110
- * 创建未找到错误
111
- */
112
- function notFound(resource) {
113
- return new CommandError(CommandErrorType.NOT_FOUND, resource);
114
- }
115
- /**
116
- * 创建已存在错误
117
- */
118
- function alreadyExists(resource) {
119
- return new CommandError(CommandErrorType.ALREADY_EXISTS, resource);
120
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * RSS 订阅命令
3
- * 处理 RSS 订阅的添加、删除、列表、关注等操作
4
- */
5
- import { Context } from 'koishi';
6
- import { Config } from '../types';
7
- import { RssItemProcessor } from '../core/item-processor';
8
- /**
9
- * 注册 RSS 订阅命令
10
- */
11
- export declare function registerRssSubscribeCommand(ctx: Context, config: Config, processor: RssItemProcessor, getRssDataLocal: (url: string, arg: any) => Promise<any[]>, parseRssItem: (item: any, arg: any, authorId: string | number) => Promise<string>, parseQuickUrlLocal: (url: string) => string, parsePubDateLocal: (pubDate: any) => Date): void;