A Telegram-bot-controlled IP-limit enforcer for the PasarGuard panel.
This is a fork of the original V2IpLimit by houshmand-2005, rewritten from the ground up to talk to PasarGuard natively instead of Marzban.
🔗 Fork repository: https://github.com/sinahinson/V2IpLimit
The original project was built for the Marzban panel. Marzban and PasarGuard look similar, but PasarGuard's API has diverged enough that the original code cannot work against it without changes:
- PasarGuard identifies users by a numeric
idon every mutating endpoint (/api/user/by-id/{user_id}/disabled), not byusernamelike Marzban did. - The xray core log
emailfield used by the original project to recognize which user a connection belongs to no longer reliably contains the username on PasarGuard. - PasarGuard exposes a native, purpose-built endpoint for this exact use
case —
GET /api/node/online_stats/{user_id}/ip— which returns, per node, the IP addresses currently seen for a user. This fork uses that endpoint directly instead of streaming and regex-parsing raw xray logs over a websocket.
That last point is actually a meaningful architectural improvement, not just a compatibility patch: no persistent per-node websocket connections to keep alive, no log-format regressions to chase, no username-recovery regex. The program just polls a small, stable JSON API on a fixed interval.
- Every
CHECK_INTERVALseconds, the bot fetches the list of active users from PasarGuard (GET /api/users). - For each user, it asks PasarGuard which IPs are currently online for
that user, across every node (
GET /api/node/online_stats/{id}/ip). - Each IP is filtered:
- private/loopback/reserved IPs are dropped,
- IPs you've explicitly blacklisted (
INVALID_IPS) are dropped, - if you've set
IP_LOCATION, only IPs resolving to that country are kept.
- If a user's remaining IP count exceeds their limit (their entry in
SPECIAL_LIMIT, orGENERAL_LIMITotherwise), the bot disables them viaPUT /api/user/by-id/{id}/disabled. - After
TIME_TO_ACTIVE_USERSseconds, disabled users are automatically re-enabled, giving them a fresh chance to reconnect within their limit. - Everything is controllable and observable from a Telegram bot — no shell access to the server needed after initial setup.
- Python 3.11+
- A running PasarGuard panel you have admin credentials for
- A Telegram bot token (from @BotFather)
screen,git, andjqif you use the providedv2iplimit.shmanager script
wget https://raw.githubusercontent.com/sinahinson/V2IpLimit/main/v2iplimit.sh
chmod +x v2iplimit.sh
./v2iplimit.shThe interactive menu lets you:
- Start the program
- Stop the program
- Attach to its
screensession (view live logs) - Update to the latest version
- Set/change the Telegram
BOT_TOKEN - Set/change the admin chat ID(s)
The script clones this repository, creates an isolated Python virtual
environment, installs dependencies, and runs the bot inside a screen
session so it keeps running after you disconnect.
git clone https://github.com/sinahinson/V2IpLimit.git
cd V2IpLimit
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp config.example.json config.json # then edit BOT_TOKEN and ADMINS at least
python3 v2iplimit.pyEverything besides BOT_TOKEN and ADMINS can be set (and changed later)
from inside Telegram — you never need to hand-edit config.json after the
first run. config.example.json documents every field:
| Key | Description |
|---|---|
BOT_TOKEN |
Your Telegram bot token from @BotFather. |
ADMINS |
List of Telegram chat IDs allowed to control the bot. |
PANEL_DOMAIN |
PasarGuard panel address, e.g. sub.domain.com:8000 (no scheme). |
PANEL_USERNAME / PANEL_PASSWORD |
Admin credentials for the panel. |
GENERAL_LIMIT |
Default max concurrent IPs per user. |
SPECIAL_LIMIT |
Per-user overrides of GENERAL_LIMIT. |
EXCEPT_USERS |
Usernames that are never limited. |
IP_LOCATION |
If set (e.g. IR), only count IPs from that country. None disables this. |
INVALID_IPS |
Extra IPs to always ignore (on top of the built-in defaults). |
CHECK_INTERVAL |
Seconds between usage checks. |
TIME_TO_ACTIVE_USERS |
Seconds a user stays disabled before being automatically re-enabled. |
| Command | What it does |
|---|---|
/start |
Shows the command list |
/create_config |
Set/update the PasarGuard panel address, username, and password |
/set_special_limit |
Set a per-user IP limit |
/show_special_limit |
List all per-user limit overrides |
/set_general_limit_number |
Set the default IP limit |
/set_except_user / /remove_except_user / /show_except_users |
Manage the exclusion list |
/country_code |
Restrict counted IPs to a specific country |
/set_check_interval |
Set how often (seconds) the bot checks usage |
/set_time_to_active_users |
Set how long (seconds) a disabled user stays disabled |
/add_admin / /remove_admin / /admins_list |
Manage who can control the bot |
/backup |
Sends you the current config.json |
If you're comparing this fork against upstream, the key files that were rewritten are:
utils/types.py—UserTypenow carries anid(PasarGuard's identifier) alongside the username.utils/panel_api.py— every panel call rewritten against PasarGuard's OpenAPI schema (/api/admin/token,/api/users,/api/node/online_stats/{id}/ip,/api/user/by-id/{id}/disabled).utils/check_usage.py— replaced the log-drivenACTIVE_USERSaccumulator with a polling loop against the online-stats endpoint.utils/ip_utils.py— new module; the IP-validity/geolocation logic that used to live inside the log parser, now reusable and panel-agnostic.utils/handel_dis_users.py— the disabled-users store now keeps ausername -> idmapping (PasarGuard needs the id to re-enable a user).utils/get_logs.pyandutils/parse_logs.pyfrom upstream are removed entirely — there is no websocket log stream to maintain anymore.
This project talks directly to your panel's admin API and can disable real user accounts. Test any changes against a disposable/staging PasarGuard instance before pointing it at production, and keep regular backups of your panel database. Neither this fork's maintainer nor the original author are responsible for misuse or misconfiguration.
- TRON network (TRX):
TPX8bBrZnAXD9sbHJYu1kUwTbKkdZfpXgH
Thank you for your support! If this program was useful for you, please give it a star ⭐
- Original project & idea: houshmand-2005/V2IpLimit
- PasarGuard panel: PasarGuard/panel
- This fork: sinahinson/V2IpLimit
Licensed under the MIT License — see LICENSE.