koishi-plugin-oni-sync-bot 0.6.2 → 0.7.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 CHANGED
@@ -32,15 +32,13 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  Config: () => Config,
34
34
  apply: () => apply,
35
- inject: () => inject,
36
35
  name: () => name
37
36
  });
38
37
  module.exports = __toCommonJS(src_exports);
39
- var import_koishi5 = require("koishi");
40
- var import_path = require("path");
41
- var import_plugin_console = require("@koishijs/plugin-console");
38
+ var import_koishi11 = require("koishi");
42
39
 
43
- // src/utils/login.ts
40
+ // src/services/wikiBotService.ts
41
+ var import_koishi2 = require("koishi");
44
42
  var import_mwn = require("mwn");
45
43
 
46
44
  // src/utils/tools.ts
@@ -88,63 +86,335 @@ function generatePinyinInfo(text) {
88
86
  };
89
87
  }
90
88
  __name(generatePinyinInfo, "generatePinyinInfo");
89
+ function getErrorMessage(error) {
90
+ if (error instanceof Error) {
91
+ return error.message;
92
+ }
93
+ return String(error);
94
+ }
95
+ __name(getErrorMessage, "getErrorMessage");
91
96
  var logger = new import_koishi.Logger("oni-sync");
92
97
 
93
- // src/utils/login.ts
94
- async function login(siteConfig) {
95
- const bot = new import_mwn.Mwn({
96
- apiUrl: siteConfig.api,
97
- username: siteConfig.username,
98
- password: siteConfig.password,
99
- userAgent: siteConfig.userAgent,
100
- defaultParams: {
101
- assert: "user"
98
+ // src/services/wikiBotService.ts
99
+ var WikiBotService = class _WikiBotService extends import_koishi2.Service {
100
+ static {
101
+ __name(this, "WikiBotService");
102
+ }
103
+ static inject = [];
104
+ ggbot = null;
105
+ bwikibot = null;
106
+ isReady = false;
107
+ botConfig;
108
+ static USER_AGENT = `OniSyncBot/1.0 (https://klei.vip; Charles@klei.vip)`;
109
+ static MAX_RETRIES = 3;
110
+ static RETRY_DELAY = 5e3;
111
+ constructor(ctx, config) {
112
+ super(ctx, "wikiBot", true);
113
+ this.botConfig = config;
114
+ }
115
+ getSitesConfig() {
116
+ return {
117
+ gg: {
118
+ name: "WIKIGG",
119
+ api: "https://oxygennotincluded.wiki.gg/zh/api.php",
120
+ username: this.botConfig.ggUsername,
121
+ password: this.botConfig.ggPassword,
122
+ userAgent: _WikiBotService.USER_AGENT
123
+ },
124
+ bwiki: {
125
+ name: "bwiki",
126
+ api: "https://wiki.biligame.com/oni/api.php",
127
+ username: this.botConfig.bwikiusername,
128
+ password: this.botConfig.bwikipassword,
129
+ userAgent: _WikiBotService.USER_AGENT
130
+ }
131
+ };
132
+ }
133
+ delay(ms) {
134
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
135
+ }
136
+ async loginWithRetry(siteConfig, attempt = 1) {
137
+ try {
138
+ logger.info(
139
+ `正在登录 ${siteConfig.name}... (尝试 ${attempt}/${_WikiBotService.MAX_RETRIES})`
140
+ );
141
+ const bot = await this.login(siteConfig);
142
+ return bot;
143
+ } catch (error) {
144
+ const errorMsg = getErrorMessage(error);
145
+ if (attempt < _WikiBotService.MAX_RETRIES) {
146
+ logger.warn(
147
+ `登录 ${siteConfig.name} 失败,${_WikiBotService.RETRY_DELAY / 1e3}秒后重试...`,
148
+ errorMsg
149
+ );
150
+ await this.delay(_WikiBotService.RETRY_DELAY);
151
+ return this.loginWithRetry(siteConfig, attempt + 1);
152
+ }
153
+ logger.error(
154
+ `登录 ${siteConfig.name} 失败,已达到最大重试次数`,
155
+ errorMsg
156
+ );
157
+ throw error;
102
158
  }
103
- });
104
- if (siteConfig.name === "bwiki") {
105
- const cookieString = "SESSDATA=666; Domain=wiki.biligame.com; Path=/oni; HttpOnly; Secure;";
106
- bot.cookieJar.setCookie(cookieString, bot.options.apiUrl, (err) => {
107
- if (err) console.error("Cookie 注入失败:", err);
159
+ }
160
+ async login(siteConfig) {
161
+ logger.info(`正在连接 ${siteConfig.name} API: ${siteConfig.api}`);
162
+ const bot = new import_mwn.Mwn({
163
+ apiUrl: siteConfig.api,
164
+ username: siteConfig.username,
165
+ password: siteConfig.password,
166
+ userAgent: siteConfig.userAgent,
167
+ defaultParams: {
168
+ assert: "user"
169
+ },
170
+ maxRetries: 0,
171
+ retryPause: 0
108
172
  });
109
- bot.setRequestOptions({
110
- headers: {
111
- referer: "https://wiki.biligame.com/oni/"
173
+ if (siteConfig.name === "bwiki") {
174
+ const cookieString = "SESSDATA=666; Domain=wiki.biligame.com; Path=/oni; HttpOnly; Secure;";
175
+ bot.cookieJar.setCookie(cookieString, bot.options.apiUrl, (err) => {
176
+ if (err) {
177
+ logger.warn("Cookie 注入失败:", err);
178
+ } else {
179
+ logger.info("Cookie 注入成功");
180
+ }
181
+ });
182
+ bot.setRequestOptions({
183
+ headers: {
184
+ referer: "https://wiki.biligame.com/oni/",
185
+ "User-Agent": siteConfig.userAgent
186
+ }
187
+ });
188
+ }
189
+ logger.info(`正在执行 ${siteConfig.name} 登录...`);
190
+ await bot.login();
191
+ logger.info(`✅ 成功登录 ${siteConfig.name}`);
192
+ return bot;
193
+ }
194
+ async start() {
195
+ logger.info("WikiBotService 正在初始化...");
196
+ try {
197
+ const sitesConfig = this.getSitesConfig();
198
+ logger.info("开始登录 WIKIGG...");
199
+ try {
200
+ this.ggbot = await this.loginWithRetry(sitesConfig.gg);
201
+ } catch (error) {
202
+ const errorMsg = getErrorMessage(error);
203
+ logger.error(
204
+ "WIKIGG 登录失败,服务将继续运行,但 WIKIGG 相关功能不可用",
205
+ errorMsg
206
+ );
207
+ }
208
+ logger.info("开始登录 bwiki...");
209
+ try {
210
+ this.bwikibot = await this.loginWithRetry(sitesConfig.bwiki);
211
+ } catch (error) {
212
+ const errorMsg = getErrorMessage(error);
213
+ logger.error(
214
+ "bwiki 登录失败,服务将继续运行,但 bwiki 相关功能不可用",
215
+ errorMsg
216
+ );
217
+ }
218
+ if (this.ggbot && this.bwikibot) {
219
+ this.isReady = true;
220
+ logger.info("WikiBotService 初始化成功,两个 Wiki 已登录");
221
+ } else if (this.ggbot || this.bwikibot) {
222
+ this.isReady = true;
223
+ logger.warn(
224
+ `WikiBotService 部分初始化成功,已登录: ${this.ggbot ? "WIKIGG" : ""} ${this.bwikibot ? "bwiki" : ""}`
225
+ );
226
+ } else {
227
+ logger.error("WikiBotService 初始化失败,所有登录都失败");
112
228
  }
229
+ } catch (error) {
230
+ const errorMsg = getErrorMessage(error);
231
+ logger.error("WikiBotService 初始化出错:", errorMsg);
232
+ }
233
+ }
234
+ stop() {
235
+ this.isReady = false;
236
+ this.ggbot = null;
237
+ this.bwikibot = null;
238
+ logger.info("WikiBotService 已停止");
239
+ }
240
+ async relogin() {
241
+ const sitesConfig = this.getSitesConfig();
242
+ let ggSuccess = false;
243
+ let bwikiSuccess = false;
244
+ logger.info("开始重新登录 WIKIGG...");
245
+ try {
246
+ this.ggbot = await this.loginWithRetry(sitesConfig.gg);
247
+ ggSuccess = true;
248
+ logger.info("✅ 成功重新登录 WIKIGG");
249
+ } catch (error) {
250
+ this.ggbot = null;
251
+ const errorMsg = getErrorMessage(error);
252
+ logger.error("❌ 重新登录 WIKIGG 失败", errorMsg);
253
+ }
254
+ logger.info("开始重新登录 bwiki...");
255
+ try {
256
+ this.bwikibot = await this.loginWithRetry(sitesConfig.bwiki);
257
+ bwikiSuccess = true;
258
+ logger.info("✅ 成功重新登录 bwiki");
259
+ } catch (error) {
260
+ this.bwikibot = null;
261
+ const errorMsg = getErrorMessage(error);
262
+ logger.error("❌ 重新登录 bwiki 失败", errorMsg);
263
+ }
264
+ if (this.ggbot && this.bwikibot) {
265
+ this.isReady = true;
266
+ logger.info("WikiBotService 重新登录成功,两个 Wiki 已登录");
267
+ } else if (this.ggbot || this.bwikibot) {
268
+ this.isReady = true;
269
+ logger.warn(
270
+ `WikiBotService 部分重新登录成功,已登录: ${this.ggbot ? "WIKIGG" : ""} ${this.bwikibot ? "bwiki" : ""}`
271
+ );
272
+ } else {
273
+ this.isReady = false;
274
+ logger.error("WikiBotService 重新登录失败,所有登录都失败");
275
+ }
276
+ return { gg: ggSuccess, bwiki: bwikiSuccess };
277
+ }
278
+ isGGBotReady() {
279
+ return this.ggbot !== null;
280
+ }
281
+ isBWikiBotReady() {
282
+ return this.bwikibot !== null;
283
+ }
284
+ getGGBot() {
285
+ if (!this.ggbot) {
286
+ throw new Error("WIKIGG bot 尚未就绪,请检查登录配置或查看日志");
287
+ }
288
+ return this.ggbot;
289
+ }
290
+ getBWikiBot() {
291
+ if (!this.bwikibot) {
292
+ throw new Error("bwiki bot 尚未就绪,请检查登录配置或查看日志");
293
+ }
294
+ return this.bwikibot;
295
+ }
296
+ };
297
+ ((WikiBotService2) => {
298
+ WikiBotService2.Config = import_koishi2.Schema.object({
299
+ ggUsername: import_koishi2.Schema.string().description("WIKIGG 用户名").default("1"),
300
+ ggPassword: import_koishi2.Schema.string().description("WIKIGG 密码").default("1"),
301
+ bwikiusername: import_koishi2.Schema.string().description("bwiki用户名").default("1"),
302
+ bwikipassword: import_koishi2.Schema.string().description("bwiki密码").default("1")
303
+ });
304
+ })(WikiBotService || (WikiBotService = {}));
305
+
306
+ // src/plugins/consoleLogProvider.ts
307
+ var import_koishi3 = require("koishi");
308
+ var import_plugin_console = require("@koishijs/plugin-console");
309
+ var import_path = require("path");
310
+ var logBuffer = [];
311
+ var PublicLogProvider = class extends import_plugin_console.DataService {
312
+ static {
313
+ __name(this, "PublicLogProvider");
314
+ }
315
+ constructor(ctx) {
316
+ super(ctx, "onilogs", { authority: 0 });
317
+ }
318
+ async get() {
319
+ return logBuffer;
320
+ }
321
+ };
322
+ var ConsoleLogProvider = class {
323
+ static {
324
+ __name(this, "ConsoleLogProvider");
325
+ }
326
+ static inject = ["console"];
327
+ constructor(ctx) {
328
+ ctx.console.addEntry({
329
+ dev: (0, import_path.resolve)(__dirname, "../../client/index.ts"),
330
+ prod: (0, import_path.resolve)(__dirname, "../../dist")
331
+ });
332
+ ctx.plugin(PublicLogProvider);
333
+ const target = {
334
+ colors: 0,
335
+ record: /* @__PURE__ */ __name((record) => {
336
+ if (record.name !== "oni-sync") return;
337
+ logBuffer.push(record);
338
+ if (logBuffer.length > 1e3) {
339
+ logBuffer = logBuffer.slice(-1e3);
340
+ }
341
+ ctx.get("console")?.patch("onilogs", logBuffer);
342
+ }, "record")
343
+ };
344
+ import_koishi3.Logger.targets.push(target);
345
+ ctx.on("dispose", () => {
346
+ const index = import_koishi3.Logger.targets.indexOf(target);
347
+ if (index > -1) import_koishi3.Logger.targets.splice(index, 1);
113
348
  });
114
349
  }
115
- await bot.login();
116
- logger.info(`✅ 成功登录 ${siteConfig.name}`);
117
- return bot;
118
- }
119
- __name(login, "login");
350
+ };
120
351
 
121
- // src/config/index.ts
122
- var userAgent = `OniSyncBot/1.0 (https://klei.vip; Charles@klei.vip)`;
123
- function getSitesConfig(config) {
124
- return {
125
- gg: {
126
- name: "WIKIGG",
127
- api: "https://oxygennotincluded.wiki.gg/zh/api.php",
128
- username: config.ggUsername,
129
- password: config.ggPassword,
130
- userAgent
131
- },
132
- bwiki: {
133
- name: "bwiki",
134
- api: "https://wiki.biligame.com/oni/api.php",
135
- username: config.bwikiusername,
136
- password: config.bwikipassword,
137
- userAgent
138
- }
139
- };
140
- }
141
- __name(getSitesConfig, "getSitesConfig");
352
+ // src/plugins/databaseExtension.ts
353
+ var DatabaseExtension = class {
354
+ static {
355
+ __name(this, "DatabaseExtension");
356
+ }
357
+ static inject = ["database"];
358
+ constructor(ctx) {
359
+ ctx.model.extend("wikipages", {
360
+ id: "integer",
361
+ title: "string",
362
+ pinyin_full: "string",
363
+ pinyin_first: "string"
364
+ });
365
+ }
366
+ };
367
+
368
+ // src/plugins/routeRedirect.ts
369
+ var import_koishi4 = require("koishi");
370
+ var RouteRedirect = class {
371
+ static {
372
+ __name(this, "RouteRedirect");
373
+ }
374
+ static inject = ["database", "server"];
375
+ config;
376
+ constructor(ctx, config) {
377
+ this.config = config;
378
+ ctx.server.get("/gg/:id", async (router) => {
379
+ const pageId = Number(router.params.id);
380
+ if (isNaN(pageId)) return router.body = "❌ 无效的页面ID,必须为数字!";
381
+ const [page] = await ctx.database.get("wikipages", { id: pageId });
382
+ if (!page)
383
+ return router.body = `❌ 未找到ID为【${pageId}】的页面,请联系管理员更新缓存!`;
384
+ const targetUrl = `https://${this.config.main_site}/${encodeURIComponent(
385
+ page.title
386
+ )}?variant=zh`;
387
+ router.redirect(targetUrl);
388
+ });
389
+ ctx.server.get("/bw/:id", async (router) => {
390
+ const pageId = Number(router.params.id);
391
+ if (isNaN(pageId)) return router.body = "❌ 无效的页面ID,必须为数字!";
392
+ const [page] = await ctx.database.get("wikipages", { id: pageId });
393
+ if (!page)
394
+ return router.body = `❌ 未找到ID为【${pageId}】的页面,请联系管理员更新缓存!`;
395
+ const targetUrl = `https://${this.config.bwiki_site}/${encodeURIComponent(
396
+ page.title
397
+ )}`;
398
+ router.redirect(targetUrl);
399
+ });
400
+ }
401
+ };
402
+ ((RouteRedirect2) => {
403
+ RouteRedirect2.Config = import_koishi4.Schema.object({
404
+ domain: import_koishi4.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip"),
405
+ main_site: import_koishi4.Schema.string().description("主站域名(必填,如:oxygennotincluded.wiki.gg)").default("oxygennotincluded.wiki.gg/zh"),
406
+ bwiki_site: import_koishi4.Schema.string().description("镜像站域名(必填,如:wiki.biligame.com)").default("wiki.biligame.com/oni")
407
+ });
408
+ })(RouteRedirect || (RouteRedirect = {}));
409
+
410
+ // src/plugins/syncCommands.ts
411
+ var import_koishi8 = require("koishi");
142
412
 
143
413
  // src/sync/pageSync.ts
144
- var import_koishi3 = require("koishi");
414
+ var import_koishi6 = require("koishi");
145
415
 
146
416
  // src/sync/imgSync.ts
147
- var import_koishi2 = require("koishi");
417
+ var import_koishi5 = require("koishi");
148
418
  var import_node_fetch = __toESM(require("node-fetch"));
149
419
  var import_form_data = __toESM(require("form-data"));
150
420
  var CONFIG = {
@@ -174,11 +444,56 @@ async function getImageInfo(site, fileName) {
174
444
  size: imageInfo.size
175
445
  };
176
446
  } catch (error) {
177
- logger.error(`[GetImageInfo] 获取 ${fileName} 信息失败:`, error);
447
+ const errorMsg = getErrorMessage(error);
448
+ logger.error(`[GetImageInfo] 获取 ${fileName} 信息失败:`, errorMsg);
178
449
  return null;
179
450
  }
180
451
  }
181
452
  __name(getImageInfo, "getImageInfo");
453
+ async function deleteOldVersions(bot, fileName) {
454
+ try {
455
+ const response = await bot.query({
456
+ action: "query",
457
+ titles: fileName,
458
+ prop: "revisions",
459
+ rvprop: "ids|timestamp",
460
+ rvlimit: "max",
461
+ rvdir: "newer"
462
+ // 按时间正序排列,最新的在最后
463
+ });
464
+ const pages = response.query?.pages || {};
465
+ const page = Object.values(pages)[0];
466
+ if (!page || !page.revisions || page.revisions.length <= 1) {
467
+ return;
468
+ }
469
+ const revisionIds = page.revisions.slice(0, -1).map((rev) => rev.revid);
470
+ const token = await bot.getCsrfToken();
471
+ for (const revid of revisionIds) {
472
+ try {
473
+ await bot.rawRequest({
474
+ method: "POST",
475
+ url: bot.options.apiUrl,
476
+ data: {
477
+ action: "delete",
478
+ title: fileName,
479
+ revid,
480
+ token,
481
+ reason: "自动清理旧版本,只保留最新同步版本",
482
+ format: "json"
483
+ }
484
+ });
485
+ logger.info(`[SyncImg] 🗑️ 删除旧版本 ${revid} 成功`);
486
+ } catch (error) {
487
+ const errorMsg = getErrorMessage(error);
488
+ logger.error(`[SyncImg] ❌ 删除旧版本 ${revid} 失败:`, errorMsg);
489
+ }
490
+ }
491
+ } catch (error) {
492
+ const errorMsg = getErrorMessage(error);
493
+ logger.error(`[SyncImg] ❌ 获取版本信息失败:`, errorMsg);
494
+ }
495
+ }
496
+ __name(deleteOldVersions, "deleteOldVersions");
182
497
  async function syncSingleImage(sourceBot, targetBot, fileName, config) {
183
498
  if (CONFIG.IGNORED_IMAGES.includes(fileName)) {
184
499
  logger.info(`[SyncImg] 🚫 图片 ${fileName} 在忽略列表,跳过`);
@@ -193,7 +508,7 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
193
508
  }
194
509
  const targetImageInfo = await getImageInfo(targetBot, fileName);
195
510
  logger.info(`原图片sha1: ${sourceImageInfo.sha1}`);
196
- logger.info(`目标图片sha1: ${targetImageInfo.sha1}`);
511
+ logger.info(`目标图片sha1: ${targetImageInfo?.sha1}`);
197
512
  if (targetImageInfo && targetImageInfo.sha1 === sourceImageInfo.sha1) {
198
513
  logger.info(`[SyncImg] 🟡 图片 ${fileName} 已存在且内容一致,跳过`);
199
514
  return { success: true, reason: "no_change" };
@@ -236,6 +551,7 @@ async function syncSingleImage(sourceBot, targetBot, fileName, config) {
236
551
  const responseData = rawResponse.data;
237
552
  if (responseData.upload && responseData.upload.result === "Success") {
238
553
  logger.info(`[SyncImg] ✅ 图片 ${fileName} 同步成功`);
554
+ await deleteOldVersions(targetBot, fileName);
239
555
  return { success: true, reason: "synced" };
240
556
  } else if (responseData.error) {
241
557
  throw new Error(`${responseData.error.code}: ${responseData.error.info}`);
@@ -303,13 +619,13 @@ async function syncAllImages(sourceBot, targetBot, config) {
303
619
  if (!result.success) {
304
620
  failCount++;
305
621
  failedImages.push(fileName);
306
- await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_FAILED);
622
+ await (0, import_koishi5.sleep)(CONFIG.SYNC_INTERVAL_FAILED);
307
623
  } else {
308
624
  successCount++;
309
625
  if (result.reason === "ignored" || result.reason === "no_change") {
310
626
  skipCount++;
311
627
  }
312
- await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
628
+ await (0, import_koishi5.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
313
629
  }
314
630
  }
315
631
  if (failedImages.length > 0) {
@@ -335,7 +651,7 @@ async function syncAllImages(sourceBot, targetBot, config) {
335
651
  stillFailed.push(fileName);
336
652
  logger.info(`[SyncAllImg] ❌ 重试失败: ${fileName}`);
337
653
  }
338
- await (0, import_koishi2.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
654
+ await (0, import_koishi5.sleep)(CONFIG.SYNC_INTERVAL_SUCCESS);
339
655
  }
340
656
  if (stillFailed.length > 0) {
341
657
  logger.info(`
@@ -422,13 +738,13 @@ async function processPageWithStats(oldSite, newSite, pageTitle, user, stats, fa
422
738
  if (!syncResult.success) {
423
739
  stats.failCount++;
424
740
  failedPages.push(pageTitle);
425
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
741
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
426
742
  } else {
427
743
  stats.successCount++;
428
744
  if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
429
745
  stats.skipCount++;
430
746
  }
431
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
747
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
432
748
  }
433
749
  }
434
750
  __name(processPageWithStats, "processPageWithStats");
@@ -504,11 +820,11 @@ async function syncPages(oldSite, newSite) {
504
820
  stats.skipCount++;
505
821
  }
506
822
  logger.info(`[SyncAllPages] ✅ 页面 ${pageTitle} 重试成功`);
507
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
823
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
508
824
  } else {
509
825
  stillFailed.push(pageTitle);
510
826
  logger.info(`[SyncAllPages] ❌ 页面 ${pageTitle} 再次失败`);
511
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
827
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
512
828
  }
513
829
  }
514
830
  }
@@ -577,11 +893,11 @@ async function incrementalUpdate(oldSite, newSite, config) {
577
893
  CONFIG2.INCREMENTAL_USER
578
894
  );
579
895
  }
580
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
896
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_SUCCESS);
581
897
  } catch (error) {
582
898
  const errMsg = error instanceof Error ? error.message : String(error);
583
899
  logger.error(`[增量更新流程] ❌ 处理 ${title} 时出错:`, errMsg);
584
- await (0, import_koishi3.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
900
+ await (0, import_koishi6.sleep)(CONFIG2.SYNC_INTERVAL_FAILED);
585
901
  }
586
902
  }
587
903
  }
@@ -596,7 +912,7 @@ async function incrementalUpdate(oldSite, newSite, config) {
596
912
  __name(incrementalUpdate, "incrementalUpdate");
597
913
 
598
914
  // src/sync/moduleSync.ts
599
- var import_koishi4 = require("koishi");
915
+ var import_koishi7 = require("koishi");
600
916
  var CONFIG3 = {
601
917
  MODLE_NAMESPACE: 828,
602
918
  // 模块命名空间 (注意:这里原代码拼写为 MODLE,保留原样)
@@ -700,13 +1016,13 @@ async function syncModules(oldSite, newSite) {
700
1016
  if (!syncResult.success) {
701
1017
  failCount++;
702
1018
  failedModules.push(moduleTitle);
703
- await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
1019
+ await (0, import_koishi7.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
704
1020
  } else {
705
1021
  successCount++;
706
1022
  if (syncResult.reason === "ignored" || syncResult.reason === "no_change") {
707
1023
  skipCount++;
708
1024
  }
709
- await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
1025
+ await (0, import_koishi7.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
710
1026
  }
711
1027
  }
712
1028
  if (failedModules.length > 0) {
@@ -731,11 +1047,11 @@ async function syncModules(oldSite, newSite) {
731
1047
  skipCount++;
732
1048
  }
733
1049
  logger.info(`[SyncAllModules] ✅ 模块 ${moduleTitle} 重试成功`);
734
- await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
1050
+ await (0, import_koishi7.sleep)(CONFIG3.SYNC_INTERVAL_SUCCESS);
735
1051
  } else {
736
1052
  stillFailed.push(moduleTitle);
737
1053
  logger.info(`[SyncAllModules] ❌ 模块 ${moduleTitle} 再次失败`);
738
- await (0, import_koishi4.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
1054
+ await (0, import_koishi7.sleep)(CONFIG3.SYNC_INTERVAL_FAILED);
739
1055
  }
740
1056
  }
741
1057
  logger.info(`
@@ -755,303 +1071,366 @@ async function syncModules(oldSite, newSite) {
755
1071
  logger.info(`├─ 成功:${successCount} 个(含跳过 ${skipCount} 个)`);
756
1072
  logger.info(`└─ 失败:${failCount} 个`);
757
1073
  } catch (error) {
758
- logger.error(`[SyncAllModules] 💥 批量同步流程异常终止:`, error);
1074
+ const errorMsg = getErrorMessage(error);
1075
+ logger.error(`[SyncAllModules] 💥 批量同步流程异常终止:`, errorMsg);
759
1076
  throw error;
760
1077
  }
761
1078
  }
762
1079
  __name(syncModules, "syncModules");
763
1080
 
764
- // src/index.ts
765
- var name = "oni-sync-bot";
766
- var inject = ["console", "database", "server", "cron"];
767
- var logBuffer = [];
768
- var Config = import_koishi5.Schema.object({
769
- ggUsername: import_koishi5.Schema.string().description("WIKIGG 用户名").default("1"),
770
- ggPassword: import_koishi5.Schema.string().description("WIKIGG 密码").default("1"),
771
- bwikiusername: import_koishi5.Schema.string().description("bwiki用户名").default("1"),
772
- bwikipassword: import_koishi5.Schema.string().description("bwiki密码").default("1"),
773
- domain: import_koishi5.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip"),
774
- main_site: import_koishi5.Schema.string().description("主站域名(必填,如:oxygennotincluded.wiki.gg)").default("oxygennotincluded.wiki.gg/zh"),
775
- bwiki_site: import_koishi5.Schema.string().description("镜像站域名(必填,如:wiki.biligame.com)").default("wiki.biligame.com/oni"),
776
- logsUrl: import_koishi5.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs")
777
- });
778
- var PublicLogProvider = class extends import_plugin_console.DataService {
1081
+ // src/plugins/syncCommands.ts
1082
+ var SyncCommands = class {
779
1083
  static {
780
- __name(this, "PublicLogProvider");
781
- }
782
- constructor(ctx) {
783
- super(ctx, "onilogs", { authority: 0 });
784
- }
785
- async get() {
786
- return logBuffer;
1084
+ __name(this, "SyncCommands");
787
1085
  }
788
- };
789
- function apply(ctx, config) {
790
- const log = ctx.logger("oni-sync");
791
- let ggbot;
792
- let bwikibot;
793
- ctx.inject(["console"], (ctx2) => {
794
- ctx2.console.addEntry({
795
- dev: (0, import_path.resolve)(__dirname, "../client/index.ts"),
796
- prod: (0, import_path.resolve)(__dirname, "../dist")
797
- });
798
- });
799
- ctx.plugin(PublicLogProvider);
800
- const target = {
801
- colors: 0,
802
- record: /* @__PURE__ */ __name((record) => {
803
- if (record.name !== "oni-sync") return;
804
- logBuffer.push(record);
805
- if (logBuffer.length > 100) {
806
- logBuffer = logBuffer.slice(-100);
807
- }
808
- ctx.get("console")?.patch("onilogs", logBuffer);
809
- }, "record")
810
- };
811
- import_koishi5.Logger.targets.push(target);
812
- ctx.on("dispose", () => {
813
- const index = import_koishi5.Logger.targets.indexOf(target);
814
- if (index > -1) import_koishi5.Logger.targets.splice(index, 1);
815
- });
816
- ctx.model.extend("wikipages", {
817
- id: "integer",
818
- title: "string",
819
- pinyin_full: "string",
820
- // 全拼
821
- pinyin_first: "string"
822
- // 首字母
823
- });
824
- ctx.server.get("/gg/:id", async (router) => {
825
- const pageId = Number(router.params.id);
826
- if (isNaN(pageId)) return router.body = "❌ 无效的页面ID,必须为数字!";
827
- const [page] = await ctx.database.get("wikipages", { id: pageId });
828
- if (!page)
829
- return router.body = `❌ 未找到ID为【${pageId}】的页面,请联系管理员更新缓存!`;
830
- const targetUrl = `https://${config.main_site}/${encodeURIComponent(
831
- page.title
832
- )}?variant=zh`;
833
- router.redirect(targetUrl);
834
- });
835
- ctx.server.get("/bw/:id", async (router) => {
836
- const pageId = Number(router.params.id);
837
- if (isNaN(pageId)) return router.body = "❌ 无效的页面ID,必须为数字!";
838
- const [page] = await ctx.database.get("wikipages", { id: pageId });
839
- if (!page)
840
- return router.body = `❌ 未找到ID为【${pageId}】的页面,请联系管理员更新缓存!`;
841
- const targetUrl = `https://${config.bwiki_site}/${encodeURIComponent(
842
- page.title
843
- )}`;
844
- router.redirect(targetUrl);
845
- });
846
- ctx.on("ready", async () => {
847
- logger.info("初始化中...");
848
- const sitesConfig = getSitesConfig(config);
849
- ggbot = await login(sitesConfig.gg);
850
- bwikibot = await login(sitesConfig.bwiki);
851
- if (ggbot.login && bwikibot.login) {
852
- logger.info("登录成功,插件已准备就绪");
853
- } else {
854
- logger.error("登录失败,请检查配置");
855
- }
1086
+ static inject = ["wikiBot", "cron"];
1087
+ config;
1088
+ log;
1089
+ constructor(ctx, config) {
1090
+ this.config = config;
1091
+ this.log = ctx.logger("oni-sync");
1092
+ logger.info("WikiBot 服务已就绪,初始化定时任务和指令");
856
1093
  ctx.cron("15 * * * *", async () => {
857
- await incrementalUpdate(ggbot, bwikibot, config);
1094
+ if (!ctx.wikiBot.isGGBotReady() || !ctx.wikiBot.isBWikiBotReady()) {
1095
+ logger.warn("增量更新跳过:Wiki 机器人未就绪");
1096
+ return;
1097
+ }
1098
+ await incrementalUpdate(
1099
+ ctx.wikiBot.getGGBot(),
1100
+ ctx.wikiBot.getBWikiBot(),
1101
+ config
1102
+ );
858
1103
  });
859
1104
  ctx.cron("30 8 * * 4", async () => {
860
- await syncPages(ggbot, bwikibot).then(() => {
1105
+ if (!ctx.wikiBot.isGGBotReady() || !ctx.wikiBot.isBWikiBotReady()) {
1106
+ logger.warn("同步所有页面跳过:Wiki 机器人未就绪");
1107
+ return;
1108
+ }
1109
+ await syncPages(ctx.wikiBot.getGGBot(), ctx.wikiBot.getBWikiBot()).then(() => {
861
1110
  logger.info("自动任务:尝试同步所有页面,从 WIKIGG 到 bwiki");
862
1111
  }).catch((err) => {
863
1112
  logger.error(`同步所有页面失败`);
864
- log.error(`,错误信息:${err}`);
1113
+ this.log.error(`,错误信息:${err}`);
865
1114
  });
866
1115
  });
867
1116
  ctx.cron("30 8 * * 3", async () => {
868
- await syncAllImages(ggbot, bwikibot, config).then(() => {
1117
+ if (!ctx.wikiBot.isGGBotReady() || !ctx.wikiBot.isBWikiBotReady()) {
1118
+ logger.warn("同步所有图片跳过:Wiki 机器人未就绪");
1119
+ return;
1120
+ }
1121
+ await syncAllImages(
1122
+ ctx.wikiBot.getGGBot(),
1123
+ ctx.wikiBot.getBWikiBot(),
1124
+ config
1125
+ ).then(() => {
869
1126
  logger.info("自动任务:尝试同步所有图片,从 WIKIGG 到 bwiki");
870
1127
  }).catch((err) => {
871
1128
  logger.error(`同步所有图片失败`);
872
- log.error(`,错误信息:${err}`);
1129
+ this.log.error(`,错误信息:${err}`);
873
1130
  });
874
1131
  });
875
- });
876
- ctx.command("sync <pageTitle:string>", "同步指定页面", { authority: 2 }).action(async ({ session }, pageTitle) => {
877
- await syncSinglePage(ggbot, bwikibot, pageTitle, "sync-bot").then(() => {
878
- session.send(
879
- `✅ 已尝试同步页面:${pageTitle},请前往控制台查看:${config.logsUrl}`
880
- );
881
- }).catch((err) => {
882
- session.send(`❌ 同步页面失败:${pageTitle}`);
883
- log.error(`,错误信息:${err}`);
1132
+ this.registerCommands(ctx);
1133
+ }
1134
+ checkBotsReady(ctx) {
1135
+ const ggReady = ctx.wikiBot.isGGBotReady();
1136
+ const bwReady = ctx.wikiBot.isBWikiBotReady();
1137
+ return ggReady && bwReady;
1138
+ }
1139
+ registerCommands(ctx) {
1140
+ ctx.command("sync <pageTitle:string>", "同步指定页面", { authority: 2 }).action(async ({ session }, pageTitle) => {
1141
+ if (!this.checkBotsReady(ctx)) {
1142
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1143
+ }
1144
+ await syncSinglePage(
1145
+ ctx.wikiBot.getGGBot(),
1146
+ ctx.wikiBot.getBWikiBot(),
1147
+ pageTitle,
1148
+ "sync-bot"
1149
+ ).then(() => {
1150
+ session.send(
1151
+ `✅ 已尝试同步页面:${pageTitle},请前往控制台查看:${this.config.logsUrl}`
1152
+ );
1153
+ }).catch((err) => {
1154
+ session.send(`❌ 同步页面失败:${pageTitle}`);
1155
+ this.log.error(`,错误信息:${err}`);
1156
+ });
884
1157
  });
885
- });
886
- ctx.command("sync.incrementalUpdate", "获取3h内的编辑并尝试更新", {
887
- authority: 2
888
- }).alias("增量更新").action(async ({ session }) => {
889
- session.send(
890
- `🚀 获取3h内的编辑并尝试更新,任务耗时可能较长,请前往控制台查看日志:${config.logsUrl}`
891
- );
892
- await incrementalUpdate(ggbot, bwikibot, config).then(() => {
893
- session.send(
894
- `✅ 已尝试获取三小时前的编辑并同步,请前往控制台查看:${config.logsUrl}`
895
- );
896
- }).catch((err) => {
1158
+ ctx.command("sync.incrementalUpdate", "获取3h内的编辑并尝试更新", {
1159
+ authority: 2
1160
+ }).alias("增量更新").action(async ({ session }) => {
1161
+ if (!this.checkBotsReady(ctx)) {
1162
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1163
+ }
897
1164
  session.send(
898
- `❌ 同步所有页面失败,请前往控制台查看日志:${config.logsUrl}`
1165
+ `🚀 获取3h内的编辑并尝试更新,任务耗时可能较长,请前往控制台查看日志:${this.config.logsUrl}`
899
1166
  );
900
- log.error(`同步所有页面失败,错误信息:${err}`);
1167
+ await incrementalUpdate(
1168
+ ctx.wikiBot.getGGBot(),
1169
+ ctx.wikiBot.getBWikiBot(),
1170
+ this.config
1171
+ ).then(() => {
1172
+ session.send(
1173
+ `✅ 已尝试获取三小时前的编辑并同步,请前往控制台查看:${this.config.logsUrl}`
1174
+ );
1175
+ }).catch((err) => {
1176
+ session.send(
1177
+ `❌ 同步所有页面失败,请前往控制台查看日志:${this.config.logsUrl}`
1178
+ );
1179
+ this.log.error(`同步所有页面失败,错误信息:${err}`);
1180
+ });
901
1181
  });
902
- });
903
- ctx.command("sync.allpages", "同步所有页面", { authority: 2 }).action(async ({ session }) => {
904
- session.send(
905
- `🚀 开始同步所有页面,任务耗时较长,请前往控制台查看日志:${config.logsUrl}`
906
- );
907
- await syncPages(ggbot, bwikibot).then(() => {
908
- session.send(
909
- `✅ 已尝试同步所有页面,请前往控制台查看:${config.logsUrl}`
910
- );
911
- }).catch((err) => {
1182
+ ctx.command("sync.allpages", "同步所有页面", { authority: 2 }).action(async ({ session }) => {
1183
+ if (!this.checkBotsReady(ctx)) {
1184
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1185
+ }
912
1186
  session.send(
913
- `❌ 同步所有页面失败,请前往控制台查看日志:${config.logsUrl}`
1187
+ `🚀 开始同步所有页面,任务耗时较长,请前往控制台查看日志:${this.config.logsUrl}`
914
1188
  );
915
- log.error(`同步所有页面失败,错误信息:${err}`);
1189
+ await syncPages(ctx.wikiBot.getGGBot(), ctx.wikiBot.getBWikiBot()).then(() => {
1190
+ session.send(
1191
+ `✅ 已尝试同步所有页面,请前往控制台查看:${this.config.logsUrl}`
1192
+ );
1193
+ }).catch((err) => {
1194
+ session.send(
1195
+ `❌ 同步所有页面失败,请前往控制台查看日志:${this.config.logsUrl}`
1196
+ );
1197
+ this.log.error(`同步所有页面失败,错误信息:${err}`);
1198
+ });
916
1199
  });
917
- });
918
- ctx.command("sync.module <moduleTitle:string>", "同步指定模块", {
919
- authority: 2
920
- }).action(async ({ session }, moduleTitle) => {
921
- await session.send(`✅ 同步中,请前往控制台查看:${config.logsUrl}`);
922
- await syncSingleModule(ggbot, bwikibot, moduleTitle, "sync-bot").then(() => {
923
- session.send(
924
- `✅ 已尝试同步模块:${moduleTitle},请前往控制台查看:${config.logsUrl}`
1200
+ ctx.command("sync.module <moduleTitle:string>", "同步指定模块", {
1201
+ authority: 2
1202
+ }).action(async ({ session }, moduleTitle) => {
1203
+ if (!this.checkBotsReady(ctx)) {
1204
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1205
+ }
1206
+ await session.send(
1207
+ `✅ 同步中,请前往控制台查看:${this.config.logsUrl}`
925
1208
  );
926
- }).catch((err) => {
927
- session.send(`❌ 同步模块失败:${moduleTitle}`);
928
- log.error(`错误信息:${err}`);
1209
+ await syncSingleModule(
1210
+ ctx.wikiBot.getGGBot(),
1211
+ ctx.wikiBot.getBWikiBot(),
1212
+ moduleTitle,
1213
+ "sync-bot"
1214
+ ).then(() => {
1215
+ session.send(
1216
+ `✅ 已尝试同步模块:${moduleTitle},请前往控制台查看:${this.config.logsUrl}`
1217
+ );
1218
+ }).catch((err) => {
1219
+ session.send(`❌ 同步模块失败:${moduleTitle}`);
1220
+ this.log.error(`错误信息:${err}`);
1221
+ });
929
1222
  });
930
- });
931
- ctx.command("sync.allmodules", "同步所有模块", { authority: 2 }).action(async ({ session }) => {
932
- await session.send(
933
- `🚀 开始同步所有模块,任务耗时较长,请前往控制台查看:${config.logsUrl}`
934
- );
935
- await syncModules(ggbot, bwikibot).then(() => {
936
- session.send(
937
- `✅ 已尝试同步所有模块,请前往控制台查看:${config.logsUrl}`
938
- );
939
- }).catch((err) => {
940
- session.send(
941
- `❌ 同步所有模块失败,请前往控制台查看日志:${config.logsUrl}`
1223
+ ctx.command("sync.allmodules", "同步所有模块", { authority: 2 }).action(async ({ session }) => {
1224
+ if (!this.checkBotsReady(ctx)) {
1225
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1226
+ }
1227
+ await session.send(
1228
+ `🚀 开始同步所有模块,任务耗时较长,请前往控制台查看:${this.config.logsUrl}`
942
1229
  );
943
- log.error(`同步所有模块失败,错误信息:${err}`);
944
- });
945
- });
946
- ctx.command("sync.img <imgTitle:string>", "同步指定图片", { authority: 2 }).action(async ({ session }, imgTitle) => {
947
- await session.send(
948
- `🚀 开始同步,任务可能耗时较长,请前往控制台查看:${config.logsUrl}`
949
- );
950
- await syncSingleImage(
951
- ggbot,
952
- bwikibot,
953
- `${imgTitle.startsWith("File:") ? "" : "File:"}${imgTitle}`,
954
- config
955
- ).then(() => {
956
- session.send(`✅ 已尝试同步图片:${imgTitle}`);
957
- }).catch((err) => {
958
- session.send(`❌ 同步图片失败:${imgTitle}`);
959
- log.error(`同步图片失败:${imgTitle},错误信息:${err}`);
1230
+ await syncModules(ctx.wikiBot.getGGBot(), ctx.wikiBot.getBWikiBot()).then(() => {
1231
+ session.send(
1232
+ `✅ 已尝试同步所有模块,请前往控制台查看:${this.config.logsUrl}`
1233
+ );
1234
+ }).catch((err) => {
1235
+ session.send(
1236
+ `❌ 同步所有模块失败,请前往控制台查看日志:${this.config.logsUrl}`
1237
+ );
1238
+ this.log.error(`同步所有模块失败,错误信息:${err}`);
1239
+ });
960
1240
  });
961
- });
962
- ctx.command("sync.allimgs", "同步所有图片", { authority: 2 }).action(async ({ session }) => {
963
- session.send(
964
- `🚀 开始同步所有图片,任务耗时较长,请前往控制台查看:${config.logsUrl}`
965
- );
966
- await syncAllImages(ggbot, bwikibot, config).then(() => {
967
- session.send(
968
- `✅ 已尝试同步所有图片,请前往控制台查看:${config.logsUrl}`
1241
+ ctx.command("sync.img <imgTitle:string>", "同步指定图片", { authority: 2 }).action(async ({ session }, imgTitle) => {
1242
+ if (!this.checkBotsReady(ctx)) {
1243
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1244
+ }
1245
+ await session.send(
1246
+ `🚀 开始同步,任务可能耗时较长,请前往控制台查看:${this.config.logsUrl}`
969
1247
  );
970
- }).catch((err) => {
1248
+ await syncSingleImage(
1249
+ ctx.wikiBot.getGGBot(),
1250
+ ctx.wikiBot.getBWikiBot(),
1251
+ `${imgTitle.startsWith("File:") ? "" : "File:"}${imgTitle}`,
1252
+ this.config
1253
+ ).then(() => {
1254
+ session.send(`✅ 已尝试同步图片:${imgTitle}`);
1255
+ }).catch((err) => {
1256
+ session.send(`❌ 同步图片失败:${imgTitle}`);
1257
+ this.log.error(`同步图片失败:${imgTitle},错误信息:${err}`);
1258
+ });
1259
+ });
1260
+ ctx.command("sync.allimgs", "同步所有图片", { authority: 2 }).action(async ({ session }) => {
1261
+ if (!this.checkBotsReady(ctx)) {
1262
+ return session.send("❌ Wiki 机器人未就绪,请检查登录配置或查看日志");
1263
+ }
971
1264
  session.send(
972
- `❌ 同步所有图片失败,请前往控制台查看日志:${config.logsUrl}`
1265
+ `🚀 开始同步所有图片,任务耗时较长,请前往控制台查看:${this.config.logsUrl}`
973
1266
  );
974
- log.error(`同步所有图片失败,错误信息:${err}`);
1267
+ await syncAllImages(
1268
+ ctx.wikiBot.getGGBot(),
1269
+ ctx.wikiBot.getBWikiBot(),
1270
+ this.config
1271
+ ).then(() => {
1272
+ session.send(
1273
+ `✅ 已尝试同步所有图片,请前往控制台查看:${this.config.logsUrl}`
1274
+ );
1275
+ }).catch((err) => {
1276
+ session.send(
1277
+ `❌ 同步所有图片失败,请前往控制台查看日志:${this.config.logsUrl}`
1278
+ );
1279
+ this.log.error(`同步所有图片失败,错误信息:${err}`);
1280
+ });
975
1281
  });
1282
+ }
1283
+ };
1284
+ ((SyncCommands2) => {
1285
+ SyncCommands2.Config = import_koishi8.Schema.object({
1286
+ ggUsername: import_koishi8.Schema.string().description("WIKIGG 用户名").default("1"),
1287
+ ggPassword: import_koishi8.Schema.string().description("WIKIGG 密码").default("1"),
1288
+ bwikiusername: import_koishi8.Schema.string().description("bwiki用户名").default("1"),
1289
+ bwikipassword: import_koishi8.Schema.string().description("bwiki密码").default("1"),
1290
+ domain: import_koishi8.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip"),
1291
+ main_site: import_koishi8.Schema.string().description("主站域名(必填,如:oxygennotincluded.wiki.gg)").default("oxygennotincluded.wiki.gg/zh"),
1292
+ bwiki_site: import_koishi8.Schema.string().description("镜像站域名(必填,如:wiki.biligame.com)").default("wiki.biligame.com/oni"),
1293
+ logsUrl: import_koishi8.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs")
976
1294
  });
977
- ctx.command("x <itemName>", "查询缺氧中文wiki,精准匹配+拼音模糊匹配").alias("/查wiki").action(async ({ session }, itemName = "") => {
978
- const queryKey = itemName.trim().toLowerCase();
979
- if (queryKey === "")
980
- return `以下是使用说明:
981
- 原站点: https://${config.domain}/gg/88888888
1295
+ })(SyncCommands || (SyncCommands = {}));
982
1296
 
983
- bwiki: https://${config.domain}/bw/88888888`;
984
- if (queryKey === "火箭计算器") {
985
- return "请前往以下站点使用火箭计算器工具:\n\nhttps://klei.vip/calculator\n\n(注:该工具正在测试阶段,数据可能不够准确,仅供参考)";
986
- }
987
- const { pinyin_full: queryPinyinFull, pinyin_first: queryPinyinFirst } = generatePinyinInfo(queryKey);
988
- const preciseTitleRes = await ctx.database.get("wikipages", {
989
- title: queryKey
990
- });
991
- if (preciseTitleRes.length > 0) {
992
- const { id: id2 } = preciseTitleRes[0];
993
- return `✅ 精准匹配成功
994
- 原站点: https://${config.domain}/gg/${id2}
995
-
996
- bwiki: https://${config.domain}/bw/${id2}`;
997
- }
998
- const preciseFullPinyinRes = await ctx.database.get("wikipages", {
999
- pinyin_full: queryKey
1297
+ // src/plugins/queryCommands.ts
1298
+ var import_koishi9 = require("koishi");
1299
+ var SPECIAL_CASES = /* @__PURE__ */ new Map([
1300
+ [
1301
+ "火箭计算器",
1302
+ "请前往以下站点使用火箭计算器工具:\n\nhttps://klei.vip/calculator\n\n(注:该工具正在测试阶段,数据可能不够准确,仅供参考)"
1303
+ ]
1304
+ ]);
1305
+ var QueryCommands = class {
1306
+ static {
1307
+ __name(this, "QueryCommands");
1308
+ }
1309
+ static inject = ["database"];
1310
+ config;
1311
+ constructor(ctx, config) {
1312
+ this.config = config;
1313
+ this.registerCommands(ctx);
1314
+ }
1315
+ registerCommands(ctx) {
1316
+ ctx.command("x <itemName>", "查询缺氧中文wiki,精准匹配+拼音模糊匹配").alias("/查wiki").action(async ({ session }, itemName = "") => {
1317
+ const queryKey = itemName.trim().toLowerCase();
1318
+ if (!queryKey) {
1319
+ return this.getUsageMessage();
1320
+ }
1321
+ if (SPECIAL_CASES.has(queryKey)) {
1322
+ return SPECIAL_CASES.get(queryKey);
1323
+ }
1324
+ const { pinyin_full: queryPinyinFull, pinyin_first: queryPinyinFirst } = generatePinyinInfo(queryKey);
1325
+ const preciseMatch = await this.findPreciseMatch(
1326
+ ctx,
1327
+ queryKey,
1328
+ queryPinyinFull,
1329
+ queryPinyinFirst
1330
+ );
1331
+ if (preciseMatch) {
1332
+ return this.formatResultMessage(
1333
+ preciseMatch,
1334
+ preciseMatch.prefix
1335
+ );
1336
+ }
1337
+ const allPages = await ctx.database.get("wikipages", {});
1338
+ if (allPages.length === 0) {
1339
+ return `❌ 本地缓存为空,请联系管理员执行【update】指令更新缓存!`;
1340
+ }
1341
+ const fuzzyMatches = this.findFuzzyMatches(
1342
+ allPages,
1343
+ queryKey,
1344
+ queryPinyinFull,
1345
+ queryPinyinFirst
1346
+ );
1347
+ if (fuzzyMatches.length === 0) {
1348
+ return `❌ 未找到【${queryKey}】相关内容,请按游戏内标准名称重新查询!`;
1349
+ }
1350
+ return await this.handleSelection(session, fuzzyMatches);
1000
1351
  });
1001
- if (preciseFullPinyinRes.length > 0) {
1002
- const { id: id2, title } = preciseFullPinyinRes[0];
1003
- return `✅ 拼音精准匹配成功(${queryKey} → ${title})
1004
- 原站点: https://${config.domain}/gg/${id2}
1352
+ }
1353
+ getUsageMessage() {
1354
+ return `以下是使用说明:
1355
+ 原站点: https://${this.config.domain}/gg/88888888
1005
1356
 
1006
- bwiki: https://${config.domain}/bw/${id2}`;
1357
+ bwiki: https://${this.config.domain}/bw/88888888`;
1358
+ }
1359
+ formatResultMessage(match, prefix) {
1360
+ let message = "";
1361
+ if (prefix) {
1362
+ message += prefix + "\n";
1007
1363
  }
1008
- const preciseFirstPinyinRes = await ctx.database.get("wikipages", {
1009
- pinyin_first: queryKey
1010
- });
1011
- if (preciseFirstPinyinRes.length > 0) {
1012
- const { id: id2, title } = preciseFirstPinyinRes[0];
1013
- return `✅ 首字母精准匹配成功(${queryKey} → ${title})
1014
- 原站点: https://${config.domain}/gg/${id2}
1364
+ message += `原站点: https://${this.config.domain}/gg/${match.id}
1015
1365
 
1016
- bwiki: https://${config.domain}/bw/${id2} `;
1366
+ bwiki: https://${this.config.domain}/bw/${match.id}`;
1367
+ return message;
1368
+ }
1369
+ async findPreciseMatch(ctx, queryKey, queryPinyinFull, queryPinyinFirst) {
1370
+ const checks = [
1371
+ { query: { title: queryKey }, prefix: "✅ 精准匹配成功" },
1372
+ {
1373
+ query: { pinyin_full: queryKey },
1374
+ prefix: `✅ 拼音精准匹配成功(${queryKey} → `
1375
+ },
1376
+ {
1377
+ query: { pinyin_first: queryKey },
1378
+ prefix: `✅ 首字母精准匹配成功(${queryKey} → `
1379
+ }
1380
+ ];
1381
+ for (const check of checks) {
1382
+ const results = await ctx.database.get("wikipages", check.query);
1383
+ if (results.length > 0) {
1384
+ const match = results[0];
1385
+ let prefix = check.prefix;
1386
+ if (prefix && prefix.includes("→")) {
1387
+ prefix += `${match.title})`;
1388
+ }
1389
+ match.prefix = prefix;
1390
+ return match;
1391
+ }
1017
1392
  }
1018
- const allPages = await ctx.database.get("wikipages", {});
1019
- if (allPages.length === 0) {
1020
- return `❌ 本地缓存为空,请联系管理员执行【update】指令更新缓存!`;
1393
+ return null;
1394
+ }
1395
+ calculateScore(page, queryKey, queryPinyinFull, queryPinyinFirst) {
1396
+ let score = 0;
1397
+ const { title, pinyin_full, pinyin_first } = page;
1398
+ if (title.includes(queryKey)) score += 10;
1399
+ if (pinyin_full.startsWith(queryPinyinFull)) score += 9;
1400
+ if (pinyin_full.includes(queryPinyinFull)) score += 8;
1401
+ if (pinyin_first.includes(queryPinyinFirst)) score += 6;
1402
+ if (queryPinyinFull.includes(
1403
+ pinyin_full.substring(
1404
+ 0,
1405
+ Math.min(pinyin_full.length, queryPinyinFull.length)
1406
+ )
1407
+ )) {
1408
+ score += 5;
1021
1409
  }
1022
- const matchResult = [];
1023
- allPages.forEach((page) => {
1024
- const { title, pinyin_full, pinyin_first } = page;
1025
- let score = 0;
1026
- if (title.includes(queryKey)) score += 10;
1027
- if (pinyin_full.startsWith(queryPinyinFull)) score += 9;
1028
- if (pinyin_full.includes(queryPinyinFull)) score += 8;
1029
- if (pinyin_first.includes(queryPinyinFirst)) score += 6;
1030
- if (queryPinyinFull.includes(
1031
- pinyin_full.substring(
1032
- 0,
1033
- Math.min(pinyin_full.length, queryPinyinFull.length)
1034
- )
1035
- ))
1036
- score += 5;
1410
+ return score;
1411
+ }
1412
+ findFuzzyMatches(allPages, queryKey, queryPinyinFull, queryPinyinFirst) {
1413
+ const matchResults = [];
1414
+ for (const page of allPages) {
1415
+ const score = this.calculateScore(
1416
+ page,
1417
+ queryKey,
1418
+ queryPinyinFull,
1419
+ queryPinyinFirst
1420
+ );
1037
1421
  if (score > 0) {
1038
- matchResult.push({ id: page.id, title, score });
1422
+ matchResults.push({ id: page.id, title: page.title, score });
1039
1423
  }
1040
- });
1041
- if (matchResult.length === 0) {
1042
- return `❌ 未找到【${queryKey}】相关内容,请按游戏内标准名称重新查询!`;
1043
1424
  }
1044
- const sortedResult = matchResult.sort((a, b) => {
1045
- if (b.score !== a.score) return b.score - a.score;
1046
- return a.title.length - b.title.length;
1047
- });
1048
- const uniqueResult = Array.from(
1049
- new Map(sortedResult.map((item) => [item.title, item])).values()
1425
+ return matchResults.sort((a, b) => b.score - a.score || a.title.length - b.title.length).filter(
1426
+ (item, index, array) => array.findIndex((t) => t.title === item.title) === index
1050
1427
  ).slice(0, 5);
1051
- const resultCount = uniqueResult.length;
1428
+ }
1429
+ async handleSelection(session, matches) {
1430
+ const resultCount = matches.length;
1052
1431
  let replyMsg = `🔍 未找到精准匹配,为你找到【 ${resultCount} 】个相似结果,请输入序号选择(10秒内有效):
1053
1432
  `;
1054
- uniqueResult.forEach((item, index) => {
1433
+ matches.forEach((item, index) => {
1055
1434
  replyMsg += `${index + 1}. ${item.title}
1056
1435
  `;
1057
1436
  });
@@ -1060,92 +1439,163 @@ bwiki: https://${config.domain}/bw/${id2} `;
1060
1439
  await session.send(replyMsg);
1061
1440
  const userInput = await session.prompt(15e3);
1062
1441
  if (!userInput) return;
1063
- const selectNum = parseInt(userInput.trim());
1442
+ const selectNum = parseInt(userInput.trim(), 10);
1064
1443
  if (isNaN(selectNum) || selectNum < 1 || selectNum > resultCount) {
1065
1444
  return `❌ 输入无效!请输入 1-${resultCount} 之间的数字序号`;
1066
1445
  }
1067
- const { id } = uniqueResult[selectNum - 1];
1068
- return `✅ 选择成功
1069
- 原站点: https://${config.domain}/gg/${id}
1070
-
1071
- bwiki: https://${config.domain}/bw/${id}`;
1446
+ const selected = matches[selectNum - 1];
1447
+ return this.formatResultMessage(
1448
+ selected,
1449
+ "✅ 选择成功"
1450
+ );
1451
+ }
1452
+ };
1453
+ ((QueryCommands2) => {
1454
+ QueryCommands2.Config = import_koishi9.Schema.object({
1455
+ domain: import_koishi9.Schema.string().description("你的短链域名(必填,如:klei.vip)").default("klei.vip")
1072
1456
  });
1073
- ctx.command("update", "更新本地页面缓存(主站)", { authority: 2 }).action(async ({ session }) => {
1074
- await session.execute("update.status");
1075
- try {
1076
- const res = await ggbot.request({
1077
- action: "query",
1078
- list: "allpages",
1079
- format: "json",
1080
- aplimit: "max"
1081
- });
1082
- logger.info("主站页面查询成功");
1083
- const pages = res.query.allpages || [];
1084
- const pageData = pages.map((page) => {
1085
- const { pinyin_full, pinyin_first } = generatePinyinInfo(page.title);
1086
- return {
1087
- id: page.pageid,
1088
- title: page.title,
1089
- pinyin_full,
1090
- pinyin_first
1091
- };
1092
- });
1093
- if (pageData.length > 0) {
1094
- await ctx.database.upsert("wikipages", pageData);
1457
+ })(QueryCommands || (QueryCommands = {}));
1458
+
1459
+ // src/plugins/updateCommands.ts
1460
+ var import_koishi10 = require("koishi");
1461
+ var UpdateCommands = class {
1462
+ static {
1463
+ __name(this, "UpdateCommands");
1464
+ }
1465
+ static inject = ["database", "wikiBot"];
1466
+ config;
1467
+ log;
1468
+ constructor(ctx, config) {
1469
+ this.config = config;
1470
+ this.log = ctx.logger("oni-sync");
1471
+ this.registerCommands(ctx);
1472
+ }
1473
+ registerCommands(ctx) {
1474
+ ctx.command("update", "更新本地页面缓存(主站)", { authority: 2 }).action(async ({ session }) => {
1475
+ await session.execute("update.status");
1476
+ try {
1477
+ const res = await ctx.wikiBot.getGGBot().request({
1478
+ action: "query",
1479
+ list: "allpages",
1480
+ format: "json",
1481
+ aplimit: "max"
1482
+ });
1483
+ logger.info("主站页面查询成功");
1484
+ const pages = res.query.allpages || [];
1485
+ const pageData = pages.map((page) => {
1486
+ const { pinyin_full, pinyin_first } = generatePinyinInfo(
1487
+ page.title
1488
+ );
1489
+ return {
1490
+ id: page.pageid,
1491
+ title: page.title,
1492
+ pinyin_full,
1493
+ pinyin_first
1494
+ };
1495
+ });
1496
+ if (pageData.length > 0) {
1497
+ await ctx.database.upsert("wikipages", pageData);
1498
+ }
1499
+ session.send(`✅ 检索到 ${pages.length} 个页面,已更新至数据库`);
1500
+ logger.info(`检索到 ${pages.length} 个页面,已更新至数据库`);
1501
+ } catch (err) {
1502
+ this.log.error("主站缓存更新失败", err);
1503
+ session.send("❌ 主站缓存更新失败,请联系管理员查看日志");
1095
1504
  }
1096
- session.send(`✅ 检索到 ${pages.length} 个页面,已更新至数据库`);
1097
- logger.info(`检索到 ${pages.length} 个页面,已更新至数据库`);
1098
- } catch (err) {
1099
- log.error("主站缓存更新失败", err);
1100
- session.send("❌ 主站缓存更新失败,请联系管理员查看日志");
1101
- }
1102
- });
1103
- ctx.command("update.delete", "删除本地页面缓存", { authority: 4 }).action(async ({ session }) => {
1104
- try {
1105
- const count = await ctx.database.remove("wikipages", {});
1106
- session.send(`✅ 已删除 ${count.removed} 条本地缓存`);
1107
- logger.info(`已删除 ${count.removed} 条本地缓存`);
1108
- } catch (err) {
1109
- log.error("删除缓存失败", err);
1110
- session.send("❌ 删除缓存失败,请联系管理员查看日志");
1111
- }
1112
- });
1113
- ctx.command("update.status", "查询本地缓存数量", { authority: 1 }).action(async ({ session }) => {
1114
- try {
1115
- const pages = await ctx.database.get("wikipages", {});
1116
- session.send(`📊 数据库中缓存了 ${pages.length} 条页面`);
1117
- logger.info(`数据库中缓存了 ${pages.length} 条页面`);
1118
- } catch (err) {
1119
- log.error("查询缓存状态失败", err);
1120
- session.send("❌ 查询缓存状态失败,请联系管理员查看日志");
1121
- }
1122
- });
1123
- ctx.command("redirect <pageName> <targetPageName>", "添加原站点重定向", {
1124
- authority: 2
1125
- }).alias("重定向").action(async ({ session }, pageName, targetPageName) => {
1126
- if (!pageName || !targetPageName) {
1127
- return "❌ 参数错误!用法:redirect <原页面名> <目标页面名>";
1128
- }
1129
- try {
1130
- await ggbot.create(
1131
- pageName,
1132
- `#REDIRECT [[${targetPageName}]]`,
1133
- "来自qq机器人的添加重定向页面请求"
1134
- );
1135
- logger.info(`已为 ${pageName} 添加重定向至 ${targetPageName}`);
1136
- session.send(`✅ 已尝试添加重定向 ${pageName} -> ${targetPageName}`);
1137
- await session.execute(`update`);
1138
- } catch (err) {
1139
- log.error(`添加重定向 ${pageName} -> ${targetPageName} 失败`, err);
1140
- session.send(`❌ 添加重定向失败,请联系管理员查看日志`);
1141
- }
1505
+ });
1506
+ ctx.command("update.delete", "删除本地页面缓存", { authority: 4 }).action(async ({ session }) => {
1507
+ try {
1508
+ const count = await ctx.database.remove("wikipages", {});
1509
+ session.send(`✅ 已删除 ${count.removed} 条本地缓存`);
1510
+ logger.info(`已删除 ${count.removed} 条本地缓存`);
1511
+ } catch (err) {
1512
+ this.log.error("删除缓存失败", err);
1513
+ session.send("❌ 删除缓存失败,请联系管理员查看日志");
1514
+ }
1515
+ });
1516
+ ctx.command("update.status", "查询本地缓存数量", { authority: 1 }).action(async ({ session }) => {
1517
+ try {
1518
+ const pages = await ctx.database.get("wikipages", {});
1519
+ session.send(`📊 数据库中缓存了 ${pages.length} 条页面`);
1520
+ logger.info(`数据库中缓存了 ${pages.length} 条页面`);
1521
+ } catch (err) {
1522
+ this.log.error("查询缓存状态失败", err);
1523
+ session.send("❌ 查询缓存状态失败,请联系管理员查看日志");
1524
+ }
1525
+ });
1526
+ ctx.command("redirect <pageName> <targetPageName>", "添加原站点重定向", {
1527
+ authority: 2
1528
+ }).alias("重定向").action(async ({ session }, pageName, targetPageName) => {
1529
+ if (!pageName || !targetPageName) {
1530
+ return "❌ 参数错误!用法:redirect <原页面名> <目标页面名>";
1531
+ }
1532
+ try {
1533
+ await ctx.wikiBot.getGGBot().create(
1534
+ pageName,
1535
+ `#REDIRECT [[${targetPageName}]]`,
1536
+ "来自qq机器人的添加重定向页面请求"
1537
+ );
1538
+ logger.info(`已为 ${pageName} 添加重定向至 ${targetPageName}`);
1539
+ session.send(`✅ 已尝试添加重定向 ${pageName} -> ${targetPageName}`);
1540
+ await session.execute(`update`);
1541
+ } catch (err) {
1542
+ this.log.error(
1543
+ `添加重定向 ${pageName} -> ${targetPageName} 失败`,
1544
+ err
1545
+ );
1546
+ session.send(`❌ 添加重定向失败,请联系管理员查看日志`);
1547
+ }
1548
+ });
1549
+ ctx.command("relogin", "手动重新登录 Wiki 机器人", { authority: 2 }).alias("重新登录").action(async ({ session }) => {
1550
+ session.send("🚀 开始重新登录 Wiki 机器人...");
1551
+ try {
1552
+ const result = await ctx.wikiBot.relogin();
1553
+ let message = "📋 重新登录结果:\n";
1554
+ message += result.gg ? "✅ WIKIGG 登录成功\n" : "❌ WIKIGG 登录失败\n";
1555
+ message += result.bwiki ? "✅ bwiki 登录成功\n" : "❌ bwiki 登录失败\n";
1556
+ if (result.gg && result.bwiki) {
1557
+ message += "\n🎉 两个 Wiki 机器人都已成功登录!";
1558
+ } else if (result.gg || result.bwiki) {
1559
+ message += "\n⚠️ 部分 Wiki 机器人已登录";
1560
+ } else {
1561
+ message += "\n💥 所有 Wiki 机器人登录都失败了,请检查配置";
1562
+ }
1563
+ session.send(message);
1564
+ } catch (err) {
1565
+ this.log.error("重新登录失败", err);
1566
+ session.send("❌ 重新登录过程中发生错误,请查看日志");
1567
+ }
1568
+ });
1569
+ }
1570
+ };
1571
+ ((UpdateCommands2) => {
1572
+ UpdateCommands2.Config = import_koishi10.Schema.object({
1573
+ logsUrl: import_koishi10.Schema.string().description("日志查看地址").default("https://klei.vip/onilogs")
1142
1574
  });
1575
+ })(UpdateCommands || (UpdateCommands = {}));
1576
+
1577
+ // src/index.ts
1578
+ var name = "oni-sync-bot";
1579
+ var Config = import_koishi11.Schema.intersect([
1580
+ WikiBotService.Config,
1581
+ RouteRedirect.Config,
1582
+ SyncCommands.Config,
1583
+ QueryCommands.Config,
1584
+ UpdateCommands.Config
1585
+ ]);
1586
+ function apply(ctx, config) {
1587
+ ctx.plugin(WikiBotService, config);
1588
+ ctx.plugin(ConsoleLogProvider);
1589
+ ctx.plugin(DatabaseExtension);
1590
+ ctx.plugin(RouteRedirect, config);
1591
+ ctx.plugin(SyncCommands, config);
1592
+ ctx.plugin(QueryCommands, config);
1593
+ ctx.plugin(UpdateCommands, config);
1143
1594
  }
1144
1595
  __name(apply, "apply");
1145
1596
  // Annotate the CommonJS export names for ESM import in node:
1146
1597
  0 && (module.exports = {
1147
1598
  Config,
1148
1599
  apply,
1149
- inject,
1150
1600
  name
1151
1601
  });