├── requirements.txt ├── shot.png ├── install.sh ├── README-fa.md ├── README.md └── main.py /requirements.txt: -------------------------------------------------------------------------------- 1 | DateTime==5.1 2 | aiohttp==3.9.5 3 | pytz==2022.5 4 | -------------------------------------------------------------------------------- /shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfjab/Marzban_User_Manager/HEAD/shot.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | check_and_install_python3() { 4 | if ! command -v python3 &>/dev/null; then 5 | echo -e "\e[34mInstalling Python3...\e[0m" 6 | apt update 7 | apt install -y python3 || { echo -e "\e[31mFailed to install Python3. Exiting...\e[0m"; exit 1; } 8 | else 9 | echo -e "\e[34mPython3 is already installed.\e[0m" 10 | fi 11 | } 12 | 13 | check_and_install_pip() { 14 | if ! command -v pip &>/dev/null; then 15 | echo -e "\e[34mInstalling pip...\e[0m" 16 | apt install -y python3-pip || { echo -e "\e[31mFailed to install pip. Exiting...\e[0m"; exit 1; } 17 | else 18 | echo -e "\e[34mPip is already installed.\e[0m" 19 | fi 20 | } 21 | 22 | clone_repository() { 23 | echo -e "\e[34mCloning Marzban_User_Manager repository...\e[0m" 24 | if [ -d "Marzban_User_Manager" ]; then 25 | echo -e "\e[34mRemoving existing Marzban_User_Manager directory...\e[0m" 26 | rm -rf Marzban_User_Manager || { echo -e "\e[31mFailed to remove existing directory. Exiting...\e[0m"; exit 1; } 27 | fi 28 | git clone https://github.com/erfjab/Marzban_User_Manager || { echo -e "\e[31mFailed to clone the repository. Exiting...\e[0m"; exit 1; } 29 | } 30 | 31 | install_dependencies() { 32 | cd Marzban_User_Manager || { echo -e "\e[31mFailed to change directory. Exiting...\e[0m"; exit 1; } 33 | echo -e "\e[34mInstalling Python dependencies...\e[0m" 34 | python3 -m pip install -r requirements.txt || { echo -e "\e[31mFailed to install dependencies. Exiting...\e[0m"; exit 1; } 35 | } 36 | 37 | run_main_script() { 38 | echo -e "\e[34mRunning the main script...\e[0m" 39 | python3 main.py 40 | } 41 | 42 | clear 43 | check_and_install_python3 44 | check_and_install_pip 45 | clone_repository 46 | install_dependencies 47 | run_main_script 48 | -------------------------------------------------------------------------------- /README-fa.md: -------------------------------------------------------------------------------- 1 | # معرفی اسکرپیت 2 | 3 | این اسکریپت جهت تسهیل کار شما در استفاده از پنل مرزبان و مدیریت کاربرانتون توسعه داده شده است که قابلیتهای زیر را در اختیار شما میزارد : 4 | 5 | - امکان افزایش / کاهش ترافیک کاربران ( به صورت درصدی یا عددی) 6 | - امکان افزایش / کاهش زمان کاربران ( به صورت عددی) 7 | - امکان غیرفعال / فعال سازی کاربران به صورت گروهی 8 | - امکان حذف کاربران با فیلترهای دلخواه به صورت گروهی 9 | - امکان دریافت کامل کاربران با فیلترهای دلخواه (تعداد کاربران و وضعیت کاربران و مقدار کل مصرف، لیمیت، مصرف lifetime) 10 | 11 | همهی این قابلیت های میتوانید برای برای دسته بندی های زیر اعمال کنید : 12 | 13 | - همهی کاربران 14 | - کاربران یک ادمین دلخواه 15 | - کاربرانی با پیشوند یوزرنیم دلخواه 16 | 17 | # نحوهی استفادهی اسکریپت 18 | 19 | ### اطلاعات مورد نیاز 20 | 21 | - **نام کاربری پنل :** نام کاربری ادمین sudo پنل تون رو وارد کنید. 22 | - **رمز عبور پنل:** رمز عبور ادمین sudo پنل تون رو وارد کنید. 23 | - **دامنهی پنل:** دامنه پنل تون رو طبق الگو وارد کنید. (sub.domain.com) 24 | - **پورت پنل:** پورت ورودی پنلتون رو وارد کنید. 25 | - **گواهی پنل:** اگر گواهی ssl دارید 'y' وارد کنید، در فیر اینصورت 'n' را وارد کنید. 26 | 27 | ### لینوکس (پیشنهادی) 28 | 29 | دستور زیر را وارد کنید تا اسکرپیت اتوماتیک نصب و اجرا شود. 30 | ```bash 31 | sudo bash -c "$(curl -sL https://github.com/erfjab/Marzban_User_Manager/raw/main/install.sh)" 32 | ``` 33 | ### ویندوز 34 | 35 | 1. پروژه رو دانلود و اون رو اکسترکت کنید 36 | 2. حال Python رو نصب کنید ( نسخه 3.10 یا بالاتر ) 37 | 3. یک cmd باز کنید 38 | 4. دستورات زیر رو اجرا کنید 39 | ``` 40 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 41 | python get-pip.py 42 | pip install -r requirements.txt 43 | ``` 44 | حالا با این دستور میتونید اسکریپت رو اجرا کنید. 45 | ``` 46 | python main.py 47 | ``` 48 | # مشارکت 49 | اگر اشکالی می بینید یا ایده ای برای بهتر کردن اسکریپت دارید، می توانید یک Pull Request باز کنید و تغییرات را Commit کنید. 50 | 51 |  52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | فارسی 4 | 5 |
6 | 7 | # Script Introduction 8 | 9 | This script is developed to facilitate your work with Marzban panel and manage your users. It provides the following features: 10 | 11 | - Ability to increase/decrease users' traffic (in percentage or numeric values). 12 | - Ability to increase/decrease users' time (in numeric values). 13 | - Ability to deactivate/activate users in bulk. 14 | - Ability to delete users with custom filters in bulk. 15 | - Ability to get comprehensive user information with custom filters (number of users, user statuses, total usage, limit, lifetime usage). 16 | 17 | All these features can be applied to the following categories: 18 | 19 | - All users 20 | - Users of a specific admin 21 | - Users with a specific username prefix 22 | 23 | # How to Use the Script 24 | 25 | ### Required Information 26 | 27 | - **Panel Username:** Enter the username of your sudo admin panel. 28 | - **Panel Password:** Enter the password of your sudo admin panel. 29 | - **Panel Domain:** Enter your panel domain according to the pattern (sub.domain.com). 30 | - **Panel Port:** Enter your panel's login port. 31 | - **Panel SSL:** Enter 'y' if have ssl else enter 'n'. 32 | 33 | ### Linux (Recommended) 34 | 35 | Enter the following command to automatically install and run the script. 36 | ```bash 37 | sudo bash -c "$(curl -sL https://github.com/erfjab/Marzban_User_Manager/raw/main/install.sh)" 38 | ``` 39 | 40 | ## Windows 41 | 1. Download Project And Extract It 42 | 2. Install Python +3.10 43 | 3. Open cmd 44 | 4. Run These Commands 45 | ``` 46 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 47 | python get-pip.py 48 | pip install -r requirements.txt 49 | ``` 50 | Now You Can Run Script With This Command 51 | ``` 52 | python main.py 53 | ``` 54 | # Contributors 55 | If You See A Bug Or You Have Idea To Make Script Better You Can Make Pull Request And Commit The Changes 56 | 57 |  58 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | import logging 3 | import os 4 | from datetime import datetime, timedelta 5 | import platform 6 | import pytz 7 | import asyncio 8 | import time 9 | 10 | class Marzban: 11 | 12 | def __init__(self, username: str, password: str, domain: str, port: int = 443, ssl: str = "https") -> None: 13 | self.username = username 14 | self.password = password 15 | self.domain = domain 16 | self.port = port 17 | self.ssl = ssl 18 | 19 | async def access_panel(self, username: str = None, password: str = None) -> dict : 20 | """ 21 | Obtains access token for accessing the panel API. 22 | 23 | Args: 24 | username (str, optional): The username for authentication. If not provided, uses the default username. 25 | password (str, optional): The password for authentication. If not provided, uses the default password. 26 | 27 | Returns: 28 | dict: A dictionary containing the access headers. 29 | """ 30 | try: 31 | username = self.username or username 32 | password = self.password or password 33 | url = f'{self.ssl}://{self.domain}:{self.port}/api/admin/token' 34 | data = { 35 | 'username': username, 36 | 'password': password 37 | } 38 | async with aiohttp.ClientSession() as session: 39 | async with session.post(url, data=data) as response: 40 | response.raise_for_status() 41 | access_token = (await response.json())['access_token'] 42 | access_headers = { 43 | 'accept': 'application/json', 44 | 'Content-Type': 'application/json', 45 | 'Authorization': f"Bearer {access_token}" 46 | } 47 | return access_headers 48 | except aiohttp.ClientError as e: 49 | logging.error(f'❌\tError occurred while obtaining access token: {str(e)}') 50 | return False 51 | 52 | async def get_users(self, username: str = None, password: str = None, user_status: str = None, access_panel: dict = None) -> dict: 53 | """ 54 | Retrieves the list of users from the panel. 55 | 56 | Args: 57 | username (str, optional): The username for authentication. If not provided, uses the default username. 58 | password (str, optional): The password for authentication. If not provided, uses the default password. 59 | user_status (str, optional): Filter users by status (e.g., 'active', 'disabled'). Default is None. 60 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 61 | 62 | Returns: 63 | dict: A dictionary containing the list of users. 64 | """ 65 | try: 66 | username = username or self.username 67 | password = password or self.password 68 | user_status = (('?status=' + user_status.lower()) if user_status.lower() in ['on_hold', 'disabled', 'active', 'expired', 'limited'] else '' ) if user_status else '' 69 | url = f'{self.ssl}://{self.domain}:{self.port}/api/users{user_status}' 70 | headers = access_panel or await self.access_panel(username=username, password=password) 71 | async with aiohttp.ClientSession() as session: 72 | async with session.get(url, headers=headers) as response: 73 | response.raise_for_status() 74 | users_list = await response.json() 75 | return users_list['users'] 76 | except aiohttp.ClientError as e: 77 | logging.error(f'❌\tError occurred while retrieving users list: {e}') 78 | return False 79 | 80 | async def delete_user(self, username: str, access_panel: dict = None) -> bool: 81 | """ 82 | Deletes a user from the panel. 83 | 84 | Args: 85 | username (str): The username of the user to be deleted. 86 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 87 | 88 | Returns: 89 | bool: True if the user is successfully deleted, False otherwise. 90 | """ 91 | try: 92 | url = f'{self.ssl}://{self.domain}:{self.port}/api/user/{username}' 93 | headers = access_panel or await self.access_panel() 94 | async with aiohttp.ClientSession() as session: 95 | async with session.delete(url, headers=headers) as response: 96 | response.raise_for_status() 97 | return True 98 | except aiohttp.ClientError as e: 99 | logging.error(f'❌\tError occurred while deleting user: {e}') 100 | return False 101 | 102 | async def users_statistics(self, username: str = None, password: str = None, users_list: dict = None, prefix: str = '', access_panel: dict = None) -> str: 103 | """ 104 | Generates statistics about users. 105 | 106 | Args: 107 | username (str, optional): The username for authentication. If not provided, uses the default username. 108 | password (str, optional): The password for authentication. If not provided, uses the default password. 109 | users_list (dict, optional): A dictionary containing the list of users. If not provided, retrieves the list of users. 110 | prefix (str, optional): Filter users by username prefix. Default is an empty string. 111 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 112 | 113 | Returns: 114 | str: A formatted string containing the statistics. 115 | """ 116 | try: 117 | username = username or self.username 118 | password = password or self.password 119 | headers = access_panel or await self.access_panel(username=username, password=password) 120 | users_list = users_list or await self.get_users(username=username, password=password, access_panel=headers) 121 | 122 | all_traffic_used, all_traffic_limit, all_time_traffic_used, all_users = 0, 0, 0, 0 123 | users_count = {'active': 0, 'on_hold': 0, 'expired': 0, 'limited': 0, 'disabled': 0} 124 | 125 | for user in users_list: 126 | user_name = user['username'] 127 | if user_name.startswith(prefix): 128 | all_users += 1 129 | all_traffic_used += user['used_traffic'] or 0 130 | all_traffic_limit += user['data_limit'] or 0 131 | all_time_traffic_used += user['lifetime_used_traffic'] or 0 132 | users_count[user['status']] += 1 133 | 134 | all_traffic_used = round((all_traffic_used / 1073741824), 3) 135 | all_traffic_limit = round((all_traffic_limit / 1073741824), 3) 136 | all_traffic_remaining = round((all_traffic_limit - all_traffic_used), 3) 137 | all_time_traffic_used = round((all_time_traffic_used / 1073741824), 3) 138 | 139 | return f'\nadmin username: {username}\n' \ 140 | f'all users: {all_users}\n' \ 141 | f'active users: {users_count["active"]}\n' \ 142 | f'expired users: {users_count["expired"]}\n' \ 143 | f'limited users: {users_count["limited"]}\n' \ 144 | f'on_hold users: {users_count["on_hold"]}\n' \ 145 | f'disabled users: {users_count["disabled"]}\n' \ 146 | f'all traffic used: {all_traffic_used} GB\n' \ 147 | f'all traffic limited: {all_traffic_limit} GB\n' \ 148 | f'all traffic remaining: {all_traffic_remaining} GB\n' \ 149 | f'all time traffic used: {all_time_traffic_used} GB\n' 150 | except aiohttp.ClientError as e: 151 | logging.error(f'❌\tError occurred admin traffic: {e}') 152 | return False 153 | 154 | async def calculation(self, calculation_type: str, data_limit: int, date_limit: int, traffic: int = 0, days: int = 0, coefficient_data: float = 1.0) -> tuple: 155 | """ 156 | Performs calculation on data limit and date limit. 157 | 158 | Args: 159 | calculation_type (str): Type of calculation ('+' or '-'). 160 | data_limit (int): The data limit. 161 | date_limit (int): The date limit. 162 | traffic (int, optional): The traffic to be added or subtracted. Default is 0. 163 | days (int, optional): The number of days to be added to the date limit. Default is 0. 164 | coefficient_data (float, optional): Coefficient for data limit calculation. Default is 1.0. 165 | 166 | Returns: 167 | tuple: A tuple containing the new data limit and date limit. 168 | """ 169 | try: 170 | if calculation_type == '+': 171 | data_limit = (float(data_limit) * float(coefficient_data)) + (float(traffic) * 1073741824) 172 | date_limit = (datetime.fromtimestamp(date_limit) + timedelta(days=float(days))).timestamp() 173 | elif calculation_type == '-': 174 | data_limit = (float(data_limit) * float(coefficient_data)) - (float(traffic) * 1073741824) 175 | date_limit = (datetime.fromtimestamp(date_limit) + timedelta(days=float(days))).timestamp() 176 | else: 177 | raise ValueError("❌\tInvalid calculation type. Use '+' or '-'") 178 | return data_limit, date_limit 179 | except Exception as e: 180 | logging.error(f"❌\tError occurred during calculation: {e}") 181 | return False 182 | 183 | async def modify_user(self, modify_type: str, data: bool = 0, date: bool = 0, coefficient_data: float = 1.0, prefix: str = '', username: str = None, password: str = None, access_panel: dict = None) -> str: 184 | """ 185 | Modifies users' data limit and date limit. 186 | 187 | Args: 188 | modify_type (str): Type of modification ('+' or '-'). 189 | data (bool, optional): The amount of data to be added or subtracted. Default is 0. 190 | date (bool, optional): The number of days to be added to the date limit. Default is 0. 191 | coefficient_data (float, optional): Coefficient for data limit calculation. Default is 1.0. 192 | prefix (str, optional): Filter users by username prefix. Default is an empty string. 193 | username (str, optional): The username for authentication. If not provided, uses the default username. 194 | password (str, optional): The password for authentication. If not provided, uses the default password. 195 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 196 | 197 | Returns: 198 | str: A message indicating the success or failure of the modification process. 199 | """ 200 | try: 201 | username = username or self.username 202 | password = password or self.password 203 | headers = access_panel or await self.access_panel(username=username, password=password) 204 | users_list = await self.get_users(username=username, password=password, user_status='active', access_panel=headers) 205 | if modify_type in ['+', '-']: 206 | for user in users_list: 207 | username = user['username'] 208 | if username.startswith(prefix): 209 | data_limit = user['data_limit'] 210 | date_limit = user['expire'] 211 | data_used = user['used_traffic'] 212 | if data_limit is not None and date_limit is not None and data_used is not None: 213 | new_data_limit, new_date_limit = await self.calculation(calculation_type=modify_type, data_limit=data_limit, date_limit=date_limit, traffic=data, days=date, coefficient_data=coefficient_data) 214 | url = f'{self.ssl}://{self.domain}:{self.port}/api/user/{username}' 215 | user_details = {'data_limit': new_data_limit, 'expire': new_date_limit} 216 | async with aiohttp.ClientSession() as session: 217 | async with session.put(url, json=user_details, headers=headers) as response: 218 | response.raise_for_status() 219 | logging.info(f'🟢 user {username} is updated!') 220 | else: 221 | logging.info(f'🔵 Skipping user {username}.') 222 | logging.info(f'⚪\tThe process is done!\t{len(users_list)} is updated.') 223 | else: 224 | logging.warning('🔴 modify data is not currect') 225 | except aiohttp.ClientError as e: 226 | logging.error(f'❌\tError occurred modify data: {e}') 227 | return False 228 | 229 | async def users_deleter(self, username: str = None, password: str = None, users_status: str = None, prefix: str = '', last_online: int = 0, access_panel: dict = None) -> str: 230 | """ 231 | Deletes users based on certain criteria. 232 | 233 | Args: 234 | username (str, optional): The username for authentication. If not provided, uses the default username. 235 | password (str, optional): The password for authentication. If not provided, uses the default password. 236 | users_status (str, optional): Filter users by status (e.g., 'active', 'disabled'). Default is None. 237 | prefix (str, optional): Filter users by username prefix. Default is an empty string. 238 | last_online (int, optional): The threshold for last online time (in days). Default is 0. 239 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 240 | 241 | Returns: 242 | str: A message indicating the success or failure of the deletion process. 243 | """ 244 | try: 245 | username = username or self.username 246 | password = password or self.password 247 | headers = access_panel or await self.access_panel(username=username, password=password) 248 | users_status = users_status if users_status in ['on_hold', 'disabled', 'active', 'expired', 'limited'] else None 249 | users_list = await self.get_users(username=username, password=password, user_status=users_status, access_panel=headers) 250 | count_all, count_delete = 0, 0 251 | start_time = time.time() 252 | for user in users_list: 253 | user_name = user['username'] 254 | if user_name.startswith(prefix): 255 | if user['online_at']: 256 | user_last_online = await self.convert_to_secend(user['online_at']) 257 | count_all += 1 258 | if user_last_online and (user_last_online > (last_online * 86400)): 259 | user_delete = await self.delete_user(username=user_name, access_panel=headers) 260 | if user_delete: 261 | logging.info(f'🟢 user {user_name} is deleted!') 262 | count_delete += 1 263 | else: 264 | logging.error(f'🔴 user {user_name} is not deleted!') 265 | else: 266 | logging.info(f'🔵 Skipping user {user_name}.') 267 | end_time = time.time() 268 | elapsed_time = end_time - start_time 269 | logging.info(f'⚪\tThe process is done!\tdelete {count_delete} user of {count_all} user in {elapsed_time} secend!') 270 | except aiohttp.ClientError as e: 271 | logging.error(f'❌\tError occurred users_deleter : {e}') 272 | return False 273 | 274 | async def convert_to_secend(self, time_str: str) -> int: 275 | """ 276 | Converts a datetime string to seconds. 277 | 278 | Args: 279 | time_str (str): The datetime string in ISO format. 280 | 281 | Returns: 282 | int: The equivalent time in seconds. 283 | """ 284 | try : 285 | time = datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%S.%f" if '.' in time_str else "%Y-%m-%dT%H:%M:%S") 286 | delta = datetime.now(pytz.timezone('Asia/Tehran')) - pytz.utc.localize(time).astimezone(pytz.timezone('Asia/Tehran')) 287 | return delta.days * 86400 + delta.seconds 288 | except Exception as e: 289 | logging.error(f'❌\tError occurred convert to secend : {e}') 290 | return False 291 | 292 | async def change_status(self, get_user: str = 'active', new_status: str = 'disabled', prefix: str = '', username: str = None, password: str = None, access_panel: dict = None) -> str: 293 | """ 294 | Changes the status of users. 295 | 296 | Args: 297 | get_user (str, optional): The status of users to be modified (e.g., 'active', 'disabled'). Default is 'active'. 298 | new_status (str, optional): The new status for users (e.g., 'active', 'disabled'). Default is 'disabled'. 299 | prefix (str, optional): Filter users by username prefix. Default is an empty string. 300 | username (str, optional): The username for authentication. If not provided, uses the default username. 301 | password (str, optional): The password for authentication. If not provided, uses the default password. 302 | access_panel (dict, optional): Access headers for the panel API. If not provided, obtains access headers. 303 | 304 | Returns: 305 | str: A message indicating the success or failure of the status change process. 306 | """ 307 | try: 308 | username = username or self.username 309 | password = password or self.password 310 | users_status = get_user if get_user in ['on_hold', 'disabled', 'active', 'expired', 'limited'] else 'active' 311 | headers = access_panel or await self.access_panel(username=username, password=password) 312 | users_list = await self.get_users(username=username, password=password, user_status=users_status, access_panel=headers) 313 | count = 0 314 | start_time = time.time() 315 | for user in users_list: 316 | user_name = user['username'] 317 | if user_name.startswith(prefix): 318 | count += 1 319 | url = f'{self.ssl}://{self.domain}:{self.port}/api/user/{user_name}' 320 | user_details = {'status': new_status} 321 | async with aiohttp.ClientSession() as session: 322 | async with session.put(url, json=user_details, headers=headers) as response: 323 | response.raise_for_status() 324 | logging.info(f'🟢 user {user_name} is updated!') 325 | else: 326 | logging.info(f'🔵 Skipping user {user_name}.') 327 | end_time = time.time() 328 | elapsed_time = end_time - start_time 329 | logging.info(f'⚪\tThe process is done!\t{count} user is updated in {elapsed_time} secend!') 330 | except aiohttp.ClientError as e: 331 | logging.error(f'❌\tError occurred change status: {e}') 332 | return False 333 | 334 | logging.basicConfig(level=logging.INFO, format='%(levelname)-8s->\t%(message)s') 335 | 336 | def clear() -> None : 337 | os.system('cls') if platform.system() == 'Windows' else os.system('clear') 338 | 339 | clear() 340 | 341 | 342 | 343 | 344 | async def main(): 345 | 346 | USERNAME = input('Please enter panel username: ') 347 | PASSWORD = input('Please enter panel password: ') 348 | DOMAIN = input('Please enter panel domain (without https and port): ') 349 | PORT = int(input('Please enter panel port: ')) 350 | SSL = "https" if input('do you have ssl? (y/n) ') == 'y' else "http" 351 | 352 | clear() 353 | 354 | panel = Marzban( 355 | username=USERNAME, 356 | password=PASSWORD, 357 | domain=DOMAIN, 358 | port=PORT, 359 | ssl=SSL) 360 | 361 | if await panel.access_panel() : 362 | while True : 363 | print('\n\tWelcome to Marzban_User_Manager\n') 364 | print('-' * 50) 365 | print('\n1) Users statistics') 366 | print('\n2) Increase(+) or Decrease(-) Traffic to users') 367 | print('\n3) Increase(+) or Decrease(-) Days to users') 368 | print('\n4) Delete users with filters') 369 | print('\n5) Disabled/Activated users with filters') 370 | option_input = input('\nPlease Select an option number: ') 371 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 372 | clear() 373 | 374 | if option == 1 : 375 | print('Which one do you want user statistics based on?') 376 | print('\n\n1) All users') 377 | print('\n2) Users of an admin') 378 | print('\n3) Users with prefixes') 379 | option_input = input('\nPlease Select an option number: ') 380 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 381 | clear() 382 | if option == 1 : 383 | print('please wait...') 384 | print(await panel.users_statistics()) 385 | elif option == 2 : 386 | username = input('Please enter admin username: ') 387 | password = input('Please enter admin password: ') 388 | if len(username.split(' ')) == 1 and len(password.split(' ')) == 1 : 389 | print('please wait...') 390 | print(await panel.users_statistics(username=username, password=password)) 391 | else : 392 | logging.error('❌\tnot currect, please try again.') 393 | elif option == 3 : 394 | prefix = input('Please enter a prefix: ') 395 | if len(prefix.split(' ')) == 1 : 396 | print('please wait...') 397 | print(await panel.users_statistics(prefix=prefix)) 398 | else : 399 | logging.error('❌\tnot currect, please try again.') 400 | else : 401 | logging.error('❌\tnot currect, please try again.') 402 | 403 | elif option == 2 : 404 | print('Do you want to increase or decrease??') 405 | print('\n\n1) Increase (+)') 406 | print('\n2) Decrease (-)') 407 | option_input = input('\nPlease Select an option number: ') 408 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 409 | clear() 410 | modify_type = '+' if option == 1 else '-' 411 | print('Do you want to add a coefficient or a number?') 412 | print('\n\n1) Number (like: 10 Gb, 25 Gb)') 413 | print('\n2) coefficient (like: 1.10, 2.0)') 414 | option_input = input('\nPlease Select an option number: ') 415 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 416 | clear() 417 | if option == 1: 418 | number_data = input('Please enter number (like 10, 25, 40): ') 419 | number_data = int(number_data) if len(number_data.split(' ')) == 1 else (logging.error('❌\tNot correct, please try again.') or exit()) 420 | coefficient_data = 1.0 421 | elif option == 2: 422 | coefficient_data = input('Please enter coefficient (like 1.05, 1.22, 2.10): ') 423 | coefficient_data = float(coefficient_data) if len(coefficient_data.split(' ')) == 1 else (logging.error('❌\tNot correct, please try again.') or exit()) 424 | number_data = 0 425 | 426 | clear() 427 | print('Which category of users do you want to apply to?') 428 | print('\n\n1) All users') 429 | print('\n2) Users of an admin') 430 | print('\n3) Users with prefixes') 431 | option_input = input('\nPlease Select an option number: ') 432 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 433 | 434 | clear() 435 | if option == 1 : 436 | print('please wait...') 437 | await panel.modify_user(modify_type=modify_type ,data=number_data, coefficient_data=coefficient_data) 438 | elif option == 2 : 439 | username = input('Please enter admin username: ') 440 | password = input('Please enter admin password: ') 441 | if len(username.split(' ')) == 1 and len(password.split(' ')) == 1 : 442 | await panel.modify_user(modify_type=modify_type, data=number_data, coefficient_data=coefficient_data) 443 | else : 444 | logging.error('❌\tnot currect, please try again.') 445 | elif option == 3 : 446 | prefix = input('Please enter a prefix: ') 447 | if len(prefix.split(' ')) == 1 : 448 | print('please wait...') 449 | await panel.modify_user(modify_type=modify_type, data=number_data, coefficient_data=coefficient_data, prefix=prefix) 450 | else : 451 | logging.error('❌\tnot currect, please try again.') 452 | else : 453 | logging.error('❌\tnot currect, please try again.') 454 | 455 | elif option == 3 : 456 | print('Do you want to increase or decrease??') 457 | print('\n\n1) Increase (+)') 458 | print('\n2) Decrease (-)') 459 | option_input = input('\nPlease Select an option number: ') 460 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 461 | clear() 462 | modify_type = '+' if option == 1 else '-' 463 | 464 | date_value = input('How much do you want to add (Days) (like: 10, 1, 5) ?') 465 | date_value = int(date_value) if date_value.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 466 | 467 | print('Which category of users do you want to apply to?') 468 | print('\n\n1) All users') 469 | print('\n2) Users of an admin') 470 | print('\n3) Users with prefixes') 471 | option_input = input('\nPlease Select an option number: ') 472 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 473 | 474 | clear() 475 | if option == 1 : 476 | print('please wait...') 477 | await panel.modify_user(modify_type=modify_type ,date=date_value) 478 | elif option == 2 : 479 | username = input('Please enter admin username: ') 480 | password = input('Please enter admin password: ') 481 | if len(username.split(' ')) == 1 and len(password.split(' ')) == 1 : 482 | await panel.modify_user(modify_type=modify_type, date=date_value, username=username, password=password) 483 | else : 484 | logging.error('❌\tnot currect admin info, please try again.') 485 | elif option == 3 : 486 | prefix = input('Please enter a prefix: ') 487 | if len(prefix.split(' ')) == 1 : 488 | print('please wait...') 489 | await panel.modify_user(modify_type=modify_type, date=date_value, prefix=prefix) 490 | else : 491 | logging.error('❌\tnot currect prefix, please try again.') 492 | else : 493 | logging.error('❌\tnot currect option, please try again.') 494 | 495 | elif option == 4 : 496 | last_online_time = input("What is the minimum user's last online time(Days) (like: 1,5,10) ? ") 497 | last_online_time = int(last_online_time) if last_online_time.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 498 | clear() 499 | print('Users of which status should be deleted?') 500 | print('\n\n1) All status') 501 | print('\n2) Active') 502 | print('\n3) Disabled') 503 | print('\n4) Limited') 504 | print('\n5) Expired') 505 | option_input = input('\nPlease Select an option number: ') 506 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 507 | status_texts = {1: None, 2: 'active', 3: 'disabled', 4: 'limited', 5: 'expired'} 508 | users_status = status_texts[option] 509 | clear() 510 | 511 | print('Which category of users do you want to delete?') 512 | print('\n\n1) From all users') 513 | print('\n2) From Users of an admin') 514 | print('\n3) From Users with prefixes') 515 | option_input = input('\nPlease Select an option number: ') 516 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 517 | clear() 518 | 519 | if option == 1 : 520 | print('please wait...') 521 | await panel.users_deleter(users_status=users_status, last_online=last_online_time) 522 | elif option == 2 : 523 | username = input('Please enter admin username: ') 524 | password = input('Please enter admin password: ') 525 | if len(username.split(' ')) == 1 and len(password.split(' ')) == 1 : 526 | await panel.users_deleter(users_status=users_status, last_online=last_online_time, username=username, password=password) 527 | else : 528 | logging.error('❌\tnot currect admin info, please try again.') 529 | elif option == 3 : 530 | prefix = input('Please enter a prefix: ') 531 | if len(prefix.split(' ')) == 1 : 532 | print('please wait...') 533 | await panel.users_deleter(users_status=users_status, last_online=last_online_time, prefix=prefix) 534 | else : 535 | logging.error('❌\tnot currect prefix, please try again.') 536 | else : 537 | logging.error('❌\tnot currect option, please try again.') 538 | 539 | elif option == 5: 540 | print('Do you want to increase or decrease??') 541 | print('\n\n1) activa to disabled') 542 | print('\n2) disabled to active') 543 | option_input = input('\nPlease Select an option number: ') 544 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 545 | 546 | users_list_get = 'active' if option == 1 else 'disabled' 547 | user_new_status = 'disabled' if option == 1 else 'active' 548 | clear() 549 | 550 | print('Which category of users do you want to apply to?') 551 | print('\n\n1) Users of an admin') 552 | print('\n2) Users with prefixes') 553 | option_input = input('\nPlease Select an option number: ') 554 | option = int(option_input) if option_input.isdigit() else (logging.error("❌\tInvalid input! Please enter a valid integer.") or exit()) 555 | clear() 556 | if option == 1 : 557 | username = input('Please enter admin username: ') 558 | password = input('Please enter admin password: ') 559 | if len(username.split(' ')) == 1 and len(password.split(' ')) == 1 : 560 | await panel.change_status(get_user=users_list_get, new_status=user_new_status, username=username, password=password) 561 | else : 562 | logging.error('❌\tnot currect admin info, please try again.') 563 | 564 | elif option == 2 : 565 | prefix = input('Please enter a prefix: ') 566 | if len(prefix.split(' ')) == 1 : 567 | print('please wait...') 568 | await panel.change_status(get_user=users_list_get, new_status=user_new_status, prefix=prefix) 569 | else : 570 | logging.error('❌\tnot currect prefix, please try again.') 571 | else : 572 | logging.error('❌\tnot currect option, please try again.') 573 | else : 574 | logging.error('❌\tnot currect option, please try again.') 575 | want_continue = input('\n\n\tyou want continue (y/n)? ') 576 | if want_continue != 'y' : 577 | clear() 578 | break 579 | else : 580 | print('\n\n\tYour Panel information is not currect, please try again.\n\n') 581 | asyncio.run(main()) 582 | --------------------------------------------------------------------------------