Skip to content

Repository files navigation

V2IpLimit — PasarGuard Edition

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


Why a separate fork instead of a config option?

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 id on every mutating endpoint (/api/user/by-id/{user_id}/disabled), not by username like Marzban did.
  • The xray core log email field 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.

How it works

  1. Every CHECK_INTERVAL seconds, the bot fetches the list of active users from PasarGuard (GET /api/users).
  2. For each user, it asks PasarGuard which IPs are currently online for that user, across every node (GET /api/node/online_stats/{id}/ip).
  3. 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.
  4. If a user's remaining IP count exceeds their limit (their entry in SPECIAL_LIMIT, or GENERAL_LIMIT otherwise), the bot disables them via PUT /api/user/by-id/{id}/disabled.
  5. After TIME_TO_ACTIVE_USERS seconds, disabled users are automatically re-enabled, giving them a fresh chance to reconnect within their limit.
  6. Everything is controllable and observable from a Telegram bot — no shell access to the server needed after initial setup.

Requirements

  • Python 3.11+
  • A running PasarGuard panel you have admin credentials for
  • A Telegram bot token (from @BotFather)
  • screen, git, and jq if you use the provided v2iplimit.sh manager script

Installation

Option A — using the manager script (recommended)

wget https://raw.githubusercontent.com/sinahinson/V2IpLimit/main/v2iplimit.sh
chmod +x v2iplimit.sh
./v2iplimit.sh

The interactive menu lets you:

  1. Start the program
  2. Stop the program
  3. Attach to its screen session (view live logs)
  4. Update to the latest version
  5. Set/change the Telegram BOT_TOKEN
  6. 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.

Option B — manual install

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.py

Configuration

Everything 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.

Telegram commands

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

What changed vs. the upstream (Marzban) version — for contributors

If you're comparing this fork against upstream, the key files that were rewritten are:

  • utils/types.pyUserType now carries an id (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-driven ACTIVE_USERS accumulator 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 a username -> id mapping (PasarGuard needs the id to re-enable a user).
  • utils/get_logs.py and utils/parse_logs.py from upstream are removed entirely — there is no websocket log stream to maintain anymore.

Disclaimer

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.

Donations

  • TRON network (TRX): TPX8bBrZnAXD9sbHJYu1kUwTbKkdZfpXgH

BuyMeACoffee

Thank you for your support! If this program was useful for you, please give it a star ⭐

Credits

Licensed under the MIT License — see LICENSE.

About

Limiting the number of active users with IP for PasarGuard

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages