karin-plugin-qgroup-file2openlist 0.0.22 → 0.0.24
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/README.md +27 -5
- package/config/config.json +13 -2
- package/lib/apps/groupFiles.backup.js +229 -0
- package/lib/apps/groupFiles.js +305 -10
- package/lib/apps/groupSyncConfig.js +18 -411
- package/lib/apps/groupSyncScheduler.js +0 -188
- package/lib/apps/help.js +1 -1
- package/lib/apps/opCommands.js +725 -0
- package/lib/apps/opltScheduler.js +0 -0
- package/lib/apps/ownerUi.js +1174 -0
- package/lib/apps/render.js +1 -1
- package/lib/apps/scheduler.js +77 -0
- package/lib/apps/sendMsg.js +1 -1
- package/lib/chunk-BU2GD6GJ.js +867 -0
- package/lib/chunk-K2XRORRU.js +44 -0
- package/lib/chunk-KFOQIZ6H.js +489 -0
- package/lib/chunk-N5HMQFRM.js +1206 -0
- package/lib/chunk-PBBZ5KAD.js +41 -0
- package/lib/chunk-TL3HO4ZL.js +199 -0
- package/lib/chunk-WJNM5RHT.js +60 -0
- package/package.json +1 -1
- package/resources/template/help.html +25 -2
- package/resources/template/myBackup.html +209 -0
- package/resources/template/ui/action-result.html +241 -0
- package/resources/template/ui/panel.html +355 -0
- package/lib/chunk-AWSGUQPA.js +0 -2282
- /package/lib/{chunk-5WVKHIPK.js → chunk-DA4U55JC.js} +0 -0
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
- [前言](#前言)
|
|
6
6
|
- [群文件导出](#群文件导出)
|
|
7
7
|
- [同步到 OpenList](#同步到-openlist)
|
|
8
|
+
- [主人管理(图片面板)](#主人管理图片面板)
|
|
9
|
+
- [OpenList → OpenList 转发](#openlist--openlist-转发)
|
|
8
10
|
- [快速开始](#快速开始)
|
|
9
11
|
- [详细开发流程](#详细开发流程)
|
|
10
12
|
- [常见问题与建议](#常见问题与建议)
|
|
@@ -42,8 +44,8 @@ TypeScript 插件开发流程现在更加简单,无需手动克隆模板仓库
|
|
|
42
44
|
- `openlistBaseUrl`:例如 `http://127.0.0.1:5244`
|
|
43
45
|
- `openlistUsername` / `openlistPassword`:用于 WebDAV BasicAuth 登录
|
|
44
46
|
- `openlistTargetDir`:目标目录(例:`/挂载目录/QQ群文件`)
|
|
45
|
-
- `groupSyncDefaults
|
|
46
|
-
- `groupSyncTargets
|
|
47
|
+
- `groupSyncDefaults`:群同步默认策略(并发、单文件超时、重试等)
|
|
48
|
+
- `groupSyncTargets`:同步对象群配置(每群可单独覆盖目录/并发/同步时段等)
|
|
47
49
|
|
|
48
50
|
私聊命令:
|
|
49
51
|
|
|
@@ -51,7 +53,8 @@ TypeScript 插件开发流程现在更加简单,无需手动克隆模板仓库
|
|
|
51
53
|
- `#同步群文件 <群号> --to /目标目录`:覆盖配置里的目标目录
|
|
52
54
|
- `#同步群文件 <群号> --flat`:不保留群文件夹结构,全部平铺上传
|
|
53
55
|
- `#同步群文件 <群号> --max <n>` / `--folder <id>` / `--concurrency <n>`:同导出命令
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
固定策略:同步模式为增量(incremental),单文件超时 3000 秒(不再通过命令配置)。
|
|
55
58
|
|
|
56
59
|
群聊命令(简化):
|
|
57
60
|
|
|
@@ -62,8 +65,27 @@ TypeScript 插件开发流程现在更加简单,无需手动克隆模板仓库
|
|
|
62
65
|
- `#群同步配置 列表`
|
|
63
66
|
- `#群同步配置 <群号> 添加/删除/查看`
|
|
64
67
|
- `#群同步配置 <群号> 启用/停用`
|
|
65
|
-
- `#群同步配置 <群号> 模式 全量|增量`
|
|
66
|
-
- `#群同步配置 <群号> 计划 <cron>`:设置定时同步(例:`0 0 3 * * *`)
|
|
67
68
|
- `#群同步配置 <群号> 时段 00:00-06:00,23:00-23:59`:限制定时同步时间段(空=不限制)
|
|
68
69
|
|
|
70
|
+
说明:夜间自动备份默认每天 02:00 触发(先群后 oplts),固定增量 + 单文件超时 3000s;不再支持通过 `#群同步配置` 配置 cron/mode/timeout。
|
|
71
|
+
|
|
72
|
+
## 主人管理(图片面板)
|
|
73
|
+
|
|
74
|
+
仅主人私聊可用:
|
|
75
|
+
|
|
76
|
+
- `#群文件面板`:输出管理总览面板(群绑定/监听/OP转发规则摘要/快捷命令)
|
|
77
|
+
- `#绑定备份群 <群号> [--to /目标目录] [--flat|--keep]`:写入群配置并默认开启群文件上传监听
|
|
78
|
+
- `#解绑备份群 <群号>`:删除该群配置
|
|
79
|
+
- `#开启群文件监听 <群号>` / `#关闭群文件监听 <群号>`:切换 uploadBackup 监听开关
|
|
80
|
+
|
|
81
|
+
## OpenList → OpenList 转发
|
|
82
|
+
|
|
83
|
+
用于“多源站 → 单目的端(固定为配置 openlistBaseUrl)”的转发规则(仅主人私聊可用):
|
|
84
|
+
|
|
85
|
+
- `#添加op转发 <源OpenListBaseUrl> [--src /] [--to /backup] [--name xxx] [--user u] [--pass p] [--full|--inc] [--auto|--api|--webdav]`
|
|
86
|
+
- `#op转发 列表`
|
|
87
|
+
- `#op转发 查看 <ruleId>`
|
|
88
|
+
- `#op转发 执行 <ruleId>`(默认全量)
|
|
89
|
+
- `#op转发 删除 <ruleId>`
|
|
90
|
+
|
|
69
91
|
## 🚀 快速开始
|
package/config/config.json
CHANGED
|
@@ -9,12 +9,23 @@
|
|
|
9
9
|
"flat": false,
|
|
10
10
|
"urlConcurrency": 3,
|
|
11
11
|
"transferConcurrency": 3,
|
|
12
|
-
"fileTimeoutSec":
|
|
12
|
+
"fileTimeoutSec": 3000,
|
|
13
13
|
"retryTimes": 2,
|
|
14
14
|
"retryDelayMs": 1500,
|
|
15
15
|
"progressReportEvery": 10,
|
|
16
16
|
"downloadLimitKbps": 0,
|
|
17
17
|
"uploadLimitKbps": 0
|
|
18
18
|
},
|
|
19
|
-
"groupSyncTargets": []
|
|
19
|
+
"groupSyncTargets": [],
|
|
20
|
+
"openlistForwardRules": [],
|
|
21
|
+
"scheduler": {
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"tickCron": "0 0 2 * * *",
|
|
24
|
+
"groupSync": {
|
|
25
|
+
"enabled": true
|
|
26
|
+
},
|
|
27
|
+
"opltNightly": {
|
|
28
|
+
"enabled": true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
20
31
|
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildActionCard,
|
|
3
|
+
replyImages
|
|
4
|
+
} from "../chunk-K2XRORRU.js";
|
|
5
|
+
import "../chunk-PBBZ5KAD.js";
|
|
6
|
+
import "../chunk-QB3GSENE.js";
|
|
7
|
+
import {
|
|
8
|
+
handleGroupFileUploadedAutoBackup
|
|
9
|
+
} from "../chunk-BU2GD6GJ.js";
|
|
10
|
+
import "../chunk-WJNM5RHT.js";
|
|
11
|
+
import {
|
|
12
|
+
backupOpenListToOpenListCore,
|
|
13
|
+
formatErrorMessage
|
|
14
|
+
} from "../chunk-N5HMQFRM.js";
|
|
15
|
+
import {
|
|
16
|
+
config
|
|
17
|
+
} from "../chunk-DA4U55JC.js";
|
|
18
|
+
import "../chunk-IZS467MR.js";
|
|
19
|
+
|
|
20
|
+
// src/apps/groupFiles.backup.ts
|
|
21
|
+
import { karin, logger } from "node-karin";
|
|
22
|
+
var FIXED_BACKUP_MODE = "incremental";
|
|
23
|
+
var FIXED_TIMEOUT_SEC = 3e3;
|
|
24
|
+
var parseBackupOpenListArgs = (text) => {
|
|
25
|
+
const raw = text.trim();
|
|
26
|
+
const tokens = raw ? raw.split(/\s+/).filter(Boolean) : [];
|
|
27
|
+
const help = /(^|\s)(--help|-h|help|\?)(\s|$)/i.test(raw);
|
|
28
|
+
const first = tokens[0];
|
|
29
|
+
const sourceBaseUrl = first && /^https?:\/\//i.test(first) ? first : void 0;
|
|
30
|
+
const restRaw = sourceBaseUrl ? raw.slice(first.length).trim() : raw;
|
|
31
|
+
const srcMatch = restRaw.match(/--src\s+(\S+)/i) ?? restRaw.match(/(^|\s)src=(\S+)/i);
|
|
32
|
+
const srcDir = srcMatch ? srcMatch[srcMatch.length - 1] : void 0;
|
|
33
|
+
const toMatch = restRaw.match(/--to\s+(\S+)/i) ?? restRaw.match(/(^|\s)to=(\S+)/i);
|
|
34
|
+
const toDir = toMatch ? toMatch[toMatch.length - 1] : void 0;
|
|
35
|
+
const maxMatch = restRaw.match(/--max\s+(\d+)/i) ?? restRaw.match(/(^|\s)max=(\d+)/i);
|
|
36
|
+
const maxFiles = maxMatch ? Number(maxMatch[maxMatch.length - 1]) : void 0;
|
|
37
|
+
const concurrencyMatch = restRaw.match(/--concurrency\s+(\d+)/i) ?? restRaw.match(/(^|\s)concurrency=(\d+)/i);
|
|
38
|
+
const concurrency = concurrencyMatch ? Number(concurrencyMatch[concurrencyMatch.length - 1]) : void 0;
|
|
39
|
+
const scanMatch = restRaw.match(/--scan(?:-concurrency)?\s+(\d+)/i) ?? restRaw.match(/(^|\s)scan=(\d+)/i) ?? restRaw.match(/(^|\s)scanConcurrency=(\d+)/i) ?? restRaw.match(/(^|\s)scan_concurrency=(\d+)/i);
|
|
40
|
+
const scanConcurrency = scanMatch ? Number(scanMatch[scanMatch.length - 1]) : void 0;
|
|
41
|
+
const perPageMatch = restRaw.match(/--per-page\s+(\d+)/i) ?? restRaw.match(/--perpage\s+(\d+)/i) ?? restRaw.match(/--page-size\s+(\d+)/i) ?? restRaw.match(/(^|\s)per[_-]?page=(\d+)/i) ?? restRaw.match(/(^|\s)pageSize=(\d+)/i) ?? restRaw.match(/(^|\s)per_page=(\d+)/i);
|
|
42
|
+
const perPage = perPageMatch ? Number(perPageMatch[perPageMatch.length - 1]) : void 0;
|
|
43
|
+
const transportApi = /(^|\s)(--api)(\s|$)/i.test(restRaw);
|
|
44
|
+
const transportWebDav = /(^|\s)(--webdav|--dav)(\s|$)/i.test(restRaw);
|
|
45
|
+
const transportAuto = /(^|\s)(--auto)(\s|$)/i.test(restRaw);
|
|
46
|
+
const transport = transportApi ? "api" : transportWebDav ? "webdav" : transportAuto ? "auto" : void 0;
|
|
47
|
+
return {
|
|
48
|
+
sourceBaseUrl,
|
|
49
|
+
srcDir,
|
|
50
|
+
toDir,
|
|
51
|
+
maxFiles,
|
|
52
|
+
concurrency,
|
|
53
|
+
scanConcurrency,
|
|
54
|
+
perPage,
|
|
55
|
+
transport,
|
|
56
|
+
help
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
var openListToOpenListHelpText = [
|
|
60
|
+
"OpenList -> OpenList \u5907\u4EFD\u7528\u6CD5\uFF1A",
|
|
61
|
+
"- \u79C1\u804A\uFF1A#\u5907\u4EFDoplist [\u6E90OpenList\u5730\u5740] [\u53C2\u6570]",
|
|
62
|
+
"- \u793A\u4F8B\uFF1A#\u5907\u4EFDoplist https://pan.example.com",
|
|
63
|
+
`- \u56FA\u5B9A\u7B56\u7565\uFF1Amode=${FIXED_BACKUP_MODE} \u5355\u6587\u4EF6\u8D85\u65F6=${FIXED_TIMEOUT_SEC}s\uFF08\u4E0D\u518D\u901A\u8FC7\u547D\u4EE4\u914D\u7F6E\uFF09`,
|
|
64
|
+
"- #\u5907\u4EFDoplist https://pan.example.com --api",
|
|
65
|
+
"- #\u5907\u4EFDoplist https://pan.example.com --webdav",
|
|
66
|
+
"- #\u5907\u4EFDoplist https://pan.example.com --concurrency 3",
|
|
67
|
+
"- #\u5907\u4EFDoplist https://pan.example.com --scan 30 --per-page 2000",
|
|
68
|
+
"\u63D0\u793A\uFF1A\u76EE\u6807\u7AEF\u4F7F\u7528 openlistBaseUrl/openlistUsername/openlistPassword\uFF08\u4E0E\u7FA4\u6587\u4EF6\u540C\u6B65\u5171\u7528\uFF09\u3002",
|
|
69
|
+
"\u63D0\u793A\uFF1A\u4F20\u8F93\u9ED8\u8BA4 auto\uFF08\u6E90\u7AEF\u4E0B\u8F7D\u504F\u5411 API\uFF0C\u76EE\u6807\u7AEF\u4E0A\u4F20\u504F\u5411 WebDAV\uFF1B\u5931\u8D25\u4F1A\u56DE\u9000\uFF09\u3002",
|
|
70
|
+
'\u8BF4\u660E\uFF1A\u4F1A\u5728\u76EE\u6807\u76EE\u5F55\u4E0B\u521B\u5EFA\u5B50\u76EE\u5F55\uFF08\u6E90 OpenList \u57DF\u540D\uFF0C"." \u66FF\u6362\u4E3A "_"\uFF09\u3002'
|
|
71
|
+
].join("\n");
|
|
72
|
+
var backupOpenListToOpenList = karin.command(/^#?备份oplist(.*)$/i, async (e) => {
|
|
73
|
+
if (!e.isPrivate) return false;
|
|
74
|
+
const argsText = e.msg.replace(/^#?备份oplist/i, "");
|
|
75
|
+
const {
|
|
76
|
+
sourceBaseUrl,
|
|
77
|
+
srcDir,
|
|
78
|
+
toDir,
|
|
79
|
+
maxFiles,
|
|
80
|
+
concurrency,
|
|
81
|
+
scanConcurrency,
|
|
82
|
+
perPage,
|
|
83
|
+
transport,
|
|
84
|
+
help
|
|
85
|
+
} = parseBackupOpenListArgs(argsText);
|
|
86
|
+
if (help || !sourceBaseUrl) {
|
|
87
|
+
try {
|
|
88
|
+
const img = await buildActionCard({
|
|
89
|
+
title: "\u5907\u4EFDoplist \u5E2E\u52A9",
|
|
90
|
+
status: "ok",
|
|
91
|
+
subtitle: "\u79C1\u804A \xB7 \u56FE\u7247\u8BF4\u660E",
|
|
92
|
+
sections: [
|
|
93
|
+
{
|
|
94
|
+
title: "\u8BF4\u660E",
|
|
95
|
+
lines: openListToOpenListHelpText.split("\n").map((text) => ({
|
|
96
|
+
text,
|
|
97
|
+
mono: /(^|\s)#/.test(text)
|
|
98
|
+
}))
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
footerRight: "#\u5907\u4EFDoplist"
|
|
102
|
+
});
|
|
103
|
+
await replyImages(e, img);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger.error(error);
|
|
106
|
+
await e.reply(openListToOpenListHelpText);
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const cfg = config();
|
|
112
|
+
const targetBaseUrl = String(cfg.openlistBaseUrl ?? "").trim() || "-";
|
|
113
|
+
const startedAt = Date.now();
|
|
114
|
+
try {
|
|
115
|
+
const startCard = await buildActionCard({
|
|
116
|
+
title: "\u5907\u4EFD\u4EFB\u52A1\u5DF2\u542F\u52A8",
|
|
117
|
+
status: "ok",
|
|
118
|
+
subtitle: "OpenList \u2192 OpenList\uFF08\u5355\u6B21\uFF09",
|
|
119
|
+
sections: [
|
|
120
|
+
{
|
|
121
|
+
title: "\u6267\u884C\u4FE1\u606F",
|
|
122
|
+
rows: [
|
|
123
|
+
{ k: "\u6E90", v: String(sourceBaseUrl), mono: true },
|
|
124
|
+
{ k: "\u6E90\u76EE\u5F55", v: String(srcDir ?? "/"), mono: true },
|
|
125
|
+
{ k: "\u76EE\u6807", v: targetBaseUrl, mono: true },
|
|
126
|
+
{ k: "\u76EE\u6807\u76EE\u5F55", v: String(toDir ?? "(\u9ED8\u8BA4 openlistTargetDir)"), mono: true },
|
|
127
|
+
{ k: "mode", v: String(FIXED_BACKUP_MODE), mono: true },
|
|
128
|
+
{ k: "timeout", v: `${FIXED_TIMEOUT_SEC}s`, mono: true },
|
|
129
|
+
{ k: "transport", v: String(transport ?? "auto"), mono: true }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
title: "\u63D0\u793A",
|
|
134
|
+
lines: [
|
|
135
|
+
{ text: "\u6267\u884C\u8FC7\u7A0B\u4E2D\u4E0D\u53D1\u9001\u6587\u672C\u8FDB\u5EA6\uFF1B\u5B8C\u6210\u540E\u53D1\u9001\u7ED3\u679C\u56FE\u3002" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
footerRight: "#\u5907\u4EFDoplist"
|
|
140
|
+
});
|
|
141
|
+
await replyImages(e, startCard);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.error(error);
|
|
144
|
+
}
|
|
145
|
+
const res = await backupOpenListToOpenListCore({
|
|
146
|
+
sourceBaseUrl,
|
|
147
|
+
srcDir,
|
|
148
|
+
toDir,
|
|
149
|
+
maxFiles,
|
|
150
|
+
concurrency,
|
|
151
|
+
timeoutSec: FIXED_TIMEOUT_SEC,
|
|
152
|
+
scanConcurrency,
|
|
153
|
+
perPage,
|
|
154
|
+
mode: FIXED_BACKUP_MODE,
|
|
155
|
+
transport
|
|
156
|
+
});
|
|
157
|
+
const elapsed = Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
|
|
158
|
+
try {
|
|
159
|
+
const doneCard = await buildActionCard({
|
|
160
|
+
title: "\u5907\u4EFD\u5B8C\u6210",
|
|
161
|
+
status: res.fail ? "warn" : "ok",
|
|
162
|
+
statusText: res.fail ? "\u90E8\u5206\u5931\u8D25" : "\u6210\u529F",
|
|
163
|
+
subtitle: "OpenList \u2192 OpenList\uFF08\u5355\u6B21\uFF09",
|
|
164
|
+
sections: [
|
|
165
|
+
{
|
|
166
|
+
title: "\u7ED3\u679C",
|
|
167
|
+
rows: [
|
|
168
|
+
{ k: "ok", v: String(res.ok), mono: true },
|
|
169
|
+
{ k: "skipped", v: String(res.skipped), mono: true },
|
|
170
|
+
{ k: "fail", v: String(res.fail), mono: true },
|
|
171
|
+
{ k: "\u8017\u65F6", v: `${elapsed}s`, mono: true }
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
title: "\u4E0B\u4E00\u6B65",
|
|
176
|
+
lines: [
|
|
177
|
+
{ text: "#\u6211\u7684\u5907\u4EFD", mono: true }
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
footerRight: "#\u5907\u4EFDoplist"
|
|
182
|
+
});
|
|
183
|
+
await replyImages(e, doneCard);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
logger.error(error);
|
|
186
|
+
await e.reply(`\u5907\u4EFD\u5B8C\u6210\uFF1A\u6210\u529F ${res.ok}\uFF0C\u8DF3\u8FC7 ${res.skipped}\uFF0C\u5931\u8D25 ${res.fail}`);
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger.error(error);
|
|
191
|
+
const msg = formatErrorMessage(error);
|
|
192
|
+
try {
|
|
193
|
+
const errCard = await buildActionCard({
|
|
194
|
+
title: "\u5907\u4EFD\u5931\u8D25",
|
|
195
|
+
status: "bad",
|
|
196
|
+
statusText: "\u5F02\u5E38",
|
|
197
|
+
subtitle: "OpenList \u2192 OpenList\uFF08\u5355\u6B21\uFF09",
|
|
198
|
+
sections: [
|
|
199
|
+
{
|
|
200
|
+
title: "\u9519\u8BEF",
|
|
201
|
+
lines: [{ text: msg }]
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
footerRight: "#\u5907\u4EFDoplist"
|
|
205
|
+
});
|
|
206
|
+
await replyImages(e, errCard);
|
|
207
|
+
} catch (renderError) {
|
|
208
|
+
logger.error(renderError);
|
|
209
|
+
await e.reply(msg);
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}, {
|
|
214
|
+
priority: 9999,
|
|
215
|
+
log: true,
|
|
216
|
+
name: "OpenList\u5907\u4EFD\u5230\u5BF9\u7AEFOpenList",
|
|
217
|
+
permission: "all"
|
|
218
|
+
});
|
|
219
|
+
var groupFileUploadedAutoBackup = karin.accept("notice.groupFileUploaded", (e, next) => {
|
|
220
|
+
try {
|
|
221
|
+
handleGroupFileUploadedAutoBackup(e);
|
|
222
|
+
} finally {
|
|
223
|
+
next();
|
|
224
|
+
}
|
|
225
|
+
}, { log: false, name: "\u7FA4\u6587\u4EF6\u4E0A\u4F20\u81EA\u52A8\u5907\u4EFD" });
|
|
226
|
+
export {
|
|
227
|
+
backupOpenListToOpenList,
|
|
228
|
+
groupFileUploadedAutoBackup
|
|
229
|
+
};
|
package/lib/apps/groupFiles.js
CHANGED
|
@@ -1,16 +1,311 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
exportGroupFiles,
|
|
4
|
-
groupFileUploadedAutoBackup,
|
|
5
|
-
syncGroupFilesToOpenList,
|
|
2
|
+
exportGroupFilesToDisk,
|
|
6
3
|
syncGroupFilesToOpenListCore
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-BU2GD6GJ.js";
|
|
5
|
+
import "../chunk-WJNM5RHT.js";
|
|
6
|
+
import {
|
|
7
|
+
formatErrorMessage,
|
|
8
|
+
normalizePosixPath
|
|
9
|
+
} from "../chunk-N5HMQFRM.js";
|
|
10
|
+
import {
|
|
11
|
+
config
|
|
12
|
+
} from "../chunk-DA4U55JC.js";
|
|
9
13
|
import "../chunk-IZS467MR.js";
|
|
14
|
+
|
|
15
|
+
// src/apps/groupFiles.ts
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { pathToFileURL } from "url";
|
|
18
|
+
import { karin, logger } from "node-karin";
|
|
19
|
+
var MAX_FILE_TIMEOUT_SEC = 3e3;
|
|
20
|
+
var DEFAULT_PROGRESS_REPORT_EVERY = 10;
|
|
21
|
+
var FIXED_SYNC_MODE = "incremental";
|
|
22
|
+
var buildUploadFileCandidates = (filePath) => {
|
|
23
|
+
const normalized = filePath.replaceAll("\\", "/");
|
|
24
|
+
const candidates = [
|
|
25
|
+
filePath,
|
|
26
|
+
normalized
|
|
27
|
+
];
|
|
28
|
+
try {
|
|
29
|
+
candidates.push(pathToFileURL(filePath).href);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
if (/^[a-zA-Z]:\//.test(normalized)) {
|
|
33
|
+
candidates.push(`file:///${normalized}`);
|
|
34
|
+
}
|
|
35
|
+
return [...new Set(candidates.filter(Boolean))];
|
|
36
|
+
};
|
|
37
|
+
var parseArgs = (text) => {
|
|
38
|
+
const raw = text.trim();
|
|
39
|
+
const tokens = raw ? raw.split(/\s+/).filter(Boolean) : [];
|
|
40
|
+
const format = /(^|\s)(--csv|csv)(\s|$)/i.test(raw) ? "csv" : "json";
|
|
41
|
+
const withUrl = !/(^|\s)(--no-url|--nourl|no-url|nourl)(\s|$)/i.test(raw);
|
|
42
|
+
const urlOnly = /(^|\s)(--url-only|--urlonly|url-only|urlonly)(\s|$)/i.test(raw);
|
|
43
|
+
const sendFile = /(^|\s)(--send-file|--sendfile|send-file|sendfile)(\s|$)/i.test(raw);
|
|
44
|
+
let groupId;
|
|
45
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
46
|
+
const token = tokens[i];
|
|
47
|
+
const nextToken = tokens[i + 1];
|
|
48
|
+
if (/^--(group|gid|groupid)$/i.test(token) && nextToken && /^\d+$/.test(nextToken)) {
|
|
49
|
+
groupId = nextToken;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const assignMatch = token.match(/^(group|gid|groupid)=(\d+)$/i);
|
|
53
|
+
if (assignMatch) {
|
|
54
|
+
groupId = assignMatch[2];
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!groupId) {
|
|
59
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
60
|
+
const token = tokens[i];
|
|
61
|
+
const prevToken = tokens[i - 1];
|
|
62
|
+
if (!/^\d+$/.test(token)) continue;
|
|
63
|
+
if (prevToken && /^--(folder|max|concurrency|group|gid|groupid|to|timeout)$/i.test(prevToken)) continue;
|
|
64
|
+
groupId = token;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const folderMatch = raw.match(/--folder\s+(\S+)/i) ?? raw.match(/(^|\s)folder=(\S+)/i);
|
|
69
|
+
const folderId = folderMatch ? folderMatch[folderMatch.length - 1] : void 0;
|
|
70
|
+
const maxMatch = raw.match(/--max\s+(\d+)/i) ?? raw.match(/(^|\s)max=(\d+)/i);
|
|
71
|
+
const maxFiles = maxMatch ? Number(maxMatch[maxMatch.length - 1]) : void 0;
|
|
72
|
+
const concurrencyMatch = raw.match(/--concurrency\s+(\d+)/i) ?? raw.match(/(^|\s)concurrency=(\d+)/i);
|
|
73
|
+
const concurrency = concurrencyMatch ? Number(concurrencyMatch[concurrencyMatch.length - 1]) : void 0;
|
|
74
|
+
const concurrencySpecified = Boolean(concurrencyMatch);
|
|
75
|
+
const help = /(^|\s)(--help|-h|help|\?)(\s|$)/i.test(raw);
|
|
76
|
+
return { groupId, format, withUrl, urlOnly, sendFile, folderId, maxFiles, concurrency, concurrencySpecified, help };
|
|
77
|
+
};
|
|
78
|
+
var parseSyncArgs = (text) => {
|
|
79
|
+
const raw = text.trim();
|
|
80
|
+
const base = parseArgs(text);
|
|
81
|
+
const toMatch = raw.match(/--to\s+(\S+)/i) ?? raw.match(/(^|\s)to=(\S+)/i);
|
|
82
|
+
const to = toMatch ? toMatch[toMatch.length - 1] : void 0;
|
|
83
|
+
const toSpecified = Boolean(toMatch);
|
|
84
|
+
const flatFlag = /(^|\s)(--flat|flat)(\s|$)/i.test(raw);
|
|
85
|
+
const keepFlag = /(^|\s)(--keep|keep)(\s|$)/i.test(raw);
|
|
86
|
+
const flatSpecified = flatFlag || keepFlag;
|
|
87
|
+
const flat = flatFlag ? true : keepFlag ? false : void 0;
|
|
88
|
+
return {
|
|
89
|
+
groupId: base.groupId,
|
|
90
|
+
folderId: base.folderId,
|
|
91
|
+
maxFiles: base.maxFiles,
|
|
92
|
+
concurrency: base.concurrency,
|
|
93
|
+
concurrencySpecified: base.concurrencySpecified,
|
|
94
|
+
flat,
|
|
95
|
+
flatSpecified,
|
|
96
|
+
to,
|
|
97
|
+
toSpecified,
|
|
98
|
+
help: base.help
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
var helpText = [
|
|
102
|
+
"\u7FA4\u6587\u4EF6\u5BFC\u51FA\u7528\u6CD5\uFF1A",
|
|
103
|
+
"- \u8BF7\u79C1\u804A\u53D1\u9001\uFF1A#\u5BFC\u51FA\u7FA4\u6587\u4EF6 <\u7FA4\u53F7> [\u53C2\u6570]",
|
|
104
|
+
"- \u793A\u4F8B\uFF1A#\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456",
|
|
105
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --no-url\uFF1A\u53EA\u5BFC\u51FA\u5217\u8868\uFF0C\u4E0D\u89E3\u6790 URL",
|
|
106
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --url-only\uFF1A\u4EC5\u8F93\u51FA URL\uFF08\u66F4\u65B9\u4FBF\u590D\u5236\uFF09",
|
|
107
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --csv\uFF1A\u5BFC\u51FA\u4E3A CSV\uFF08\u9ED8\u8BA4 JSON\uFF09",
|
|
108
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --folder <id>\uFF1A\u4ECE\u6307\u5B9A\u6587\u4EF6\u5939\u5F00\u59CB\u5BFC\u51FA",
|
|
109
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --max <n>\uFF1A\u6700\u591A\u5BFC\u51FA n \u6761\u6587\u4EF6\u8BB0\u5F55",
|
|
110
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --concurrency <n>\uFF1A\u89E3\u6790 URL \u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 3\uFF09",
|
|
111
|
+
"- #\u5BFC\u51FA\u7FA4\u6587\u4EF6 123456 --send-file\uFF1A\u5C1D\u8BD5\u53D1\u9001\u5BFC\u51FA\u6587\u4EF6\uFF08\u4F9D\u8D56\u534F\u8BAE\u7AEF\u652F\u6301\uFF09",
|
|
112
|
+
"\u63D0\u793A\uFF1A\u4E0B\u8F7D URL \u901A\u5E38\u6709\u65F6\u6548\uFF0C\u8FC7\u671F\u540E\u9700\u91CD\u65B0\u5BFC\u51FA\u3002"
|
|
113
|
+
].join("\n");
|
|
114
|
+
var exportGroupFiles = karin.command(/^#?(导出群文件|群文件导出)(.*)$/i, async (e) => {
|
|
115
|
+
if (!e.isPrivate) return false;
|
|
116
|
+
const argsText = e.msg.replace(/^#?(导出群文件|群文件导出)/i, "");
|
|
117
|
+
const { groupId, format, withUrl, urlOnly, sendFile, folderId, maxFiles, concurrency, help } = parseArgs(argsText);
|
|
118
|
+
if (help) {
|
|
119
|
+
await e.reply(helpText);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
if (!groupId) {
|
|
123
|
+
await e.reply(`\u7F3A\u5C11\u7FA4\u53F7\u53C2\u6570
|
|
124
|
+
|
|
125
|
+
${helpText}`);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
const groupContact = karin.contactGroup(groupId);
|
|
129
|
+
await e.reply(`\u5F00\u59CB\u5BFC\u51FA\u7FA4\u6587\u4EF6\u5217\u8868\uFF0C\u8BF7\u7A0D\u5019..
|
|
130
|
+
- \u7FA4\u53F7\uFF1A${groupId}
|
|
131
|
+
- \u683C\u5F0F\uFF1A${format}
|
|
132
|
+
- \u5305\u542BURL\uFF1A${withUrl ? "\u662F" : "\u5426"}`);
|
|
133
|
+
let result;
|
|
134
|
+
try {
|
|
135
|
+
const urlConcurrency = typeof concurrency === "number" && Number.isFinite(concurrency) && concurrency > 0 ? concurrency : 3;
|
|
136
|
+
result = await exportGroupFilesToDisk({
|
|
137
|
+
bot: e.bot,
|
|
138
|
+
contact: groupContact,
|
|
139
|
+
groupId,
|
|
140
|
+
folderId,
|
|
141
|
+
maxFiles,
|
|
142
|
+
withUrl,
|
|
143
|
+
urlConcurrency,
|
|
144
|
+
format
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
logger.error(error);
|
|
148
|
+
await e.reply(`\u5BFC\u51FA\u5931\u8D25\uFF1A${formatErrorMessage(error)}`);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
const { limitedList, exportPath, exportName, urlErrors } = result;
|
|
152
|
+
const errorsByFileId = /* @__PURE__ */ new Map();
|
|
153
|
+
for (const err of urlErrors) {
|
|
154
|
+
if (!err.fileId) continue;
|
|
155
|
+
if (!errorsByFileId.has(err.fileId)) errorsByFileId.set(err.fileId, err.message);
|
|
156
|
+
}
|
|
157
|
+
await e.reply([
|
|
158
|
+
"\u5BFC\u51FA\u5B8C\u6210\uFF1A",
|
|
159
|
+
`- \u603B\u6570\uFF1A${limitedList.length}`,
|
|
160
|
+
`- URL\u83B7\u53D6\u5931\u8D25\uFF1A${urlErrors.length}`,
|
|
161
|
+
`- \u6587\u4EF6\uFF1A${exportPath}`
|
|
162
|
+
].join("\n"));
|
|
163
|
+
const compactError = (message) => message.replace(/\s+/g, " ").slice(0, 120);
|
|
164
|
+
const preview = limitedList.slice(0, 20);
|
|
165
|
+
const lines = preview.map((item, index) => {
|
|
166
|
+
if (urlOnly) return `${index + 1}. ${item.url ?? ""}`.trim();
|
|
167
|
+
const errMsg = errorsByFileId.get(item.fileId);
|
|
168
|
+
if (item.url) return `${index + 1}. ${item.path}
|
|
169
|
+
${item.url}`;
|
|
170
|
+
return `${index + 1}. ${item.path}
|
|
171
|
+
(\u83B7\u53D6URL\u5931\u8D25) fileId=${item.fileId}${errMsg ? `
|
|
172
|
+
\u539F\u56E0\uFF1A${compactError(errMsg)}` : ""}`;
|
|
173
|
+
});
|
|
174
|
+
const chunks = [];
|
|
175
|
+
const maxChunkLen = 1500;
|
|
176
|
+
let buf = "";
|
|
177
|
+
for (const line of lines) {
|
|
178
|
+
const next = buf ? `${buf}
|
|
179
|
+
|
|
180
|
+
${line}` : line;
|
|
181
|
+
if (next.length > maxChunkLen) {
|
|
182
|
+
if (buf) chunks.push(buf);
|
|
183
|
+
buf = line;
|
|
184
|
+
} else {
|
|
185
|
+
buf = next;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (buf) chunks.push(buf);
|
|
189
|
+
const maxMessages = 10;
|
|
190
|
+
for (const chunk of chunks.slice(0, maxMessages)) {
|
|
191
|
+
await e.reply(chunk);
|
|
192
|
+
}
|
|
193
|
+
if (limitedList.length > preview.length) {
|
|
194
|
+
await e.reply(`\uFF08\u5DF2\u7701\u7565 ${limitedList.length - preview.length} \u6761\uFF0C\u53EF\u4F7F\u7528 --max \u8C03\u6574\uFF09`);
|
|
195
|
+
} else if (chunks.length > maxMessages) {
|
|
196
|
+
await e.reply("\uFF08\u6D88\u606F\u8FC7\u957F\uFF0C\u5DF2\u7701\u7565\u540E\u7EED\u5185\u5BB9\uFF1B\u53EF\u4F7F\u7528 --max \u51CF\u5C11\u6761\u6570\uFF09");
|
|
197
|
+
}
|
|
198
|
+
if (sendFile && typeof e.bot?.uploadFile === "function") {
|
|
199
|
+
const candidates = buildUploadFileCandidates(exportPath);
|
|
200
|
+
for (const fileParam of candidates) {
|
|
201
|
+
try {
|
|
202
|
+
await e.bot.uploadFile(e.contact, fileParam, exportName);
|
|
203
|
+
break;
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}, {
|
|
210
|
+
priority: 9999,
|
|
211
|
+
log: true,
|
|
212
|
+
name: "\u5BFC\u51FA\u7FA4\u6587\u4EF6",
|
|
213
|
+
permission: "all"
|
|
214
|
+
});
|
|
215
|
+
var syncHelpText = [
|
|
216
|
+
"\u7FA4\u6587\u4EF6\u540C\u6B65\u5230 OpenList \u7528\u6CD5\uFF1A",
|
|
217
|
+
"- \u79C1\u804A\uFF1A#\u540C\u6B65\u7FA4\u6587\u4EF6 <\u7FA4\u53F7> [\u53C2\u6570]",
|
|
218
|
+
"- \u6CE8\u610F\uFF1A\u9ED8\u8BA4\u4EC5\u79C1\u804A\u54CD\u5E94\uFF08\u7FA4\u804A\u4E0D\u4F1A\u89E6\u53D1\u8BE5\u6307\u4EE4\uFF09",
|
|
219
|
+
"- \u793A\u4F8B\uFF1A#\u540C\u6B65\u7FA4\u6587\u4EF6 123456",
|
|
220
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --to /\u76EE\u6807\u76EE\u5F55\uFF1A\u4E0A\u4F20\u5230\u6307\u5B9A\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E openlistTargetDir\uFF09",
|
|
221
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --flat\uFF1A\u4E0D\u4FDD\u7559\u7FA4\u6587\u4EF6\u5939\u7ED3\u6784\uFF0C\u5168\u90E8\u5E73\u94FA\u5230\u76EE\u6807\u76EE\u5F55",
|
|
222
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --keep\uFF1A\u5F3A\u5236\u4FDD\u7559\u76EE\u5F55\u7ED3\u6784\uFF08\u8986\u76D6\u7FA4\u914D\u7F6E flat\uFF09",
|
|
223
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --folder <id>\uFF1A\u4ECE\u6307\u5B9A\u6587\u4EF6\u5939\u5F00\u59CB",
|
|
224
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --max <n>\uFF1A\u6700\u591A\u5904\u7406 n \u4E2A\u6587\u4EF6",
|
|
225
|
+
"- #\u540C\u6B65\u7FA4\u6587\u4EF6 123456 --concurrency <n>\uFF1A\u5E76\u53D1\u6570\uFF08\u4F1A\u8986\u76D6\u7FA4\u914D\u7F6E\u7684\u5E76\u53D1\uFF09",
|
|
226
|
+
`- \u56FA\u5B9A\u7B56\u7565\uFF1Amode=${FIXED_SYNC_MODE} \u5355\u6587\u4EF6\u8D85\u65F6=${MAX_FILE_TIMEOUT_SEC}s\uFF08\u4E0D\u518D\u901A\u8FC7\u547D\u4EE4\u914D\u7F6E\uFF09`,
|
|
227
|
+
"\u524D\u7F6E\uFF1A\u8BF7\u5148\u5728\u914D\u7F6E\u6587\u4EF6\u586B\u5199 openlistBaseUrl/openlistUsername/openlistPassword\u3002"
|
|
228
|
+
].join("\n");
|
|
229
|
+
var getGroupSyncTarget = (cfg, groupId) => {
|
|
230
|
+
const list = cfg?.groupSyncTargets;
|
|
231
|
+
if (!Array.isArray(list)) return void 0;
|
|
232
|
+
return list.find((it) => String(it?.groupId) === String(groupId));
|
|
233
|
+
};
|
|
234
|
+
var syncGroupFilesToOpenList = karin.command(/^#?(同步群文件|群文件同步)(.*)$/i, async (e) => {
|
|
235
|
+
if (!e.isPrivate) return false;
|
|
236
|
+
const argsText = e.msg.replace(/^#?(同步群文件|群文件同步)/i, "");
|
|
237
|
+
const {
|
|
238
|
+
groupId: parsedGroupId,
|
|
239
|
+
folderId: parsedFolderId,
|
|
240
|
+
maxFiles: parsedMaxFiles,
|
|
241
|
+
concurrency,
|
|
242
|
+
concurrencySpecified,
|
|
243
|
+
flat,
|
|
244
|
+
flatSpecified,
|
|
245
|
+
to,
|
|
246
|
+
toSpecified,
|
|
247
|
+
help
|
|
248
|
+
} = parseSyncArgs(argsText);
|
|
249
|
+
if (help) {
|
|
250
|
+
await e.reply(syncHelpText);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
const cfg = config();
|
|
254
|
+
const groupId = parsedGroupId ?? (e.isGroup ? e.groupId : void 0);
|
|
255
|
+
if (!groupId) {
|
|
256
|
+
await e.reply(`\u7F3A\u5C11\u7FA4\u53F7\u53C2\u6570
|
|
257
|
+
|
|
258
|
+
${syncHelpText}`);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
const defaults = cfg.groupSyncDefaults ?? {};
|
|
262
|
+
const targetCfg = getGroupSyncTarget(cfg, groupId);
|
|
263
|
+
const mode = FIXED_SYNC_MODE;
|
|
264
|
+
const urlC = concurrencySpecified ? typeof concurrency === "number" ? concurrency : 3 : typeof targetCfg?.urlConcurrency === "number" ? targetCfg.urlConcurrency : typeof defaults?.urlConcurrency === "number" ? defaults.urlConcurrency : 3;
|
|
265
|
+
const transferC = concurrencySpecified ? typeof concurrency === "number" ? concurrency : 3 : typeof targetCfg?.transferConcurrency === "number" ? targetCfg.transferConcurrency : typeof defaults?.transferConcurrency === "number" ? defaults.transferConcurrency : 3;
|
|
266
|
+
const retryTimes = typeof targetCfg?.retryTimes === "number" ? targetCfg.retryTimes : typeof defaults?.retryTimes === "number" ? defaults.retryTimes : 2;
|
|
267
|
+
const retryDelayMs = typeof targetCfg?.retryDelayMs === "number" ? targetCfg.retryDelayMs : typeof defaults?.retryDelayMs === "number" ? defaults.retryDelayMs : 1500;
|
|
268
|
+
const progressEvery = typeof targetCfg?.progressReportEvery === "number" ? targetCfg.progressReportEvery : typeof defaults?.progressReportEvery === "number" ? defaults.progressReportEvery : DEFAULT_PROGRESS_REPORT_EVERY;
|
|
269
|
+
const downloadLimitKbps = typeof targetCfg?.downloadLimitKbps === "number" ? targetCfg.downloadLimitKbps : typeof defaults?.downloadLimitKbps === "number" ? defaults.downloadLimitKbps : 0;
|
|
270
|
+
const uploadLimitKbps = typeof targetCfg?.uploadLimitKbps === "number" ? targetCfg.uploadLimitKbps : typeof defaults?.uploadLimitKbps === "number" ? defaults.uploadLimitKbps : 0;
|
|
271
|
+
const targetDir = normalizePosixPath(
|
|
272
|
+
toSpecified ? to ?? "" : String(targetCfg?.targetDir ?? "").trim() || path.posix.join(String(cfg.openlistTargetDir ?? "/"), String(groupId))
|
|
273
|
+
);
|
|
274
|
+
const finalFlat = flatSpecified ? Boolean(flat) : typeof targetCfg?.flat === "boolean" ? targetCfg.flat : Boolean(defaults?.flat ?? false);
|
|
275
|
+
const folderId = parsedFolderId ?? targetCfg?.sourceFolderId;
|
|
276
|
+
const maxFiles = typeof parsedMaxFiles === "number" ? parsedMaxFiles : targetCfg?.maxFiles;
|
|
277
|
+
try {
|
|
278
|
+
await syncGroupFilesToOpenListCore({
|
|
279
|
+
bot: e.bot,
|
|
280
|
+
groupId,
|
|
281
|
+
folderId,
|
|
282
|
+
maxFiles,
|
|
283
|
+
flat: Boolean(finalFlat),
|
|
284
|
+
targetDir,
|
|
285
|
+
mode,
|
|
286
|
+
urlConcurrency: Math.max(1, Math.floor(urlC) || 1),
|
|
287
|
+
transferConcurrency: Math.max(1, Math.floor(transferC) || 1),
|
|
288
|
+
fileTimeoutSec: MAX_FILE_TIMEOUT_SEC,
|
|
289
|
+
retryTimes: Math.max(0, Math.floor(retryTimes) || 0),
|
|
290
|
+
retryDelayMs: Math.max(0, Math.floor(retryDelayMs) || 0),
|
|
291
|
+
progressReportEvery: Math.max(0, Math.floor(progressEvery) || 0),
|
|
292
|
+
downloadLimitKbps: Math.max(0, Math.floor(downloadLimitKbps) || 0),
|
|
293
|
+
uploadLimitKbps: Math.max(0, Math.floor(uploadLimitKbps) || 0),
|
|
294
|
+
report: (msg) => e.reply(msg)
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
logger.error(error);
|
|
298
|
+
await e.reply(formatErrorMessage(error));
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
}, {
|
|
303
|
+
priority: 9999,
|
|
304
|
+
log: true,
|
|
305
|
+
name: "\u540C\u6B65\u7FA4\u6587\u4EF6\u5230OpenList",
|
|
306
|
+
permission: "all"
|
|
307
|
+
});
|
|
10
308
|
export {
|
|
11
|
-
backupOpenListToOpenList,
|
|
12
309
|
exportGroupFiles,
|
|
13
|
-
|
|
14
|
-
syncGroupFilesToOpenList,
|
|
15
|
-
syncGroupFilesToOpenListCore
|
|
310
|
+
syncGroupFilesToOpenList
|
|
16
311
|
};
|