koishi-plugin-oni-sync-bot 0.8.5 → 0.8.6

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
@@ -537,6 +537,12 @@ var RouteRedirect = class {
537
537
  )}`;
538
538
  router.redirect(targetUrl);
539
539
  });
540
+ ctx.server.get("/ggwiki/*", async (router) => {
541
+ const suffix = router.params[""] || "";
542
+ const queryString = router.querystring ? `?${router.querystring}` : "";
543
+ const targetUrl = `https://${this.config.main_site}/${suffix}${queryString}`;
544
+ router.redirect(targetUrl);
545
+ });
540
546
  }
541
547
  };
542
548
  ((RouteRedirect2) => {
@@ -1424,18 +1430,21 @@ var QueryCommands = class {
1424
1430
  }
1425
1431
  static inject = ["database"];
1426
1432
  config;
1433
+ ctx;
1427
1434
  constructor(ctx, config) {
1435
+ this.ctx = ctx;
1428
1436
  this.config = config;
1429
1437
  this.registerCommands(ctx);
1430
1438
  }
1431
1439
  registerCommands(ctx) {
1432
- ctx.command("x <itemName>", "查询缺氧中文wiki,精准匹配+拼音模糊匹配").alias("/查wiki").action(async ({ session }, itemName = "") => {
1440
+ ctx.command("x <itemName:text>", "查询缺氧中文wiki,精准匹配+拼音模糊匹配").alias("/查wiki").action(async ({ session }, itemName = "") => {
1433
1441
  const queryKey = itemName.trim().toLowerCase();
1434
1442
  if (!queryKey) {
1435
- return this.getUsageMessage();
1443
+ return this.renderUsageMessage(session);
1436
1444
  }
1437
1445
  if (SPECIAL_CASES.has(queryKey)) {
1438
- return SPECIAL_CASES.get(queryKey);
1446
+ const content = SPECIAL_CASES.get(queryKey);
1447
+ return this.sendResponse(session, content);
1439
1448
  }
1440
1449
  const { pinyin_full: queryPinyinFull, pinyin_first: queryPinyinFirst } = generatePinyinInfo(queryKey);
1441
1450
  const preciseMatch = await this.findPreciseMatch(
@@ -1445,14 +1454,18 @@ var QueryCommands = class {
1445
1454
  queryPinyinFirst
1446
1455
  );
1447
1456
  if (preciseMatch) {
1448
- return this.formatResultMessage(
1457
+ return this.renderResultMessage(
1458
+ session,
1449
1459
  preciseMatch,
1450
1460
  preciseMatch.prefix
1451
1461
  );
1452
1462
  }
1453
1463
  const allPages = await ctx.database.get("wikipages", {});
1454
1464
  if (allPages.length === 0) {
1455
- return `❌ 本地缓存为空,请联系管理员执行【update】指令更新缓存!`;
1465
+ return this.sendResponse(
1466
+ session,
1467
+ "❌ 本地缓存为空,请联系管理员执行【update】指令更新缓存!"
1468
+ );
1456
1469
  }
1457
1470
  const fuzzyMatches = this.findFuzzyMatches(
1458
1471
  allPages,
@@ -1461,23 +1474,139 @@ var QueryCommands = class {
1461
1474
  queryPinyinFirst
1462
1475
  );
1463
1476
  if (fuzzyMatches.length === 0) {
1464
- return `❌ 未找到【${queryKey}】相关内容,请按游戏内标准名称重新查询!`;
1477
+ return this.sendResponse(
1478
+ session,
1479
+ `❌ 未找到【${queryKey}】相关内容,请按游戏内标准名称重新查询!`
1480
+ );
1465
1481
  }
1466
1482
  return await this.handleSelection(session, fuzzyMatches);
1467
1483
  });
1468
1484
  }
1469
- getUsageMessage() {
1485
+ isQQPlatform(session) {
1486
+ return session?.platform === "qq";
1487
+ }
1488
+ buildLinkButton(idSuffix, label, url) {
1489
+ return {
1490
+ id: `btn-${idSuffix}`,
1491
+ render_data: {
1492
+ label,
1493
+ visited_label: "已打开",
1494
+ style: 1
1495
+ },
1496
+ action: {
1497
+ type: 0,
1498
+ permission: {
1499
+ type: 2
1500
+ },
1501
+ data: url
1502
+ }
1503
+ };
1504
+ }
1505
+ buildQQKeyboard(matches) {
1506
+ const rows = [];
1507
+ for (let i = 0; i < matches.length; i += 2) {
1508
+ const buttonsInRow = matches.slice(i, i + 2).map((item) => {
1509
+ const label = item.title.length > 10 ? item.title.slice(0, 10) : item.title;
1510
+ return this.buildLinkButton(
1511
+ `${item.id}`,
1512
+ label,
1513
+ `https://${this.config.domain}/bw/${item.id}`
1514
+ );
1515
+ });
1516
+ rows.push({ buttons: buttonsInRow });
1517
+ }
1518
+ return {
1519
+ content: { rows }
1520
+ };
1521
+ }
1522
+ async sendResponse(session, content, keyboard) {
1523
+ if (this.isQQPlatform(session)) {
1524
+ try {
1525
+ const payload = {
1526
+ msg_id: session.messageId,
1527
+ msg_type: 2,
1528
+ markdown: {
1529
+ content
1530
+ }
1531
+ };
1532
+ if (keyboard) {
1533
+ payload.keyboard = keyboard;
1534
+ }
1535
+ await session.bot.internal.sendMessage(session.channelId, payload);
1536
+ return;
1537
+ } catch (e) {
1538
+ this.ctx.logger("oni-sync-bot").warn("QQ Markdown 发送失败,降级为纯文本:", e);
1539
+ return content;
1540
+ }
1541
+ }
1542
+ return content;
1543
+ }
1544
+ renderUsageMessage(session) {
1545
+ if (this.isQQPlatform(session)) {
1546
+ const md = `**以下是使用说明:**
1547
+ > GG原站点: [https://${this.config.domain}/gg/88888888](https://${this.config.domain}/gg/88888888)
1548
+
1549
+ > bwiki: [https://${this.config.domain}/bw/88888888](https://${this.config.domain}/bw/88888888)
1550
+ `;
1551
+ return this.sendResponse(session, md);
1552
+ }
1470
1553
  return `以下是使用说明:
1471
- 原站点: https://${this.config.domain}/gg/88888888
1554
+ GG原站点: https://${this.config.domain}/gg/88888888
1472
1555
 
1473
1556
  bwiki: https://${this.config.domain}/bw/88888888`;
1474
1557
  }
1475
- formatResultMessage(match, prefix) {
1558
+ buildResultKeyboard(match) {
1559
+ return {
1560
+ content: {
1561
+ rows: [
1562
+ {
1563
+ buttons: [
1564
+ this.buildLinkButton(
1565
+ `gg-${match.id}`,
1566
+ "GG 原站",
1567
+ `https://${this.config.domain}/gg/${match.id}`
1568
+ )
1569
+ ]
1570
+ },
1571
+ {
1572
+ buttons: [
1573
+ this.buildLinkButton(
1574
+ `bw-${match.id}`,
1575
+ "Bwiki 镜像",
1576
+ `https://${this.config.domain}/bw/${match.id}`
1577
+ )
1578
+ ]
1579
+ },
1580
+ {
1581
+ buttons: [
1582
+ this.buildLinkButton(
1583
+ `wiki-${match.id}`,
1584
+ "GG直链",
1585
+ `https://oxygennotincluded.wiki.gg/zh/${encodeURIComponent(match.title)}`
1586
+ )
1587
+ ]
1588
+ }
1589
+ ]
1590
+ }
1591
+ };
1592
+ }
1593
+ renderResultMessage(session, match, prefix) {
1594
+ if (this.isQQPlatform(session)) {
1595
+ let md = "";
1596
+ if (prefix) {
1597
+ md += `**${prefix}**
1598
+ `;
1599
+ }
1600
+ md += `**📖 ${match.title}**
1601
+ `;
1602
+ const keyboard = this.buildResultKeyboard(match);
1603
+ return this.sendResponse(session, md, keyboard);
1604
+ }
1476
1605
  let message = "";
1477
1606
  if (prefix) {
1478
1607
  message += prefix + "\n";
1479
1608
  }
1480
- message += `原站点: https://${this.config.domain}/gg/${match.id}
1609
+ message += `GG原站点: https://${this.config.domain}/gg/${match.id}
1481
1610
 
1482
1611
  bwiki: https://${this.config.domain}/bw/${match.id}`;
1483
1612
  return message;
@@ -1544,7 +1673,20 @@ bwiki: https://${this.config.domain}/bw/${match.id}`;
1544
1673
  }
1545
1674
  async handleSelection(session, matches) {
1546
1675
  const resultCount = matches.length;
1547
- let replyMsg = `🔍 未找到精准匹配,为你找到【 ${resultCount} 】个相似结果,请输入序号选择(10秒内有效):
1676
+ let replyMsg;
1677
+ if (this.isQQPlatform(session)) {
1678
+ let md = `**🔍 为你找到【 ${resultCount} 】个相似结果,请点击下方按钮查看详情:**
1679
+ `;
1680
+ matches.forEach((item, index) => {
1681
+ md += `
1682
+ > \`${index + 1}\`. **${item.title}**
1683
+ `;
1684
+ });
1685
+ const keyboard = this.buildQQKeyboard(matches);
1686
+ await this.sendResponse(session, md, keyboard);
1687
+ return;
1688
+ }
1689
+ replyMsg = `🔍 未找到精准匹配,为你找到【 ${resultCount} 】个相似结果,请输入序号选择(10秒内有效):
1548
1690
  `;
1549
1691
  matches.forEach((item, index) => {
1550
1692
  replyMsg += `${index + 1}. ${item.title}
@@ -1552,15 +1694,19 @@ bwiki: https://${this.config.domain}/bw/${match.id}`;
1552
1694
  });
1553
1695
  replyMsg += `
1554
1696
  ❗️ 提示:超时将静默结束,无任何回应`;
1555
- await session.send(replyMsg);
1697
+ await this.sendResponse(session, replyMsg);
1556
1698
  const userInput = await session.prompt(15e3);
1557
1699
  if (!userInput) return;
1558
1700
  const selectNum = parseInt(userInput.trim(), 10);
1559
1701
  if (isNaN(selectNum) || selectNum < 1 || selectNum > resultCount) {
1560
- return `❌ 输入无效!请输入 1-${resultCount} 之间的数字序号`;
1702
+ return this.sendResponse(
1703
+ session,
1704
+ `❌ 输入无效!请输入 1-${resultCount} 之间的数字序号`
1705
+ );
1561
1706
  }
1562
1707
  const selected = matches[selectNum - 1];
1563
- return this.formatResultMessage(
1708
+ return this.renderResultMessage(
1709
+ session,
1564
1710
  selected,
1565
1711
  "✅ 选择成功"
1566
1712
  );
@@ -5,10 +5,16 @@ export interface QueryCommandsConfig {
5
5
  export declare class QueryCommands {
6
6
  static readonly inject: string[];
7
7
  config: QueryCommandsConfig;
8
+ private ctx;
8
9
  constructor(ctx: Context, config: QueryCommandsConfig);
9
10
  private registerCommands;
10
- private getUsageMessage;
11
- private formatResultMessage;
11
+ private isQQPlatform;
12
+ private buildLinkButton;
13
+ private buildQQKeyboard;
14
+ private sendResponse;
15
+ private renderUsageMessage;
16
+ private buildResultKeyboard;
17
+ private renderResultMessage;
12
18
  private findPreciseMatch;
13
19
  private calculateScore;
14
20
  private findFuzzyMatches;
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.8.5",
4
+ "version": "0.8.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -2,235 +2,311 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-oni-sync-bot?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-oni-sync-bot)
4
4
  [![Koishi](https://img.shields.io/badge/koishi-plugin-42b983?style=flat-square)](https://koishi.chat)
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
6
 
6
- 缺氧 Wiki 站镜像点同步插件 - 自动同步两个缺氧 Wiki 站点的内容,同时提供 TodoList 管理功能。
7
+ **缺氧 Wiki 镜像站同步机器人插件** 实现 [oxygennotincluded.wiki.gg](https://oxygennotincluded.wiki.gg) B站 Wiki 镜像的双向内容同步、拼音匹配页面查询、模块同步管理,以及页面短链接重定向服务。
7
8
 
8
- ## 功能特性
9
+ ## ✨ 核心功能
9
10
 
10
- ### 核心功能
11
+ ### 📄 Wiki 同步
11
12
 
12
- - 🚀 **自动登录** - 支持 WIKIGG bwiki 双站点登录
13
- - 🔄 **自动重连** - 登录过期时自动重新登录并重试
14
- - 📄 **页面同步** - 单个页面同步到目标站点
15
- - 📦 **模块同步** - 批量同步指定前缀的页面
16
- - 🖼️ **图片同步** - 同步页面引用的图片文件
17
- - 🔍 **页面查询** - 查询页面历史版本和当前版本
18
- - **增量更新** - 定时检测并同步最近修改的内容
13
+ - **双向登录** 自动登录 wiki.gg B站 Wiki(bwiki)两个站点
14
+ - **页面同步** 单个/批量页面从 wiki.gg 同步到 bwiki
15
+ - **模块同步** 批量同步 Module 命名空间页面
16
+ - **图片同步** 同步页面引用的图片文件(支持单个/批量)
17
+ - **增量更新** 每小时自动检测最近编辑并同步到镜像站
18
+ - **自动重连** 登录过期自动检测并重新登录(3次重试机制)
19
+ - **登录重试** 单次登录失败自动重试(最多3次,间隔5秒)
19
20
 
20
- ### TodoList 功能
21
+ ### 🔍 页面查询
21
22
 
22
- - **待办事项管理** - 增删改查待办事项
23
- - 📋 **控制台页面** - 可视化管理 TodoList
24
- - 🔄 **模块同步** - 自动同步 wiki.gg 模块到 TodoList
25
- - 📊 **同步状态检查** - 自动检查模块同步状态
26
- - 🕐 **定时任务** - 每周一同步模块,每周二检查状态
27
- - 🎨 **可爱风格** - 萌系风格的 UI 设计
28
- - 🔍 **搜索筛选** - 支持关键词搜索和状态筛选
29
- - 📄 **分页显示** - 支持分页和自定义每页条数
30
- - 📱 **移动端适配** - 响应式设计,支持手机访问
23
+ - **精准匹配** 标题/全拼/首字母精确匹配
24
+ - **拼音模糊匹配** 支持中文名称的拼音搜索
25
+ - **QQ Markdown 按钮** QQ群查询结果以可点击按钮形式呈现(点击即跳转页面)
26
+ - **命令别名** `x` / `查wiki` 快捷命令
31
27
 
32
- ## 安装
28
+ ### 📋 模块同步 TodoList
33
29
 
34
- ```bash
35
- npm install koishi-plugin-oni-sync-bot
36
- #
37
- yarn add koishi-plugin-oni-sync-bot
38
- ```
39
-
40
- ## 配置
41
-
42
- 在 Koishi 控制台中配置以下参数:
30
+ - **自动同步** — 每周一 00:00 自动同步 wiki.gg 所有 Module 到待办
31
+ - **状态检查** — 每周二 00:00 自动检查模块同步状态(对比修订时间)
32
+ - **控制台管理** — 萌系风格可视化管理界面,支持搜索/筛选/分页
33
+ - **增删改查** — 完整的待办管理命令和控制台操作
34
+ - **权限控制** — 删除/清空操作需权限 2,删除缓存需权限 4
43
35
 
44
- | 参数 | 说明 | 默认值 |
45
- | --------------- | ------------- | ------ |
46
- | `ggUsername` | WIKIGG 用户名 | 1 |
47
- | `ggPassword` | WIKIGG 密码 | 1 |
48
- | `bwikiusername` | bwiki 用户名 | 1 |
49
- | `bwikipassword` | bwiki 密码 | 1 |
36
+ ### 路由重定向
50
37
 
51
- ## 命令列表
38
+ - **短链接服务** — `/:id` → 跳转至完整 Wiki 页面 URL
39
+ - **GG 原站跳转** — `/gg/:id` → 跳转至 oxygennotincluded.wiki.gg 对应页面
40
+ - **Bwiki 镜像跳转** — `/bw/:id` → 跳转至 wiki.biligame.com/oni 对应页面
41
+ - **通配路径转发** — `/ggwiki/*` → 转发任意路径和查询参数到原站
42
+ - **持久化缓存** — 页面标题和页面ID映射保存在本地数据库,首次启动需执行 `update`
52
43
 
53
- ### 同步命令
44
+ ### 🎮 特殊指令
54
45
 
55
- | 命令 | 说明 |
56
- | -------------------- | ---------------------- |
57
- | `sync.page <标题>` | 同步单个页面到 bwiki |
58
- | `sync.gg <标题>` | 同步单个页面到 WIKIGG |
59
- | `sync.module <前缀>` | 同步指定前缀的所有页面 |
60
- | `sync.img <标题>` | 同步页面引用的图片 |
61
- | `sync.update` | 执行增量更新 |
46
+ - **火箭计算器** — 查询 "火箭计算器" 返回计算器工具链接提示
62
47
 
63
- ### TodoList 命令
48
+ ## 📦 项目结构
64
49
 
65
- | 命令 | 说明 | 权限要求 |
66
- | ---------------------- | ------------------------------------- | -------- |
67
- | `todo.list` | 查看 TodoList(重定向到网页) | - |
68
- | `todo.add <标题>` | 添加待办事项 | - |
69
- | `todo.edit <id>` | 编辑待办事项 | - |
70
- | `todo.complete <id>` | 标记待办事项为完成 | - |
71
- | `todo.uncomplete <id>` | 标记待办事项为未完成 | - |
72
- | `todo.delete <id>` | 删除待办事项 | 2 |
73
- | `todo.clear` | 清空 TodoList | 2 |
74
- | `todo.syncmodules` | 同步 wiki.gg 模块到 TodoList | - |
75
- | `todo.checksync` | 检查模块同步状态 | - |
76
-
77
- ### 管理命令
50
+ ```
51
+ oni-sync-bot/
52
+ ├── src/
53
+ │ ├── index.ts # 插件入口(子插件注册表)
54
+ │ ├── services/
55
+ │ │ ├── wikiBotService.ts # Wiki 机器人核心服务(登录、重连、代理)
56
+ │ │ └── consoleLogProvider.ts # 控制台日志服务
57
+ │ ├── plugins/
58
+ │ │ ├── databaseExtension.ts # 数据库模型扩展(wikipages 表)
59
+ │ │ ├── queryCommands.ts # 页面查询命令(x / 查wiki
60
+ │ │ ├── syncCommands.ts # Wiki 同步命令(sync.*)+ 定时任务
61
+ │ │ ├── updateCommands.ts # 缓存管理命令(update.* / redirect / relogin)
62
+ │ │ ├── routeRedirect.ts # 路由重定向服务(/gg / /bw / /ggwiki)
63
+ │ │ └── todoList.ts # TodoList 模块同步管理(命令 + 控制台 + 定时任务)
64
+ │ ├── sync/
65
+ │ │ ├── pageSync.ts # 页面同步逻辑(单页、全量、增量)
66
+ │ │ ├── moduleSync.ts # 模块同步逻辑
67
+ │ │ └── imgSync.ts # 图片同步逻辑
68
+ │ └── utils/
69
+ │ └── tools.ts # 工具函数(拼音生成、日志、错误处理)
70
+ ├── client/
71
+ │ ├── Rocketcalculator/ # 火箭计算器前端页面
72
+ │ │ ├── calculator.ts
73
+ │ │ ├── calculator.vue
74
+ │ │ ├── config.ts
75
+ │ │ ├── index.vue
76
+ │ ├── onitodolist/ # TodoList 管理前端页面
77
+ │ │ ├── index.vue
78
+ │ │ └── todolist.vue
79
+ │ ├── index.ts
80
+ │ ├── page.vue # 日志查看页面
81
+ │ └── tsconfig.json
82
+ ├── package.json
83
+ ├── tsconfig.json
84
+ └── README.md
85
+ ```
78
86
 
79
- | 命令 | 说明 |
80
- | --------- | ---------------------- |
81
- | `relogin` | 手动重新登录两个机器人 |
87
+ ## 🔧 架构设计
82
88
 
83
- ## TodoList 命令示例
89
+ ### 子插件模式
84
90
 
85
- ### 添加待办
91
+ 主入口 `src/index.ts` 采用依赖顺序注册子插件:
86
92
 
87
93
  ```
88
- /todo.add 同步模块 -c "同步 Module:Data" -u "管理员"
94
+ Service 层(数据库、WikiBot 服务)
95
+
96
+ Core 层(同步逻辑、路由重定向)
97
+
98
+ Command 层(查询命令、同步命令、更新命令、TodoList)
99
+
100
+ UI 层(控制台日志、TodoList 可视化面板、火箭计算器)
89
101
  ```
90
102
 
91
- ### 编辑待办
103
+ ### 数据库模型
104
+
105
+ | 表名 | 字段 | 说明 |
106
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
107
+ | `wikipages` | `id` (integer), `title` (string), `pinyin_full` (string), `pinyin_first` (string) | Wiki 页面索引缓存,支持拼音检索 |
108
+ | `onitodos` | `id` (unsigned, auto), `title` (string), `content` (text), `completed` (boolean), `createdBy` (string), `createdAt` (timestamp), `updatedAt` (timestamp) | 模块同步待办事项 |
109
+
110
+ ### 核心技术
111
+
112
+ | 技术 | 用途 |
113
+ | ---------------------------- | -------------------------------------------- |
114
+ | **TypeScript** | 完整类型支持,严格模式编译 |
115
+ | **Koishi 4** | 机器人框架,基于 Service 和 Plugin 架构 |
116
+ | **Mwn** | MediaWiki API 客户端(wiki.gg / bwiki 交互) |
117
+ | **pinyin-pro** | 中文转拼音,支持页面名称模糊匹配 |
118
+ | **koishi-plugin-cron** | 定时任务调度(每小时增量更新、每周同步等) |
119
+ | **@koishijs/plugin-console** | Koishi 控制台(提供可视化管理界面) |
120
+ | **@koishijs/plugin-server** | HTTP 服务器(提供短链接重定向路由) |
121
+
122
+ ## ⚙️ 配置项
123
+
124
+ | 参数 | 说明 | 默认值 | 必需 |
125
+ | --------------- | ------------------- | ------------------------------ | ---- |
126
+ | `ggUsername` | WIKIGG 用户名 | `1` | ✅ |
127
+ | `ggPassword` | WIKIGG 密码 | `1` | ✅ |
128
+ | `bwikiusername` | Bwiki 用户名 | `1` | ✅ |
129
+ | `bwikipassword` | Bwiki 密码 | `1` | ✅ |
130
+ | `domain` | 你的短链域名 | `klei.vip` | ✅ |
131
+ | `main_site` | 主站域名(wiki.gg) | `oxygennotincluded.wiki.gg/zh` | ✅ |
132
+ | `bwiki_site` | 镜像站域名(bwiki) | `wiki.biligame.com/oni` | ✅ |
133
+ | `logsUrl` | 日志查看地址 | `https://klei.vip/onilogs` | - |
134
+
135
+ ## 📚 命令列表
136
+
137
+ ### 🔍 查询命令
138
+
139
+ | 命令 | 别名 | 说明 | 权限 |
140
+ | --------------- | -------- | ------------------------------ | ---- |
141
+ | `x <itemName>` | `查wiki` | 查询页面(标题/拼音/模糊匹配) | - |
142
+ | `查询 <关键词>` | - | 同上(备用命令名) | - |
143
+
144
+ ### 🔄 同步命令
145
+
146
+ | 命令 | 别名 | 说明 | 权限 |
147
+ | ------------------------ | ---------- | -------------------- | ---- |
148
+ | `sync <标题>` | - | 同步单个页面到 bwiki | 2 |
149
+ | `sync.allpages` | - | 同步所有页面 | 2 |
150
+ | `sync.module <模块名>` | - | 同步单个模块 | 2 |
151
+ | `sync.allmodules` | - | 同步所有模块 | 2 |
152
+ | `sync.img <图片名>` | - | 同步单张图片 | 2 |
153
+ | `sync.allimgs` | - | 同步所有图片 | 2 |
154
+ | `sync.incrementalUpdate` | `增量更新` | 执行增量更新 | 2 |
155
+
156
+ ### 📝 缓存管理
157
+
158
+ | 命令 | 别名 | 说明 | 权限 |
159
+ | ------------------------------ | ---------- | ----------------------------------- | ---- |
160
+ | `update` | - | 更新本地页面缓存(从 wiki.gg 获取) | 2 |
161
+ | `update.status` | - | 查询缓存数量和状态 | 1 |
162
+ | `update.delete` | - | 清空页面缓存 | 4 |
163
+ | `redirect <原页面> <目标页面>` | `重定向` | 在 wiki.gg 创建重定向页面 | 2 |
164
+ | `relogin` | `重新登录` | 手动重新登录两个 Wiki 机器人 | 2 |
165
+
166
+ ### ✅ TodoList 命令
167
+
168
+ | 命令 | 别名 | 说明 | 权限 |
169
+ | ---------------------- | -------- | ---------------------------------------- | ---- |
170
+ | `todolist` | `todo` | 查看 TodoList(跳转网页) | - |
171
+ | `todo.list` | `todo.l` | 查看 TodoList(跳转网页) | - |
172
+ | `todo.add <标题>` | `todo.a` | 添加待办事项(支持 `-c` `-u` 选项) | - |
173
+ | `todo.edit <id>` | `todo.e` | 编辑待办事项(支持 `-t` `-c` `-u` 选项) | - |
174
+ | `todo.complete <id>` | `todo.c` | 标记待办为完成 | - |
175
+ | `todo.uncomplete <id>` | `todo.u` | 标记待办为未完成 | - |
176
+ | `todo.delete <id>` | `todo.d` | 删除待办事项 | 2 |
177
+ | `todo.clear` | - | 清空 TodoList | 2 |
178
+ | `todo.syncmodules` | - | 手动同步 wiki.gg 模块到 TodoList | - |
179
+ | `todo.checksync` | - | 手动检查模块同步状态 | - |
180
+
181
+ ## ⏰ 定时任务
182
+
183
+ | 时间 | 任务 | 说明 |
184
+ | ---------------- | ------------------- | -------------------------------------- |
185
+ | **每小时 15 分** | 增量更新 | 获取最近编辑并同步到 bwiki |
186
+ | **每周四 08:30** | 同步所有页面 | 从 wiki.gg 全量同步页面到 bwiki |
187
+ | **每周三 08:30** | 同步所有图片 | 从 wiki.gg 全量同步图片资源 |
188
+ | **每周一 00:00** | 同步模块到 TodoList | 获取所有 Module 命名空间页面并写入待办 |
189
+ | **每周二 00:00** | 检查模块同步状态 | 对比修订时间,自动标记完成/未完成 |
190
+
191
+ ## 🔌 路由接口
192
+
193
+ | 路径 | 方法 | 说明 |
194
+ | ----------- | ---- | --------------------------------------------------------- |
195
+ | `/gg/:id` | GET | 跳转至 oxygennotincluded.wiki.gg 对应页面(自动编码标题) |
196
+ | `/bw/:id` | GET | 跳转至 wiki.biligame.com/oni 对应页面 |
197
+ | `/ggwiki/*` | GET | 通配路径转发至 oxygennotincluded.wiki.gg,保留查询参数 |
198
+
199
+ > � **提示**:首次部署请先执行 `update` 命令填充 `wikipages` 表,否则路由跳转将返回"未找到页面"提示。
200
+
201
+ ## 🚀 安装
92
202
 
93
- ```
94
- /todo.edit 1 -t "新标题" -c "新内容" -u "编辑者"
203
+ ```bash
204
+ npm install koishi-plugin-oni-sync-bot
205
+ # 或
206
+ yarn add koishi-plugin-oni-sync-bot
95
207
  ```
96
208
 
97
- ### 同步模块
209
+ ## 🔨 开发
98
210
 
99
- ```
100
- /todo.syncmodules
101
- ```
211
+ ```bash
212
+ # 克隆项目
213
+ git clone https://github.com/Charles-LF/oni-sync-bot.git
102
214
 
103
- ### 检查同步状态
215
+ # 安装依赖
216
+ yarn install
104
217
 
105
- ```
106
- /todo.checksync
218
+ # 构建
219
+ yarn build
220
+
221
+ # 开发模式
222
+ yarn dev
107
223
  ```
108
224
 
109
- ## 控制台功能
225
+ ### 依赖服务
110
226
 
111
- 插件提供了两个可视化的控制台界面:
227
+ 插件正常运行需以下 Koishi 核心插件(Koishi 默认已提供):
112
228
 
113
- ### 日志控制台
229
+ - **@koishijs/plugin-database** — 数据持久化(wikipages / onitodos)
230
+ - **@koishijs/plugin-server** — HTTP 服务器(短链接路由)
231
+ - **@koishijs/plugin-console** — 控制台 UI(TodoList 管理界面)
232
+ - **koishi-plugin-cron** — 定时任务调度
233
+ - **@koishijs/plugin-adapter-\*(可选)** — 如 QQ 适配器,以获得 Markdown 按钮效果
114
234
 
115
- - 📋 **日志查看** - 按级别(全部/信息/警告/错误)筛选日志
116
- - 🔍 **搜索功能** - 快速搜索日志内容
117
- - ⏬ **自动滚动** - 新日志自动滚动到底部
118
- - 🎯 **一键滚动** - 手动跳转到顶部或底部
235
+ ## 📊 使用示例
119
236
 
120
- ### TodoList 控制台
237
+ ### 🔍 查询页面
121
238
 
122
- - ✨ **可爱风格** - 萌系设计的 UI 界面
123
- - **新增待办** - 弹窗表单添加待办
124
- - ✏️ **编辑待办** - 修改待办内容和状态
125
- - ✅ **完成标记** - 一键标记完成/未完成
126
- - 🔍 **搜索筛选** - 支持按标题、内容搜索,按状态筛选
127
- - 📄 **分页显示** - 超过 8 条自动分页,支持自定义每页条数
128
- - 📊 **统计信息** - 显示总数、已完成、未完成数量
129
- - 📱 **移动端适配** - 响应式布局,完美支持手机访问
239
+ ```
240
+ > x 精炼器
130
241
 
131
- **注意:控制台界面无法删除待办事项,删除操作仅可通过命令完成(需要权限 2)。**
242
+ 🔍 为你找到【3】个相似结果,请点击下方按钮查看详情:
132
243
 
133
- ## 定时任务
244
+ 1. [精炼器]
245
+ 2. [蒸汽精炼器]
246
+ 3. [岩浆精炼器]
134
247
 
135
- 插件使用 `koishi-plugin-cron` 提供定时任务功能:
248
+ (QQ 平台显示可点击按钮,点击即跳转 Wiki 页面;非 QQ 平台返回纯文本)
249
+ ```
136
250
 
137
- - **每周一 00:00** - 自动同步 wiki.gg 所有模块到 TodoList
138
- - **每周二 00:00** - 自动检查模块同步状态,更新完成标记
251
+ ### 🔄 同步页面
139
252
 
140
- ## 技术特性
253
+ ```
254
+ > sync 精炼器
141
255
 
142
- - **TypeScript** - 完整的类型支持
143
- - 🎯 **面向对象** - 基于类的插件架构
144
- - 🔌 **服务化设计** - 核心功能封装为独立服务
145
- - 🧩 **模块化** - 功能拆分为独立子插件
146
- - 🔄 **自动重试** - 登录失败自动重试(最多 3 次)
147
- - 🔐 **自动重连** - 登录过期自动重新登录
148
- - 🛡️ **错误处理** - 完善的错误处理和日志记录
149
- - ⏱️ **API 限流** - 模块同步时请求间隔 1 秒
150
- - 🔄 **API 重试** - API 调用失败自动重试(最多 3 次)
151
- - 📦 **数据库持久化** - TodoList 数据存储在数据库中
256
+ 已尝试同步页面:精炼器,请前往控制台查看
257
+ ```
152
258
 
153
- ## 项目结构
259
+ ### ✅ 标记待办完成
154
260
 
155
261
  ```
156
- koishi-plugin-oni-sync-bot/
157
- ├── src/
158
- │ ├── services/ # 服务层
159
- │ │ ├── wikiBotService.ts # Wiki 机器人服务
160
- │ │ └── consoleLogProvider.ts # 日志控制台服务
161
- │ ├── plugins/ # 插件层
162
- │ │ ├── queryCommands.ts # 查询命令插件
163
- │ │ ├── syncCommands.ts # 同步命令插件
164
- │ │ ├── updateCommands.ts # 更新命令插件
165
- │ │ ├── todoList.ts # TodoList 插件
166
- │ │ ├── databaseExtension.ts # 数据库扩展
167
- │ │ └── routeRedirect.ts # 路由重定向
168
- │ ├── sync/ # 同步逻辑
169
- │ │ ├── pageSync.ts # 页面同步
170
- │ │ ├── moduleSync.ts # 模块同步
171
- │ │ └── imgSync.ts # 图片同步
172
- │ ├── utils/ # 工具函数
173
- │ │ └── tools.ts # 通用工具
174
- │ ├── client/ # 前端
175
- │ │ ├── page.vue # 日志页面
176
- │ │ ├── onitodolist/ # TodoList 页面
177
- │ │ │ └── todolist.vue # TodoList 主组件
178
- │ │ └── index.ts # 入口
179
- │ └── index.ts # 主入口
180
- ├── package.json
181
- └── tsconfig.json
262
+ > todo.complete 5
263
+
264
+ 已标记完成,请访问 https://klei.vip/onitodos 查看
182
265
  ```
183
266
 
184
- ## TodoList 模块同步逻辑
267
+ ## 🎨 控制台界面
185
268
 
186
- ### 模块同步流程
269
+ 插件在 Koishi 控制台提供以下管理页面:
187
270
 
188
- 1. wiki.gg 获取命名空间 828(Module)的所有页面
189
- 2. 逐个获取每个模块的最新修订信息(用户、时间、注释)
190
- 3. 将模块信息存入 TodoList:
191
- - `title` = 模块名称
192
- - `content` = 修订注释
193
- - `createdBy` = 修订用户
194
- - `createdAt` / `updatedAt` = 修订时间
195
- - `completed` = false(默认未完成)
271
+ | 页面 | 功能 |
272
+ | ------------------------------------ | ---------------------------------------------------- |
273
+ | **日志控制台** (`page.vue`) | 按级别筛选日志,搜索内容,自动滚动,快速跳转 |
274
+ | **TodoList 管理** (`onitodolist/`) | 萌系 UI 设计,新增/编辑/标记完成,搜索筛选,分页显示 |
275
+ | **火箭计算器** (`Rocketcalculator/`) | ONI 火箭设计计算器 |
196
276
 
197
- ### 同步状态检查
277
+ ## 🔒 安全与权限
198
278
 
199
- 1. 获取所有 TodoList 中的待办事项
200
- 2. 对每个待办:
201
- - wiki.gg 获取最新修订时间
202
- - bwikia 获取最新修订时间
203
- - 对比时间:
204
- - 如果 bwikia 时间 ≥ wiki.gg 时间 → 标记为已完成
205
- - 如果 bwikia 时间 < wiki.gg 时间 → 标记为未完成
279
+ - 所有 Wiki 编辑操作(同步、创建重定向)需权限 **2**
280
+ - 删除页面缓存需权限 **4**
281
+ - 登录凭证(用户名/密码)仅保存在 Koishi 配置中,不会写入日志
282
+ - WikiBotService 通过 **Proxy** 模式透明处理登录过期,自动重新登录后继续执行原请求
206
283
 
207
- ## 开发
284
+ ## 🐛 故障排查
208
285
 
209
- ```bash
210
- # 创建 KOISHI 项目
211
- yarn create koishi
286
+ ### "❌ 本地缓存为空"
212
287
 
213
- # 克隆项目
214
- git clone https://github.com/Charles-LF/oni-sync-bot.git
288
+ 请管理员执行 `update` 命令从 wiki.gg 获取页面列表。
215
289
 
216
- # 安装依赖
217
- yarn install
290
+ ### "❌ Wiki 机器人未就绪"
218
291
 
219
- # 构建
220
- yarn build
292
+ 检查配置中的账号密码是否正确;可执行 `relogin` 手动重新登录。
221
293
 
222
- # 开发模式
223
- yarn dev
224
- ```
294
+ ### "❌ 未找到ID为【...】的页面"
295
+
296
+ 页面ID不在本地缓存中,可能是最近新增的页面。执行 `update` 更新缓存后再试。
297
+
298
+ ### QQ 机器人只显示纯文本
299
+
300
+ QQ 平台的 Markdown 与按钮效果需要 `session.platform === "qq"` 检测生效。请确认适配器正确上报平台信息。
225
301
 
226
- ## License
302
+ ## 📜 License
227
303
 
228
304
  MIT License
229
305
 
230
- ## 贡献
306
+ ## 🤝 贡献
231
307
 
232
308
  欢迎提交 Issue 和 Pull Request!
233
309
 
234
- ## 支持
310
+ ## 📧 支持
235
311
 
236
- 如有问题,请在 GitHub 上提交 Issue
312
+ 如有问题,请在 GitHub 上提交 Issue,或访问 <https://klei.vip>。