Files
cg_api_secure-webshare/docs/FORWARD_SYSTEM.md

224 lines
8.1 KiB
Markdown

# Forward Submission System
This document describes the submission-forward flow that allows users to upload content through the bot for moderator review before it is posted to a destination channel or group.
---
## Database Schema
Defined in `migrations/003_forward_system.sql`.
### `forward_definitions`
```sql
CREATE TABLE forward_definitions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
creator_user_id INTEGER NOT NULL,
source_chat_id INTEGER NOT NULL,
destination_chat_id INTEGER NOT NULL,
review_group_id INTEGER NOT NULL,
forward_message TEXT NOT NULL DEFAULT '',
code TEXT NOT NULL UNIQUE,
share_mode TEXT NOT NULL DEFAULT 'b',
revoked_at TEXT,
created_at TEXT NOT NULL DEFAULT datetime('now')
);
```
| Field | Description |
|-------|-------------|
| `id` | Primary key. |
| `creator_user_id` | Telegram ID of the admin who created the forward. |
| `source_chat_id` | The group/chat where `/create_submit_forward` was invoked. |
| `destination_chat_id` | The target channel/group where approved content is posted. |
| `review_group_id` | The moderator group where submissions are sent for review. |
| `forward_message` | Optional template text prepended to approved posts. |
| `code` | Unique 16-character alphanumeric access code. |
| `share_mode` | `'b'` = blacklist mode (default), `'w'` = whitelist mode. |
| `revoked_at` | Timestamp if the forward link was revoked; `NULL` while active. |
**Indexes:** `idx_forward_code`, `idx_forward_source`.
### `forward_submissions`
```sql
CREATE TABLE forward_submissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
forward_id INTEGER NOT NULL REFERENCES forward_definitions(id),
user_id INTEGER NOT NULL,
content_id TEXT NOT NULL REFERENCES contents(id),
status TEXT NOT NULL DEFAULT 'pending',
review_message_id INTEGER,
created_at TEXT NOT NULL DEFAULT datetime('now'),
resolved_at TEXT,
resolver_id INTEGER
);
```
| Field | Description |
|-------|-------------|
| `id` | Primary key (submission number). |
| `forward_id` | The forward this submission belongs to. |
| `user_id` | Telegram ID of the submitting user. |
| `content_id` | The uploaded content entry (`contents.id`). |
| `status` | `pending`, `approved`, `ignored`, or `blacklisted`. |
| `review_message_id` | Telegram message ID of the review post in the review group. |
| `resolved_at` | Timestamp when a moderator acted on the submission. |
| `resolver_id` | Telegram ID of the moderator who resolved it. |
**Indexes:** `idx_fwd_sub_forward`, `idx_fwd_sub_user`, `idx_fwd_sub_status`.
### `forward_lists`
```sql
CREATE TABLE forward_lists (
forward_id INTEGER NOT NULL REFERENCES forward_definitions(id),
user_id INTEGER NOT NULL,
list_type TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT datetime('now'),
PRIMARY KEY (forward_id, user_id, list_type)
);
```
| Field | Description |
|-------|-------------|
| `forward_id` | The forward definition. |
| `user_id` | The affected user. |
| `list_type` | `blacklist` or `allow`. |
This table implements **per-forward** scoped access control.
---
## Creating a Forward (`/create_submit_forward`)
**Usage (group-only, admin-gated):**
```
/create_submit_forward <destination_chat_id> <review_group_id> [forward_message]
```
**Requirements:**
1. Caller must be an **administrator or owner** of the source group.
2. The bot must be an **administrator** in both the `destination_chat_id` and `review_group_id`.
**What happens:**
1. A 16-character alphanumeric `code` is generated (`generate_forward_code`).
2. A row is inserted into `forward_definitions` with:
- `source_chat_id` = current chat
- `share_mode` = `'b'` (blacklist mode)
3. The bot replies with a deep-link URL:
```
https://t.me/<bot_username>?start=submitfwdid<code>
```
---
## Entering Submission Mode
Users click the deep link or send:
```
/start submitfwdid<CODE>
```
**Validation:**
1. The code is looked up in `forward_definitions`.
2. If the forward has been revoked (`revoked_at IS NOT NULL`), the user is told the link is revoked.
3. The scoped access check `ForwardRepo::is_allowed(forward_id, user_id)` is performed:
- The creator is always allowed.
- In **blacklist mode** (`'b'`): allowed unless the user has a `blacklist` entry.
- In **whitelist mode** (`'w'`): allowed only if the user has an `allow` entry.
4. If allowed, the bot enters `BotState::SubmitMode { forward_id, code }` and presents **Continue / Exit** buttons.
---
## Submission Flow
1. **Continue** — The user is transitioned to `BotState::MainMenu { pending_forward_id: Some(forward_id) }`. All uploads staged from this point are tagged with the pending forward ID.
2. **Upload** — The user stages files (media, documents, or text) and confirms options just like a normal upload.
3. **Finalize** — When the user confirms, `finalize_upload`:
- Creates and encrypts the content entry.
- Inserts a row into `forward_submissions` with `status = 'pending'`.
- Posts a review message to the `review_group_id` with inline buttons:
- `[ Approve ]` → callback `v1:fwd:approve:{submission_id}`
- `[ Ignore ]` → callback `v1:fwd:ignore:{submission_id}`
- `[ Blacklist User ]` → callback `v1:fwd:blk:{submission_id}`
- Stores the sent message ID back into `forward_submissions.review_message_id`.
4. **Review** — Moderators in the review group click the buttons to act.
---
## Review Actions
All review callbacks require the clicking user to be an **administrator in the review group** (`is_admin_in_chat`).
### Approve (`v1:fwd:approve`)
1. Generates a random 12-character direct-access password (`generate_direct_password`).
2. Hashes the password with Argon2 and stores it in `contents.password_hash`.
3. Builds the direct link: `{base_url}/?cxid={content_id}&sc={password}`.
4. Posts the link to the destination chat, prefixed with `forward_message` (if set).
5. DM the submitter:
- "Your submission was approved."
- Includes the posted message URL and the direct access link.
6. Edits the review message to show `[ APPROVED ]` and the moderator ID.
7. Sets `forward_submissions.status = 'approved'`.
### Ignore (`v1:fwd:ignore`)
1. DM the submitter: "Your submission was rejected."
2. Edits the review message to show `[ IGNORED ]` and the moderator ID.
3. Sets `forward_submissions.status = 'ignored'`.
### Blacklist User (`v1:fwd:blk`)
1. Adds the submitter to `forward_lists` with `list_type = 'blacklist'` for this forward.
2. Edits the review message to show `[ BLACKLISTED ]` and the moderator ID.
3. Sets `forward_submissions.status = 'blacklisted'`.
4. The user is now blocked from using this forward link again (until removed).
---
## Management Commands
All group-only, admin-gated.
### `/show_c_forward [page]`
Lists forward links created in the current source chat (5 per page).
- Shows code, destination chat ID, review group ID, and status (`Active` or `Revoked`).
- Active forwards include an inline `[ Revoke ]` button.
- Pagination via `<<` / `>>` buttons.
### `/add_blacklist <user_id>`
Iterates all **active** forwards for the current source chat and inserts the user into each forward's `forward_lists` as `blacklist`.
Replies with the count of forwards affected.
### `/rm_blacklist <user_id>`
Iterates all **active** forwards for the current source chat and removes the user from each forward's `forward_lists` where `list_type = 'blacklist'`.
Replies with the count of forwards affected.
---
## Scoped Access Model
Each forward has its own independent access list stored in `forward_lists`.
| `share_mode` | Behavior |
|--------------|----------|
| `'b'` (blacklist) | **Default.** Everyone is allowed unless explicitly blacklisted. |
| `'w'` (whitelist) | Only explicitly allowed users (and the creator) may submit. |
**Note:** The `share_mode` is stored per forward but there is currently no admin command to change it after creation; it defaults to `'b'` at creation time.
---
## Revoking a Forward
Admins or the creator can revoke a forward via the `[ Revoke ]` button on `/show_c_forward`.
- Validates the forward belongs to the current chat.
- Requires creator status **or** admin status.
- Sets `revoked_at = datetime('now')`.
- Revoked forwards reject new submissions immediately.