Created
November 10, 2024 14:02
-
-
Save rezazoom/a987b6dc720d6be134f2bb23864b5a48 to your computer and use it in GitHub Desktop.
Remove expired inbounds in 3x-ui panel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| File: remove-expired.py | |
| Author: Reza Esmaeili | |
| Date: Nov 09, 2024 | |
| Description: | |
| This script helps administrators manage inbounds on a 3x-ui panel by identifying and | |
| removing expired inbounds or those that exceed specified data usage limits. Additionally, | |
| it can notify administrators of actions taken through Telegram Bot API notifications. | |
| Features: | |
| 1. Logs into the 3x-ui panel API and retrieves a list of inbounds. | |
| 2. Checks each inbound for expiration based on the `expiryTime` field or data limit. | |
| 3. Deletes expired or overused inbounds if user confirmation is provided. | |
| 4. Sends a detailed report of actions to a specified Telegram channel. | |
| Dependencies: | |
| - requests: For HTTP communication with the 3x-ui API and Telegram Bot API. | |
| - json: For parsing JSON responses from the API. | |
| - datetime: For handling timestamps and calculating expiration. | |
| - getpass: For securely capturing the user's password. | |
| - time: For managing delays during deletion requests. | |
| Requirements: | |
| - Python 3.x | |
| - Internet connection for API communication | |
| - Access credentials for the 3x-ui panel | |
| Usage: | |
| 1. Run the script and provide the 3x-ui panel URL, username, and password. | |
| 2. Optionally, enable Telegram notifications by providing a Telegram bot token and channel ID. | |
| 3. The script will log in, retrieve inbound information, and identify expired or overused inbounds. | |
| 4. After confirmation, expired or overused inbounds will be deleted, and a report will be sent to Telegram if enabled. | |
| Note: | |
| - Telegram messages are split into chunks to avoid exceeding Telegram's character limit. | |
| - The script requires confirmation before deletion of inbounds. | |
| - Ensure the Telegram bot has permission to message the provided channel or user. | |
| Example Run: | |
| $ python remove-expired.py | |
| """ | |
| import requests | |
| import json | |
| from datetime import datetime | |
| import getpass | |
| import time | |
| def log_message(message): | |
| print(f"[{datetime.now()}] {message}") | |
| # Function to split and send the report if it exceeds the Telegram character limit | |
| def send_report_to_telegram(report_message, telegram_token, telegram_chat_id): | |
| telegram_url = f"https://api.telegram.org/bot{telegram_token}/sendMessage" | |
| max_length = 4096 # Telegram's message character limit | |
| # Split the report into chunks of complete lines | |
| report_lines = report_message.splitlines(keepends=True) | |
| current_chunk = "" | |
| for line in report_lines: | |
| if len(current_chunk) + len(line) > max_length: | |
| payload = { | |
| "chat_id": telegram_chat_id, | |
| "text": current_chunk, | |
| "parse_mode": "markdown" | |
| } | |
| response = requests.post(telegram_url, data=payload) | |
| if response.status_code != 200: | |
| log_message(f"Failed to send part of the report to Telegram channel.") | |
| log_message(f"Status Code: {response.status_code}, Response: {response.text}") | |
| current_chunk = line # Start a new chunk | |
| else: | |
| current_chunk += line | |
| # Send any remaining text | |
| if current_chunk: | |
| payload = { | |
| "chat_id": telegram_chat_id, | |
| "text": current_chunk + "\n--- End of the report ---", | |
| "parse_mode": "markdown" | |
| } | |
| response = requests.post(telegram_url, data=payload) | |
| if response.status_code != 200: | |
| log_message(f"Failed to send the last part of the report to Telegram channel.") | |
| log_message(f"Status Code: {response.status_code}, Response: {response.text}") | |
| print("INBOUND CLEANER: This script will help you to delete expired inbounds in 3x-ui panel!\n\n") | |
| panel_url = input("1. Enter the panel's URL (eg. https://mypanel.theserver.com:8998): ") | |
| username = input("2. Enter your panel's username: ") | |
| password = getpass.getpass("3. Enter your panel's password: ") | |
| # Define API URLs | |
| login_url = f"{panel_url}/login" | |
| inbounds_url = f"{panel_url}/panel/api/inbounds/list" | |
| delete_inbound_url = f"{panel_url}/panel/api/inbounds/del/" | |
| # Define login credentials | |
| login_payload = { | |
| "username": username, | |
| "password": password | |
| } | |
| use_telegram = input("4. Do you want Telegram notification (via Telegram's bot API)? (Y/n): ").lower() == 'y' | |
| if use_telegram: | |
| # Telegram Bot API information | |
| telegram_token = input("Enter your bot token (from @botfather): ") | |
| telegram_chat_id = input("Enter your channel's ID or username (preceded by @): ") | |
| # Create a session object to persist the session cookie | |
| session = requests.Session() | |
| # Step 1: Login to the API and capture the session cookie | |
| log_message("Logging in to your panel's API...") | |
| response = session.post(login_url, data=login_payload) | |
| if response.status_code == 200: | |
| log_message("Login successful!") | |
| else: | |
| log_message("Login failed!") | |
| log_message(f"Status Code: {response.status_code}, Response: {response.text}") | |
| exit() | |
| # Step 2: Get the list of inbounds using the session | |
| log_message("\nRetrieving list of inbounds...") | |
| response = session.get(inbounds_url) | |
| if response.status_code == 200: | |
| try: | |
| inbounds = response.json().get("obj") | |
| if inbounds is None: | |
| log_message("No 'obj' key found in the response.") | |
| exit() | |
| except json.JSONDecodeError as e: | |
| log_message("Failed to parse JSON response.") | |
| log_message(f"Error: {e}") | |
| log_message(f"Response text: {response.text}") | |
| exit() | |
| else: | |
| log_message("Failed to retrieve the list of inbounds!") | |
| log_message(f"Status Code: {response.status_code}, Response: {response.text}") | |
| exit() | |
| if use_telegram: | |
| # Prepare report content | |
| report = [] | |
| report.append(f"*INBOUND CLEANER*\nInbound Deletion Report for {panel_url}\n") | |
| report.append(f"Script executed on: {datetime.now()}\n\n") | |
| # Step 3: Check for expired inbounds and those that exceed total data usage | |
| log_message("\nChecking for expired inbounds or those that exceed total data usage...") | |
| current_time = int(datetime.now().timestamp() * 1000) | |
| expired_inbounds = [] | |
| for inbound in inbounds: | |
| expiry_time = inbound.get("expiryTime") | |
| inbound_id = inbound.get("id") | |
| remark = inbound.get("remark") | |
| up = inbound.get("up", 0) | |
| down = inbound.get("down", 0) | |
| total = inbound.get("total", 0) | |
| # Check if inbound has expired with a valid expiry time | |
| if expiry_time < current_time and expiry_time > 0: | |
| expired_inbounds.append({"id": inbound_id, "remark": remark, "reason": "expired"}) | |
| log_message(f"Inbound ID {inbound_id} (Remark: {remark}) has expired.") | |
| # Check if data usage exceeds total | |
| elif total > 0 and (up + down) > total: | |
| expired_inbounds.append({"id": inbound_id, "remark": remark, "reason": "exceeded total data"}) | |
| log_message(f"Inbound ID {inbound_id} (Remark: {remark}) exceeded total data usage.") | |
| else: | |
| # print(f"Inbound ID {inbound_id} (Remark: {remark}) is still valid.") | |
| pass | |
| # Ask for confirmation before deletion | |
| if input("Do you want to delete the expired or data-exceeded inbounds? (Y/n): ").lower() == "y": | |
| # Step 4: Delete inbounds based on conditions | |
| if expired_inbounds: | |
| log_message("\nDeleting inbounds...") | |
| for inbound in expired_inbounds: | |
| inbound_id = inbound["id"] | |
| remark = inbound["remark"] | |
| reason = inbound["reason"] | |
| del_url = f"{delete_inbound_url}{inbound_id}" | |
| response = session.post(del_url) | |
| time.sleep(0.2) # NOTE: Do we need this? | |
| if response.status_code == 200: | |
| log_message(f"Inbound ID {inbound_id} (Remark: `{remark}`) deleted successfully! Reason: {reason}") | |
| if use_telegram: report.append(f"Inbound ID {inbound_id} (Remark: `{remark}`) deleted successfully! Reason: {reason}\n") | |
| else: | |
| log_message(f"Failed to delete Inbound ID {inbound_id} (Remark: `{remark}`)! Reason: {reason}") | |
| if use_telegram: report.append(f"Failed to delete Inbound ID {inbound_id} (Remark: `{remark}`)! Reason: {reason}\n") | |
| log_message(f"Status Code: {response.status_code}, Response: {response.text}") | |
| else: | |
| log_message("No inbounds found for deletion.") | |
| if use_telegram: report.append("No inbounds found for deletion.\n") | |
| else: | |
| if use_telegram: report.append("No deletion performed.\n") | |
| if use_telegram: | |
| # Send the report to the Telegram channel | |
| log_message("\nSending report to Telegram channel...") | |
| report_message = "".join(report) | |
| send_report_to_telegram(report_message, telegram_token, telegram_chat_id) | |
| log_message("\nScript completed.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment