147 lines
4.0 KiB
Markdown
147 lines
4.0 KiB
Markdown
# 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 GB`
|
|
- `data_limit_reset_strategy = month`
|
|
- `proxies = {"vless": {"flow": "xtls-rprx-vision"}}`
|
|
- `inbounds = {"vless": ["VLESS TCP REALITY"]}`
|
|
- `note` containing 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).
|
|
- `/stats` command 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
|
|
```bash
|
|
python -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
cp .env.example .env
|
|
```
|
|
|
|
Fill `.env` and run:
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```bash
|
|
git tag v0.1.0
|
|
git push origin main v0.1.0
|
|
```
|
|
|
|
### K3s deployment
|
|
|
|
Create the namespace first:
|
|
|
|
```bash
|
|
kubectl apply -f k8s/namespace.yaml
|
|
```
|
|
|
|
Create or update the application Secret from the local `.env` file. The Secret
|
|
is never stored in Git:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```dotenv
|
|
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 empty `provider_token`.
|
|
- Local SQLite database stores only payment statistics. User state is read from Marzban API.
|