# LTD-ManagerBot Module Documentation ## Table of Contents - [Quick Start](#quick-start) - [Infrastructure Modules](#infrastructure-modules) - [New Modules](#new-modules) - [RCON Command Module](#rcon-command-module) - [Gitea Webhook Module](#gitea-webhook-module) - [Whitelist Audit Module](#whitelist-audit-module) - [Configuration Reference](#configuration-reference) --- ## Quick Start 1. Copy the example configs from `src/main/resources/` to `config/` 2. Edit `config/application.yaml` — set database, API URLs, tokens 3. Edit `config/module.yaml` — uncomment and configure desired modules 4. Place SQL templates in `config/sql/` if using InvitationCodes or WhitelistAudit 5. Launch the bot ### Minimal `application.yaml` additions for new modules ```yaml # Whitelist System API (required by WhitelistAuditModule) whitelist-system: url: "https://whitelist.your-server.top" encrypted-token: "your-api-token-here" ``` --- ## Infrastructure Modules ### GroupMessagePollingModule Polls a QQ group's message history at a fixed interval and emits a `SharedFlow>`. Other modules subscribe to this flow to react to messages. ```yaml - name: "talkGroup" type: "GROUP_MESSAGE_POLLING_MODULE" enabled: true config: target-group-id: 538751386 poll-interval-millis: 5000 # optional, default 5000 msg-history-check: 15 # optional, default 15 ``` ### MailModule Queues and sends emails via SMTP. Used by InvitationCodesModule and WhitelistAuditModule. ```yaml - name: "talkGroup" type: "MAIL_MODULE" enabled: true ``` SMTP settings go in `application.yaml` → `mail:` section. --- ## New Modules ### RCON Command Module **Type:** `RCON_COMMAND_MODULE` Allows specified QQ users to execute arbitrary Minecraft RCON commands via QQ group messages, with a configurable blocklist to prevent dangerous operations. #### Workflow ``` QQ Group: "rcon list" │ ├── User NOT in admin-ids → reply: "no permission" ├── Command matches blocklist → reply: "blocked" └── Execute via RCON binary → reply with output ``` #### Example Config ```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" admin-ids: [2561098830] rcon-timeout-sec: 5 command-blocklist: - "stop" - "restart" - "op" - "deop" - "whitelist" - "ban" - "ban-ip" - "kick" - "debug" - "execute" ``` #### Config Reference | Field | Type | Required | Default | Description | |---|---|---|---|---| | `self-id` | long | yes | — | Bot's QQ ID | | `self-nick-name` | string | yes | — | Bot's display name | | `command-prefix` | string | yes | — | Trigger prefix in QQ group | | `admin-ids` | long[] | yes | — | QQ IDs allowed to execute RCON | | `command-blocklist` | string[] | yes | — | Blocked command prefixes (case-insensitive, `/`-prefix stripped) | | `rcon-timeout-sec` | long | no | `5` | RCON timeout in seconds | The RCON binary path and config path are read from `application.yaml` → `tools.rcon`. #### Blocklist Matching The blocklist uses **prefix matching** after stripping leading `/`: - `"stop"` blocks: `stop`, `stop 10s`, `/stop`, `/minecraft:stop` - `"ban"` blocks: `ban Steve`, `ban-ip`, `/ban`, etc. --- ### Gitea Webhook Module **Type:** `GITEA_WEBHOOK_MODULE` Starts an embedded HTTP server to receive [Gitea webhook](https://docs.gitea.com/usage/webhooks) events and forwards formatted notifications to a QQ group. #### Supported Events | Event | QQ Message Format | |---|---| | `push` | Author, branch, commit count, first 5 commit messages, compare URL | | `issues` | Action (opened/closed/reopened), title, body preview, link | | `pull_request` | Action, branches (head → base), state, link | | `create` | Branch/tag created, author | | `delete` | Branch/tag deleted, author | | `release` | Tag name, release name, body preview, draft/prerelease flags | | `repository` | Action (created/deleted/transferred) | | `fork` | Source repo → forked repo | #### Example Config ```yaml - name: "talkGroup" type: "GITEA_WEBHOOK_MODULE" enabled: true config: webhook-port: 8080 webhook-path: "/gitea-webhook" webhook-secret: "your-webhook-secret" target-group-id: 538751386 events: - "push" - "issues" - "pull_request" - "create" - "delete" - "release" ``` #### Config Reference | Field | Type | Required | Default | Description | |---|---|---|---|---| | `webhook-port` | int | yes | — | HTTP server listen port | | `webhook-path` | string | no | `/gitea-webhook` | Webhook endpoint path | | `webhook-secret` | string | no | `""` | HMAC-SHA256 secret for signature verification | | `target-group-id` | long | yes | — | QQ group to send notifications to | | `events` | string[] | no | all | Event types to listen for | #### Gitea-side Setup In your Gitea repository → **Settings** → **Webhooks** → **Add Webhook** → **Gitea**: - **Target URL**: `http://:8080/gitea-webhook` - **Secret**: Same as `webhook-secret` - **Trigger On**: Select desired events #### Security - Signature verification: `X-Gitea-Signature` header validated via HMAC-SHA256 - Empty `webhook-secret` disables verification (not recommended for production) --- ### Whitelist Audit Module **Type:** `WHITELIST_AUDIT_MODULE` Periodically checks the whitelist group membership against the whitelist API. Automatically rejects users who left the group, with a configurable grace period for re-activation via keywords in the main group. Supports admin-triggered manual audits with detailed per-user reporting. **Storage:** MySQL `ltd_manager_bot.whitelist_audit` table (auto-created on first load). #### Auto vs Manual Audit | | Auto Audit | Manual Audit | |---|---|---| | Trigger | Every N minutes (poll-interval) | Admin sends keyword in whitelist group | | Group messages | Silent (none) | Full summary with per-user detail | | Email | Always sent for every action | Always sent for every action | | Log prefix | `[自动]` | `[手动审计]` | #### Lifecycle States ``` In Whitelist Group ───────────────────────────────► OK (no action) │ └── User left group ──► Detected ──► Rejected (grace period starts) │ ├── Keyword in main group ──► Re-Activated ├── Warning threshold ──► Email sent (once) └── Grace period expired ──► Permanently Removed ``` #### Example Config ```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" # only if 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 templates (omit = use built-in defaults) email-reject-template: "" email-warning-template: "" email-removed-template: "" email-reactivated-template: "" ``` #### Config Reference | Field | Type | Required | Default | Description | |---|---|---|---|---| | `self-id` | long | yes | — | Bot's QQ ID | | `filter-qq-list` | long[] | yes | — | QQ IDs never auto-rejected | | `audit-allowed-ids` | long[] | yes | — | QQ IDs allowed to trigger manual audit | | `whitelist-group-polling-dep-name` | string | yes | — | Dependency name for the whitelist group polling module | | `audit-command-prefixes` | string[] | no | `["审计"]` | Keywords to trigger manual audit (multiple) | | `re-activation-keywords` | string[] | yes | — | Keywords to re-activate from main group | | `grace-period-days` | int | yes | — | Days user has to re-activate after being rejected | | `expiry-warning-days` | int | yes | — | Days before expiry to send warning | | `poll-interval-minutes` | long | no | `60` | Auto-audit interval in minutes | | `enable-email` | boolean | no | `false` | Enable email notifications (requires MailModule) | | `email-reject-template` | string | no | `""` | Rejection email body. Placeholders: `${playerName}` `${graceDays}` `${keywords}` | | `email-warning-template` | string | no | `""` | Expiry warning email body. Placeholders: `${playerName}` `${remainDays}` `${keywords}` | | `email-removed-template` | string | no | `""` | Removal email body. Placeholders: `${playerName}` | | `email-reactivated-template` | string | no | `""` | Re-activation email body. Placeholders: `${playerName}` `${graceDays}` | #### Manual Audit Output Example ``` 审计完成 ──────────────── 白名单总数: 42 在群正常: 35 人 新发现不在群(已拒绝): 2 人 • Steve(123456789) • Alex(987654321) 宽限期中: Notch(111222333, 剩5天) 已过期删除: 1 人 • Hero(444555666) 过滤列表跳过: 3 人 ``` #### Email Deduplication Emails are sent exactly once per event: | Email | Guard | |---|---| | Rejection | Only on first detection (`existing == null`) | | Re-rejection | Only when previously-reactivated user leaves again | | Expiry warning | `warningSentTime == 0L` — sent once | | Removal | Record deleted from DB after send — never repeats | | Re-activation | Only on successful keyword-triggered approve | #### State Persistence Audit state is stored in **MySQL** `ltd_manager_bot.whitelist_audit` table (auto-created, zero config). Survives bot restarts. Historical JSON file-based state is no longer used. SQL templates are in `config/sql/whitelist/`: - `create_audit_table.sql` — DDL - `query_all_audit.sql` — fetch all records - `query_audit_by_qq.sql` — fetch by QQ - `upsert_audit.sql` — insert or update - `delete_audit.sql` — remove record #### Dependencies | Dependency | Type | Required | Purpose | |---|---|---|---| | Main group polling | `GROUP_MESSAGE_POLLING_MODULE` | yes | Re-activation keyword monitoring | | Whitelist group polling | `GROUP_MESSAGE_POLLING_MODULE` | yes | Audit command monitoring + member list | | Mail module | `MAIL_MODULE` | if `enable-email: true` | Email notifications | #### Whitetelist API Config API URL and token are in `application.yaml` `whitelist-system:` section for security (auto-encrypted): ```yaml whitelist-system: url: "https://whitelist.your-server.top" encrypted-token: "your-api-token-here" ``` --- ## Configuration Reference ### SQL Templates Some modules (InvitationCodesModule, GroupRequestHandlerModule) use SQL templates stored in `config/sql/`. Templates use `${placeholder}` syntax: ```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}) ``` Usage in code: ```kotlin val sql = SqlTemplate.fromFile("invitation/query_qualification.sql") val result = sql.bind("placeholders" to "?, ?, ?") ``` ### Message Filter Pipeline All message-driven modules use a common `TriggerMessageFilter` pipeline: ``` IgnoreSelfFilter → NewMessageFilter → KeywordFilter → [CooldownFilter] → handler ``` This ensures messages from the bot itself are ignored, only new messages are processed, and only keyword-matching messages reach the handler.