koishi-plugin-oni-sync-bot 0.8.7 → 0.9.0
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/index.js +316 -157
- package/lib/plugins/syncCommands.d.ts +16 -0
- package/lib/plugins/updateCommands.d.ts +4 -0
- package/lib/services/wikiBotService.d.ts +2 -0
- package/lib/sync/pageSync.d.ts +12 -1
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -186,30 +186,19 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
186
186
|
}
|
|
187
187
|
async loginWithRetry(siteConfig, attempt = 1) {
|
|
188
188
|
try {
|
|
189
|
-
logger.info(
|
|
190
|
-
`正在登录 ${siteConfig.name}... (尝试 ${attempt}/${_WikiBotService.MAX_RETRIES})`
|
|
191
|
-
);
|
|
192
189
|
const bot = await this.login(siteConfig);
|
|
193
190
|
return bot;
|
|
194
191
|
} catch (error) {
|
|
195
192
|
const errorMsg = getErrorMessage(error);
|
|
196
193
|
if (attempt < _WikiBotService.MAX_RETRIES) {
|
|
197
|
-
logger.warn(
|
|
198
|
-
`登录 ${siteConfig.name} 失败,${_WikiBotService.RETRY_DELAY / 1e3}秒后重试...`,
|
|
199
|
-
errorMsg
|
|
200
|
-
);
|
|
201
194
|
await this.delay(_WikiBotService.RETRY_DELAY);
|
|
202
195
|
return this.loginWithRetry(siteConfig, attempt + 1);
|
|
203
196
|
}
|
|
204
|
-
logger.error(
|
|
205
|
-
`登录 ${siteConfig.name} 失败,已达到最大重试次数`,
|
|
206
|
-
errorMsg
|
|
207
|
-
);
|
|
197
|
+
logger.error(`❌ ${siteConfig.name} 登录失败`, errorMsg);
|
|
208
198
|
throw error;
|
|
209
199
|
}
|
|
210
200
|
}
|
|
211
201
|
async login(siteConfig) {
|
|
212
|
-
logger.info(`正在连接 ${siteConfig.name} API: ${siteConfig.api}`);
|
|
213
202
|
const bot = new import_mwn.Mwn({
|
|
214
203
|
apiUrl: siteConfig.api,
|
|
215
204
|
username: siteConfig.username,
|
|
@@ -225,9 +214,7 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
225
214
|
const cookieString = "SESSDATA=666; Domain=wiki.biligame.com; Path=/oni; HttpOnly; Secure;";
|
|
226
215
|
bot.cookieJar.setCookie(cookieString, bot.options.apiUrl, (err) => {
|
|
227
216
|
if (err) {
|
|
228
|
-
logger.warn("Cookie 注入失败:", err);
|
|
229
|
-
} else {
|
|
230
|
-
logger.info("Cookie 注入成功");
|
|
217
|
+
logger.warn("bwiki Cookie 注入失败:", err);
|
|
231
218
|
}
|
|
232
219
|
});
|
|
233
220
|
bot.setRequestOptions({
|
|
@@ -237,96 +224,63 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
237
224
|
}
|
|
238
225
|
});
|
|
239
226
|
}
|
|
240
|
-
logger.info(`正在执行 ${siteConfig.name} 登录...`);
|
|
241
227
|
await bot.login();
|
|
242
|
-
logger.info(`✅ 成功登录 ${siteConfig.name}`);
|
|
243
228
|
return bot;
|
|
244
229
|
}
|
|
245
230
|
async start() {
|
|
246
|
-
logger.info("WikiBotService 正在初始化...");
|
|
247
231
|
try {
|
|
248
232
|
const sitesConfig = this.getSitesConfig();
|
|
249
|
-
logger.info("开始登录 WIKIGG...");
|
|
250
233
|
try {
|
|
251
234
|
this.ggbot = await this.loginWithRetry(sitesConfig.gg);
|
|
235
|
+
logger.info("✅ WIKIGG 登录成功");
|
|
252
236
|
} catch (error) {
|
|
253
|
-
|
|
254
|
-
logger.error(
|
|
255
|
-
"WIKIGG 登录失败,服务将继续运行,但 WIKIGG 相关功能不可用",
|
|
256
|
-
errorMsg
|
|
257
|
-
);
|
|
237
|
+
this.ggbot = null;
|
|
258
238
|
}
|
|
259
|
-
logger.info("开始登录 bwiki...");
|
|
260
239
|
try {
|
|
261
240
|
this.bwikibot = await this.loginWithRetry(sitesConfig.bwiki);
|
|
241
|
+
logger.info("✅ bwiki 登录成功");
|
|
262
242
|
} catch (error) {
|
|
263
|
-
|
|
264
|
-
logger.error(
|
|
265
|
-
"bwiki 登录失败,服务将继续运行,但 bwiki 相关功能不可用",
|
|
266
|
-
errorMsg
|
|
267
|
-
);
|
|
243
|
+
this.bwikibot = null;
|
|
268
244
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
logger.
|
|
272
|
-
} else if (this.ggbot || this.bwikibot) {
|
|
273
|
-
this.isReady = true;
|
|
274
|
-
logger.warn(
|
|
275
|
-
`WikiBotService 部分初始化成功,已登录: ${this.ggbot ? "WIKIGG" : ""} ${this.bwikibot ? "bwiki" : ""}`
|
|
276
|
-
);
|
|
277
|
-
} else {
|
|
278
|
-
logger.error("WikiBotService 初始化失败,所有登录都失败");
|
|
245
|
+
this.isReady = this.ggbot !== null || this.bwikibot !== null;
|
|
246
|
+
if (!this.isReady) {
|
|
247
|
+
logger.error("❌ WikiBotService 初始化失败,两个 Wiki 均无法登录");
|
|
279
248
|
}
|
|
280
249
|
} catch (error) {
|
|
281
250
|
const errorMsg = getErrorMessage(error);
|
|
282
|
-
logger.error("WikiBotService
|
|
251
|
+
logger.error("❌ WikiBotService 初始化异常:", errorMsg);
|
|
283
252
|
}
|
|
284
253
|
}
|
|
285
254
|
stop() {
|
|
286
255
|
this.isReady = false;
|
|
287
256
|
this.ggbot = null;
|
|
288
257
|
this.bwikibot = null;
|
|
289
|
-
|
|
258
|
+
this.ggbotProxy = null;
|
|
259
|
+
this.bwikibotProxy = null;
|
|
290
260
|
}
|
|
291
261
|
async relogin() {
|
|
292
262
|
const sitesConfig = this.getSitesConfig();
|
|
293
263
|
let ggSuccess = false;
|
|
294
264
|
let bwikiSuccess = false;
|
|
295
|
-
logger.info("开始重新登录 WIKIGG...");
|
|
296
265
|
try {
|
|
297
266
|
this.ggbot = await this.loginWithRetry(sitesConfig.gg);
|
|
298
267
|
ggSuccess = true;
|
|
299
|
-
logger.info("✅
|
|
268
|
+
logger.info("✅ WIKIGG 重新登录成功");
|
|
300
269
|
} catch (error) {
|
|
301
270
|
this.ggbot = null;
|
|
302
|
-
const errorMsg = getErrorMessage(error);
|
|
303
|
-
logger.error("❌ 重新登录 WIKIGG 失败", errorMsg);
|
|
304
271
|
}
|
|
305
|
-
logger.info("开始重新登录 bwiki...");
|
|
306
272
|
try {
|
|
307
273
|
this.bwikibot = await this.loginWithRetry(sitesConfig.bwiki);
|
|
308
274
|
bwikiSuccess = true;
|
|
309
|
-
logger.info("✅
|
|
275
|
+
logger.info("✅ bwiki 重新登录成功");
|
|
310
276
|
} catch (error) {
|
|
311
277
|
this.bwikibot = null;
|
|
312
|
-
const errorMsg = getErrorMessage(error);
|
|
313
|
-
logger.error("❌ 重新登录 bwiki 失败", errorMsg);
|
|
314
|
-
}
|
|
315
|
-
if (this.ggbot && this.bwikibot) {
|
|
316
|
-
this.isReady = true;
|
|
317
|
-
logger.info("WikiBotService 重新登录成功,两个 Wiki 已登录");
|
|
318
|
-
} else if (this.ggbot || this.bwikibot) {
|
|
319
|
-
this.isReady = true;
|
|
320
|
-
logger.warn(
|
|
321
|
-
`WikiBotService 部分重新登录成功,已登录: ${this.ggbot ? "WIKIGG" : ""} ${this.bwikibot ? "bwiki" : ""}`
|
|
322
|
-
);
|
|
323
|
-
} else {
|
|
324
|
-
this.isReady = false;
|
|
325
|
-
logger.error("WikiBotService 重新登录失败,所有登录都失败");
|
|
326
278
|
}
|
|
279
|
+
this.isReady = this.ggbot !== null || this.bwikibot !== null;
|
|
327
280
|
return { gg: ggSuccess, bwiki: bwikiSuccess };
|
|
328
281
|
}
|
|
329
282
|
async reloginSite(site) {
|
|
283
|
+
const siteName = site === "gg" ? "WIKIGG" : "bwiki";
|
|
330
284
|
const sitesConfig = this.getSitesConfig();
|
|
331
285
|
const siteConfig = site === "gg" ? sitesConfig.gg : sitesConfig.bwiki;
|
|
332
286
|
try {
|
|
@@ -336,15 +290,13 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
336
290
|
} else {
|
|
337
291
|
this.bwikibot = bot;
|
|
338
292
|
}
|
|
339
|
-
logger.info(`✅ ${
|
|
293
|
+
logger.info(`✅ ${siteName} 自动重新登录成功`);
|
|
340
294
|
} catch (error) {
|
|
341
|
-
const errorMsg = getErrorMessage(error);
|
|
342
295
|
if (site === "gg") {
|
|
343
296
|
this.ggbot = null;
|
|
344
297
|
} else {
|
|
345
298
|
this.bwikibot = null;
|
|
346
299
|
}
|
|
347
|
-
logger.error(`❌ ${siteConfig.name} 自动重新登录失败`, errorMsg);
|
|
348
300
|
throw error;
|
|
349
301
|
}
|
|
350
302
|
}
|
|
@@ -354,71 +306,77 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
354
306
|
isBWikiBotReady() {
|
|
355
307
|
return this.bwikibot !== null;
|
|
356
308
|
}
|
|
357
|
-
createBotProxy(
|
|
309
|
+
createBotProxy(site) {
|
|
358
310
|
const self = this;
|
|
359
|
-
const METHODS_TO_SKIP = /* @__PURE__ */ new Set([
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
311
|
+
const METHODS_TO_SKIP = /* @__PURE__ */ new Set(["cookieJar", "setRequestOptions"]);
|
|
312
|
+
const getCurrentBot = /* @__PURE__ */ __name(() => {
|
|
313
|
+
const bot = site === "gg" ? self.ggbot : self.bwikibot;
|
|
314
|
+
if (!bot) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`${site === "gg" ? "WIKIGG" : "bwiki"} bot 尚未就绪,请检查登录配置或查看日志`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
return bot;
|
|
320
|
+
}, "getCurrentBot");
|
|
321
|
+
return new Proxy({}, {
|
|
322
|
+
get(_target, prop) {
|
|
323
|
+
const propKey = prop;
|
|
324
|
+
const currentBot = getCurrentBot();
|
|
325
|
+
const value = currentBot[propKey];
|
|
326
|
+
if (typeof value !== "function") {
|
|
327
|
+
return value;
|
|
368
328
|
}
|
|
369
329
|
if (METHODS_TO_SKIP.has(prop)) {
|
|
370
|
-
return
|
|
330
|
+
return value.bind(currentBot);
|
|
371
331
|
}
|
|
372
332
|
if (prop === "continuedQueryGen") {
|
|
373
333
|
return function(...args) {
|
|
374
|
-
|
|
334
|
+
let activeBot = getCurrentBot();
|
|
335
|
+
let activeMethod = activeBot.continuedQueryGen;
|
|
336
|
+
let activeIterator = activeMethod.apply(activeBot, args)[Symbol.asyncIterator]();
|
|
375
337
|
return {
|
|
376
338
|
[Symbol.asyncIterator]() {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
throw new Error(
|
|
391
|
-
`${site === "gg" ? "WIKIGG" : "bwiki"} 自动重新登录失败`
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
const newGen = newBot.continuedQueryGen(...args);
|
|
395
|
-
const newIt = newGen[Symbol.asyncIterator]();
|
|
396
|
-
return await newIt.next();
|
|
397
|
-
}
|
|
398
|
-
throw error;
|
|
399
|
-
}
|
|
339
|
+
return this;
|
|
340
|
+
},
|
|
341
|
+
async next() {
|
|
342
|
+
try {
|
|
343
|
+
return await activeIterator.next();
|
|
344
|
+
} catch (error) {
|
|
345
|
+
if (error.code === "assertuserfailed") {
|
|
346
|
+
await self.reloginSite(site);
|
|
347
|
+
activeBot = getCurrentBot();
|
|
348
|
+
activeMethod = activeBot.continuedQueryGen;
|
|
349
|
+
const newGen = activeMethod.apply(activeBot, args);
|
|
350
|
+
activeIterator = newGen[Symbol.asyncIterator]();
|
|
351
|
+
return await activeIterator.next();
|
|
400
352
|
}
|
|
401
|
-
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
402
355
|
}
|
|
403
356
|
};
|
|
404
357
|
};
|
|
405
358
|
}
|
|
406
359
|
return async function(...args) {
|
|
360
|
+
let executeBot = getCurrentBot();
|
|
361
|
+
let method = executeBot[propKey];
|
|
362
|
+
if (typeof method !== "function") {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`${site === "gg" ? "WIKIGG" : "bwiki"} bot 缺少方法 ${String(prop)}`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
407
367
|
try {
|
|
408
|
-
return await
|
|
368
|
+
return await method.apply(executeBot, args);
|
|
409
369
|
} catch (error) {
|
|
410
370
|
if (error.code === "assertuserfailed") {
|
|
411
|
-
logger.warn(
|
|
412
|
-
`检测到 ${site === "gg" ? "WIKIGG" : "bwiki"} 登录过期,正在自动重新登录...`
|
|
413
|
-
);
|
|
414
371
|
await self.reloginSite(site);
|
|
415
|
-
|
|
416
|
-
|
|
372
|
+
executeBot = getCurrentBot();
|
|
373
|
+
method = executeBot[propKey];
|
|
374
|
+
if (typeof method !== "function") {
|
|
417
375
|
throw new Error(
|
|
418
|
-
`${site === "gg" ? "WIKIGG" : "bwiki"}
|
|
376
|
+
`${site === "gg" ? "WIKIGG" : "bwiki"} 新 bot 缺少方法 ${String(prop)}`
|
|
419
377
|
);
|
|
420
378
|
}
|
|
421
|
-
return await
|
|
379
|
+
return await method.apply(executeBot, args);
|
|
422
380
|
}
|
|
423
381
|
throw error;
|
|
424
382
|
}
|
|
@@ -426,17 +384,25 @@ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
|
|
|
426
384
|
}
|
|
427
385
|
});
|
|
428
386
|
}
|
|
387
|
+
ggbotProxy = null;
|
|
388
|
+
bwikibotProxy = null;
|
|
429
389
|
getGGBot() {
|
|
430
390
|
if (!this.ggbot) {
|
|
431
391
|
throw new Error("WIKIGG bot 尚未就绪,请检查登录配置或查看日志");
|
|
432
392
|
}
|
|
433
|
-
|
|
393
|
+
if (!this.ggbotProxy) {
|
|
394
|
+
this.ggbotProxy = this.createBotProxy("gg");
|
|
395
|
+
}
|
|
396
|
+
return this.ggbotProxy;
|
|
434
397
|
}
|
|
435
398
|
getBWikiBot() {
|
|
436
399
|
if (!this.bwikibot) {
|
|
437
400
|
throw new Error("bwiki bot 尚未就绪,请检查登录配置或查看日志");
|
|
438
401
|
}
|
|
439
|
-
|
|
402
|
+
if (!this.bwikibotProxy) {
|
|
403
|
+
this.bwikibotProxy = this.createBotProxy("bwiki");
|
|
404
|
+
}
|
|
405
|
+
return this.bwikibotProxy;
|
|
440
406
|
}
|
|
441
407
|
};
|
|
442
408
|
((WikiBotService2) => {
|
|
@@ -537,6 +503,20 @@ var RouteRedirect = class {
|
|
|
537
503
|
)}`;
|
|
538
504
|
router.redirect(targetUrl);
|
|
539
505
|
});
|
|
506
|
+
ctx.server.use("/ggwiki", async (router, next) => {
|
|
507
|
+
const fullPath = router.path.replace("/ggwiki", "") || "";
|
|
508
|
+
const decodedPath = decodeURIComponent(fullPath);
|
|
509
|
+
if (decodedPath.includes("..")) {
|
|
510
|
+
return router.body = "❌ 非法路径访问!";
|
|
511
|
+
}
|
|
512
|
+
if (!/^(\/[\p{L}\p{N}_\-:.%]+)*\/?$/u.test(fullPath)) {
|
|
513
|
+
return router.body = "❌ 路径包含非法字符!";
|
|
514
|
+
}
|
|
515
|
+
const queryString = router.querystring ? `?${router.querystring}` : "";
|
|
516
|
+
const targetUrl = `https://${this.config.main_site}${fullPath}${queryString}`;
|
|
517
|
+
router.redirect(targetUrl);
|
|
518
|
+
return;
|
|
519
|
+
});
|
|
540
520
|
}
|
|
541
521
|
};
|
|
542
522
|
((RouteRedirect2) => {
|
|
@@ -564,11 +544,21 @@ var CONFIG = {
|
|
|
564
544
|
UPLOAD_COMMENT: "从 WikiGG 自动同步转存",
|
|
565
545
|
UPLOAD_TEXT: "== 授权说明 ==\n本文件从 WikiGG 转存,遵循原站点授权协议。"
|
|
566
546
|
};
|
|
547
|
+
function normalizeImageTitle(fileName) {
|
|
548
|
+
const trimmed = fileName.trim();
|
|
549
|
+
const lower = trimmed.toLowerCase();
|
|
550
|
+
if (lower.startsWith("file:") || lower.startsWith("image:") || lower.startsWith("文件:")) {
|
|
551
|
+
return trimmed;
|
|
552
|
+
}
|
|
553
|
+
return `File:${trimmed}`;
|
|
554
|
+
}
|
|
555
|
+
__name(normalizeImageTitle, "normalizeImageTitle");
|
|
567
556
|
async function getImageInfo(site, fileName) {
|
|
568
557
|
try {
|
|
558
|
+
const normalizedTitle = normalizeImageTitle(fileName);
|
|
569
559
|
const response = await site.query({
|
|
570
560
|
action: "query",
|
|
571
|
-
titles:
|
|
561
|
+
titles: normalizedTitle,
|
|
572
562
|
prop: "imageinfo",
|
|
573
563
|
iiprop: "url|sha1|size|mime"
|
|
574
564
|
});
|
|
@@ -592,9 +582,10 @@ async function getImageInfo(site, fileName) {
|
|
|
592
582
|
__name(getImageInfo, "getImageInfo");
|
|
593
583
|
async function deleteOldVersions(bot, fileName) {
|
|
594
584
|
try {
|
|
585
|
+
const normalizedTitle = normalizeImageTitle(fileName);
|
|
595
586
|
const response = await bot.query({
|
|
596
587
|
action: "query",
|
|
597
|
-
titles:
|
|
588
|
+
titles: normalizedTitle,
|
|
598
589
|
prop: "revisions",
|
|
599
590
|
rvprop: "ids|timestamp",
|
|
600
591
|
rvlimit: "max",
|
|
@@ -634,19 +625,32 @@ async function deleteOldVersions(bot, fileName) {
|
|
|
634
625
|
}
|
|
635
626
|
}
|
|
636
627
|
__name(deleteOldVersions, "deleteOldVersions");
|
|
628
|
+
function stripImagePrefix(fileName) {
|
|
629
|
+
const trimmed = fileName.trim();
|
|
630
|
+
const lower = trimmed.toLowerCase();
|
|
631
|
+
const prefixes = ["file:", "image:", "文件:"];
|
|
632
|
+
for (const p of prefixes) {
|
|
633
|
+
if (lower.startsWith(p)) {
|
|
634
|
+
return trimmed.substring(p.length);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return trimmed;
|
|
638
|
+
}
|
|
639
|
+
__name(stripImagePrefix, "stripImagePrefix");
|
|
637
640
|
async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
638
641
|
if (CONFIG.IGNORED_IMAGES.includes(fileName)) {
|
|
639
642
|
logger.info(`[SyncImg] 🚫 图片 ${fileName} 在忽略列表,跳过`);
|
|
640
643
|
return { success: true, reason: "ignored" };
|
|
641
644
|
}
|
|
645
|
+
const bareFileName = stripImagePrefix(fileName);
|
|
642
646
|
try {
|
|
643
|
-
logger.info(`[SyncImg] 🚀 开始处理: ${
|
|
644
|
-
const sourceImageInfo = await getImageInfo(sourceBot,
|
|
647
|
+
logger.info(`[SyncImg] 🚀 开始处理: ${bareFileName}`);
|
|
648
|
+
const sourceImageInfo = await getImageInfo(sourceBot, bareFileName);
|
|
645
649
|
if (!sourceImageInfo) {
|
|
646
650
|
logger.info(`[SyncImg] ❌ 源站未找到图片: ${fileName}`);
|
|
647
651
|
return { success: false, reason: "source_missing" };
|
|
648
652
|
}
|
|
649
|
-
const targetImageInfo = await getImageInfo(targetBot,
|
|
653
|
+
const targetImageInfo = await getImageInfo(targetBot, bareFileName);
|
|
650
654
|
logger.info(`原图片sha1: ${sourceImageInfo.sha1}`);
|
|
651
655
|
logger.info(`目标图片sha1: ${targetImageInfo?.sha1}`);
|
|
652
656
|
if (targetImageInfo && targetImageInfo.sha1 === sourceImageInfo.sha1) {
|
|
@@ -664,19 +668,19 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
|
664
668
|
}
|
|
665
669
|
const imageBuffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
666
670
|
logger.info(
|
|
667
|
-
`[SyncImg] 📤 上传图片: ${
|
|
671
|
+
`[SyncImg] 📤 上传图片: ${bareFileName} (大小: ${(imageBuffer.length / 1024).toFixed(1)} KB)`
|
|
668
672
|
);
|
|
669
673
|
const token = await targetBot.getCsrfToken();
|
|
670
674
|
const form = new import_form_data.default();
|
|
671
675
|
form.append("action", "upload");
|
|
672
|
-
form.append("filename",
|
|
676
|
+
form.append("filename", bareFileName);
|
|
673
677
|
form.append("text", CONFIG.UPLOAD_TEXT);
|
|
674
678
|
form.append("comment", CONFIG.UPLOAD_COMMENT);
|
|
675
679
|
form.append("token", token);
|
|
676
680
|
form.append("ignorewarnings", "0");
|
|
677
681
|
form.append("format", "json");
|
|
678
682
|
form.append("file", imageBuffer, {
|
|
679
|
-
filename:
|
|
683
|
+
filename: bareFileName,
|
|
680
684
|
contentType: imageResponse.headers.get("content-type") || "application/octet-stream"
|
|
681
685
|
});
|
|
682
686
|
const rawResponse = await targetBot.rawRequest({
|
|
@@ -981,7 +985,7 @@ async function syncPages(oldSite, newSite) {
|
|
|
981
985
|
}
|
|
982
986
|
}
|
|
983
987
|
__name(syncPages, "syncPages");
|
|
984
|
-
async function incrementalUpdate(oldSite, newSite, config) {
|
|
988
|
+
async function incrementalUpdate(oldSite, newSite, config, onSynced) {
|
|
985
989
|
try {
|
|
986
990
|
const now = /* @__PURE__ */ new Date();
|
|
987
991
|
const threeHoursAgo = new Date(now.getTime() - 3 * 60 * 60 * 1e3);
|
|
@@ -999,6 +1003,7 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
999
1003
|
const processedTitles = /* @__PURE__ */ new Set();
|
|
1000
1004
|
let totalProcessed = 0;
|
|
1001
1005
|
let totalSkipped = 0;
|
|
1006
|
+
const syncedItems = [];
|
|
1002
1007
|
for await (const res of queryGen) {
|
|
1003
1008
|
const pages = res.query?.recentchanges || [];
|
|
1004
1009
|
for (const page of pages) {
|
|
@@ -1019,19 +1024,45 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
1019
1024
|
processedTitles.add(title);
|
|
1020
1025
|
totalProcessed++;
|
|
1021
1026
|
try {
|
|
1027
|
+
const rcUser = page.user || "未知";
|
|
1028
|
+
const rcTimestamp = page.timestamp || "";
|
|
1029
|
+
const rcComment = page.comment || "";
|
|
1022
1030
|
if (title.startsWith(CONFIG2.FILE_NAMESPACE_PREFIX)) {
|
|
1023
1031
|
const fileName = title.replace(CONFIG2.FILE_NAMESPACE_PREFIX, "");
|
|
1024
1032
|
logger.info(
|
|
1025
1033
|
`[增量更新流程] 🖼️ 检查到图片: ${title},正在尝试转存`
|
|
1026
1034
|
);
|
|
1027
|
-
await syncSingleImage(
|
|
1035
|
+
const result = await syncSingleImage(
|
|
1036
|
+
oldSite,
|
|
1037
|
+
newSite,
|
|
1038
|
+
fileName,
|
|
1039
|
+
config
|
|
1040
|
+
);
|
|
1041
|
+
if (result.success && result.reason === "synced") {
|
|
1042
|
+
syncedItems.push({
|
|
1043
|
+
title,
|
|
1044
|
+
type: "image",
|
|
1045
|
+
user: rcUser,
|
|
1046
|
+
timestamp: rcTimestamp,
|
|
1047
|
+
comment: rcComment
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1028
1050
|
} else {
|
|
1029
|
-
await syncSinglePage(
|
|
1051
|
+
const result = await syncSinglePage(
|
|
1030
1052
|
oldSite,
|
|
1031
1053
|
newSite,
|
|
1032
1054
|
title,
|
|
1033
|
-
|
|
1055
|
+
rcUser
|
|
1034
1056
|
);
|
|
1057
|
+
if (result.success && result.reason === "synced") {
|
|
1058
|
+
syncedItems.push({
|
|
1059
|
+
title,
|
|
1060
|
+
type: "page",
|
|
1061
|
+
user: rcUser,
|
|
1062
|
+
timestamp: rcTimestamp,
|
|
1063
|
+
comment: rcComment
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1035
1066
|
}
|
|
1036
1067
|
await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
|
|
1037
1068
|
} catch (error) {
|
|
@@ -1042,8 +1073,11 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
1042
1073
|
}
|
|
1043
1074
|
}
|
|
1044
1075
|
logger.info(
|
|
1045
|
-
`[增量更新流程] ✅ 增量更新完成!处理: ${totalProcessed}, 跳过: ${totalSkipped}`
|
|
1076
|
+
`[增量更新流程] ✅ 增量更新完成!处理: ${totalProcessed}, 跳过: ${totalSkipped}, 通知: ${syncedItems.length}`
|
|
1046
1077
|
);
|
|
1078
|
+
if (syncedItems.length > 0) {
|
|
1079
|
+
onSynced?.(syncedItems);
|
|
1080
|
+
}
|
|
1047
1081
|
} catch (globalError) {
|
|
1048
1082
|
logger.error(`[增量更新流程] 💥 增量更新流程异常终止:`, globalError);
|
|
1049
1083
|
throw globalError;
|
|
@@ -1218,21 +1252,40 @@ async function syncModules(oldSite, newSite) {
|
|
|
1218
1252
|
__name(syncModules, "syncModules");
|
|
1219
1253
|
|
|
1220
1254
|
// src/plugins/syncCommands.ts
|
|
1255
|
+
function formatWikiTime(isoString) {
|
|
1256
|
+
try {
|
|
1257
|
+
const date = new Date(isoString);
|
|
1258
|
+
if (Number.isNaN(date.getTime())) return isoString;
|
|
1259
|
+
const pad = /* @__PURE__ */ __name((n) => String(n).padStart(2, "0"), "pad");
|
|
1260
|
+
const y = date.getFullYear();
|
|
1261
|
+
const m = pad(date.getMonth() + 1);
|
|
1262
|
+
const d = pad(date.getDate());
|
|
1263
|
+
const hh = pad(date.getHours());
|
|
1264
|
+
const mm = pad(date.getMinutes());
|
|
1265
|
+
return `${y}-${m}-${d} ${hh}:${mm}`;
|
|
1266
|
+
} catch {
|
|
1267
|
+
return isoString;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
__name(formatWikiTime, "formatWikiTime");
|
|
1221
1271
|
var SyncCommands = class {
|
|
1222
1272
|
static {
|
|
1223
1273
|
__name(this, "SyncCommands");
|
|
1224
1274
|
}
|
|
1225
1275
|
static inject = ["wikiBot", "cron"];
|
|
1226
1276
|
config;
|
|
1277
|
+
notifyFn;
|
|
1227
1278
|
constructor(ctx, config) {
|
|
1228
1279
|
this.config = config;
|
|
1229
1280
|
logger.info("WikiBot 服务已就绪,初始化定时任务和指令");
|
|
1281
|
+
this.notifyFn = this.buildNotifyFn(ctx);
|
|
1230
1282
|
ctx.cron("15 * * * *", async () => {
|
|
1231
1283
|
if (!await this.ensureBotsReady(ctx, "增量更新")) return;
|
|
1232
1284
|
await incrementalUpdate(
|
|
1233
1285
|
ctx.wikiBot.getGGBot(),
|
|
1234
1286
|
ctx.wikiBot.getBWikiBot(),
|
|
1235
|
-
config
|
|
1287
|
+
config,
|
|
1288
|
+
this.notifyFn
|
|
1236
1289
|
);
|
|
1237
1290
|
});
|
|
1238
1291
|
ctx.cron("30 8 * * 4", async () => {
|
|
@@ -1257,6 +1310,86 @@ var SyncCommands = class {
|
|
|
1257
1310
|
});
|
|
1258
1311
|
this.registerCommands(ctx);
|
|
1259
1312
|
}
|
|
1313
|
+
/**
|
|
1314
|
+
* 规范化 channelId:兼容多种前缀格式
|
|
1315
|
+
*/
|
|
1316
|
+
normalizeChannelId(rawId) {
|
|
1317
|
+
const trimmed = rawId.trim();
|
|
1318
|
+
if (!trimmed) return "";
|
|
1319
|
+
const lower = trimmed.toLowerCase();
|
|
1320
|
+
if (lower.startsWith("group:")) return trimmed.slice(6);
|
|
1321
|
+
if (lower.startsWith("channel:")) return trimmed.slice(8);
|
|
1322
|
+
if (lower.startsWith("qq:")) return trimmed.slice(3);
|
|
1323
|
+
return trimmed;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* 截断摘要到指定长度,超出部分用 ...
|
|
1327
|
+
*/
|
|
1328
|
+
truncateComment(text, max = 10) {
|
|
1329
|
+
if (!text) return "";
|
|
1330
|
+
const clean = text.trim();
|
|
1331
|
+
if (clean.length <= max) return clean;
|
|
1332
|
+
return clean.slice(0, max) + "...";
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* 构建增量更新同步成功的通知回调
|
|
1336
|
+
* 当配置了 notifyGroupIds 时,将同步成功的页面/图片合并成一条消息推送到指定 QQ 群
|
|
1337
|
+
*/
|
|
1338
|
+
buildNotifyFn(ctx) {
|
|
1339
|
+
const groupIds = this.config.notifyGroupIds;
|
|
1340
|
+
if (!groupIds || groupIds.length === 0) return void 0;
|
|
1341
|
+
const platform = this.config.notifyBotPlatform || "qq";
|
|
1342
|
+
const self = this;
|
|
1343
|
+
return async (items) => {
|
|
1344
|
+
try {
|
|
1345
|
+
if (!items || items.length === 0) return;
|
|
1346
|
+
const bots = [];
|
|
1347
|
+
for (const bot of ctx.bots) {
|
|
1348
|
+
if (bot.platform === platform && bot.isActive) {
|
|
1349
|
+
bots.push(bot);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (bots.length === 0) {
|
|
1353
|
+
logger.warn(`[通知] 未找到可用的 ${platform} 平台 bot,跳过群通知`);
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
const lines = [];
|
|
1357
|
+
lines.push(`Wiki 同步更新(${items.length} 条)`);
|
|
1358
|
+
lines.push("");
|
|
1359
|
+
for (let i = 0; i < items.length; i++) {
|
|
1360
|
+
const item = items[i];
|
|
1361
|
+
const label = item.type === "image" ? "🖼️" : "📖";
|
|
1362
|
+
const zhTime = item.timestamp ? formatWikiTime(item.timestamp) : "";
|
|
1363
|
+
const user = item.user || "未知";
|
|
1364
|
+
const shortComment = self.truncateComment(item.comment || "", 10);
|
|
1365
|
+
const commentLine = shortComment ? `|${shortComment}` : "";
|
|
1366
|
+
lines.push(
|
|
1367
|
+
`${label} ${i + 1}. ${item.title}|${user}|${zhTime}${commentLine}`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
const message = lines.join("\n");
|
|
1371
|
+
for (const bot of bots) {
|
|
1372
|
+
for (const rawChannelId of groupIds) {
|
|
1373
|
+
const channelId = self.normalizeChannelId(rawChannelId);
|
|
1374
|
+
if (!channelId) continue;
|
|
1375
|
+
try {
|
|
1376
|
+
await bot.sendMessage(channelId, message);
|
|
1377
|
+
logger.info(
|
|
1378
|
+
`[通知] 向 ${bot.platform}/${channelId} 推送成功 (${items.length} 条)`
|
|
1379
|
+
);
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
logger.error(
|
|
1382
|
+
`[通知] 向 ${bot.platform}/${channelId} 发送消息失败:`,
|
|
1383
|
+
getErrorMessage(error)
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
logger.error(`[通知] 处理通知过程出错:`, getErrorMessage(error));
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1260
1393
|
/**
|
|
1261
1394
|
* 确保机器人就绪,如果未就绪则尝试重新登录
|
|
1262
1395
|
* @param ctx Koishi 上下文
|
|
@@ -1311,7 +1444,8 @@ var SyncCommands = class {
|
|
|
1311
1444
|
await incrementalUpdate(
|
|
1312
1445
|
ctx.wikiBot.getGGBot(),
|
|
1313
1446
|
ctx.wikiBot.getBWikiBot(),
|
|
1314
|
-
this.config
|
|
1447
|
+
this.config,
|
|
1448
|
+
this.notifyFn
|
|
1315
1449
|
);
|
|
1316
1450
|
return `✅ 已尝试获取三小时前的编辑并同步,请前往控制台查看:${this.config.logsUrl}`;
|
|
1317
1451
|
} catch (err) {
|
|
@@ -1406,7 +1540,11 @@ var SyncCommands = class {
|
|
|
1406
1540
|
domain: import_koishi8.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip"),
|
|
1407
1541
|
main_site: import_koishi8.Schema.string().description("主站域名(必填,如:oxygennotincluded.wiki.gg)").default("oxygennotincluded.wiki.gg/zh"),
|
|
1408
1542
|
bwiki_site: import_koishi8.Schema.string().description("镜像站域名(必填,如:wiki.biligame.com)").default("wiki.biligame.com/oni"),
|
|
1409
|
-
logsUrl: import_koishi8.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs")
|
|
1543
|
+
logsUrl: import_koishi8.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs"),
|
|
1544
|
+
notifyGroupIds: import_koishi8.Schema.array(import_koishi8.Schema.string()).description(
|
|
1545
|
+
'增量更新同步成功时通知的群频道ID列表,如 ["group:123456"],留空则不发送通知'
|
|
1546
|
+
).default([]),
|
|
1547
|
+
notifyBotPlatform: import_koishi8.Schema.string().description("通知 bot 的平台标识,默认 qq").default("qq")
|
|
1410
1548
|
});
|
|
1411
1549
|
})(SyncCommands || (SyncCommands = {}));
|
|
1412
1550
|
|
|
@@ -1715,45 +1853,65 @@ bwiki: https://${this.config.domain}/bw/${match.id}`;
|
|
|
1715
1853
|
// src/plugins/updateCommands.ts
|
|
1716
1854
|
var import_koishi10 = require("koishi");
|
|
1717
1855
|
var UpdateCommands = class {
|
|
1856
|
+
constructor(ctx, config) {
|
|
1857
|
+
this.ctx = ctx;
|
|
1858
|
+
this.config = config;
|
|
1859
|
+
this.log = ctx.logger("oni-sync");
|
|
1860
|
+
this.registerCommands(ctx);
|
|
1861
|
+
this.registerCronJobs(ctx);
|
|
1862
|
+
}
|
|
1718
1863
|
static {
|
|
1719
1864
|
__name(this, "UpdateCommands");
|
|
1720
1865
|
}
|
|
1721
|
-
static inject = ["database", "wikiBot"];
|
|
1866
|
+
static inject = ["database", "wikiBot", "cron"];
|
|
1722
1867
|
config;
|
|
1723
1868
|
log;
|
|
1724
|
-
|
|
1725
|
-
this.config
|
|
1726
|
-
|
|
1727
|
-
|
|
1869
|
+
registerCronJobs(ctx) {
|
|
1870
|
+
const cronExpression = this.config.updateCron || "0 2 * * *";
|
|
1871
|
+
ctx.cron(cronExpression, async () => {
|
|
1872
|
+
if (!this.ctx.wikiBot.isReady) {
|
|
1873
|
+
this.log.warn("定时任务:WikiBot 服务未就绪,跳过页面缓存更新");
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
this.log.info("定时任务:开始每日自动更新本地页面缓存(主站)");
|
|
1877
|
+
try {
|
|
1878
|
+
const result = await this.updatePageCache();
|
|
1879
|
+
this.log.info(`定时任务完成:更新 ${result.count} 条页面到本地缓存`);
|
|
1880
|
+
} catch (err) {
|
|
1881
|
+
this.log.error("定时任务失败:自动更新页面缓存", err);
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
async updatePageCache() {
|
|
1886
|
+
const res = await this.ctx.wikiBot.getGGBot().request({
|
|
1887
|
+
action: "query",
|
|
1888
|
+
list: "allpages",
|
|
1889
|
+
format: "json",
|
|
1890
|
+
aplimit: "max"
|
|
1891
|
+
});
|
|
1892
|
+
logger.info("主站页面查询成功");
|
|
1893
|
+
const pages = res.query.allpages || [];
|
|
1894
|
+
const pageData = pages.map((page) => {
|
|
1895
|
+
const { pinyin_full, pinyin_first } = generatePinyinInfo(page.title);
|
|
1896
|
+
return {
|
|
1897
|
+
id: page.pageid,
|
|
1898
|
+
title: page.title,
|
|
1899
|
+
pinyin_full,
|
|
1900
|
+
pinyin_first
|
|
1901
|
+
};
|
|
1902
|
+
});
|
|
1903
|
+
if (pageData.length > 0) {
|
|
1904
|
+
await this.ctx.database.upsert("wikipages", pageData);
|
|
1905
|
+
}
|
|
1906
|
+
logger.info(`检索到 ${pages.length} 个页面,已更新至数据库`);
|
|
1907
|
+
return { count: pages.length };
|
|
1728
1908
|
}
|
|
1729
1909
|
registerCommands(ctx) {
|
|
1730
1910
|
ctx.command("update", "更新本地页面缓存(主站)", { authority: 2 }).action(async ({ session }) => {
|
|
1731
1911
|
await session.execute("update.status");
|
|
1732
1912
|
try {
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
list: "allpages",
|
|
1736
|
-
format: "json",
|
|
1737
|
-
aplimit: "max"
|
|
1738
|
-
});
|
|
1739
|
-
logger.info("主站页面查询成功");
|
|
1740
|
-
const pages = res.query.allpages || [];
|
|
1741
|
-
const pageData = pages.map((page) => {
|
|
1742
|
-
const { pinyin_full, pinyin_first } = generatePinyinInfo(
|
|
1743
|
-
page.title
|
|
1744
|
-
);
|
|
1745
|
-
return {
|
|
1746
|
-
id: page.pageid,
|
|
1747
|
-
title: page.title,
|
|
1748
|
-
pinyin_full,
|
|
1749
|
-
pinyin_first
|
|
1750
|
-
};
|
|
1751
|
-
});
|
|
1752
|
-
if (pageData.length > 0) {
|
|
1753
|
-
await ctx.database.upsert("wikipages", pageData);
|
|
1754
|
-
}
|
|
1755
|
-
session.send(`✅ 检索到 ${pages.length} 个页面,已更新至数据库`);
|
|
1756
|
-
logger.info(`检索到 ${pages.length} 个页面,已更新至数据库`);
|
|
1913
|
+
const result = await this.updatePageCache();
|
|
1914
|
+
session.send(`✅ 检索到 ${result.count} 个页面,已更新至数据库`);
|
|
1757
1915
|
} catch (err) {
|
|
1758
1916
|
this.log.error("主站缓存更新失败", err);
|
|
1759
1917
|
session.send("❌ 主站缓存更新失败,请联系管理员查看日志");
|
|
@@ -1826,7 +1984,8 @@ var UpdateCommands = class {
|
|
|
1826
1984
|
};
|
|
1827
1985
|
((UpdateCommands2) => {
|
|
1828
1986
|
UpdateCommands2.Config = import_koishi10.Schema.object({
|
|
1829
|
-
logsUrl: import_koishi10.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs")
|
|
1987
|
+
logsUrl: import_koishi10.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs"),
|
|
1988
|
+
updateCron: import_koishi10.Schema.string().description("页面缓存自动更新 cron 表达式(默认每天凌晨 2 点)").default("0 2 * * *")
|
|
1830
1989
|
});
|
|
1831
1990
|
})(UpdateCommands || (UpdateCommands = {}));
|
|
1832
1991
|
|
|
@@ -8,11 +8,27 @@ export interface SyncCommandsConfig {
|
|
|
8
8
|
domain: string;
|
|
9
9
|
main_site: string;
|
|
10
10
|
bwiki_site: string;
|
|
11
|
+
notifyGroupIds?: string[];
|
|
12
|
+
notifyBotPlatform?: string;
|
|
11
13
|
}
|
|
12
14
|
export declare class SyncCommands {
|
|
13
15
|
static readonly inject: string[];
|
|
14
16
|
config: SyncCommandsConfig;
|
|
17
|
+
private notifyFn?;
|
|
15
18
|
constructor(ctx: Context, config: SyncCommandsConfig);
|
|
19
|
+
/**
|
|
20
|
+
* 规范化 channelId:兼容多种前缀格式
|
|
21
|
+
*/
|
|
22
|
+
private normalizeChannelId;
|
|
23
|
+
/**
|
|
24
|
+
* 截断摘要到指定长度,超出部分用 ...
|
|
25
|
+
*/
|
|
26
|
+
private truncateComment;
|
|
27
|
+
/**
|
|
28
|
+
* 构建增量更新同步成功的通知回调
|
|
29
|
+
* 当配置了 notifyGroupIds 时,将同步成功的页面/图片合并成一条消息推送到指定 QQ 群
|
|
30
|
+
*/
|
|
31
|
+
private buildNotifyFn;
|
|
16
32
|
/**
|
|
17
33
|
* 确保机器人就绪,如果未就绪则尝试重新登录
|
|
18
34
|
* @param ctx Koishi 上下文
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { Context, Logger, Schema } from "koishi";
|
|
2
2
|
export interface UpdateCommandsConfig {
|
|
3
3
|
logsUrl: string;
|
|
4
|
+
updateCron?: string;
|
|
4
5
|
}
|
|
5
6
|
export declare class UpdateCommands {
|
|
7
|
+
ctx: Context;
|
|
6
8
|
static readonly inject: string[];
|
|
7
9
|
config: UpdateCommandsConfig;
|
|
8
10
|
log: Logger;
|
|
9
11
|
constructor(ctx: Context, config: UpdateCommandsConfig);
|
|
12
|
+
private registerCronJobs;
|
|
13
|
+
private updatePageCache;
|
|
10
14
|
private registerCommands;
|
|
11
15
|
}
|
|
12
16
|
export declare namespace UpdateCommands {
|
package/lib/sync/pageSync.d.ts
CHANGED
|
@@ -20,11 +20,22 @@ declare function syncSinglePage(oldSite: Mwn, newSite: Mwn, pageTitle: string, u
|
|
|
20
20
|
* @returns null
|
|
21
21
|
*/
|
|
22
22
|
declare function syncPages(oldSite: Mwn, newSite: Mwn): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* 同步成功事件信息
|
|
25
|
+
*/
|
|
26
|
+
export interface SyncNotifyItem {
|
|
27
|
+
title: string;
|
|
28
|
+
type: "page" | "image";
|
|
29
|
+
user?: string;
|
|
30
|
+
timestamp?: string;
|
|
31
|
+
comment?: string;
|
|
32
|
+
}
|
|
23
33
|
/**
|
|
24
34
|
* 增量更新
|
|
25
35
|
* @param oldSite 源站点机器人实例
|
|
26
36
|
* @param newSite 目标站点机器人实例
|
|
27
37
|
* @param config KOISHI用户配置的项
|
|
38
|
+
* @param onSynced 同步成功批量回调(仅 reason==="synced" 的条目,函数结束时一次调用)
|
|
28
39
|
*/
|
|
29
|
-
declare function incrementalUpdate(oldSite: Mwn, newSite: Mwn, config: Config): Promise<void>;
|
|
40
|
+
declare function incrementalUpdate(oldSite: Mwn, newSite: Mwn, config: Config, onSynced?: (items: SyncNotifyItem[]) => void): Promise<void>;
|
|
30
41
|
export { syncSinglePage, syncPages, incrementalUpdate };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-oni-sync-bot",
|
|
3
3
|
"description": "缺氧Wiki站镜像点同步-测试",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@types/node-fetch": "^2.6.13",
|
|
33
|
-
"form-data": "^4.0.
|
|
33
|
+
"form-data": "^4.0.6",
|
|
34
34
|
"koishi-plugin-cron": "^3.1.0",
|
|
35
|
-
"mwn": "^3.0.
|
|
35
|
+
"mwn": "^3.0.3",
|
|
36
36
|
"node-fetch": "^3.3.2",
|
|
37
|
-
"pinyin-pro": "^3.28.
|
|
37
|
+
"pinyin-pro": "^3.28.1"
|
|
38
38
|
}
|
|
39
39
|
}
|