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.
@@ -1,417 +1,24 @@
1
1
  import {
2
- config
3
- } from "../chunk-5WVKHIPK.js";
4
- import {
5
- dir
6
- } from "../chunk-IZS467MR.js";
2
+ handleGroupSyncConfigCommand
3
+ } from "../chunk-KFOQIZ6H.js";
4
+ import "../chunk-BU2GD6GJ.js";
5
+ import "../chunk-WJNM5RHT.js";
6
+ import "../chunk-N5HMQFRM.js";
7
+ import "../chunk-DA4U55JC.js";
8
+ import "../chunk-IZS467MR.js";
7
9
 
8
10
  // src/apps/groupSyncConfig.ts
9
- import fs from "fs";
10
- import path from "path";
11
- import { karin, logger } from "node-karin";
12
- var readJsonSafe = (filePath) => {
13
- try {
14
- if (!fs.existsSync(filePath)) return {};
15
- const raw = fs.readFileSync(filePath, "utf8");
16
- return raw ? JSON.parse(raw) : {};
17
- } catch (error) {
18
- logger.error(error);
19
- return {};
20
- }
21
- };
22
- var writeJsonSafe = (filePath, data) => {
23
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
24
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
25
- };
26
- var getConfigFilePath = () => path.join(dir.ConfigDir, "config.json");
27
- var getDefaultConfigPath = () => path.join(dir.defConfigDir, "config.json");
28
- var normalizePosixPath = (inputPath, { ensureLeadingSlash = true, stripTrailingSlash = true } = {}) => {
29
- let value = String(inputPath ?? "").trim().replaceAll("\\", "/");
30
- value = value.replace(/\/+/g, "/");
31
- if (!value) value = "/";
32
- if (ensureLeadingSlash && !value.startsWith("/")) value = `/${value}`;
33
- if (stripTrailingSlash && value.length > 1) value = value.replace(/\/+$/, "");
34
- return value;
35
- };
36
- var parseBoolean = (value) => {
37
- const v = String(value ?? "").trim().toLowerCase();
38
- if (!v) return void 0;
39
- if (["1", "true", "yes", "y", "on", "\u5F00\u542F", "\u5F00", "\u662F"].includes(v)) return true;
40
- if (["0", "false", "no", "n", "off", "\u5173\u95ED", "\u5173", "\u5426"].includes(v)) return false;
41
- return void 0;
42
- };
43
- var parseIntSafe = (value) => {
44
- if (value == null) return void 0;
45
- const n = Number(value);
46
- if (!Number.isFinite(n)) return void 0;
47
- return Math.floor(n);
48
- };
49
- var parseMode = (value) => {
50
- const v = String(value ?? "").trim().toLowerCase();
51
- if (!v) return void 0;
52
- if (v === "full" || v === "\u5168\u91CF") return "full";
53
- if (v === "incremental" || v === "\u589E\u91CF") return "incremental";
54
- return void 0;
55
- };
56
- var formatTargetLine = (t) => {
57
- const mode = t.mode ?? "-";
58
- const enabled = t.enabled ?? true ? "\u542F\u7528" : "\u505C\u7528";
59
- const cron = t.schedule?.enabled ? t.schedule?.cron || "(\u672A\u586Bcron)" : "\u5173\u95ED";
60
- const targetDir = t.targetDir ? t.targetDir : "(\u9ED8\u8BA4)";
61
- const uploadBackup = t.uploadBackup ?? false ? "on" : "off";
62
- return `- ${t.groupId} | ${enabled} | ${mode} | uploadBackup:${uploadBackup} | \u8BA1\u5212:${cron} | \u76EE\u5F55:${targetDir}`;
63
- };
64
- var upsertTarget = (cfg, groupId, patch) => {
65
- const list = getTargets(cfg);
66
- const next = [...list];
67
- const index = next.findIndex((it) => String(it.groupId) === String(groupId));
68
- const base = index >= 0 ? next[index] : ensureTargetWithDefaults(groupId, cfg);
69
- const merged = {
70
- ...base,
71
- ...patch,
72
- groupId: String(groupId)
73
- };
74
- if (index >= 0) next[index] = merged;
75
- else next.push(merged);
76
- return next;
77
- };
78
- var ensureTargetWithDefaults = (groupId, cfg) => {
79
- const defaults = cfg.groupSyncDefaults ?? {};
80
- const baseTargetDir = normalizePosixPath(String(cfg.openlistTargetDir ?? "/"));
81
- return {
82
- groupId,
83
- enabled: true,
84
- mode: defaults.mode === "full" ? "full" : "incremental",
85
- flat: Boolean(defaults.flat ?? false),
86
- urlConcurrency: parseIntSafe(String(defaults.urlConcurrency ?? "")) ?? 3,
87
- transferConcurrency: parseIntSafe(String(defaults.transferConcurrency ?? "")) ?? 3,
88
- fileTimeoutSec: parseIntSafe(String(defaults.fileTimeoutSec ?? "")) ?? 600,
89
- retryTimes: parseIntSafe(String(defaults.retryTimes ?? "")) ?? 2,
90
- retryDelayMs: parseIntSafe(String(defaults.retryDelayMs ?? "")) ?? 1500,
91
- targetDir: normalizePosixPath(path.posix.join(baseTargetDir, String(groupId))),
92
- schedule: { enabled: false, cron: "" }
93
- };
94
- };
95
- var updateConfigFile = (updater) => {
96
- const configPath = getConfigFilePath();
97
- const current = readJsonSafe(configPath);
98
- const def = readJsonSafe(getDefaultConfigPath());
99
- const merged = { ...def, ...current };
100
- const next = updater({ ...merged });
101
- writeJsonSafe(configPath, next);
102
- return next;
103
- };
104
- var getTargets = (cfg) => {
105
- const list = cfg?.groupSyncTargets;
106
- return Array.isArray(list) ? list : [];
107
- };
108
- var cfgHelp = [
109
- "\u7FA4\u540C\u6B65\u914D\u7F6E\u7528\u6CD5\uFF08\u5EFA\u8BAE\u5728 WebUI \u914D\u7F6E\u66F4\u5B8C\u6574\uFF09\uFF1A",
110
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E \u5217\u8868",
111
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u67E5\u770B",
112
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u6DFB\u52A0",
113
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u5220\u9664",
114
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u542F\u7528 / \u505C\u7528",
115
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u6A21\u5F0F \u5168\u91CF|\u589E\u91CF",
116
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u76EE\u5F55 /\u6302\u8F7D/QQ\u7FA4\u6587\u4EF6/123456",
117
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u5E73\u94FA \u5F00|\u5173",
118
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> uploadBackup on|off\uFF08\u76D1\u542C\u7FA4\u6587\u4EF6\u4E0A\u4F20\u81EA\u52A8\u5907\u4EFD\uFF09",
119
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u5E76\u53D1 <n>\uFF08\u4E0B\u8F7D/\u4E0A\u4F20\u5E76\u53D1\uFF09",
120
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> url\u5E76\u53D1 <n>\uFF08\u89E3\u6790URL\u5E76\u53D1\uFF09",
121
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u8D85\u65F6 <sec>\uFF08\u5355\u6587\u4EF6\u8D85\u65F6\uFF09",
122
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u91CD\u8BD5 <n>",
123
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u65F6\u6BB5 00:00-06:00,23:00-23:59\uFF08\u7A7A=\u4E0D\u9650\u5236\uFF09",
124
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u8BA1\u5212 <cron>\uFF08\u4F8B\uFF1A0 0 3 * * *\uFF09",
125
- "- #\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u8BA1\u5212 \u5F00\u542F|\u5173\u95ED"
126
- ].join("\n");
127
- var groupSyncConfig = karin.command(/^#?(群同步配置|同步群配置|群文件同步配置)(.*)$/i, async (e) => {
128
- if (!e.isPrivate) return false;
129
- const raw = e.msg.replace(/^#?(群同步配置|同步群配置|群文件同步配置)/i, "").trim();
130
- if (!raw || /^(help|帮助|\?)$/i.test(raw)) {
131
- await e.reply(cfgHelp);
132
- return true;
133
- }
134
- const tokens = raw.split(/\s+/).filter(Boolean);
135
- const first = tokens[0]?.toLowerCase();
136
- const isList = ["\u5217\u8868", "list", "ls"].includes(first);
137
- if (isList) {
138
- const cfg = config();
139
- const targets = getTargets(cfg);
140
- if (!targets.length) {
141
- await e.reply("\u5F53\u524D\u6CA1\u6709\u914D\u7F6E\u4EFB\u4F55\u540C\u6B65\u76EE\u6807\u7FA4\u3002\u53EF\u5728 WebUI \u6216\u7528\uFF1A#\u7FA4\u540C\u6B65\u914D\u7F6E <\u7FA4\u53F7> \u6DFB\u52A0");
142
- return true;
143
- }
144
- const lines = targets.map(formatTargetLine);
145
- await e.reply(["\u540C\u6B65\u76EE\u6807\u7FA4\u5217\u8868\uFF1A", ...lines].join("\n"));
146
- return true;
147
- }
148
- const actionWords = /* @__PURE__ */ new Map([
149
- ["\u67E5\u770B", "show"],
150
- ["\u8BE6\u60C5", "show"],
151
- ["show", "show"],
152
- ["get", "show"],
153
- ["\u6DFB\u52A0", "add"],
154
- ["\u65B0\u589E", "add"],
155
- ["add", "add"],
156
- ["create", "add"],
157
- ["\u5220\u9664", "remove"],
158
- ["\u79FB\u9664", "remove"],
159
- ["remove", "remove"],
160
- ["del", "remove"],
161
- ["delete", "remove"],
162
- ["\u542F\u7528", "enable"],
163
- ["\u5F00\u542F", "enable"],
164
- ["enable", "enable"],
165
- ["on", "enable"],
166
- ["\u505C\u7528", "disable"],
167
- ["\u5173\u95ED", "disable"],
168
- ["disable", "disable"],
169
- ["off", "disable"],
170
- ["\u6A21\u5F0F", "mode"],
171
- ["\u7B56\u7565", "mode"],
172
- ["mode", "mode"],
173
- ["\u76EE\u5F55", "dir"],
174
- ["\u76EE\u6807\u76EE\u5F55", "dir"],
175
- ["dir", "dir"],
176
- ["to", "dir"],
177
- ["\u5E73\u94FA", "flat"],
178
- ["flat", "flat"],
179
- ["\u4E0A\u4F20\u5907\u4EFD", "uploadBackup"],
180
- ["uploadBackup", "uploadBackup"],
181
- ["uploadbackup", "uploadBackup"],
182
- ["\u5E76\u53D1", "concurrency"],
183
- ["\u7EBF\u7A0B", "concurrency"],
184
- ["concurrency", "concurrency"],
185
- ["threads", "concurrency"],
186
- ["url\u5E76\u53D1", "urlConcurrency"],
187
- ["url\u7EBF\u7A0B", "urlConcurrency"],
188
- ["urlconcurrency", "urlConcurrency"],
189
- ["\u8D85\u65F6", "timeout"],
190
- ["timeout", "timeout"],
191
- ["\u91CD\u8BD5", "retry"],
192
- ["retry", "retry"],
193
- ["\u65F6\u6BB5", "timeWindows"],
194
- ["\u65F6\u95F4\u7A97", "timeWindows"],
195
- ["time", "timeWindows"],
196
- ["window", "timeWindows"],
197
- ["\u8BA1\u5212", "schedule"],
198
- ["\u5B9A\u65F6", "schedule"],
199
- ["cron", "schedule"],
200
- ["schedule", "schedule"]
201
- ]);
202
- let actionToken;
203
- let groupId;
204
- let rest = [];
205
- if (/^\d+$/.test(tokens[0] ?? "")) {
206
- groupId = tokens[0];
207
- actionToken = tokens[1];
208
- rest = tokens.slice(2);
209
- } else if (actionWords.has(tokens[0] ?? "") && /^\d+$/.test(tokens[1] ?? "")) {
210
- actionToken = tokens[0];
211
- groupId = tokens[1];
212
- rest = tokens.slice(2);
213
- } else if (e.isGroup && actionWords.has(tokens[0] ?? "")) {
214
- groupId = e.groupId;
215
- actionToken = tokens[0];
216
- rest = tokens.slice(1);
217
- } else if (e.isGroup && /^\d+$/.test(tokens[0] ?? "") === false) {
218
- groupId = e.groupId;
219
- actionToken = tokens[0];
220
- rest = tokens.slice(1);
221
- }
222
- if (!groupId || !/^\d+$/.test(groupId)) {
223
- await e.reply(`\u7F3A\u5C11\u7FA4\u53F7\u53C2\u6570\u3002
224
-
225
- ${cfgHelp}`);
226
- return true;
227
- }
228
- const action = actionWords.get(actionToken ?? "") ?? "";
229
- if (!action) {
230
- await e.reply(`\u672A\u77E5\u64CD\u4F5C\uFF1A${actionToken ?? ""}
231
-
232
- ${cfgHelp}`);
233
- return true;
234
- }
235
- if (action === "show") {
236
- const cfg = config();
237
- const t = getTargets(cfg).find((it) => String(it.groupId) === String(groupId));
238
- if (!t) {
239
- await e.reply(`\u672A\u627E\u5230\u7FA4 ${groupId} \u7684\u914D\u7F6E\uFF0C\u53EF\u5148\u6DFB\u52A0\uFF1A#\u7FA4\u540C\u6B65\u914D\u7F6E ${groupId} \u6DFB\u52A0`);
240
- return true;
241
- }
242
- await e.reply(["\u7FA4\u540C\u6B65\u914D\u7F6E\uFF1A", formatTargetLine(t), `
243
- \u5B8C\u6574\u914D\u7F6E\u6587\u4EF6\uFF1A${getConfigFilePath()}`].join("\n"));
244
- return true;
245
- }
246
- if (action === "add") {
247
- const cfg = config();
248
- const defaultsTarget = ensureTargetWithDefaults(groupId, cfg);
249
- updateConfigFile((next) => {
250
- const list = getTargets(next);
251
- if (list.some((it) => String(it.groupId) === String(groupId))) return next;
252
- next.groupSyncTargets = [...list, defaultsTarget];
253
- return next;
254
- });
255
- await e.reply(`\u5DF2\u6DFB\u52A0\u7FA4 ${groupId} \u7684\u540C\u6B65\u914D\u7F6E\uFF08\u9ED8\u8BA4\u542F\u7528\uFF09\u3002`);
256
- return true;
257
- }
258
- if (action === "remove") {
259
- const before = config();
260
- const beforeList = getTargets(before);
261
- if (!beforeList.some((it) => String(it.groupId) === String(groupId))) {
262
- await e.reply(`\u7FA4 ${groupId} \u672A\u914D\u7F6E\uFF0C\u65E0\u9700\u5220\u9664\u3002`);
263
- return true;
264
- }
265
- updateConfigFile((next) => {
266
- next.groupSyncTargets = getTargets(next).filter((it) => String(it.groupId) !== String(groupId));
267
- return next;
268
- });
269
- await e.reply(`\u5DF2\u5220\u9664\u7FA4 ${groupId} \u7684\u540C\u6B65\u914D\u7F6E\u3002`);
270
- return true;
271
- }
272
- if (action === "enable" || action === "disable") {
273
- const enabled = action === "enable";
274
- updateConfigFile((next) => {
275
- next.groupSyncTargets = upsertTarget(next, groupId, { enabled });
276
- return next;
277
- });
278
- await e.reply(`\u7FA4 ${groupId} \u5DF2${enabled ? "\u542F\u7528" : "\u505C\u7528"}\u81EA\u52A8\u540C\u6B65\u3002`);
279
- return true;
280
- }
281
- if (action === "mode") {
282
- const mode = parseMode(rest[0]);
283
- if (!mode) {
284
- await e.reply("\u6A21\u5F0F\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u4F7F\u7528\uFF1A\u5168\u91CF/\u589E\u91CF \u6216 full/incremental");
285
- return true;
286
- }
287
- updateConfigFile((next) => {
288
- next.groupSyncTargets = upsertTarget(next, groupId, { mode });
289
- return next;
290
- });
291
- await e.reply(`\u7FA4 ${groupId} \u540C\u6B65\u6A21\u5F0F\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${mode === "full" ? "\u5168\u91CF" : "\u589E\u91CF"}`);
292
- return true;
293
- }
294
- if (action === "dir") {
295
- const dirValue = normalizePosixPath(rest.join(" ").trim() || "");
296
- if (!dirValue || dirValue === "/") {
297
- await e.reply("\u76EE\u5F55\u53C2\u6570\u4E3A\u7A7A\u6216\u975E\u6CD5\u3002");
298
- return true;
299
- }
300
- updateConfigFile((next) => {
301
- next.groupSyncTargets = upsertTarget(next, groupId, { targetDir: dirValue });
302
- return next;
303
- });
304
- await e.reply(`\u7FA4 ${groupId} \u76EE\u6807\u76EE\u5F55\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${dirValue}`);
305
- return true;
306
- }
307
- if (action === "flat") {
308
- const bool = parseBoolean(rest[0]);
309
- if (typeof bool === "undefined") {
310
- await e.reply("\u5E73\u94FA\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u4F7F\u7528\uFF1A\u5F00/\u5173 \u6216 true/false");
311
- return true;
312
- }
313
- updateConfigFile((next) => {
314
- next.groupSyncTargets = upsertTarget(next, groupId, { flat: bool });
315
- return next;
316
- });
317
- await e.reply(`\u7FA4 ${groupId} \u5E73\u94FA\u4E0A\u4F20\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${bool ? "\u5F00" : "\u5173"}`);
318
- return true;
319
- }
320
- if (action === "uploadBackup") {
321
- const bool = parseBoolean(rest[0]);
322
- if (typeof bool === "undefined") {
323
- await e.reply("uploadBackup \u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u4F7F\u7528\uFF1Aon/off \u6216 \u5F00/\u5173 \u6216 true/false");
324
- return true;
325
- }
326
- updateConfigFile((next) => {
327
- next.groupSyncTargets = upsertTarget(next, groupId, { uploadBackup: bool });
328
- return next;
329
- });
330
- await e.reply(`\u7FA4 ${groupId} uploadBackup \u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${bool ? "on" : "off"}`);
331
- return true;
332
- }
333
- if (action === "concurrency" || action === "urlConcurrency") {
334
- const value = parseIntSafe(rest[0]);
335
- if (!value || value <= 0) {
336
- await e.reply("\u5E76\u53D1\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u8F93\u5165\u5927\u4E8E0\u7684\u6574\u6570\u3002");
337
- return true;
338
- }
339
- const patch = action === "concurrency" ? { transferConcurrency: value } : { urlConcurrency: value };
340
- updateConfigFile((next) => {
341
- next.groupSyncTargets = upsertTarget(next, groupId, patch);
342
- return next;
343
- });
344
- await e.reply(`\u7FA4 ${groupId} \u5DF2\u8BBE\u7F6E${action === "concurrency" ? "\u4E0B\u8F7D/\u4E0A\u4F20" : "\u89E3\u6790URL"}\u5E76\u53D1\uFF1A${value}`);
345
- return true;
346
- }
347
- if (action === "timeout") {
348
- const sec = parseIntSafe(rest[0]);
349
- if (!sec || sec <= 0) {
350
- await e.reply("\u8D85\u65F6\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u8F93\u5165\u5927\u4E8E0\u7684\u6574\u6570\uFF08\u79D2\uFF09\u3002");
351
- return true;
352
- }
353
- const capped = Math.min(3e3, sec);
354
- updateConfigFile((next) => {
355
- next.groupSyncTargets = upsertTarget(next, groupId, { fileTimeoutSec: capped });
356
- return next;
357
- });
358
- await e.reply(`\u7FA4 ${groupId} \u5355\u6587\u4EF6\u8D85\u65F6\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${capped}s${sec !== capped ? "\uFF08\u5DF2\u6309\u4E0A\u96503000s\u622A\u65AD\uFF09" : ""}`);
359
- return true;
360
- }
361
- if (action === "retry") {
362
- const n = parseIntSafe(rest[0]);
363
- if (typeof n === "undefined" || n < 0) {
364
- await e.reply("\u91CD\u8BD5\u6B21\u6570\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u8F93\u5165 >=0 \u7684\u6574\u6570\u3002");
365
- return true;
366
- }
367
- updateConfigFile((next) => {
368
- next.groupSyncTargets = upsertTarget(next, groupId, { retryTimes: n });
369
- return next;
370
- });
371
- await e.reply(`\u7FA4 ${groupId} \u91CD\u8BD5\u6B21\u6570\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${n}`);
372
- return true;
373
- }
374
- if (action === "timeWindows") {
375
- const win = rest.join(" ").trim();
376
- updateConfigFile((next) => {
377
- next.groupSyncTargets = upsertTarget(next, groupId, { timeWindows: win });
378
- return next;
379
- });
380
- await e.reply(`\u7FA4 ${groupId} \u540C\u6B65\u65F6\u6BB5\u5DF2\u8BBE\u7F6E\u4E3A\uFF1A${win || "(\u4E0D\u9650\u5236)"}`);
381
- return true;
382
- }
383
- if (action === "schedule") {
384
- const value = rest.join(" ").trim();
385
- const bool = parseBoolean(rest[0]);
386
- const cron = bool == null ? value : "";
387
- if (!value) {
388
- await e.reply("\u8BA1\u5212\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A\u3002");
389
- return true;
390
- }
391
- updateConfigFile((next) => {
392
- const existing = getTargets(next).find((it) => String(it.groupId) === String(groupId));
393
- const base = existing ?? ensureTargetWithDefaults(groupId, next);
394
- const schedule = { ...base.schedule ?? {} };
395
- if (typeof bool === "boolean") {
396
- schedule.enabled = bool;
397
- } else {
398
- schedule.cron = cron;
399
- schedule.enabled = true;
400
- }
401
- next.groupSyncTargets = upsertTarget(next, groupId, { schedule });
402
- return next;
403
- });
404
- await e.reply(`\u7FA4 ${groupId} \u5B9A\u65F6\u8BA1\u5212\u5DF2\u66F4\u65B0\uFF1A${value}`);
405
- return true;
406
- }
407
- await e.reply(cfgHelp);
408
- return true;
409
- }, {
410
- name: "\u7FA4\u540C\u6B65\u914D\u7F6E",
411
- log: true,
412
- priority: 9999,
413
- permission: "master"
414
- });
11
+ import { karin } from "node-karin";
12
+ var groupSyncConfig = karin.command(
13
+ /^#?(群同步配置|同步群配置|群文件同步配置)(.*)$/i,
14
+ handleGroupSyncConfigCommand,
15
+ {
16
+ name: "\u7FA4\u540C\u6B65\u914D\u7F6E",
17
+ log: true,
18
+ priority: 9999,
19
+ permission: "master"
20
+ }
21
+ );
415
22
  export {
416
23
  groupSyncConfig
417
24
  };
@@ -1,188 +0,0 @@
1
- import {
2
- syncGroupFilesToOpenListCore
3
- } from "../chunk-AWSGUQPA.js";
4
- import {
5
- config
6
- } from "../chunk-5WVKHIPK.js";
7
- import "../chunk-IZS467MR.js";
8
-
9
- // src/apps/groupSyncScheduler.ts
10
- import path from "path";
11
- import { getAllBot, karin, logger } from "node-karin";
12
- var normalizeMode = (value, fallback) => {
13
- const v = String(value ?? "").trim().toLowerCase();
14
- if (v === "full" || v === "\u5168\u91CF") return "full";
15
- if (v === "incremental" || v === "\u589E\u91CF" || v === "inc") return "incremental";
16
- return fallback;
17
- };
18
- var parseCronField = (field, min, max) => {
19
- const trimmed = String(field ?? "").trim();
20
- if (!trimmed || trimmed === "*" || trimmed === "?") return null;
21
- const allowed = /* @__PURE__ */ new Set();
22
- const parts = trimmed.split(",").map((p) => p.trim()).filter(Boolean);
23
- for (const part of parts) {
24
- const [rangePart, stepPart] = part.split("/");
25
- const step = stepPart ? Number(stepPart) : 1;
26
- if (!Number.isFinite(step) || step <= 0) return null;
27
- const applyRange = (start, end) => {
28
- const s = Math.max(min, start);
29
- const e = Math.min(max, end);
30
- for (let v = s; v <= e; v += step) allowed.add(v);
31
- };
32
- if (rangePart === "*" || rangePart === "?") {
33
- applyRange(min, max);
34
- continue;
35
- }
36
- const rangeMatch = rangePart.match(/^(\d+)-(\d+)$/);
37
- if (rangeMatch) {
38
- applyRange(Number(rangeMatch[1]), Number(rangeMatch[2]));
39
- continue;
40
- }
41
- const single = Number(rangePart);
42
- if (Number.isFinite(single)) {
43
- applyRange(single, single);
44
- }
45
- }
46
- return allowed;
47
- };
48
- var cronMatches = (expr, date) => {
49
- const parts = String(expr ?? "").trim().split(/\s+/).filter(Boolean);
50
- if (parts.length !== 5 && parts.length !== 6) return false;
51
- const fields = parts.length === 6 ? parts : ["0", ...parts];
52
- const sec = date.getSeconds();
53
- const min = date.getMinutes();
54
- const hour = date.getHours();
55
- const dom = date.getDate();
56
- const month = date.getMonth() + 1;
57
- const dowRaw = date.getDay();
58
- const dow = dowRaw === 0 ? 0 : dowRaw;
59
- const allowedSec = parseCronField(fields[0], 0, 59);
60
- const allowedMin = parseCronField(fields[1], 0, 59);
61
- const allowedHour = parseCronField(fields[2], 0, 23);
62
- const allowedDom = parseCronField(fields[3], 1, 31);
63
- const allowedMonth = parseCronField(fields[4], 1, 12);
64
- const allowedDow = parseCronField(fields[5], 0, 7);
65
- const match = (allowed, value) => !allowed || allowed.has(value);
66
- const dowOk = !allowedDow || allowedDow.has(dow) || dow === 0 && allowedDow.has(7);
67
- return match(allowedSec, sec) && match(allowedMin, min) && match(allowedHour, hour) && match(allowedDom, dom) && match(allowedMonth, month) && dowOk;
68
- };
69
- var parseTimeWindows = (value) => {
70
- const raw = String(value ?? "").trim();
71
- if (!raw) return [];
72
- const items = raw.split(",").map((s) => s.trim()).filter(Boolean);
73
- const windows = [];
74
- for (const item of items) {
75
- const match = item.match(/^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/);
76
- if (!match) continue;
77
- const sh = Number(match[1]);
78
- const sm = Number(match[2]);
79
- const eh = Number(match[3]);
80
- const em = Number(match[4]);
81
- if (![sh, sm, eh, em].every((n) => Number.isFinite(n))) continue;
82
- if (sh < 0 || sh > 23 || eh < 0 || eh > 23) continue;
83
- if (sm < 0 || sm > 59 || em < 0 || em > 59) continue;
84
- const start = sh * 60 + sm;
85
- const end = eh * 60 + em;
86
- if (start === end) continue;
87
- if (end > start) {
88
- windows.push({ start, end });
89
- } else {
90
- windows.push({ start, end: 24 * 60 });
91
- windows.push({ start: 0, end });
92
- }
93
- }
94
- return windows;
95
- };
96
- var isNowAllowed = (timeWindows, now) => {
97
- const windows = parseTimeWindows(String(timeWindows ?? ""));
98
- if (!windows.length) return true;
99
- const minutes = now.getHours() * 60 + now.getMinutes();
100
- return windows.some((w) => minutes >= w.start && minutes < w.end);
101
- };
102
- var lastTriggered = /* @__PURE__ */ new Map();
103
- var runScheduledSync = async (groupId) => {
104
- const cfg = config();
105
- const defaults = cfg.groupSyncDefaults ?? {};
106
- const targets = Array.isArray(cfg.groupSyncTargets) ? cfg.groupSyncTargets : [];
107
- const target = targets.find((it) => String(it?.groupId) === String(groupId));
108
- if (!target) return;
109
- if (target?.enabled === false) return;
110
- if (target?.schedule?.enabled !== true) return;
111
- const mode = normalizeMode(target?.mode, normalizeMode(defaults?.mode, "incremental"));
112
- const targetDir = String(target?.targetDir ?? "").trim() ? String(target.targetDir) : path.posix.join(String(cfg.openlistTargetDir ?? "/"), String(groupId));
113
- const folderId = String(target?.sourceFolderId ?? "").trim() ? String(target.sourceFolderId) : void 0;
114
- const maxFiles = typeof target?.maxFiles === "number" ? target.maxFiles : void 0;
115
- const urlConcurrency = typeof target?.urlConcurrency === "number" ? target.urlConcurrency : typeof defaults?.urlConcurrency === "number" ? defaults.urlConcurrency : 3;
116
- const transferConcurrency = typeof target?.transferConcurrency === "number" ? target.transferConcurrency : typeof defaults?.transferConcurrency === "number" ? defaults.transferConcurrency : 3;
117
- const fileTimeoutSec = typeof target?.fileTimeoutSec === "number" ? target.fileTimeoutSec : typeof defaults?.fileTimeoutSec === "number" ? defaults.fileTimeoutSec : 600;
118
- const retryTimes = typeof target?.retryTimes === "number" ? target.retryTimes : typeof defaults?.retryTimes === "number" ? defaults.retryTimes : 2;
119
- const retryDelayMs = typeof target?.retryDelayMs === "number" ? target.retryDelayMs : typeof defaults?.retryDelayMs === "number" ? defaults.retryDelayMs : 1500;
120
- const progressReportEvery = typeof target?.progressReportEvery === "number" ? target.progressReportEvery : typeof defaults?.progressReportEvery === "number" ? defaults.progressReportEvery : 10;
121
- const downloadLimitKbps = typeof target?.downloadLimitKbps === "number" ? target.downloadLimitKbps : typeof defaults?.downloadLimitKbps === "number" ? defaults.downloadLimitKbps : 0;
122
- const uploadLimitKbps = typeof target?.uploadLimitKbps === "number" ? target.uploadLimitKbps : typeof defaults?.uploadLimitKbps === "number" ? defaults.uploadLimitKbps : 0;
123
- const flat = typeof target?.flat === "boolean" ? target.flat : Boolean(defaults?.flat ?? false);
124
- const bots = getAllBot();
125
- if (!bots.length) {
126
- logger.warn(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u672A\u627E\u5230\u53EF\u7528Bot\uFF0C\u8DF3\u8FC7`);
127
- return;
128
- }
129
- let lastError;
130
- for (const bot of bots) {
131
- try {
132
- await syncGroupFilesToOpenListCore({
133
- bot,
134
- groupId: String(groupId),
135
- folderId,
136
- maxFiles,
137
- flat,
138
- targetDir,
139
- mode,
140
- urlConcurrency: Math.max(1, Math.floor(urlConcurrency) || 1),
141
- transferConcurrency: Math.max(1, Math.floor(transferConcurrency) || 1),
142
- fileTimeoutSec: Math.max(10, Math.floor(fileTimeoutSec) || 10),
143
- retryTimes: Math.max(0, Math.floor(retryTimes) || 0),
144
- retryDelayMs: Math.max(0, Math.floor(retryDelayMs) || 0),
145
- progressReportEvery: Math.max(0, Math.floor(progressReportEvery) || 0),
146
- downloadLimitKbps: Math.max(0, Math.floor(downloadLimitKbps) || 0),
147
- uploadLimitKbps: Math.max(0, Math.floor(uploadLimitKbps) || 0),
148
- report: (msg) => logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] ${msg}`)
149
- });
150
- return;
151
- } catch (error) {
152
- const msg = error instanceof Error ? error.message : String(error);
153
- if (msg.includes("\u6B63\u5728\u8FDB\u884C\u4E2D")) {
154
- logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u540C\u6B65\u4EFB\u52A1\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8DF3\u8FC7`);
155
- return;
156
- }
157
- lastError = error;
158
- continue;
159
- }
160
- }
161
- if (lastError) logger.error(lastError);
162
- };
163
- var groupFileSyncScheduler = karin.task("\u7FA4\u6587\u4EF6\u540C\u6B65\u8C03\u5EA6\u5668", "* * * * * *", async () => {
164
- const cfg = config();
165
- const targets = Array.isArray(cfg.groupSyncTargets) ? cfg.groupSyncTargets : [];
166
- const now = /* @__PURE__ */ new Date();
167
- const stamp = Math.floor(now.getTime() / 1e3);
168
- for (const target of targets) {
169
- const groupId = String(target?.groupId ?? "").trim();
170
- if (!groupId) continue;
171
- if (target?.enabled === false) continue;
172
- if (target?.schedule?.enabled !== true) continue;
173
- const cron = String(target?.schedule?.cron ?? "").trim();
174
- if (!cron) continue;
175
- if (!cronMatches(cron, now)) continue;
176
- const key = `${groupId}:${cron}`;
177
- if (lastTriggered.get(key) === stamp) continue;
178
- lastTriggered.set(key, stamp);
179
- if (!isNowAllowed(target?.timeWindows, now)) {
180
- logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u547D\u4E2Dcron\u4F46\u4E0D\u5728\u65F6\u6BB5\u5185\uFF0C\u8DF3\u8FC7`);
181
- continue;
182
- }
183
- void runScheduledSync(groupId);
184
- }
185
- }, { log: false, type: "skip" });
186
- export {
187
- groupFileSyncScheduler
188
- };
package/lib/apps/help.js CHANGED
@@ -22,7 +22,7 @@ var formatDateTime = (date) => {
22
22
  return date.toISOString();
23
23
  }
24
24
  };
25
- var helpImage = karin.command(/^#?(群文件帮助|同步群文件帮助|openlist帮助)$/i, async (e) => {
25
+ var helpImage = karin.command(/^#?(群文件帮助|同步群文件帮助|openlist帮助|op帮助)$/i, async (e) => {
26
26
  if (!e.isPrivate) return false;
27
27
  try {
28
28
  await ensurePluginResources();