@anyul/koishi-plugin-rss 5.3.1 → 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.
- package/lib/commands/index.js +0 -2
- package/lib/commands/subscription-management.js +0 -2
- package/lib/commands/utils.d.ts +0 -5
- package/lib/commands/utils.js +0 -16
- package/lib/config.js +1 -8
- package/lib/core/feeder-arg.js +3 -38
- package/lib/core/feeder-runtime.js +0 -5
- package/lib/core/feeder.js +0 -10
- 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.js +0 -7
- package/lib/core/telegram-video-restore.js +18 -8
- package/lib/index.js +3 -11
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +1 -9
- package/lib/utils/common.d.ts +6 -0
- package/lib/utils/common.js +15 -25
- package/lib/utils/error-handler.d.ts +49 -0
- package/lib/utils/error-handler.js +111 -1
- package/lib/utils/fetcher.js +11 -37
- package/lib/utils/logger.d.ts +6 -0
- package/lib/utils/logger.js +82 -14
- package/lib/utils/proxy.d.ts +37 -0
- package/lib/utils/proxy.js +60 -0
- package/lib/utils/tdl.d.ts +31 -0
- package/lib/utils/tdl.js +136 -0
- package/package.json +1 -1
- 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
|
@@ -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;
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* RSS 订阅命令
|
|
4
|
-
* 处理 RSS 订阅的添加、删除、列表、关注等操作
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.registerRssSubscribeCommand = registerRssSubscribeCommand;
|
|
8
|
-
const constants_1 = require("../constants");
|
|
9
|
-
const feeder_1 = require("../core/feeder");
|
|
10
|
-
const common_1 = require("../utils/common");
|
|
11
|
-
const logger_1 = require("../utils/logger");
|
|
12
|
-
const utils_1 = require("./utils");
|
|
13
|
-
const error_handler_1 = require("./error-handler");
|
|
14
|
-
/**
|
|
15
|
-
* 注册 RSS 订阅命令
|
|
16
|
-
*/
|
|
17
|
-
function registerRssSubscribeCommand(ctx, config, processor, getRssDataLocal, parseRssItem, parseQuickUrlLocal, parsePubDateLocal) {
|
|
18
|
-
const usage = 'RSS 订阅命令\n' +
|
|
19
|
-
'用法:\n' +
|
|
20
|
-
' rsso <url> - 订阅 RSS\n' +
|
|
21
|
-
' rsso -l - 查看订阅列表\n' +
|
|
22
|
-
' rsso -r <id> - 删除订阅\n' +
|
|
23
|
-
' rsso -p <id> - 拉取最新内容\n' +
|
|
24
|
-
' rsso -f <id> - 关注订阅\n';
|
|
25
|
-
ctx.guild()
|
|
26
|
-
.command('rssowl <url:text>', '*订阅 RSS 链接*')
|
|
27
|
-
.alias('rsso')
|
|
28
|
-
.usage(usage)
|
|
29
|
-
.option('list', '-l [content] 查看订阅列表(详情)')
|
|
30
|
-
.option('remove', '-r <content> [订阅id|关键字] *删除订阅*')
|
|
31
|
-
.option('removeAll', '*删除全部订阅*')
|
|
32
|
-
.option('follow', '-f <content> [订阅id|关键字] 关注订阅,在该订阅更新时提醒你')
|
|
33
|
-
.option('followAll', '<content> [订阅id|关键字] **在该订阅更新时提醒所有人**')
|
|
34
|
-
.option('target', '<content> [群组id] **跨群订阅**')
|
|
35
|
-
.option('arg', '-a <content> 自定义配置')
|
|
36
|
-
.option('template', '-i <content> 消息模板[content(文字模板)|default(图片模板)],更多见readme')
|
|
37
|
-
.option('title', '-t <content> 自定义命名')
|
|
38
|
-
.option('pull', '-p <content> [订阅id|关键字]拉取订阅id最后更新')
|
|
39
|
-
.option('force', '强行写入')
|
|
40
|
-
.option('daily', '-d <content>')
|
|
41
|
-
.option('test', '-T 测试')
|
|
42
|
-
.option('quick', '-q [content] 查询快速订阅列表')
|
|
43
|
-
.example('rsso https://hub.slarker.me/qqorw')
|
|
44
|
-
.action(async ({ session, options }, url) => {
|
|
45
|
-
return (0, error_handler_1.executeCommand)(ctx, config, '订阅', () => handleRssCommand(ctx, config, processor, session, options, url, getRssDataLocal, parseRssItem, parseQuickUrlLocal, parsePubDateLocal));
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 处理 RSS 订阅命令
|
|
50
|
-
*/
|
|
51
|
-
async function handleRssCommand(ctx, config, processor, session, options, url, getRssDataLocal, parseRssItem, parseQuickUrlLocal, parsePubDateLocal) {
|
|
52
|
-
const { guildId, platform, authorId, authority } = (0, utils_1.extractSessionInfo)(session);
|
|
53
|
-
(0, logger_1.debug)(config, options, 'options', 'info');
|
|
54
|
-
(0, logger_1.debug)(config, `${platform}:${authorId}:${guildId}`, '', 'info');
|
|
55
|
-
// 处理快速订阅查询
|
|
56
|
-
if (options?.quick !== undefined) {
|
|
57
|
-
return handleQuickList(options.quick);
|
|
58
|
-
}
|
|
59
|
-
// 沙盒环境提示
|
|
60
|
-
if (platform.includes('sandbox') && !options.test && url) {
|
|
61
|
-
session.send('沙盒中无法推送更新,但RSS依然会被订阅,建议使用 -T 选项进行测试');
|
|
62
|
-
}
|
|
63
|
-
// 获取当前订阅列表
|
|
64
|
-
const rssList = await ctx.database.get('rssOwl', { platform, guildId });
|
|
65
|
-
// 处理列表选项
|
|
66
|
-
if (options?.list !== undefined) {
|
|
67
|
-
return handleList(rssList, options.list, parsePubDateLocal);
|
|
68
|
-
}
|
|
69
|
-
// 处理删除选项
|
|
70
|
-
if (options?.remove) {
|
|
71
|
-
return handleRemove(ctx, rssList, options.remove, authority);
|
|
72
|
-
}
|
|
73
|
-
// 处理删除全部选项
|
|
74
|
-
if (options?.removeAll) {
|
|
75
|
-
return handleRemoveAll(ctx, rssList, authority);
|
|
76
|
-
}
|
|
77
|
-
// 处理关注选项
|
|
78
|
-
if (options?.follow) {
|
|
79
|
-
return handleFollow(ctx, rssList, options.follow, authorId);
|
|
80
|
-
}
|
|
81
|
-
// 处理全员关注选项
|
|
82
|
-
if (options?.followAll) {
|
|
83
|
-
return handleFollowAll(ctx, rssList, options.followAll, authority);
|
|
84
|
-
}
|
|
85
|
-
// 处理拉取选项
|
|
86
|
-
if (options?.pull) {
|
|
87
|
-
return handlePull(ctx, config, rssList, options.pull, parseRssItem, getRssDataLocal, parseQuickUrlLocal, parsePubDateLocal, feeder_1.mixinArg);
|
|
88
|
-
}
|
|
89
|
-
// 处理订阅 URL
|
|
90
|
-
if (url) {
|
|
91
|
-
return handleSubscribe(ctx, config, session, rssList, url, options, platform, guildId, authorId, authority, getRssDataLocal, parseRssItem, parseQuickUrlLocal, parsePubDateLocal, feeder_1.formatArg);
|
|
92
|
-
}
|
|
93
|
-
return '用法: rsso <url> 或 rsso -l 查看订阅列表';
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* 处理快速订阅列表查询
|
|
97
|
-
*/
|
|
98
|
-
function handleQuickList(quick) {
|
|
99
|
-
if (quick === '') {
|
|
100
|
-
return '输入 rsso -q [id] 查询详情\n' +
|
|
101
|
-
constants_1.quickList.map((v, i) => `${i + 1}.${v.name}`).join('\n');
|
|
102
|
-
}
|
|
103
|
-
const index = parseInt(quick) - 1;
|
|
104
|
-
if (index < 0 || index >= constants_1.quickList.length) {
|
|
105
|
-
return '无效的 ID';
|
|
106
|
-
}
|
|
107
|
-
const quickObj = constants_1.quickList[index];
|
|
108
|
-
return `${quickObj.name}\n${quickObj.detail}\n` +
|
|
109
|
-
`例:rsso -T ${quickObj.example}\n(${(0, common_1.parseQuickUrl)(quickObj.example, '', [])})`;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* 处理列表查询
|
|
113
|
-
*/
|
|
114
|
-
function handleList(rssList, listOption, parsePubDateLocal) {
|
|
115
|
-
(0, logger_1.debug)(config, rssList, 'rssList', 'info');
|
|
116
|
-
if (rssList.length === 0) {
|
|
117
|
-
return '当前没有任何订阅';
|
|
118
|
-
}
|
|
119
|
-
// 简单列表
|
|
120
|
-
if (listOption === '') {
|
|
121
|
-
return rssList.map((v, i) => `${i + 1}. ${v.title} [${v.id}]`).join('\n');
|
|
122
|
-
}
|
|
123
|
-
// 详细列表
|
|
124
|
-
const rssItem = (0, feeder_1.findRssItem)(rssList, listOption);
|
|
125
|
-
if (!rssItem) {
|
|
126
|
-
throw (0, error_handler_1.notFound)('未找到该订阅');
|
|
127
|
-
}
|
|
128
|
-
const lastPubDate = rssItem.lastPubDate
|
|
129
|
-
? parsePubDateLocal(rssItem.lastPubDate).toLocaleString('zh-CN', { hour12: false })
|
|
130
|
-
: '未知';
|
|
131
|
-
return `标题: ${rssItem.title}\n` +
|
|
132
|
-
`链接: ${rssItem.url}\n` +
|
|
133
|
-
`更新时间: ${lastPubDate}`;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* 处理删除订阅
|
|
137
|
-
*/
|
|
138
|
-
async function handleRemove(ctx, rssList, identifier, authority) {
|
|
139
|
-
const authCheck = (0, utils_1.checkAuthority)(authority, ctx.config.basic.authority);
|
|
140
|
-
if (!authCheck.success) {
|
|
141
|
-
throw (0, error_handler_1.permissionDenied)();
|
|
142
|
-
}
|
|
143
|
-
const rssItem = (0, feeder_1.findRssItem)(rssList, identifier);
|
|
144
|
-
if (!rssItem) {
|
|
145
|
-
throw (0, error_handler_1.notFound)('未找到该订阅');
|
|
146
|
-
}
|
|
147
|
-
await ctx.database.remove('rssOwl', rssItem.id);
|
|
148
|
-
return '删除成功';
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* 处理删除全部订阅
|
|
152
|
-
*/
|
|
153
|
-
async function handleRemoveAll(ctx, rssList, authority) {
|
|
154
|
-
const authCheck = (0, utils_1.checkAuthority)(authority, ctx.config.basic.authority);
|
|
155
|
-
if (!authCheck.success) {
|
|
156
|
-
throw (0, error_handler_1.permissionDenied)();
|
|
157
|
-
}
|
|
158
|
-
await ctx.database.remove('rssOwl', { platform, guildId });
|
|
159
|
-
return '删除成功';
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* 处理关注订阅
|
|
163
|
-
*/
|
|
164
|
-
async function handleFollow(ctx, rssList, identifier, authorId) {
|
|
165
|
-
const rssItem = (0, feeder_1.findRssItem)(rssList, identifier);
|
|
166
|
-
if (!rssItem) {
|
|
167
|
-
throw (0, error_handler_1.notFound)('未找到该订阅');
|
|
168
|
-
}
|
|
169
|
-
if (!rssItem.followers) {
|
|
170
|
-
rssItem.followers = [];
|
|
171
|
-
}
|
|
172
|
-
if (rssItem.followers.includes(authorId)) {
|
|
173
|
-
return '已经关注过了';
|
|
174
|
-
}
|
|
175
|
-
rssItem.followers.push(authorId);
|
|
176
|
-
await ctx.database.set('rssOwl', { id: rssItem.id }, { followers: rssItem.followers });
|
|
177
|
-
return '关注成功';
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* 处理全员关注
|
|
181
|
-
*/
|
|
182
|
-
async function handleFollowAll(ctx, rssList, identifier, authority) {
|
|
183
|
-
const authCheck = (0, utils_1.checkAuthority)(authority, ctx.config.basic.advancedAuthority);
|
|
184
|
-
if (!authCheck.success) {
|
|
185
|
-
throw (0, error_handler_1.permissionDenied)();
|
|
186
|
-
}
|
|
187
|
-
const rssItem = (0, feeder_1.findRssItem)(rssList, identifier);
|
|
188
|
-
if (!rssItem) {
|
|
189
|
-
throw (0, error_handler_1.notFound)('未找到该订阅');
|
|
190
|
-
}
|
|
191
|
-
if (!rssItem.followers) {
|
|
192
|
-
rssItem.followers = [];
|
|
193
|
-
}
|
|
194
|
-
rssItem.followers.push('all');
|
|
195
|
-
await ctx.database.set('rssOwl', { id: rssItem.id }, { followers: rssItem.followers });
|
|
196
|
-
return '关注成功';
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* 处理拉取最新内容
|
|
200
|
-
*/
|
|
201
|
-
async function handlePull(ctx, config, rssList, identifier, parseRssItem, getRssDataLocal, parseQuickUrlLocal, parsePubDateLocal, mixinArg) {
|
|
202
|
-
const rssItem = (0, feeder_1.findRssItem)(rssList, identifier);
|
|
203
|
-
if (!rssItem) {
|
|
204
|
-
throw (0, error_handler_1.notFound)('未找到该订阅');
|
|
205
|
-
}
|
|
206
|
-
const arg = mixinArg(rssItem.arg || {});
|
|
207
|
-
const rssItemList = (await Promise.all(rssItem.url.split('|')
|
|
208
|
-
.map((url) => parseQuickUrlLocal(url))
|
|
209
|
-
.map(async (url) => await getRssDataLocal(url, arg)))).flat(1);
|
|
210
|
-
let itemArray = rssItemList.sort((a, b) => parsePubDateLocal(b.pubDate).getTime() - parsePubDateLocal(a.pubDate).getTime());
|
|
211
|
-
if (arg.reverse) {
|
|
212
|
-
itemArray = itemArray.reverse();
|
|
213
|
-
}
|
|
214
|
-
const maxItem = arg.forceLength || 1;
|
|
215
|
-
const messageList = await Promise.all(itemArray
|
|
216
|
-
.filter((v, i) => i < maxItem)
|
|
217
|
-
.map(i => parseRssItem(i, { ...rssItem, ...arg }, rssItem.author)));
|
|
218
|
-
return messageList.join('');
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* 处理新订阅
|
|
222
|
-
*/
|
|
223
|
-
async function handleSubscribe(ctx, config, session, rssList, url, options, platform, guildId, authorId, authority, getRssDataLocal, parseRssItem, parseQuickUrlLocal, parsePubDateLocal, formatArg) {
|
|
224
|
-
// 检查是否已存在
|
|
225
|
-
if (rssList.find(i => i.url === url)) {
|
|
226
|
-
throw new Error('该订阅已存在');
|
|
227
|
-
}
|
|
228
|
-
const arg = formatArg(options);
|
|
229
|
-
let targetPlatform = platform;
|
|
230
|
-
let targetGuildId = guildId;
|
|
231
|
-
// 处理跨群订阅
|
|
232
|
-
if (options?.target) {
|
|
233
|
-
const authCheck = (0, utils_1.checkAuthority)(authority, config.basic.advancedAuthority);
|
|
234
|
-
if (!authCheck.success) {
|
|
235
|
-
throw (0, error_handler_1.permissionDenied)();
|
|
236
|
-
}
|
|
237
|
-
const target = (0, utils_1.parseTarget)(options.target);
|
|
238
|
-
if (!target) {
|
|
239
|
-
throw new Error('请输入正确的群号,格式为 platform:guildId 或 platform:guildId');
|
|
240
|
-
}
|
|
241
|
-
targetPlatform = target.platform;
|
|
242
|
-
targetGuildId = target.guildId;
|
|
243
|
-
}
|
|
244
|
-
let title = options?.title || '';
|
|
245
|
-
url = parseQuickUrlLocal(url);
|
|
246
|
-
url = (0, common_1.ensureUrlProtocol)(url);
|
|
247
|
-
// 获取 RSS 数据
|
|
248
|
-
const rssItemList = await getRssDataLocal(url, arg);
|
|
249
|
-
// 测试模式
|
|
250
|
-
if (options.test) {
|
|
251
|
-
return handleTestMode(rssItemList, arg, title, config, parseRssItem, authorId);
|
|
252
|
-
}
|
|
253
|
-
// 获取标题
|
|
254
|
-
if (!title) {
|
|
255
|
-
title = rssItemList[0]?.rss?.channel?.title;
|
|
256
|
-
if (!title) {
|
|
257
|
-
throw new Error('无法获取标题,请使用 -t 指定标题');
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// 检查权限(force 模式)
|
|
261
|
-
if (options.force && authority < config.basic.authority) {
|
|
262
|
-
throw (0, error_handler_1.permissionDenied)();
|
|
263
|
-
}
|
|
264
|
-
// 检查去重
|
|
265
|
-
if (config.basic.urlDeduplication && rssList.find(i => i.rssId === title)) {
|
|
266
|
-
throw new Error(`订阅已存在: ${title}`);
|
|
267
|
-
}
|
|
268
|
-
// 创建订阅项
|
|
269
|
-
const lastPubDate = parsePubDateLocal(rssItemList[0]?.pubDate);
|
|
270
|
-
const rssItem = {
|
|
271
|
-
url,
|
|
272
|
-
platform: targetPlatform,
|
|
273
|
-
guildId: targetGuildId,
|
|
274
|
-
author: authorId,
|
|
275
|
-
rssId: rssItemList[0]?.rss?.channel?.title || title,
|
|
276
|
-
arg,
|
|
277
|
-
title,
|
|
278
|
-
lastPubDate,
|
|
279
|
-
lastContent: [],
|
|
280
|
-
followers: [],
|
|
281
|
-
firstime: lastPubDate
|
|
282
|
-
};
|
|
283
|
-
await ctx.database.create('rssOwl', rssItem);
|
|
284
|
-
// 首次加载
|
|
285
|
-
if (config.basic.firstLoad && arg.firstLoad !== false && rssItemList.length > 0) {
|
|
286
|
-
await loadInitialItems(ctx, rssItemList, rssItem, targetPlatform, targetGuildId, parsePubDateLocal, parseRssItem);
|
|
287
|
-
}
|
|
288
|
-
return `订阅成功: ${title}`;
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* 处理测试模式
|
|
292
|
-
*/
|
|
293
|
-
async function handleTestMode(rssItemList, arg, title, config, parseRssItem, authorId) {
|
|
294
|
-
const testItem = rssItemList[0];
|
|
295
|
-
if (!testItem) {
|
|
296
|
-
throw new Error('未获取到数据');
|
|
297
|
-
}
|
|
298
|
-
const testArg = {
|
|
299
|
-
...arg,
|
|
300
|
-
url: title || testItem.rss.channel.title,
|
|
301
|
-
title: title || testItem.rss.channel.title
|
|
302
|
-
};
|
|
303
|
-
if (!testArg.template) {
|
|
304
|
-
testArg.template = config.basic.defaultTemplate;
|
|
305
|
-
}
|
|
306
|
-
const msg = await parseRssItem(testItem, testArg, authorId);
|
|
307
|
-
return msg;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* 加载初始条目
|
|
311
|
-
*/
|
|
312
|
-
async function loadInitialItems(ctx, rssItemList, rssItem, targetPlatform, targetGuildId, parsePubDateLocal, parseRssItem) {
|
|
313
|
-
let itemArray = rssItemList.sort((a, b) => parsePubDateLocal(b.pubDate).getTime() - parsePubDateLocal(a.pubDate).getTime());
|
|
314
|
-
if (rssItem.arg.reverse) {
|
|
315
|
-
itemArray = itemArray.reverse();
|
|
316
|
-
}
|
|
317
|
-
const maxItem = rssItem.arg.forceLength || 1;
|
|
318
|
-
const messageList = await Promise.all(itemArray
|
|
319
|
-
.filter((v, i) => i < maxItem)
|
|
320
|
-
.map(i => parseRssItem(i, { ...rssItem, ...rssItem.arg }, rssItem.author)));
|
|
321
|
-
const message = messageList.join('');
|
|
322
|
-
await ctx.broadcast([`${targetPlatform}:${targetGuildId}`], message);
|
|
323
|
-
}
|
package/lib/core/feeder-old.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { Config, rssArg } from '../types';
|
|
3
|
-
import { RssItemProcessor } from './item-processor';
|
|
4
|
-
export interface FeederDependencies {
|
|
5
|
-
ctx: Context;
|
|
6
|
-
config: Config;
|
|
7
|
-
$http: any;
|
|
8
|
-
}
|
|
9
|
-
export declare function findRssItem(rssList: any[], keyword: number | string): any;
|
|
10
|
-
export declare function getLastContent(item: any, config: Config): any;
|
|
11
|
-
export declare function formatArg(options: any, config: Config): rssArg;
|
|
12
|
-
export declare function mixinArg(arg: any, config: Config): rssArg;
|
|
13
|
-
export declare function feeder(deps: FeederDependencies, processor: RssItemProcessor): Promise<void>;
|
|
14
|
-
export declare function startFeeder(ctx: Context, config: Config, $http: any, processor: RssItemProcessor): void;
|
|
15
|
-
export declare function stopFeeder(): void;
|