@anyul/koishi-plugin-rss 5.3.2 → 5.3.10

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 (59) hide show
  1. package/lib/commands/index.js +1 -3
  2. package/lib/commands/subscription-create.d.ts +2 -0
  3. package/lib/commands/subscription-create.js +2 -2
  4. package/lib/commands/subscription-edit.d.ts +2 -0
  5. package/lib/commands/subscription-edit.js +8 -12
  6. package/lib/commands/subscription-management.d.ts +2 -0
  7. package/lib/commands/subscription-management.js +10 -12
  8. package/lib/commands/utils.d.ts +0 -5
  9. package/lib/commands/utils.js +0 -16
  10. package/lib/commands/web-monitor.d.ts +2 -0
  11. package/lib/commands/web-monitor.js +2 -2
  12. package/lib/config.js +4 -10
  13. package/lib/core/feeder-arg.js +3 -38
  14. package/lib/core/feeder-runtime.js +0 -5
  15. package/lib/core/feeder.js +53 -12
  16. package/lib/core/item-processor-runtime.d.ts +4 -2
  17. package/lib/core/item-processor-runtime.js +4 -12
  18. package/lib/core/notification-queue-sender.js +0 -5
  19. package/lib/core/notification-queue-store.d.ts +7 -0
  20. package/lib/core/notification-queue-store.js +28 -6
  21. package/lib/core/notification-queue.d.ts +10 -0
  22. package/lib/core/notification-queue.js +18 -7
  23. package/lib/core/parser.js +5 -5
  24. package/lib/core/renderer.js +7 -7
  25. package/lib/core/search-format.d.ts +1 -1
  26. package/lib/core/search-format.js +1 -1
  27. package/lib/core/subscription-store.d.ts +44 -0
  28. package/lib/core/subscription-store.js +60 -0
  29. package/lib/core/telegram-video-restore.js +3 -8
  30. package/lib/index.d.ts +1 -4
  31. package/lib/index.js +20 -15
  32. package/lib/tsconfig.tsbuildinfo +1 -1
  33. package/lib/types.d.ts +51 -32
  34. package/lib/utils/common.d.ts +6 -0
  35. package/lib/utils/common.js +20 -30
  36. package/lib/utils/error-handler.d.ts +49 -0
  37. package/lib/utils/error-handler.js +111 -1
  38. package/lib/utils/fetcher.js +12 -38
  39. package/lib/utils/legacy-config.js +9 -3
  40. package/lib/utils/logger.d.ts +6 -0
  41. package/lib/utils/logger.js +82 -14
  42. package/lib/utils/media.js +21 -21
  43. package/lib/utils/message-cache.d.ts +8 -0
  44. package/lib/utils/message-cache.js +12 -0
  45. package/lib/utils/proxy.d.ts +37 -0
  46. package/lib/utils/proxy.js +60 -0
  47. package/package.json +16 -3
  48. package/lib/commands/error-handler.d.ts +0 -53
  49. package/lib/commands/error-handler.js +0 -120
  50. package/lib/commands/rss-subscribe.d.ts +0 -11
  51. package/lib/commands/rss-subscribe.js +0 -323
  52. package/lib/core/feeder-old.d.ts +0 -15
  53. package/lib/core/feeder-old.js +0 -403
  54. package/lib/utils/error-tracker.d.ts +0 -127
  55. package/lib/utils/error-tracker.js +0 -352
  56. package/lib/utils/metrics.d.ts +0 -184
  57. package/lib/utils/metrics.js +0 -401
  58. package/lib/utils/structured-logger.d.ts +0 -135
  59. package/lib/utils/structured-logger.js +0 -248
package/lib/types.d.ts CHANGED
@@ -12,30 +12,53 @@ declare module 'koishi' {
12
12
  };
13
13
  }
14
14
  }
15
+ export interface RssOwlRow {
16
+ id: number;
17
+ url: string;
18
+ platform: string;
19
+ guildId: string;
20
+ author: string;
21
+ rssId: string;
22
+ arg: rssArg;
23
+ lastContent: any;
24
+ title: string;
25
+ followers: string[];
26
+ lastPubDate: Date;
27
+ }
28
+ export interface RssMessageCacheRow {
29
+ id: number;
30
+ rssId: string;
31
+ guildId: string;
32
+ platform: string;
33
+ title: string;
34
+ content: string;
35
+ link: string;
36
+ pubDate: Date;
37
+ imageUrl: string;
38
+ videoUrl: string;
39
+ finalMessage: string;
40
+ createdAt: Date;
41
+ }
42
+ export interface RssNotificationQueueRow {
43
+ id: number;
44
+ subscribeId: string;
45
+ rssId: string;
46
+ uid: string;
47
+ guildId: string;
48
+ platform: string;
49
+ content: any;
50
+ status: string;
51
+ retryCount: number;
52
+ nextRetryTime: Date;
53
+ createdAt: Date;
54
+ updatedAt: Date;
55
+ failReason: string;
56
+ }
15
57
  declare module 'koishi' {
16
- interface rssOwl {
17
- id: string | number;
18
- url: string;
19
- platform: string;
20
- guildId: string;
21
- author: string;
22
- rssId: string | number;
23
- arg: rssArg;
24
- title: string;
25
- lastPubDate: Date;
26
- }
27
- interface rss_message_cache {
28
- id: number;
29
- rssId: string;
30
- guildId: string;
31
- platform: string;
32
- title: string;
33
- content: string;
34
- link: string;
35
- pubDate: Date;
36
- imageUrl: string;
37
- videoUrl: string;
38
- createdAt: Date;
58
+ interface Tables {
59
+ rssOwl: RssOwlRow;
60
+ rss_message_cache: RssMessageCacheRow;
61
+ rss_notification_queue: RssNotificationQueueRow;
39
62
  }
40
63
  }
41
64
  export interface Config {
@@ -50,7 +73,6 @@ export interface Config {
50
73
  security?: SecurityConfig;
51
74
  debug?: "disable" | "error" | "info" | "details";
52
75
  logging?: LoggingConfig;
53
- errorTracking?: ErrorTrackingConfig;
54
76
  tdl?: TdlConfig;
55
77
  }
56
78
  export declare const debugLevel: string[];
@@ -64,6 +86,7 @@ export interface CustomTemplateItem {
64
86
  export interface BasicConfig {
65
87
  usePoster: boolean;
66
88
  mergeVideo?: boolean;
89
+ /** @deprecated 拼写错误,请使用 mergeVideo。运行时仍兼容读取。 */
67
90
  margeVideo?: boolean;
68
91
  defaultTemplate?: TemplateType;
69
92
  timeout?: number;
@@ -73,6 +96,7 @@ export interface BasicConfig {
73
96
  firstLoad?: boolean;
74
97
  urlDeduplication?: boolean;
75
98
  resendUpdatedContent?: 'disable' | 'latest' | 'all';
99
+ /** @deprecated 拼写错误,请使用 resendUpdatedContent。运行时仍兼容读取。 */
76
100
  resendUpdataContent?: 'disable' | 'latest' | 'all';
77
101
  imageMode?: 'base64' | 'File' | 'assets';
78
102
  videoMode?: 'filter' | 'href' | 'base64' | 'File' | 'assets';
@@ -155,10 +179,13 @@ export interface rssArg {
155
179
  block?: Array<string>;
156
180
  split?: number;
157
181
  nextUpdateTime?: number;
182
+ /** @deprecated 拼写错误,请使用 nextUpdateTime。运行时仍兼容读取。 */
158
183
  nextUpdataTime?: number;
159
184
  mergeVideo?: boolean;
185
+ /** @deprecated 拼写错误,请使用 mergeVideo。运行时仍兼容读取。 */
160
186
  margeVideo?: boolean;
161
187
  resendUpdatedContent?: 'disable' | 'latest' | 'all';
188
+ /** @deprecated 拼写错误,请使用 resendUpdatedContent。运行时仍兼容读取。 */
162
189
  resendUpdataContent?: 'disable' | 'latest' | 'all';
163
190
  type?: 'rss' | 'html';
164
191
  selector?: string;
@@ -186,14 +213,6 @@ export interface LoggingConfig {
186
213
  contextFields?: string[];
187
214
  sanitizeLogs?: boolean;
188
215
  }
189
- export interface ErrorTrackingConfig {
190
- enabled?: boolean;
191
- dsn?: string;
192
- environment?: string;
193
- release?: string;
194
- tracesSampleRate?: number;
195
- profilesSampleRate?: number;
196
- }
197
216
  /**
198
217
  * 联网搜索配置
199
218
  */
@@ -1,5 +1,11 @@
1
1
  import { Config } from '../types';
2
2
  export declare const sleep: (delay?: number) => Promise<unknown>;
3
+ /**
4
+ * 标准化 RSS 字段内容,统一数组 / 空值 / 非字符串输入。
5
+ *
6
+ * 统一入口,避免在各 core 模块内重复定义私有副本。
7
+ */
8
+ export declare function normalizeText(value: unknown): string;
3
9
  export declare const parsePubDate: (config: Config, pubDate: any) => Date;
4
10
  export declare const ensureUrlProtocol: (url: string) => string;
5
11
  export declare const parseContent: (content: any, attr?: any) => string | undefined;
@@ -34,10 +34,23 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.cleanContent = exports.parseQuickUrl = exports.parseTemplateContent = exports.parseContent = exports.ensureUrlProtocol = exports.parsePubDate = exports.sleep = void 0;
37
+ exports.normalizeText = normalizeText;
37
38
  const logger_1 = require("./logger");
38
39
  const cheerio = __importStar(require("cheerio"));
39
40
  const sleep = (delay = 1000) => new Promise(resolve => setTimeout(resolve, delay));
40
41
  exports.sleep = sleep;
42
+ /**
43
+ * 标准化 RSS 字段内容,统一数组 / 空值 / 非字符串输入。
44
+ *
45
+ * 统一入口,避免在各 core 模块内重复定义私有副本。
46
+ */
47
+ function normalizeText(value) {
48
+ if (Array.isArray(value))
49
+ return value.join('');
50
+ if (value === undefined || value === null)
51
+ return '';
52
+ return String(value);
53
+ }
41
54
  // 安全的时间解析函数,处理各种格式
42
55
  const parsePubDate = (config, pubDate) => {
43
56
  if (!pubDate)
@@ -87,7 +100,7 @@ const parseContent = (content, attr = undefined) => {
87
100
  else if (Object.prototype.toString.call(content) === '[object Object]') {
88
101
  return Object.values(content).reduce((t, v) => {
89
102
  if (v && (typeof v == 'string' || v?.join)) {
90
- let text = v?.join("") || v;
103
+ const text = v?.join("") || v;
91
104
  return (typeof text === 'string' && text.length > t.length) ? text : t;
92
105
  }
93
106
  else {
@@ -130,38 +143,15 @@ const parseTemplateContent = (template, item) => {
130
143
  exports.parseTemplateContent = parseTemplateContent;
131
144
  // 快速URL解析函数
132
145
  const parseQuickUrl = (url, rssHubUrl, quickList) => {
133
- let correntQuickObj = quickList.find(i => new RegExp(`^${i.prefix}:`).test(url));
146
+ const correntQuickObj = quickList.find(i => new RegExp(`^${i.prefix}:`).test(url));
134
147
  if (!correntQuickObj)
135
148
  return url;
136
- let routeMatch = url.match(new RegExp(`(?<=^${correntQuickObj.prefix}:).*`));
149
+ const routeMatch = url.match(new RegExp(`(?<=^${correntQuickObj.prefix}:).*`));
137
150
  if (!routeMatch)
138
151
  return url;
139
- let route = routeMatch[0];
140
- const parseContent = (template, item) => {
141
- return template.replace(/{{(.+?)}}/g, (i) => {
142
- // 添加空值检查,防止 match 返回 null 时崩溃
143
- const match = i.match(/^{{(.*)}}$/);
144
- if (!match)
145
- return i;
146
- const content = match[1];
147
- if (!content)
148
- return i;
149
- return content.split("|").reduce((t, v) => {
150
- const literalMatch = v.match(/^'(.*)'$/);
151
- if (literalMatch) {
152
- return t || literalMatch[1];
153
- }
154
- return v.split(".").reduce((t, v) => {
155
- if (new RegExp("Date").test(v)) {
156
- const dateVal = t?.[v];
157
- return dateVal ? new Date(dateVal).toLocaleString('zh-CN') : "";
158
- }
159
- return t?.[v] || "";
160
- }, item);
161
- }, '');
162
- });
163
- };
164
- let rUrl = parseContent(correntQuickObj.replace, { rsshub: rssHubUrl, route });
152
+ const route = routeMatch[0];
153
+ // 复用模块级 parseTemplateContent,避免内嵌重复实现
154
+ const rUrl = (0, exports.parseTemplateContent)(correntQuickObj.replace, { rsshub: rssHubUrl, route });
165
155
  return rUrl;
166
156
  };
167
157
  exports.parseQuickUrl = parseQuickUrl;
@@ -173,7 +163,7 @@ const cleanContent = (htmlContent) => {
173
163
  $('style').remove();
174
164
  $('img').remove();
175
165
  $('video').remove();
176
- let plainText = $.text().replace(/\s+/g, ' ').trim();
166
+ const plainText = $.text().replace(/\s+/g, ' ').trim();
177
167
  return plainText;
178
168
  };
179
169
  exports.cleanContent = cleanContent;
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * 将技术性错误转换为用户友好的提示信息
5
5
  */
6
+ import { Context } from 'koishi';
7
+ import type { Config } from '../types';
6
8
  /**
7
9
  * 错误类型枚举
8
10
  */
@@ -88,3 +90,50 @@ export declare function isPermissionError(error: any): boolean;
88
90
  * @returns 是否可重试
89
91
  */
90
92
  export declare function isRetryable(error: any): boolean;
93
+ /**
94
+ * 命令执行结果
95
+ */
96
+ export interface CommandResult {
97
+ success: boolean;
98
+ message: string;
99
+ error?: any;
100
+ }
101
+ /**
102
+ * 命令错误类型
103
+ */
104
+ export declare enum CommandErrorType {
105
+ PERMISSION_DENIED = "PERMISSION_DENIED",
106
+ INVALID_ARGUMENT = "INVALID_ARGUMENT",
107
+ NOT_FOUND = "NOT_FOUND",
108
+ ALREADY_EXISTS = "ALREADY_EXISTS",
109
+ NETWORK_ERROR = "NETWORK_ERROR",
110
+ INTERNAL_ERROR = "INTERNAL_ERROR"
111
+ }
112
+ /**
113
+ * 命令错误类
114
+ */
115
+ export declare class CommandError extends Error {
116
+ type: CommandErrorType;
117
+ details?: any;
118
+ constructor(type: CommandErrorType, message: string, details?: any);
119
+ }
120
+ /**
121
+ * 包装命令执行,提供统一的错误处理
122
+ */
123
+ export declare function executeCommand(ctx: Context, config: Config, operationName: string, handler: () => Promise<string>): Promise<string>;
124
+ /**
125
+ * 创建权限检查错误
126
+ */
127
+ export declare function permissionDenied(customMessage?: string): CommandError;
128
+ /**
129
+ * 创建参数错误
130
+ */
131
+ export declare function invalidArgument(message: string): CommandError;
132
+ /**
133
+ * 创建未找到错误
134
+ */
135
+ export declare function notFound(resource: string): CommandError;
136
+ /**
137
+ * 创建已存在错误
138
+ */
139
+ export declare function alreadyExists(resource: string): CommandError;
@@ -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;
@@ -106,7 +99,7 @@ const createHttpFunction = (ctx, config, requestManager) => {
106
99
  const makeRequest = async () => {
107
100
  // 基础配置
108
101
  requestDebug(`[DEBUG_PROXY] fetcher makeRequest arg.proxyAgent: ${JSON.stringify(arg?.proxyAgent)}`, 'request', 'details');
109
- let configObj = {
102
+ const configObj = {
110
103
  timeout: requestTimeout,
111
104
  headers: {
112
105
  'User-Agent': config.net.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
@@ -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) {
@@ -9,8 +9,12 @@ exports.getNextUpdateTime = getNextUpdateTime;
9
9
  exports.setNextUpdateTime = setNextUpdateTime;
10
10
  function normalizeBasicConfig(basic) {
11
11
  const normalized = { ...(basic || {}) };
12
- const mergeVideo = normalized.mergeVideo ?? normalized.margeVideo;
13
- const resendUpdatedContent = normalized.resendUpdatedContent ?? normalized.resendUpdataContent;
12
+ // 读取归一:typo(旧拼写)优先。理由——schema 会为声明的规范名注入默认值
13
+ // (如 mergeVideo:false),若用 `correct ?? typo` 会被默认值短路,吞掉老用户
14
+ // 真实意图(margeVideo:true)。typo 存在则必来自历史用户数据,应优先采纳。
15
+ // 写入仍双写两个键(保持运行时兼容,下游直接读 margeVideo 仍可拿到值)。
16
+ const mergeVideo = normalized.margeVideo ?? normalized.mergeVideo;
17
+ const resendUpdatedContent = normalized.resendUpdataContent ?? normalized.resendUpdatedContent;
14
18
  if (mergeVideo !== undefined) {
15
19
  normalized.mergeVideo = mergeVideo;
16
20
  normalized.margeVideo = mergeVideo;
@@ -47,10 +51,12 @@ function getNextUpdateTime(arg) {
47
51
  }
48
52
  function setNextUpdateTime(target, nextUpdateTime) {
49
53
  if (nextUpdateTime === undefined) {
54
+ // 清理时双删:删规范名的同时顺手清掉残留的旧拼写名(老数据可能仍有)
50
55
  delete target.nextUpdateTime;
51
56
  delete target.nextUpdataTime;
52
57
  return;
53
58
  }
59
+ // 只写规范名:不再回写 nextUpdataTime,让旧拼写从数据中自然淘汰。
60
+ // 读取侧 getNextUpdateTime 已用 `nextUpdateTime ?? nextUpdataTime` 兜底老数据。
54
61
  target.nextUpdateTime = nextUpdateTime;
55
- target.nextUpdataTime = nextUpdateTime;
56
62
  }
@@ -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
  * 增强的调试日志函数