Telegram bot that controls access to private chats/channels by referral ID.
- Start message from template with optional image and "Get access" button.
- ID validation via regex from configuration.
- Notifies administrator about new requests with inline buttons: approve for various periods, decline, ban.
- Supports multiple channels/chats. On approval bot sends invites and removes users after expiry.
- Background tasks warn about access expiration and reset idle sessions.
- Two database backends: SQLite (default) and PostgreSQL via
.env. - Admin commands:
/ban,/unban,/kick,/remove,/export_users,/user. - Language selection with
/languageand localized templates/images. - Personalized greetings using user's name and localized default username.
- All texts are rendered from Jinja2 templates (
templates/).
-
Install dependencies
pip install -r requirements.txt
-
Environment variables (
.env)BOT_TOKEN– bot token from@BotFather.ROOT_ADMIN_ID– Telegram ID of admin to notify.DB_BACKEND–sqliteorpostgres.SQLITE_DB_PATH– path to database file (SQLite).PG_HOST– PostgreSQL host (default127.0.0.1).PG_PORT– PostgreSQL port (default5432).PG_DB– database name.PG_USER– username.PG_PASSWORD– password.PG_SSLMODE– SSL mode (disable,require, etc.).ACCESS_CHATS– chat/channel IDs to purge on expiry.JOIN_INVITE_LABEL_PREFIX– optional prefix for generated invite links.LOG_LEVEL– logging verbosity (defaultINFO).DB_LOG_QUERIES– set totrueto log SQL queries.
Example
.envfor SQLite:BOT_TOKEN=123456:ABCDEF ROOT_ADMIN_ID=123456789 ACCESS_CHATS=-1001111111,-1002222222 DB_BACKEND=sqlite SQLITE_DB_PATH=./db.sqlite3
Example
.envfor PostgreSQL:BOT_TOKEN=123456:ABCDEF ROOT_ADMIN_ID=123456789 ACCESS_CHATS=-1001111111,-1002222222 DB_BACKEND=postgres PG_HOST=localhost PG_PORT=5432 PG_DB=tg_gate PG_USER=gate_user PG_PASSWORD=secret PG_SSLMODE=disable
Before first run create PostgreSQL user and database:
CREATE USER gate_user WITH PASSWORD 'secret'; CREATE DATABASE tg_gate OWNER gate_user;
-
Configuration (
config/) Parameters are stored in three YAML files:ui_config.yaml— interface and templatesstart– start message template, button text,enabled_image, image map by language.messages– maps events to template names:ask_id,waiting,banned,not_found,granted,denied,warning,expired,admin_request,renewal_warning,grace_warning,renewal_requested_admin,links_unavailable,session_timeout.admin_interface– admin button labels.approve_templateexpects{period}.language_prompt– template and image for/languageprompt.start_language_prompt– template and image for initial language choice on/start.ask_id_prompt– ID request with optional image.invalid_id_prompt– notification about invalid ID with optional image.post_join– message sent after joining chat with optional image andenabledflag.
Example
ui_config.yaml:start: template: start_user.txt action_button_text: en: "Get access" ru: "Получить доступ" enabled_image: true image: en: { path: assets/en/start.jpg } ru: { path: assets/ru/start.jpg } messages: ask_id: ask_id.txt waiting: id_waiting.txt banned: id_banned.txt not_found: id_not_found.txt granted: access_granted.txt denied: access_denied.txt session_timeout: session_timeout.txt admin_interface: approve_template: en: "Approve for {period}" ru: "Разрешить на {period}" decline_text: en: "Decline" ru: "Отклонить" ban_text: en: "Ban" ru: "Забанить" unban_text: en: "Unban" ru: "Разбанить" plans: - 259200 # 3 days - 604800 # 7 days - 2592000 # 30 days - 15552000 # 180 days enable_decline: true enable_ban: true expiration: check_interval: 60 warn_before_sec: 86400 session_timeout: seconds: 900 send_message: true renewal: warn_before_sec: 86400 grace_after_expiry_sec: 86400 user_plans: - id: trial_3d label: "Подписаться на пробный период" duration_sec: 259200 - id: month_30 label: "Купить подписку на 30 дней" duration_sec: 2592000 - id: lifetime label: "Купить бессрочную подписку" duration_sec: 0
i18n.yaml— language settingsi18n.enabled_start_prompt– iftrue, bot starts in default language and offers/languagecommand.i18n.default_langandsupported_langs– default and supported languages.i18n_buttons– button labels anddefault_usernamefallback.
Example
i18n.yaml:i18n: enabled_start_prompt: true default_lang: en supported_langs: [en, ru] i18n_buttons: en: default_username: "Friend" choose_language_title: "Choose your language" language_choices: en: "English" ru: "Русский" ru: default_username: "Друг" choose_language_title: "Выберите язык" language_choices: en: "English" ru: "Русский"
-
Run
python telegram_bot.py
Helper scripts are also available:
setup.sh/setup.bat– create a virtual environment and install dependencies (run once);start.sh/start.bat– activate the environment and launch the bot.
- Store images per language:
assets/<lang>/start.jpg,assets/<lang>/post_join.jpg, optionalassets/<lang>/language_prompt.jpgandassets/<lang>/start_language_prompt.jpg. - Set paths or
file_idinconfig/ui_config.yamlforstart,language_prompt,start_language_prompt, andpost_join. - Toggle images with
enabled_image(startandpost_joinenabled by default,language_promptandstart_language_promptdisabled). - Bot caches
file_idinmedia_cacheand reuses it. Changing the file changes hash (sha256) and refreshes cache. - Images are optional; if missing only text is sent.
- If caption exceeds 1024 characters, photo is sent without caption and text follows as separate message.
- User sends
/start. - Bot replies with greeting and "Get access" button.
- After pressing, user enters their ID.
- If ID confirmed in DB – bot sends invite links.
- If ID not found/confirmed – admin receives message with inline buttons.
- On approval user gets invites; on decline/ban – respective notifications.
- Background task warns about expiry and removes user from chats when time is up.
User
/start— launch bot and get access button./help— help./language— choose interface language.
Admin
/ban <KEY>— ban user and remove from channels./unban <KEY>— remove ban./kick <KEY>— remove from channels and reset confirmation but keep in DB./remove <KEY>— remove from channels and delete record./export_users [all|confirmed|unconfirmed|banned]— export users to CSV./user <KEY>— show user info.
<KEY> may be membership_id, numeric telegram_id, or @username.
pytestA smoke test has been run for lifetime subscription functionality and unsubscribe/ban operations. Limited-time subscriptions have not been validated.
modules/– bot code (router, handlers, DB, schedulers).templates/– message templates.schema/– SQL schemas for SQLite and PostgreSQL.config/– YAML configs for interface and access rules.
This project is released under the MIT License.
