# LTD-ManagerBot 模块文档 ## 目录 - [快速开始](#快速开始) - [基础设施模块](#基础设施模块) - [新模块](#新模块) - [RCON 命令模块](#rcon-命令模块) - [Gitea Webhook 模块](#gitea-webhook-模块) - [白名单审计模块](#白名单审计模块) - [配置参考](#配置参考) --- ## 快速开始 1. 复制 `src/main/resources/` 下的示例配置到 `config/` 2. 编辑 `config/application.yaml` — 设置数据库、API 地址、密钥 3. 编辑 `config/module.yaml` — 取消注释并配置需要的模块 4. 如需使用邀请码或白名单审计模块,将 SQL 模板放入 `config/sql/` 5. 启动机器人 ### 新模块必需的 `application.yaml` 配置 ```yaml # 白名单系统 API (WhitelistAuditModule 必需) whitelist-system: url: "https://whitelist.your-server.top" encrypted-token: "your-api-token-here" ``` --- ## 基础设施模块 ### GroupMessagePollingModule (群消息轮询模块) 按固定间隔轮询 QQ 群消息历史,通过 `SharedFlow>` 向其他模块分发消息。 ```yaml - name: "talkGroup" type: "GROUP_MESSAGE_POLLING_MODULE" enabled: true config: target-group-id: 538751386 # 目标群号 poll-interval-millis: 5000 # 轮询间隔(毫秒),可选,默认5000 msg-history-check: 15 # 每次检查的历史消息数,可选,默认15 ``` ### MailModule (邮件模块) 通过 SMTP 发送邮件。InvitationCodesModule 和 WhitelistAuditModule 会使用此模块。 ```yaml - name: "talkGroup" type: "MAIL_MODULE" enabled: true ``` SMTP 设置在 `application.yaml` → `mail:` 段配置。 --- ## 新模块 ### RCON 命令模块 **类型:** `RCON_COMMAND_MODULE` 允许指定用户在 QQ 群中执行 Minecraft RCON 命令。通过黑名单机制阻止危险操作,非授权用户无法执行。 #### 执行流程 ``` QQ群: "rcon list" │ ├── 用户不在 admin-ids 中 → 回复: "你没有权限执行 RCON 命令" ├── 命令匹配 command-blocklist → 回复: "命令已被阻止" └── 通过 RCON 二进制执行 → 回复执行结果 ``` #### 配置示例 ```yaml - name: "talkGroup" type: "RCON_COMMAND_MODULE" enabled: true dependencies: - name: "talkGroup" type: "GROUP_MESSAGE_POLLING_MODULE" config: self-id: 3327379836 self-nick-name: "Bot" command-prefix: "rcon" # QQ群触发前缀 admin-ids: [2561098830] # 允许执行RCON的QQ号 rcon-timeout-sec: 5 # RCON超时(秒) command-blocklist: # 危险命令黑名单(前缀匹配) - "stop" - "restart" - "op" - "deop" - "whitelist" - "ban" - "ban-ip" - "kick" - "debug" - "execute" ``` #### 配置项 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---|---|---| | `self-id` | long | 是 | — | 机器人 QQ 号 | | `self-nick-name` | string | 是 | — | 机器人昵称 | | `command-prefix` | string | 是 | — | QQ 群触发前缀 | | `admin-ids` | long[] | 是 | — | 允许执行 RCON 的 QQ 号列表 | | `command-blocklist` | string[] | 是 | — | 危险命令黑名单(不区分大小写,自动去除 `/` 前缀) | | `rcon-timeout-sec` | long | 否 | `5` | RCON 超时时间(秒) | RCON 二进制路径和配置文件路径从 `application.yaml` → `tools.rcon` 读取。 #### 黑名单匹配规则 黑名单使用**前缀匹配**,自动去除前置 `/`: - `"stop"` 阻止: `stop`, `stop 10s`, `/stop`, `/minecraft:stop` - `"ban"` 阻止: `ban Steve`, `ban-ip`, `/ban` 等 --- ### Gitea Webhook 模块 **类型:** `GITEA_WEBHOOK_MODULE` 启动嵌入式 HTTP 服务器,接收 [Gitea Webhook](https://docs.gitea.com/zh-cn/usage/webhooks) 事件,格式化后推送通知到 QQ 群。 #### 支持的事件类型 | 事件 | QQ 消息格式 | |---|---| | `push` | 推送者、分支、提交数、前5条提交摘要、对比链接 | | `issues` | 操作(已创建/已关闭/已重新打开)、标题、内容预览、链接 | | `pull_request` | 操作、分支流转(head → base)、状态、链接 | | `create` | 创建的分支/标签、创建者 | | `delete` | 删除的分支/标签、操作者 | | `release` | 标签名、版本名、内容预览、草稿/预发布标记 | | `repository` | 仓库操作(已创建/已删除/已转移) | | `fork` | 源仓库 → Fork 仓库 | #### 配置示例 ```yaml - name: "talkGroup" type: "GITEA_WEBHOOK_MODULE" enabled: true config: webhook-port: 8080 # 监听端口 webhook-path: "/gitea-webhook" # Webhook 路径 webhook-secret: "your-webhook-secret" # HMAC-SHA256 密钥 target-group-id: 538751386 # 推送 QQ 群号 events: # 监听事件列表(空=全部) - "push" - "issues" - "pull_request" - "create" - "delete" - "release" ``` #### 配置项 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---|---|---| | `webhook-port` | int | 是 | — | HTTP 服务器监听端口 | | `webhook-path` | string | 否 | `/gitea-webhook` | Webhook 端点路径 | | `webhook-secret` | string | 否 | `""` | HMAC-SHA256 签名验证密钥,为空则跳过验证 | | `target-group-id` | long | 是 | — | 推送通知的目标 QQ 群号 | | `events` | string[] | 否 | 全部 | 监听的事件类型列表 | #### Gitea 端设置 在 Gitea 仓库 → **设置** → **Webhooks** → **添加 Webhook** → **Gitea**: - **目标 URL**: `http://<服务器IP>:8080/gitea-webhook` - **密钥**: 与 `webhook-secret` 一致 - **触发条件**: 勾选需要的事件 #### 安全性 - 签名验证: 通过 HMAC-SHA256 验证 `X-Gitea-Signature` 请求头 - 生产环境建议务必设置 `webhook-secret` --- ### 白名单审计模块 **类型:** `WHITELIST_AUDIT_MODULE` 定期检查白名单群成员与白名单 API 的一致性。自动拒绝已离开白名单群的用户,设置宽限期供用户通过主群关键词重新激活。支持管理员手动触发审计,输出详细逐人报告。 **存储:** MySQL `ltd_manager_bot.whitelist_audit` 表(首次加载自动建表)。 #### 自动 vs 手动审计 | | 自动审计 | 手动审计 | |---|---|---| | 触发 | 每 N 分钟自动轮询 | 管理员在白名单群发关键词 | | 群消息 | 不发送(安静) | 发送完整统计摘要 + 逐人详情 | | 邮件 | 所有操作始终发送 | 所有操作始终发送 | | 日志前缀 | `[自动]` | `[手动审计]` | #### 生命周期状态 ``` 在白名单群中 ───────────────────────────────► 正常(无操作) │ └── 用户退出群 ──► 检测到离群 ──► 已被拒绝(宽限期开始) │ ├── 主群发关键词 ──► 已重新激活 ├── 进入警告窗口 ──► 邮件通知(仅一次) └── 宽限期过 ──► 永久删除 ``` #### 配置示例 ```yaml - name: "talkGroup" type: "WHITELIST_AUDIT_MODULE" enabled: true dependencies: - name: "talkGroup" # 主群轮询 (重新激活关键词) type: "GROUP_MESSAGE_POLLING_MODULE" - name: "whitelistGroup" # 白名单群轮询 (审计命令) type: "GROUP_MESSAGE_POLLING_MODULE" - name: "talkGroup" # 邮件模块 (enable-email:true 时) type: "MAIL_MODULE" config: self-id: 3327379836 filter-qq-list: [2561098830, 3327379836] audit-allowed-ids: [2561098830] whitelist-group-polling-dep-name: "whitelistGroup" audit-command-prefixes: # 支持多个关键词 - "审计" - "audit" re-activation-keywords: - "重新激活" - "激活白名单" grace-period-days: 7 expiry-warning-days: 2 poll-interval-minutes: 60 enable-email: false # 邮件模板 (空=使用内置默认,支持占位符) email-reject-template: "" email-warning-template: "" email-removed-template: "" email-reactivated-template: "" ``` #### 配置项 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---|---|---| | `self-id` | long | 是 | — | 机器人 QQ 号 | | `filter-qq-list` | long[] | 是 | — | 保护列表,永不自动拒绝 | | `audit-allowed-ids` | long[] | 是 | — | 允许执行手动审计的 QQ | | `whitelist-group-polling-dep-name` | string | 是 | — | 白名单群轮询模块的依赖名 | | `audit-command-prefixes` | string[] | 否 | `["审计"]` | 白名单群触发手动审计的关键词(支持多个) | | `re-activation-keywords` | string[] | 是 | — | 主群中触发重新激活的关键词列表 | | `grace-period-days` | int | 是 | — | 被拒绝后允许重新激活的天数 | | `expiry-warning-days` | int | 是 | — | 宽限期到期前多少天发送警告 | | `poll-interval-minutes` | long | 否 | `60` | 自动审计间隔(分钟) | | `enable-email` | boolean | 否 | `false` | 是否启用邮件通知(需 MailModule 依赖) | | `email-reject-template` | string | 否 | `""` | 拒绝邮件正文。占位符:`${playerName}` `${graceDays}` `${keywords}` | | `email-warning-template` | string | 否 | `""` | 过期警告邮件正文。占位符:`${playerName}` `${remainDays}` `${keywords}` | | `email-removed-template` | string | 否 | `""` | 删除邮件正文。占位符:`${playerName}` | | `email-reactivated-template` | string | 否 | `""` | 重新激活邮件正文。占位符:`${playerName}` `${graceDays}` | #### 手动审计输出示例 ``` 审计完成 ──────────────── 白名单总数: 42 在群正常: 35 人 新发现不在群(已拒绝): 2 人 • Steve(123456789) • Alex(987654321) 宽限期中: Notch(111222333, 剩5天) 已过期删除: 1 人 • Hero(444555666) 过滤列表跳过: 3 人 ``` #### 邮件去重 每类邮件只发送一次: | 邮件 | 去重方式 | |---|---| | 拒绝 | 仅首次检测到离群时发送(`existing == null`) | | 重新拒绝 | 仅之前已重新激活的用户再次离群时发送 | | 即将过期警告 | `warningSentTime == 0L` 守卫,仅发一次 | | 已删除 | 发送后立即从 DB 删除记录,不再匹配 | | 重新激活 | 仅在宽限期内关键词触发成功时发送 | #### 状态持久化 审计状态存储在 **MySQL** `ltd_manager_bot.whitelist_audit` 表中(自动建表,零配置)。Bot 重启不丢失已标记的待处理记录。原先基于 JSON 文件的状态持久化已移除。 SQL 模板位于 `config/sql/whitelist/`: - `create_audit_table.sql` — 建表 DDL - `query_all_audit.sql` — 查全表 - `query_audit_by_qq.sql` — 按 QQ 查询 - `upsert_audit.sql` — 新增或更新 - `delete_audit.sql` — 删除记录 #### 依赖关系 | 依赖模块 | 类型 | 是否必需 | 用途 | |---|---|---|---| | 主群轮询 | `GROUP_MESSAGE_POLLING_MODULE` | 是 | 监听主群重新激活关键词 | | 白名单群轮询 | `GROUP_MESSAGE_POLLING_MODULE` | 是 | 监听审计命令 + 获取群成员列表 | | 邮件模块 | `MAIL_MODULE` | `enable-email: true` 时 | 发送邮件通知 | #### 白名单 API 配置 API 地址和密钥在 `application.yaml` 的 `whitelist-system:` 段配置(启动后自动加密): ```yaml whitelist-system: url: "https://whitelist.your-server.top" encrypted-token: "your-api-token-here" ``` #### 邮件通知时机 | 触发条件 | 邮件主题 | |---|---| | 首次检测离群 → 被拒绝 | `白名单已被拒绝` — 含宽限期和关键词提示 | | 重新激活后再离群 → 再次拒绝 | `白名单已被拒绝` — 同上 | | 进入警告窗口 | `白名单即将过期` — 剩余天数 | | 宽限期过 → 永久删除 | `白名单已删除` | | 关键词重新激活成功 | `白名单已重新激活` — 提醒加群 | --- ## 配置参考 ### SQL 模板 部分模块(InvitationCodesModule、GroupRequestHandlerModule)使用 `config/sql/` 下的 SQL 模板文件。模板使用 `${placeholder}` 语法: ```sql -- config/sql/invitation/query_qualification.sql SELECT q.player_id, q.effective, q.is_used, q.qq, q.status FROM ltd_manager_bot.qualified_user_info q WHERE q.qq IN (${placeholders}) ``` 代码中使用: ```kotlin val sql = SqlTemplate.fromFile("invitation/query_qualification.sql") val result = sql.bind("placeholders" to "?, ?, ?") ``` ### 消息过滤管道 所有消息驱动的模块使用统一的 `TriggerMessageFilter` 管道: ``` IgnoreSelfFilter → NewMessageFilter → KeywordFilter → [CooldownFilter] → handler ``` 管道确保:机器人自己的消息被忽略、只处理新消息、只有匹配关键词的消息才会到达业务处理器。