# 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<List<MsgHistorySpecificMsg>>`. 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://<server-ip>: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.

#### 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/Group notification sent
                                └── Grace period expired ──► Permanently Removed
```

#### Two-Trigger System

| Trigger | Group | Who | Action |
|---|---|---|---|
| Keyword (e.g. "重新激活") | Main group | Any pending user | Re-activates whitelist |
| Audit command (e.g. "审计") | Whitelist group | `audit-allowed-ids` only | Runs immediate audit + posts results |

#### 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-prefix: "审计"
    re-activation-keywords:
      - "重新激活"
      - "激活白名单"
      - "reactivate"
    grace-period-days: 7
    expiry-warning-days: 2
    poll-interval-minutes: 60
    enable-email: false
```

#### 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-prefix` | string | no | `"审计"` | Command to trigger manual audit in whitelist group |
| `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) |

#### Manual Audit Output Example

```
审计完成
────────────────
白名单总数: 42
在群正常: 35 人
新发现不在群(已拒绝): 2 人
宽限期中: Steve(剩5天), Alex(剩2天)
已过期删除: 1 人
过滤列表跳过: 3 人
```

#### State Persistence

Audit state is persisted to `data/whitelist_audit_state.json` with automatic backup. Survives bot restarts without losing pending reject records.

#### 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.
