koishi-plugin-oni-sync-bot 0.0.2 → 0.0.3
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 +175 -171
- package/lib/utils/tools.d.ts +3 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -36,11 +36,60 @@ __export(src_exports, {
|
|
|
36
36
|
name: () => name
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(src_exports);
|
|
39
|
-
var
|
|
39
|
+
var import_koishi5 = require("koishi");
|
|
40
40
|
var import_path = require("path");
|
|
41
41
|
|
|
42
42
|
// src/utils/login.ts
|
|
43
43
|
var import_mwn = require("mwn");
|
|
44
|
+
|
|
45
|
+
// src/utils/tools.ts
|
|
46
|
+
var import_koishi = require("koishi");
|
|
47
|
+
var import_pinyin_pro = require("pinyin-pro");
|
|
48
|
+
var CROSS_SITE_LINK_REGEX = /\[\[(en|ru|pt-br):[^\]]*\]\]/g;
|
|
49
|
+
var DEV_TEXT_REGEX = /Dev:/g;
|
|
50
|
+
var MODULE_NAMESPACE_PREFIX = "Module:Dev/";
|
|
51
|
+
function clean_page_text(text) {
|
|
52
|
+
const textWithoutCrossLink = text.replace(CROSS_SITE_LINK_REGEX, "");
|
|
53
|
+
const textWithReplacedDev = textWithoutCrossLink.replace(
|
|
54
|
+
DEV_TEXT_REGEX,
|
|
55
|
+
MODULE_NAMESPACE_PREFIX
|
|
56
|
+
);
|
|
57
|
+
return textWithReplacedDev;
|
|
58
|
+
}
|
|
59
|
+
__name(clean_page_text, "clean_page_text");
|
|
60
|
+
async function getAndProcessPageContent(site, pageTitle) {
|
|
61
|
+
try {
|
|
62
|
+
const res = await site.read(pageTitle);
|
|
63
|
+
const rawText = res.revisions[0]?.content || "";
|
|
64
|
+
const processedText = clean_page_text(rawText);
|
|
65
|
+
return processedText.trimEnd();
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw new Error(`[${pageTitle}] 内容获取失败: ${err}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
__name(getAndProcessPageContent, "getAndProcessPageContent");
|
|
71
|
+
function generatePinyinInfo(text) {
|
|
72
|
+
if (!text) return { pinyin_full: "", pinyin_first: "" };
|
|
73
|
+
const cleanText = text.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
|
|
74
|
+
if (!cleanText) return { pinyin_full: "", pinyin_first: "" };
|
|
75
|
+
const fullPinyin = (0, import_pinyin_pro.pinyin)(cleanText, {
|
|
76
|
+
toneType: "none",
|
|
77
|
+
type: "string",
|
|
78
|
+
separator: ""
|
|
79
|
+
}).toLowerCase();
|
|
80
|
+
const firstLetter = (0, import_pinyin_pro.pinyin)(cleanText, {
|
|
81
|
+
pattern: "initial",
|
|
82
|
+
separator: ""
|
|
83
|
+
}).toLowerCase();
|
|
84
|
+
return {
|
|
85
|
+
pinyin_full: fullPinyin,
|
|
86
|
+
pinyin_first: firstLetter
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
__name(generatePinyinInfo, "generatePinyinInfo");
|
|
90
|
+
var logger = new import_koishi.Logger("oni-sync");
|
|
91
|
+
|
|
92
|
+
// src/utils/login.ts
|
|
44
93
|
async function login(siteConfig) {
|
|
45
94
|
const bot = new import_mwn.Mwn({
|
|
46
95
|
apiUrl: siteConfig.api,
|
|
@@ -61,7 +110,7 @@ async function login(siteConfig) {
|
|
|
61
110
|
}
|
|
62
111
|
bot.setRequestOptions(customRequestOptions);
|
|
63
112
|
await bot.login();
|
|
64
|
-
|
|
113
|
+
logger.info(`✅ 成功登录 ${siteConfig.name}`);
|
|
65
114
|
return bot;
|
|
66
115
|
}
|
|
67
116
|
__name(login, "login");
|
|
@@ -89,56 +138,11 @@ function getSitesConfig(config) {
|
|
|
89
138
|
}
|
|
90
139
|
__name(getSitesConfig, "getSitesConfig");
|
|
91
140
|
|
|
92
|
-
// src/utils/tools.ts
|
|
93
|
-
var import_pinyin_pro = require("pinyin-pro");
|
|
94
|
-
var CROSS_SITE_LINK_REGEX = /\[\[(en|ru|pt-br):[^\]]*\]\]/g;
|
|
95
|
-
var DEV_TEXT_REGEX = /Dev:/g;
|
|
96
|
-
var MODULE_NAMESPACE_PREFIX = "Module:Dev/";
|
|
97
|
-
function clean_page_text(text) {
|
|
98
|
-
const textWithoutCrossLink = text.replace(CROSS_SITE_LINK_REGEX, "");
|
|
99
|
-
const textWithReplacedDev = textWithoutCrossLink.replace(
|
|
100
|
-
DEV_TEXT_REGEX,
|
|
101
|
-
MODULE_NAMESPACE_PREFIX
|
|
102
|
-
);
|
|
103
|
-
return textWithReplacedDev;
|
|
104
|
-
}
|
|
105
|
-
__name(clean_page_text, "clean_page_text");
|
|
106
|
-
async function getAndProcessPageContent(site, pageTitle) {
|
|
107
|
-
try {
|
|
108
|
-
const res = await site.read(pageTitle);
|
|
109
|
-
const rawText = res.revisions[0]?.content || "";
|
|
110
|
-
const processedText = clean_page_text(rawText);
|
|
111
|
-
return processedText.trimEnd();
|
|
112
|
-
} catch (err) {
|
|
113
|
-
throw new Error(`[${pageTitle}] 内容获取失败: ${err}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
__name(getAndProcessPageContent, "getAndProcessPageContent");
|
|
117
|
-
function generatePinyinInfo(text) {
|
|
118
|
-
if (!text) return { pinyin_full: "", pinyin_first: "" };
|
|
119
|
-
const cleanText = text.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, "");
|
|
120
|
-
if (!cleanText) return { pinyin_full: "", pinyin_first: "" };
|
|
121
|
-
const fullPinyin = (0, import_pinyin_pro.pinyin)(cleanText, {
|
|
122
|
-
toneType: "none",
|
|
123
|
-
type: "string",
|
|
124
|
-
separator: ""
|
|
125
|
-
}).toLowerCase();
|
|
126
|
-
const firstLetter = (0, import_pinyin_pro.pinyin)(cleanText, {
|
|
127
|
-
pattern: "initial",
|
|
128
|
-
separator: ""
|
|
129
|
-
}).toLowerCase();
|
|
130
|
-
return {
|
|
131
|
-
pinyin_full: fullPinyin,
|
|
132
|
-
pinyin_first: firstLetter
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
__name(generatePinyinInfo, "generatePinyinInfo");
|
|
136
|
-
|
|
137
141
|
// src/sync/pageSync.ts
|
|
138
|
-
var
|
|
142
|
+
var import_koishi3 = require("koishi");
|
|
139
143
|
|
|
140
144
|
// src/sync/imgSync.ts
|
|
141
|
-
var
|
|
145
|
+
var import_koishi2 = require("koishi");
|
|
142
146
|
var import_node_fetch = __toESM(require("node-fetch"));
|
|
143
147
|
var import_form_data = __toESM(require("form-data"));
|
|
144
148
|
var CONFIG = {
|
|
@@ -168,29 +172,29 @@ async function getImageInfo(site, fileName) {
|
|
|
168
172
|
size: imageInfo.size
|
|
169
173
|
};
|
|
170
174
|
} catch (error) {
|
|
171
|
-
|
|
175
|
+
logger.error(`[GetImageInfo] 获取 ${fileName} 信息失败:`, error);
|
|
172
176
|
return null;
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
__name(getImageInfo, "getImageInfo");
|
|
176
180
|
async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
177
181
|
if (CONFIG.IGNORED_IMAGES.includes(fileName)) {
|
|
178
|
-
|
|
182
|
+
logger.info(`[SyncImg] 🚫 图片 ${fileName} 在忽略列表,跳过`);
|
|
179
183
|
return { success: true, reason: "ignored" };
|
|
180
184
|
}
|
|
181
185
|
try {
|
|
182
|
-
|
|
186
|
+
logger.info(`[SyncImg] 🚀 开始处理: ${fileName}`);
|
|
183
187
|
const sourceImageInfo = await getImageInfo(sourceBot, fileName);
|
|
184
188
|
if (!sourceImageInfo) {
|
|
185
|
-
|
|
189
|
+
logger.info(`[SyncImg] ❌ 源站未找到图片: ${fileName}`);
|
|
186
190
|
return { success: false, reason: "source_missing" };
|
|
187
191
|
}
|
|
188
192
|
const targetImageInfo = await getImageInfo(targetBot, fileName);
|
|
189
193
|
if (targetImageInfo && targetImageInfo.sha1 === sourceImageInfo.sha1) {
|
|
190
|
-
|
|
194
|
+
logger.info(`[SyncImg] 🟡 图片 ${fileName} 已存在且内容一致,跳过`);
|
|
191
195
|
return { success: true, reason: "no_change" };
|
|
192
196
|
}
|
|
193
|
-
|
|
197
|
+
logger.info(`[SyncImg] 📥 下载图片: ${sourceImageInfo.url}`);
|
|
194
198
|
const imageResponse = await (0, import_node_fetch.default)(sourceImageInfo.url, {
|
|
195
199
|
headers: {
|
|
196
200
|
"User-Agent": "OniSyncBot/1.0 (https://klei.vip; Charles@klei.vip)"
|
|
@@ -200,7 +204,7 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
|
200
204
|
throw new Error(`图片下载失败,HTTP状态码: ${imageResponse.status}`);
|
|
201
205
|
}
|
|
202
206
|
const imageBuffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
203
|
-
|
|
207
|
+
logger.info(
|
|
204
208
|
`[SyncImg] 📤 上传图片: ${fileName} (大小: ${(imageBuffer.length / 1024).toFixed(1)} KB)`
|
|
205
209
|
);
|
|
206
210
|
const token = await targetBot.getCsrfToken();
|
|
@@ -227,7 +231,7 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
|
227
231
|
});
|
|
228
232
|
const responseData = rawResponse.data;
|
|
229
233
|
if (responseData.upload && responseData.upload.result === "Success") {
|
|
230
|
-
|
|
234
|
+
logger.info(`[SyncImg] ✅ 图片 ${fileName} 同步成功`);
|
|
231
235
|
return { success: true, reason: "synced" };
|
|
232
236
|
} else if (responseData.error) {
|
|
233
237
|
throw new Error(`${responseData.error.code}: ${responseData.error.info}`);
|
|
@@ -236,13 +240,13 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
|
|
|
236
240
|
}
|
|
237
241
|
} catch (error) {
|
|
238
242
|
const errMsg = error.message || String(error);
|
|
239
|
-
|
|
243
|
+
logger.error(`[SyncImg] ❌ 图片 ${fileName} 同步失败:`, errMsg);
|
|
240
244
|
return { success: false, reason: errMsg };
|
|
241
245
|
}
|
|
242
246
|
}
|
|
243
247
|
__name(syncSingleImage, "syncSingleImage");
|
|
244
248
|
async function getAllImages(site) {
|
|
245
|
-
|
|
249
|
+
logger.info(`[SyncAllImg] 📥 开始获取WikiGG所有图片`);
|
|
246
250
|
const allImages = [];
|
|
247
251
|
const queryGen = site.continuedQueryGen({
|
|
248
252
|
action: "query",
|
|
@@ -255,9 +259,9 @@ async function getAllImages(site) {
|
|
|
255
259
|
const imageItems = res.query?.allimages || [];
|
|
256
260
|
const imageTitles = imageItems.map((img) => img.title);
|
|
257
261
|
allImages.push(...imageTitles);
|
|
258
|
-
|
|
262
|
+
logger.info(`[SyncAllImg] 📄 已获取 ${allImages.length} 个图片`);
|
|
259
263
|
}
|
|
260
|
-
|
|
264
|
+
logger.info(`[SyncAllImg] 📊 总计获取到 ${allImages.length} 个图片`);
|
|
261
265
|
return allImages;
|
|
262
266
|
}
|
|
263
267
|
__name(getAllImages, "getAllImages");
|
|
@@ -265,20 +269,20 @@ async function syncAllImages(sourceBot, targetBot, config) {
|
|
|
265
269
|
try {
|
|
266
270
|
const imageList = await getAllImages(sourceBot);
|
|
267
271
|
if (imageList.length === 0) {
|
|
268
|
-
|
|
272
|
+
logger.info(`[SyncAllImg] 📭 源站无图片可同步,结束`);
|
|
269
273
|
return;
|
|
270
274
|
}
|
|
271
275
|
let successCount = 0;
|
|
272
276
|
let failCount = 0;
|
|
273
277
|
let skipCount = 0;
|
|
274
278
|
const failedImages = [];
|
|
275
|
-
|
|
279
|
+
logger.info(
|
|
276
280
|
`[SyncAllImg] 🚦 开始批量同步,总计 ${imageList.length} 个图片`
|
|
277
281
|
);
|
|
278
282
|
for (let i = 0; i < imageList.length; i++) {
|
|
279
283
|
const fileName = imageList[i];
|
|
280
284
|
const progress = (i + 1) / imageList.length * 100;
|
|
281
|
-
|
|
285
|
+
logger.info(
|
|
282
286
|
`
|
|
283
287
|
[SyncAllImg] 📈 进度 ${i + 1}/${imageList.length} (${progress.toFixed(1)}%)`
|
|
284
288
|
);
|
|
@@ -291,23 +295,23 @@ async function syncAllImages(sourceBot, targetBot, config) {
|
|
|
291
295
|
if (!result.success) {
|
|
292
296
|
failCount++;
|
|
293
297
|
failedImages.push(fileName);
|
|
294
|
-
await (0,
|
|
298
|
+
await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_FAILED);
|
|
295
299
|
} else {
|
|
296
300
|
successCount++;
|
|
297
301
|
if (result.reason === "ignored" || result.reason === "no_change") {
|
|
298
302
|
skipCount++;
|
|
299
303
|
}
|
|
300
|
-
await (0,
|
|
304
|
+
await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
|
|
301
305
|
}
|
|
302
306
|
}
|
|
303
307
|
if (failedImages.length > 0) {
|
|
304
|
-
|
|
308
|
+
logger.info(
|
|
305
309
|
`
|
|
306
310
|
[SyncAllImg] 🔄 开始重试 ${failedImages.length} 个失败图片`
|
|
307
311
|
);
|
|
308
312
|
const stillFailed = [];
|
|
309
313
|
for (const fileName of failedImages) {
|
|
310
|
-
|
|
314
|
+
logger.info(`
|
|
311
315
|
[SyncAllImg] 🔁 重试: ${fileName}`);
|
|
312
316
|
const result = await syncSingleImage(
|
|
313
317
|
sourceBot,
|
|
@@ -318,31 +322,31 @@ async function syncAllImages(sourceBot, targetBot, config) {
|
|
|
318
322
|
if (result.success) {
|
|
319
323
|
successCount++;
|
|
320
324
|
failCount--;
|
|
321
|
-
|
|
325
|
+
logger.info(`[SyncAllImg] ✅ 重试成功: ${fileName}`);
|
|
322
326
|
} else {
|
|
323
327
|
stillFailed.push(fileName);
|
|
324
|
-
|
|
328
|
+
logger.info(`[SyncAllImg] ❌ 重试失败: ${fileName}`);
|
|
325
329
|
}
|
|
326
|
-
await (0,
|
|
330
|
+
await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
|
|
327
331
|
}
|
|
328
332
|
if (stillFailed.length > 0) {
|
|
329
|
-
|
|
333
|
+
logger.info(`
|
|
330
334
|
[SyncAllImg] ❌ 最终失败列表(需手动处理):`);
|
|
331
335
|
stillFailed.forEach(
|
|
332
|
-
(title, idx) =>
|
|
336
|
+
(title, idx) => logger.info(` ${idx + 1}. ${title}`)
|
|
333
337
|
);
|
|
334
338
|
} else {
|
|
335
|
-
|
|
339
|
+
logger.info(`
|
|
336
340
|
[SyncAllImg] 🎉 所有失败图片重试成功!`);
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
|
-
|
|
343
|
+
logger.info(`
|
|
340
344
|
[SyncAllImg] 📊 同步完成!`);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
345
|
+
logger.info(`├─ 总计:${imageList.length} 个图片`);
|
|
346
|
+
logger.info(`├─ 成功:${successCount} 个(含跳过 ${skipCount} 个)`);
|
|
347
|
+
logger.info(`└─ 失败:${failCount} 个`);
|
|
344
348
|
} catch (globalError) {
|
|
345
|
-
|
|
349
|
+
logger.error(`[SyncAllImg] 💥 同步流程异常终止:`, globalError);
|
|
346
350
|
throw globalError;
|
|
347
351
|
}
|
|
348
352
|
}
|
|
@@ -361,31 +365,31 @@ var CONFIG2 = {
|
|
|
361
365
|
};
|
|
362
366
|
async function syncSinglePage(oldSite, newSite, pageTitle, user) {
|
|
363
367
|
if (CONFIG2.IGNORED_PAGES.has(pageTitle)) {
|
|
364
|
-
|
|
368
|
+
logger.info(`[syncSinglePage] 🚫 页面 ${pageTitle} 在忽略列表中,跳过`);
|
|
365
369
|
return { success: true, reason: "ignored" };
|
|
366
370
|
}
|
|
367
371
|
try {
|
|
368
|
-
|
|
372
|
+
logger.info(`[syncSinglePage] 🚀 开始同步页面: ${pageTitle}`);
|
|
369
373
|
const [oldContent, newContent] = await Promise.all([
|
|
370
374
|
getAndProcessPageContent(oldSite, pageTitle),
|
|
371
375
|
getAndProcessPageContent(newSite, pageTitle)
|
|
372
376
|
]);
|
|
373
377
|
if (oldContent === newContent) {
|
|
374
|
-
|
|
378
|
+
logger.info(`[syncSinglePage] 🟡 页面 ${pageTitle} 内容未改变,跳过`);
|
|
375
379
|
return { success: true, reason: "no_change" };
|
|
376
380
|
}
|
|
377
381
|
await newSite.save(pageTitle, oldContent, `由:${user} 触发更改,此时同步`);
|
|
378
|
-
|
|
382
|
+
logger.info(`[syncSinglePage] ✅ 页面 ${pageTitle} 同步成功`);
|
|
379
383
|
return { success: true, reason: "synced" };
|
|
380
384
|
} catch (error) {
|
|
381
385
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
382
|
-
|
|
386
|
+
logger.error(`[syncSinglePage] ❌ 页面 ${pageTitle} 同步失败:`, errMsg);
|
|
383
387
|
return { success: false, reason: errMsg };
|
|
384
388
|
}
|
|
385
389
|
}
|
|
386
390
|
__name(syncSinglePage, "syncSinglePage");
|
|
387
391
|
async function getAllPages(site) {
|
|
388
|
-
|
|
392
|
+
logger.info(
|
|
389
393
|
`[SyncAllPages] 📥 开始获取原站点所有页面(命名空间${CONFIG2.NAMESPACE})`
|
|
390
394
|
);
|
|
391
395
|
const allPages = [];
|
|
@@ -399,9 +403,9 @@ async function getAllPages(site) {
|
|
|
399
403
|
for await (const res of queryGen) {
|
|
400
404
|
const pageTitles = res.query?.allpages?.map((page) => page.title) || [];
|
|
401
405
|
allPages.push(...pageTitles);
|
|
402
|
-
|
|
406
|
+
logger.info(`[SyncAllPages] 📄 已获取 ${allPages.length} 个页面`);
|
|
403
407
|
}
|
|
404
|
-
|
|
408
|
+
logger.info(`[SyncAllPages] 📊 原站点总计获取到 ${allPages.length} 个页面`);
|
|
405
409
|
return allPages;
|
|
406
410
|
}
|
|
407
411
|
__name(getAllPages, "getAllPages");
|
|
@@ -410,41 +414,41 @@ async function processPageWithStats(oldSite, newSite, pageTitle, user, stats, fa
|
|
|
410
414
|
if (!syncResult.success) {
|
|
411
415
|
stats.failCount++;
|
|
412
416
|
failedPages.push(pageTitle);
|
|
413
|
-
await (0,
|
|
417
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
|
|
414
418
|
} else {
|
|
415
419
|
stats.successCount++;
|
|
416
420
|
if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
|
|
417
421
|
stats.skipCount++;
|
|
418
422
|
}
|
|
419
|
-
await (0,
|
|
423
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
|
|
420
424
|
}
|
|
421
425
|
}
|
|
422
426
|
__name(processPageWithStats, "processPageWithStats");
|
|
423
427
|
function printProgress(current, total, pageTitle) {
|
|
424
428
|
const progress = (current / total * 100).toFixed(1);
|
|
425
429
|
const remaining = total - current;
|
|
426
|
-
|
|
430
|
+
logger.info(
|
|
427
431
|
`
|
|
428
432
|
[SyncAllPages] 📈 进度 [${current}/${total}] (${progress}%) - 处理 ${pageTitle} | 剩余 ${remaining} 个`
|
|
429
433
|
);
|
|
430
434
|
}
|
|
431
435
|
__name(printProgress, "printProgress");
|
|
432
436
|
function printFinalReport(total, successCount, failCount, skipCount, stillFailed) {
|
|
433
|
-
|
|
437
|
+
logger.info(`
|
|
434
438
|
[SyncAllPages] 📋 ===== 最终同步报告 =====`);
|
|
435
439
|
if (stillFailed.length > 0) {
|
|
436
|
-
|
|
440
|
+
logger.info(`❌ 以下页面经过重试仍然失败,请手动检查:`);
|
|
437
441
|
stillFailed.forEach((title, idx) => {
|
|
438
|
-
|
|
442
|
+
logger.info(` ${idx + 1}. ${title}`);
|
|
439
443
|
});
|
|
440
444
|
} else {
|
|
441
|
-
|
|
445
|
+
logger.info(`🎉 所有页面同步成功(含重试)!`);
|
|
442
446
|
}
|
|
443
|
-
|
|
447
|
+
logger.info(`
|
|
444
448
|
[SyncAllPages] 🎯 同步流程结束!`);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
logger.info(`├─ 总计:${total} 个页面`);
|
|
450
|
+
logger.info(`├─ 成功:${successCount} 个(含跳过 ${skipCount} 个)`);
|
|
451
|
+
logger.info(`└─ 失败:${failCount} 个`);
|
|
448
452
|
}
|
|
449
453
|
__name(printFinalReport, "printFinalReport");
|
|
450
454
|
async function syncPages(oldSite, newSite) {
|
|
@@ -452,12 +456,12 @@ async function syncPages(oldSite, newSite) {
|
|
|
452
456
|
const oldPageList = await getAllPages(oldSite);
|
|
453
457
|
const total = oldPageList.length;
|
|
454
458
|
if (total === 0) {
|
|
455
|
-
|
|
459
|
+
logger.info(`[SyncAllPages] 📭 原站点无页面可同步,结束`);
|
|
456
460
|
return;
|
|
457
461
|
}
|
|
458
462
|
const stats = { successCount: 0, failCount: 0, skipCount: 0 };
|
|
459
463
|
const failedPages = [];
|
|
460
|
-
|
|
464
|
+
logger.info(`[SyncAllPages] 🚦 开始批量同步,总计 ${total} 个页面`);
|
|
461
465
|
for (let index = 0; index < total; index++) {
|
|
462
466
|
const pageTitle = oldPageList[index];
|
|
463
467
|
printProgress(index + 1, total, pageTitle);
|
|
@@ -472,12 +476,12 @@ async function syncPages(oldSite, newSite) {
|
|
|
472
476
|
}
|
|
473
477
|
let stillFailed = [];
|
|
474
478
|
if (failedPages.length > 0) {
|
|
475
|
-
|
|
479
|
+
logger.info(
|
|
476
480
|
`
|
|
477
481
|
[SyncAllPages] 🔄 ===== 开始重试 ${failedPages.length} 个失败页面 =====`
|
|
478
482
|
);
|
|
479
483
|
for (const pageTitle of failedPages) {
|
|
480
|
-
|
|
484
|
+
logger.info(`
|
|
481
485
|
[SyncAllPages] 🔁 重试中: ${pageTitle}`);
|
|
482
486
|
const syncResult = await syncSinglePage(
|
|
483
487
|
oldSite,
|
|
@@ -491,12 +495,12 @@ async function syncPages(oldSite, newSite) {
|
|
|
491
495
|
if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
|
|
492
496
|
stats.skipCount++;
|
|
493
497
|
}
|
|
494
|
-
|
|
495
|
-
await (0,
|
|
498
|
+
logger.info(`[SyncAllPages] ✅ 页面 ${pageTitle} 重试成功`);
|
|
499
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
|
|
496
500
|
} else {
|
|
497
501
|
stillFailed.push(pageTitle);
|
|
498
|
-
|
|
499
|
-
await (0,
|
|
502
|
+
logger.info(`[SyncAllPages] ❌ 页面 ${pageTitle} 再次失败`);
|
|
503
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
|
|
500
504
|
}
|
|
501
505
|
}
|
|
502
506
|
}
|
|
@@ -508,7 +512,7 @@ async function syncPages(oldSite, newSite) {
|
|
|
508
512
|
stillFailed
|
|
509
513
|
);
|
|
510
514
|
} catch (globalError) {
|
|
511
|
-
|
|
515
|
+
logger.error(`[SyncAllPages] 💥 批量同步流程异常终止:`, globalError);
|
|
512
516
|
throw globalError;
|
|
513
517
|
}
|
|
514
518
|
}
|
|
@@ -517,7 +521,7 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
517
521
|
try {
|
|
518
522
|
const now = /* @__PURE__ */ new Date();
|
|
519
523
|
const threeHoursAgo = new Date(now.getTime() - 3 * 60 * 60 * 1e3);
|
|
520
|
-
|
|
524
|
+
logger.info(
|
|
521
525
|
`[增量更新流程] ⏰ 开始处理 ${threeHoursAgo.toISOString()} 到 ${now.toISOString()} 的更新...`
|
|
522
526
|
);
|
|
523
527
|
const queryGen = oldSite.continuedQueryGen({
|
|
@@ -536,12 +540,12 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
536
540
|
for (const page of pages) {
|
|
537
541
|
const title = page.title;
|
|
538
542
|
if (processedTitles.has(title)) {
|
|
539
|
-
|
|
543
|
+
logger.info(`[增量更新流程] ⏭️ 已经处理过 ${title}, 跳过`);
|
|
540
544
|
totalSkipped++;
|
|
541
545
|
continue;
|
|
542
546
|
}
|
|
543
547
|
if (CONFIG2.IGNORED_PAGES.has(title)) {
|
|
544
|
-
|
|
548
|
+
logger.info(
|
|
545
549
|
`[增量更新流程] 🚫 ${title} 在无需处理的页面列表中, 跳过`
|
|
546
550
|
);
|
|
547
551
|
processedTitles.add(title);
|
|
@@ -553,7 +557,7 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
553
557
|
try {
|
|
554
558
|
if (title.startsWith(CONFIG2.FILE_NAMESPACE_PREFIX)) {
|
|
555
559
|
const fileName = title.replace(CONFIG2.FILE_NAMESPACE_PREFIX, "");
|
|
556
|
-
|
|
560
|
+
logger.info(
|
|
557
561
|
`[增量更新流程] 🖼️ 检查到图片: ${title},正在尝试转存`
|
|
558
562
|
);
|
|
559
563
|
await syncSingleImage(oldSite, newSite, fileName, config);
|
|
@@ -565,26 +569,26 @@ async function incrementalUpdate(oldSite, newSite, config) {
|
|
|
565
569
|
CONFIG2.INCREMENTAL_USER
|
|
566
570
|
);
|
|
567
571
|
}
|
|
568
|
-
await (0,
|
|
572
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
|
|
569
573
|
} catch (error) {
|
|
570
574
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
571
|
-
|
|
572
|
-
await (0,
|
|
575
|
+
logger.error(`[增量更新流程] ❌ 处理 ${title} 时出错:`, errMsg);
|
|
576
|
+
await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
|
|
573
577
|
}
|
|
574
578
|
}
|
|
575
579
|
}
|
|
576
|
-
|
|
580
|
+
logger.info(
|
|
577
581
|
`[增量更新流程] ✅ 增量更新完成!处理: ${totalProcessed}, 跳过: ${totalSkipped}`
|
|
578
582
|
);
|
|
579
583
|
} catch (globalError) {
|
|
580
|
-
|
|
584
|
+
logger.error(`[增量更新流程] 💥 增量更新流程异常终止:`, globalError);
|
|
581
585
|
throw globalError;
|
|
582
586
|
}
|
|
583
587
|
}
|
|
584
588
|
__name(incrementalUpdate, "incrementalUpdate");
|
|
585
589
|
|
|
586
590
|
// src/sync/moduleSync.ts
|
|
587
|
-
var
|
|
591
|
+
var import_koishi4 = require("koishi");
|
|
588
592
|
var CONFIG3 = {
|
|
589
593
|
MODLE_NAMESPACE: 828,
|
|
590
594
|
// 模块命名空间 (注意:这里原代码拼写为 MODLE,保留原样)
|
|
@@ -597,17 +601,17 @@ var CONFIG3 = {
|
|
|
597
601
|
};
|
|
598
602
|
async function syncSingleModule(oldSite, newSite, moduleTitle, user) {
|
|
599
603
|
if (CONFIG3.IGNORED_MODULES.includes(moduleTitle)) {
|
|
600
|
-
|
|
604
|
+
logger.info(`[SyncModule] 🚫 模块 ${moduleTitle} 在忽略列表中,跳过`);
|
|
601
605
|
return { success: true, reason: "ignored" };
|
|
602
606
|
}
|
|
603
607
|
try {
|
|
604
|
-
|
|
608
|
+
logger.info(`[SyncModule] 🔍 开始获取模块 ${moduleTitle} 的内容`);
|
|
605
609
|
const [oldContent, newContent] = await Promise.all([
|
|
606
610
|
getAndProcessPageContent(oldSite, moduleTitle),
|
|
607
611
|
getAndProcessPageContent(newSite, moduleTitle)
|
|
608
612
|
]);
|
|
609
613
|
if (oldContent === newContent) {
|
|
610
|
-
|
|
614
|
+
logger.info(`[SyncModule] 🟡 模块 ${moduleTitle} 内容未改变,跳过`);
|
|
611
615
|
return { success: true, reason: "no_change" };
|
|
612
616
|
}
|
|
613
617
|
await newSite.save(
|
|
@@ -615,17 +619,17 @@ async function syncSingleModule(oldSite, newSite, moduleTitle, user) {
|
|
|
615
619
|
oldContent,
|
|
616
620
|
`由:${user || "同步坤器人手动"} 触发更改,此时同步`
|
|
617
621
|
);
|
|
618
|
-
|
|
622
|
+
logger.info(`[SyncModule] ✅ 模块 ${moduleTitle} 同步成功`);
|
|
619
623
|
return { success: true, reason: "synced" };
|
|
620
624
|
} catch (error) {
|
|
621
625
|
const errMsg = error.message || String(error);
|
|
622
|
-
|
|
626
|
+
logger.error(`[SyncModule] ❌ 模块 ${moduleTitle} 同步失败:`, errMsg);
|
|
623
627
|
return { success: false, reason: errMsg };
|
|
624
628
|
}
|
|
625
629
|
}
|
|
626
630
|
__name(syncSingleModule, "syncSingleModule");
|
|
627
631
|
async function getAllModules(site) {
|
|
628
|
-
|
|
632
|
+
logger.info(
|
|
629
633
|
`[SyncAllModules] 📥 开始获取原站点所有模块(命名空间${CONFIG3.MODLE_NAMESPACE})`
|
|
630
634
|
);
|
|
631
635
|
const allModules = [];
|
|
@@ -640,9 +644,9 @@ async function getAllModules(site) {
|
|
|
640
644
|
for await (const res of queryGen) {
|
|
641
645
|
const moduleTitles = res.query?.allpages?.map((page) => page.title) || [];
|
|
642
646
|
allModules.push(...moduleTitles);
|
|
643
|
-
|
|
647
|
+
logger.info(`[SyncAllModules] 📄 已获取 ${allModules.length} 个模块`);
|
|
644
648
|
}
|
|
645
|
-
|
|
649
|
+
logger.info(
|
|
646
650
|
`[SyncAllModules] 📊 原站点总计获取到 ${allModules.length} 个模块`
|
|
647
651
|
);
|
|
648
652
|
return allModules;
|
|
@@ -653,20 +657,20 @@ async function syncModules(oldSite, newSite) {
|
|
|
653
657
|
const oldModuleList = await getAllModules(oldSite);
|
|
654
658
|
const total = oldModuleList.length;
|
|
655
659
|
if (total === 0) {
|
|
656
|
-
|
|
660
|
+
logger.info(`[SyncAllModules] 📭 原站点无模块可同步,结束`);
|
|
657
661
|
return;
|
|
658
662
|
}
|
|
659
663
|
let successCount = 0;
|
|
660
664
|
let failCount = 0;
|
|
661
665
|
let skipCount = 0;
|
|
662
666
|
const failedModules = [];
|
|
663
|
-
|
|
667
|
+
logger.info(`[SyncAllModules] 🚦 开始批量同步,总计 ${total} 个模块`);
|
|
664
668
|
for (let index = 0; index < total; index++) {
|
|
665
669
|
const moduleTitle = oldModuleList[index];
|
|
666
670
|
const current = index + 1;
|
|
667
671
|
const remaining = total - current;
|
|
668
672
|
const progress = (current / total * 100).toFixed(1);
|
|
669
|
-
|
|
673
|
+
logger.info(
|
|
670
674
|
`
|
|
671
675
|
[SyncAllModules] 📈 进度 [${current}/${total}] (${progress}%) - 处理 ${moduleTitle} | 剩余 ${remaining} 个`
|
|
672
676
|
);
|
|
@@ -679,23 +683,23 @@ async function syncModules(oldSite, newSite) {
|
|
|
679
683
|
if (!syncResult.success) {
|
|
680
684
|
failCount++;
|
|
681
685
|
failedModules.push(moduleTitle);
|
|
682
|
-
await (0,
|
|
686
|
+
await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
|
|
683
687
|
} else {
|
|
684
688
|
successCount++;
|
|
685
689
|
if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
|
|
686
690
|
skipCount++;
|
|
687
691
|
}
|
|
688
|
-
await (0,
|
|
692
|
+
await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
|
|
689
693
|
}
|
|
690
694
|
}
|
|
691
695
|
if (failedModules.length > 0) {
|
|
692
|
-
|
|
696
|
+
logger.info(
|
|
693
697
|
`
|
|
694
698
|
[SyncAllModules] 🔄 ===== 开始重试 ${failedModules.length} 个失败模块 =====`
|
|
695
699
|
);
|
|
696
700
|
const stillFailed = [];
|
|
697
701
|
for (const moduleTitle of failedModules) {
|
|
698
|
-
|
|
702
|
+
logger.info(`
|
|
699
703
|
[SyncAllModules] 🔁 重试中: ${moduleTitle}`);
|
|
700
704
|
const syncResult = await syncSingleModule(
|
|
701
705
|
oldSite,
|
|
@@ -709,32 +713,32 @@ async function syncModules(oldSite, newSite) {
|
|
|
709
713
|
if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
|
|
710
714
|
skipCount++;
|
|
711
715
|
}
|
|
712
|
-
|
|
713
|
-
await (0,
|
|
716
|
+
logger.info(`[SyncAllModules] ✅ 模块 ${moduleTitle} 重试成功`);
|
|
717
|
+
await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
|
|
714
718
|
} else {
|
|
715
719
|
stillFailed.push(moduleTitle);
|
|
716
|
-
|
|
717
|
-
await (0,
|
|
720
|
+
logger.info(`[SyncAllModules] ❌ 模块 ${moduleTitle} 再次失败`);
|
|
721
|
+
await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
|
|
718
722
|
}
|
|
719
723
|
}
|
|
720
|
-
|
|
724
|
+
logger.info(`
|
|
721
725
|
[SyncAllModules] 📋 ===== 最终同步报告 =====`);
|
|
722
726
|
if (stillFailed.length > 0) {
|
|
723
|
-
|
|
727
|
+
logger.info(`❌ 以下模块经过重试仍然失败,请手动检查:`);
|
|
724
728
|
stillFailed.forEach((title, idx) => {
|
|
725
|
-
|
|
729
|
+
logger.info(` ${idx + 1}. ${title}`);
|
|
726
730
|
});
|
|
727
731
|
} else {
|
|
728
|
-
|
|
732
|
+
logger.info(`🎉 所有模块同步成功(含重试)!`);
|
|
729
733
|
}
|
|
730
734
|
}
|
|
731
|
-
|
|
735
|
+
logger.info(`
|
|
732
736
|
[SyncAllModules] 🎯 同步完成!`);
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
737
|
+
logger.info(`├─ 总计:${total} 个模块`);
|
|
738
|
+
logger.info(`├─ 成功:${successCount} 个(含跳过 ${skipCount} 个)`);
|
|
739
|
+
logger.info(`└─ 失败:${failCount} 个`);
|
|
736
740
|
} catch (error) {
|
|
737
|
-
|
|
741
|
+
logger.error(`[SyncAllModules] 💥 批量同步流程异常终止:`, error);
|
|
738
742
|
throw error;
|
|
739
743
|
}
|
|
740
744
|
}
|
|
@@ -743,18 +747,18 @@ __name(syncModules, "syncModules");
|
|
|
743
747
|
// src/index.ts
|
|
744
748
|
var name = "oni-sync-bot";
|
|
745
749
|
var inject = ["console", "database", "server", "cron"];
|
|
746
|
-
var Config =
|
|
747
|
-
ggUsername:
|
|
748
|
-
ggPassword:
|
|
749
|
-
huijiUsername:
|
|
750
|
-
huijiPassword:
|
|
751
|
-
huijiUAKey:
|
|
752
|
-
domain:
|
|
753
|
-
main_site:
|
|
754
|
-
mirror_site:
|
|
750
|
+
var Config = import_koishi5.Schema.object({
|
|
751
|
+
ggUsername: import_koishi5.Schema.string().description("WIKIGG 用户名").default("${{ env.ggUsername }}"),
|
|
752
|
+
ggPassword: import_koishi5.Schema.string().description("WIKIGG 密码").default("${{ env.ggPassword }}"),
|
|
753
|
+
huijiUsername: import_koishi5.Schema.string().description("灰机wiki 用户名").default("${{ env.huijiUsername }}"),
|
|
754
|
+
huijiPassword: import_koishi5.Schema.string().description("灰机wiki 密码").default("${{ env.huijiPassword }}"),
|
|
755
|
+
huijiUAKey: import_koishi5.Schema.string().description("灰机wiki UAKey").default("${{ env.huijiUAKey }}"),
|
|
756
|
+
domain: import_koishi5.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip"),
|
|
757
|
+
main_site: import_koishi5.Schema.string().description("主站域名(必填,如:oxygennotincluded.wiki.gg)").default("oxygennotincluded.wiki.gg/zh"),
|
|
758
|
+
mirror_site: import_koishi5.Schema.string().description("镜像站域名(必填,如:wiki.biligame.com)").default("wiki.biligame.com/oni")
|
|
755
759
|
});
|
|
756
760
|
function apply(ctx, config) {
|
|
757
|
-
const
|
|
761
|
+
const logger2 = ctx.logger("oni-sync-bot");
|
|
758
762
|
let ggbot;
|
|
759
763
|
let huijibot;
|
|
760
764
|
ctx.inject(["console"], (ctx2) => {
|
|
@@ -794,30 +798,30 @@ function apply(ctx, config) {
|
|
|
794
798
|
router.redirect(targetUrl);
|
|
795
799
|
});
|
|
796
800
|
ctx.on("ready", async () => {
|
|
797
|
-
|
|
801
|
+
logger2.info("初始化中...");
|
|
798
802
|
const sitesConfig = getSitesConfig(config);
|
|
799
803
|
ggbot = await login(sitesConfig.gg);
|
|
800
804
|
huijibot = await login(sitesConfig.huiji);
|
|
801
805
|
if (ggbot.login && huijibot.login) {
|
|
802
|
-
|
|
806
|
+
logger2.info("登录成功,插件已准备就绪");
|
|
803
807
|
} else {
|
|
804
|
-
|
|
808
|
+
logger2.error("登录失败,请检查配置");
|
|
805
809
|
}
|
|
806
810
|
ctx.cron("15 * * * *", async () => {
|
|
807
811
|
await incrementalUpdate(ggbot, huijibot, config);
|
|
808
812
|
});
|
|
809
813
|
ctx.cron("30 8 * * 4", async () => {
|
|
810
814
|
await syncPages(ggbot, huijibot).then(() => {
|
|
811
|
-
|
|
815
|
+
logger2.info("自动任务:尝试同步所有页面,从 WIKIGG 到 灰机wiki");
|
|
812
816
|
}).catch((err) => {
|
|
813
|
-
|
|
817
|
+
logger2.error(`同步所有页面失败,错误信息:${err}`);
|
|
814
818
|
});
|
|
815
819
|
});
|
|
816
820
|
ctx.cron("30 8 * * 3", async () => {
|
|
817
821
|
await syncAllImages(ggbot, huijibot, config).then(() => {
|
|
818
|
-
|
|
822
|
+
logger2.info("自动任务:尝试同步所有图片,从 WIKIGG 到 灰机wiki");
|
|
819
823
|
}).catch((err) => {
|
|
820
|
-
|
|
824
|
+
logger2.error(`同步所有图片失败,错误信息:${err}`);
|
|
821
825
|
});
|
|
822
826
|
});
|
|
823
827
|
});
|
|
@@ -989,7 +993,7 @@ function apply(ctx, config) {
|
|
|
989
993
|
format: "json",
|
|
990
994
|
aplimit: "max"
|
|
991
995
|
});
|
|
992
|
-
|
|
996
|
+
logger2.info("主站页面查询成功");
|
|
993
997
|
const pages = res.query.allpages || [];
|
|
994
998
|
const pageData = pages.map((page) => {
|
|
995
999
|
const { pinyin_full, pinyin_first } = generatePinyinInfo(page.title);
|
|
@@ -1004,9 +1008,9 @@ function apply(ctx, config) {
|
|
|
1004
1008
|
await ctx.database.upsert("wikipages", pageData);
|
|
1005
1009
|
}
|
|
1006
1010
|
session.send(`✅ 检索到 ${pages.length} 个页面,已更新至数据库`);
|
|
1007
|
-
|
|
1011
|
+
logger2.info(`检索到 ${pages.length} 个页面,已更新至数据库`);
|
|
1008
1012
|
} catch (err) {
|
|
1009
|
-
|
|
1013
|
+
logger2.error("主站缓存更新失败", err);
|
|
1010
1014
|
session.send("❌ 主站缓存更新失败,请联系管理员查看日志");
|
|
1011
1015
|
}
|
|
1012
1016
|
});
|
|
@@ -1014,9 +1018,9 @@ function apply(ctx, config) {
|
|
|
1014
1018
|
try {
|
|
1015
1019
|
const count = await ctx.database.remove("wikipages", {});
|
|
1016
1020
|
session.send(`✅ 已删除 ${count.removed} 条本地缓存`);
|
|
1017
|
-
|
|
1021
|
+
logger2.info(`已删除 ${count.removed} 条本地缓存`);
|
|
1018
1022
|
} catch (err) {
|
|
1019
|
-
|
|
1023
|
+
logger2.error("删除缓存失败", err);
|
|
1020
1024
|
session.send("❌ 删除缓存失败,请联系管理员查看日志");
|
|
1021
1025
|
}
|
|
1022
1026
|
});
|
|
@@ -1024,9 +1028,9 @@ function apply(ctx, config) {
|
|
|
1024
1028
|
try {
|
|
1025
1029
|
const pages = await ctx.database.get("wikipages", {});
|
|
1026
1030
|
session.send(`📊 数据库中缓存了 ${pages.length} 条页面`);
|
|
1027
|
-
|
|
1031
|
+
logger2.info(`数据库中缓存了 ${pages.length} 条页面`);
|
|
1028
1032
|
} catch (err) {
|
|
1029
|
-
|
|
1033
|
+
logger2.error("查询缓存状态失败", err);
|
|
1030
1034
|
session.send("❌ 查询缓存状态失败,请联系管理员查看日志");
|
|
1031
1035
|
}
|
|
1032
1036
|
});
|
|
@@ -1042,11 +1046,11 @@ function apply(ctx, config) {
|
|
|
1042
1046
|
`#REDIRECT [[${targetPageName}]]`,
|
|
1043
1047
|
"来自qq机器人的添加重定向页面请求"
|
|
1044
1048
|
);
|
|
1045
|
-
|
|
1049
|
+
logger2.info(`已为 ${pageName} 添加重定向至 ${targetPageName}`);
|
|
1046
1050
|
session.send(`✅ 已尝试添加重定向 ${pageName} -> ${targetPageName}`);
|
|
1047
1051
|
await session.execute(`update`);
|
|
1048
1052
|
} catch (err) {
|
|
1049
|
-
|
|
1053
|
+
logger2.error(`添加重定向 ${pageName} -> ${targetPageName} 失败`, err);
|
|
1050
1054
|
session.send(`❌ 添加重定向失败,请联系管理员查看日志`);
|
|
1051
1055
|
}
|
|
1052
1056
|
});
|
package/lib/utils/tools.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Logger } from "koishi";
|
|
1
2
|
import { Mwn } from "mwn";
|
|
2
3
|
/**
|
|
3
4
|
* 获取并处理页面内容
|
|
@@ -15,4 +16,5 @@ declare function generatePinyinInfo(text: string): {
|
|
|
15
16
|
pinyin_full: string;
|
|
16
17
|
pinyin_first: string;
|
|
17
18
|
};
|
|
18
|
-
|
|
19
|
+
declare const logger: Logger;
|
|
20
|
+
export { getAndProcessPageContent, generatePinyinInfo, logger };
|