224 lines
8.1 KiB
Markdown
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.
|