4.0 KiB
Telegram VPN bot (Marzban + Telegram Stars)
Async Telegram bot for selling and extending Marzban VPN subscriptions.
Features
- Plans:
- 1 month = ⭐️100
- 3 months = ⭐️270
- 6 months = ⭐️500
- Marzban username format:
tg<telegram_user_id>(example:tg12345678). - New users are created with:
data_limit = 50 GBdata_limit_reset_strategy = monthproxies = {"vless": {"flow": "xtls-rprx-vision"}}inbounds = {"vless": ["VLESS TCP REALITY"]}notecontaining Telegram first name, last name, and phone (if available)
- If user has active subscription, a new purchase extends from current expiration.
- Bot sends subscription URL after successful purchase.
- Admin notifications for every creation/extension.
- Per-user price multiplier support (
price_multiplier) that affects invoice stars (final amount is always integer). /statscommand for admins:- payments count
- stars earned
- sold months
- marzban users
- traffic usage/total quota
Admin user management
/select_user <username|telegram_id>— select user for further updates and show current values./selected_user— show the currently selected user and current values./set_expire <YYYY-MM-DD or ISO datetime>— set selected user expiration (UTC)./set_traffic <GB>— set selected user monthly traffic limit in GB./set_multiplier <float>— set selected user price multiplier.
Setup
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
Fill .env and run:
set -a
source .env
set +a
python bot.py
Container image
The image contains only the application and Python dependencies. Runtime secrets and the SQLite database are deliberately excluded.
Build and verify it locally:
docker build --pull -t tt-simple:dev .
docker run --rm --entrypoint sh tt-simple:dev -c \
'test ! -e /app/.env && test ! -e /app/stats.db && test -e /app/bot.py'
Run it with environment variables and persistent SQLite storage:
docker volume create tt-simple-data
docker run --rm \
--env-file .env \
--env DB_PATH=/data/stats.db \
--volume tt-simple-data:/data \
tt-simple:dev
Publishing to GHCR
.github/workflows/container.yml publishes a multi-architecture image to:
ghcr.io/kr0sh512/tt-simple
A push to main publishes latest and sha-<commit> tags. A Git tag such as
v0.1.0 publishes the matching version tag:
git tag v0.1.0
git push origin main v0.1.0
K3s deployment
Create the namespace first:
kubectl apply -f k8s/namespace.yaml
Create or update the application Secret from the local .env file. The Secret
is never stored in Git:
kubectl -n tt-simple create secret generic tt-simple-env \
--from-env-file=.env \
--dry-run=client -o yaml | kubectl apply -f -
For a private GHCR package, create a classic GitHub token with read:packages
and create the registry pull secret:
read -rsp "GHCR token: " GHCR_TOKEN
echo
kubectl -n tt-simple create secret docker-registry ghcr-creds \
--docker-server=ghcr.io \
--docker-username=kr0sh512 \
--docker-password="$GHCR_TOKEN" \
--dry-run=client -o yaml | kubectl apply -f -
unset GHCR_TOKEN
If the package is public, remove imagePullSecrets from
k8s/tt-simple.yaml. Deploy the PVC and bot after publishing the v0.1.0
image:
kubectl apply -f k8s/tt-simple.yaml
kubectl -n tt-simple rollout status deployment/tt-simple
kubectl -n tt-simple logs -f deployment/tt-simple
The manifest injects the Secret, mounts persistent storage at /data, and sets
DB_PATH=/data/stats.db. To use the shared non-Russian proxy, add this to the
local .env before updating tt-simple-env:
HTTP_PROXY=http://shared-http-proxy.proxy.svc.cluster.local:3128
The included namespace and pod labels satisfy the proxy NetworkPolicy.
Notes
- Telegram Stars invoices use
currency="XTR"and emptyprovider_token. - Local SQLite database stores only payment statistics. User state is read from Marzban API.