@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.
- package/lib/commands/index.js +1 -3
- package/lib/commands/subscription-create.d.ts +2 -0
- package/lib/commands/subscription-create.js +2 -2
- package/lib/commands/subscription-edit.d.ts +2 -0
- package/lib/commands/subscription-edit.js +8 -12
- package/lib/commands/subscription-management.d.ts +2 -0
- package/lib/commands/subscription-management.js +10 -12
- package/lib/commands/utils.d.ts +0 -5
- package/lib/commands/utils.js +0 -16
- package/lib/commands/web-monitor.d.ts +2 -0
- package/lib/commands/web-monitor.js +2 -2
- package/lib/config.js +4 -10
- package/lib/core/feeder-arg.js +3 -38
- package/lib/core/feeder-runtime.js +0 -5
- package/lib/core/feeder.js +53 -12
- package/lib/core/item-processor-runtime.d.ts +4 -2
- package/lib/core/item-processor-runtime.js +4 -12
- package/lib/core/notification-queue-sender.js +0 -5
- package/lib/core/notification-queue-store.d.ts +7 -0
- package/lib/core/notification-queue-store.js +28 -6
- package/lib/core/notification-queue.d.ts +10 -0
- package/lib/core/notification-queue.js +18 -7
- package/lib/core/parser.js +5 -5
- package/lib/core/renderer.js +7 -7
- package/lib/core/search-format.d.ts +1 -1
- package/lib/core/search-format.js +1 -1
- package/lib/core/subscription-store.d.ts +44 -0
- package/lib/core/subscription-store.js +60 -0
- package/lib/core/telegram-video-restore.js +3 -8
- package/lib/index.d.ts +1 -4
- package/lib/index.js +20 -15
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +51 -32
- package/lib/utils/common.d.ts +6 -0
- package/lib/utils/common.js +20 -30
- package/lib/utils/error-handler.d.ts +49 -0
- package/lib/utils/error-handler.js +111 -1
- package/lib/utils/fetcher.js +12 -38
- package/lib/utils/legacy-config.js +9 -3
- package/lib/utils/logger.d.ts +6 -0
- package/lib/utils/logger.js +82 -14
- package/lib/utils/media.js +21 -21
- package/lib/utils/message-cache.d.ts +8 -0
- package/lib/utils/message-cache.js +12 -0
- package/lib/utils/proxy.d.ts +37 -0
- package/lib/utils/proxy.js +60 -0
- package/package.json +16 -3
- package/lib/commands/error-handler.d.ts +0 -53
- package/lib/commands/error-handler.js +0 -120
- package/lib/commands/rss-subscribe.d.ts +0 -11
- package/lib/commands/rss-subscribe.js +0 -323
- package/lib/core/feeder-old.d.ts +0 -15
- package/lib/core/feeder-old.js +0 -403
- package/lib/utils/error-tracker.d.ts +0 -127
- package/lib/utils/error-tracker.js +0 -352
- package/lib/utils/metrics.d.ts +0 -184
- package/lib/utils/metrics.js +0 -401
- package/lib/utils/structured-logger.d.ts +0 -135
- 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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
*/
|
package/lib/utils/common.d.ts
CHANGED
|
@@ -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;
|
package/lib/utils/common.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
146
|
+
const correntQuickObj = quickList.find(i => new RegExp(`^${i.prefix}:`).test(url));
|
|
134
147
|
if (!correntQuickObj)
|
|
135
148
|
return url;
|
|
136
|
-
|
|
149
|
+
const routeMatch = url.match(new RegExp(`(?<=^${correntQuickObj.prefix}:).*`));
|
|
137
150
|
if (!routeMatch)
|
|
138
151
|
return url;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/lib/utils/fetcher.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
}
|
package/lib/utils/logger.d.ts
CHANGED
|
@@ -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
|
* 增强的调试日志函数
|