9.2 KiB
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
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
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
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:
- Caller must be an administrator or owner of the source group.
- The bot must be an administrator in both the
destination_chat_idandreview_group_id.
What happens:
- A 16-character alphanumeric
codeis generated (generate_forward_code). - A row is inserted into
forward_definitionswith:source_chat_id= current chatshare_mode='b'(blacklist mode)
- 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:
- The code is looked up in
forward_definitions. - If the forward has been revoked (
revoked_at IS NOT NULL), the user is told the link is revoked. - 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 ablacklistentry. - In whitelist mode (
'w'): allowed only if the user has anallowentry.
- If allowed, the bot enters
BotState::SubmitMode { forward_id, code }and presents Continue / Exit buttons.
Submission Flow
- 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. - Upload — The user stages files (media, documents, or text) and confirms options just like a normal upload.
- Finalize — When the user confirms,
finalize_upload:- Creates and encrypts the content entry.
- Inserts a row into
forward_submissionswithstatus = 'pending'. - Posts a review message to the
review_group_idwith inline buttons:[ Approve ]→ callbackv1:fwd:approve:{submission_id}[ Ignore ]→ callbackv1:fwd:ignore:{submission_id}[ Blackl. ]→ callbackv1:fwd:blk:{submission_id}[ Ban ]→ callbackv1:fwd:ban:{submission_id}[ Ban/BL u. ]→ callbackv1:fwd:banblk:{submission_id}
- Stores the sent message ID back into
forward_submissions.review_message_id.
- Review — Moderators in the review group click the buttons to act.
Note: Media batching is implemented for both review group presentation and approved destination posts. The bot decrypts and sends up to 10 files per media group, attaching the review text or approval caption to the last item.
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)
- Generates a random 12-character direct-access password (
generate_direct_password). - Hashes the password with Argon2 and stores it in
contents.password_hash. - Builds the direct link:
{base_url}/?cxid={content_id}&sc={password}. - Posts the link to the destination chat, prefixed with
forward_message(if set). - DM the submitter:
- "Your submission was approved."
- Includes the posted message URL and the direct access link.
- Edits the review message to show
[ APPROVED ]and the moderator ID. - Sets
forward_submissions.status = 'approved'.
Ignore (v1:fwd:ignore)
- DM the submitter: "Your submission was rejected."
- Edits the review message to show
[ IGNORED ]and the moderator ID. - Sets
forward_submissions.status = 'ignored'.
Blacklist User (v1:fwd:blk)
- Adds the submitter to
forward_listswithlist_type = 'blacklist'for this forward. - Edits the review message to show
[ BLACKLISTED ]and the moderator ID. - Sets
forward_submissions.status = 'blacklisted'. - The user is now blocked from using this forward link again (until removed).
Ban (v1:fwd:ban)
- Bans the submitter in both the destination chat and the review group.
- Records
banpunishments in thepunishmentstable for both chats. - Edits the review message to show
[ BANNED ]and the moderator ID. - Sets
forward_submissions.status = 'banned'.
Ban + Blacklist (v1:fwd:banblk)
- Bans the submitter in both the destination chat and the review group (same as
v1:fwd:ban). - Records
banpunishments in thepunishmentstable for both chats. - Also adds the submitter to
forward_listswithlist_type = 'blacklist'(same asv1:fwd:blk). - Edits the review message to show
[ BAN/BL ]and the moderator ID. - Sets
forward_submissions.status = 'banblk'.
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 (
ActiveorRevoked). - 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.