27
changelog
27
changelog
@ -1,21 +1,10 @@
|
|||||||
## [1.9.0] - 2025-05-02
|
## [1.9.1] - 2025-05-03
|
||||||
|
|
||||||
### ✨ Changed
|
### ✨ Changed
|
||||||
🧠 **refactor:** Migrate core Hysteria2 functionality from Bash to Python
|
🧠 **refactor:**: Implement server stats manager in Python
|
||||||
- 🔁 Uninstallation
|
🧠 **refactor:**: Migrate IP address config management to Python
|
||||||
- ⬆️ Server update
|
🧠 **refactor:**: Implement Hysteria backup functionality in Python
|
||||||
- ➕ User addition
|
🧠 **refactor:**: Transition Telegram bot service management to Python
|
||||||
- 🔁 User reset
|
🧠 **refactor:**: Migrate WARP ACL configuration handling to Python
|
||||||
- 🔍 User info retrieval
|
🧠 **refactor:**: Implement WARP status management in Python
|
||||||
- ❌ User removal
|
🧠 **refactor:**: Rewrite WARP setup and uninstallation scripts in Python3
|
||||||
- 🔄 Port update
|
|
||||||
- 🔁 Server restart
|
|
||||||
|
|
||||||
### 🐛 Fixed
|
|
||||||
🛠️ **fix:** Resolved file permission issues by explicitly calling Python in subprocess
|
|
||||||
🐛 **fix:** Removed improper usage of `asyncio.to_thread` in `remove_user.py`
|
|
||||||
🌚 **fix (UI):** Fixed JSONEditor background in dark mode for better readability
|
|
||||||
|
|
||||||
### 🎨 UI / UX
|
|
||||||
🎨 **feat:** Improved light/dark mode design with enhanced CSS
|
|
||||||
🧩 **update:** Integrated custom CSS directly into `base.html`
|
|
||||||
@ -365,16 +365,13 @@ def uninstall_warp():
|
|||||||
@click.option('--popular-sites', '-p', is_flag=True, help='Use WARP for popular sites like Google, OpenAI, etc')
|
@click.option('--popular-sites', '-p', is_flag=True, help='Use WARP for popular sites like Google, OpenAI, etc')
|
||||||
@click.option('--domestic-sites', '-d', is_flag=True, help='Use WARP for Iran domestic sites')
|
@click.option('--domestic-sites', '-d', is_flag=True, help='Use WARP for Iran domestic sites')
|
||||||
@click.option('--block-adult-sites', '-x', is_flag=True, help='Block adult content (porn)')
|
@click.option('--block-adult-sites', '-x', is_flag=True, help='Block adult content (porn)')
|
||||||
@click.option('--warp-option', '-w', type=click.Choice(['warp', 'warp plus'], case_sensitive=False), help='Specify whether to use WARP or WARP Plus')
|
def configure_warp(all: bool, popular_sites: bool, domestic_sites: bool, block_adult_sites: bool):
|
||||||
@click.option('--warp-key', '-k', help="WARP Plus key (required if warp-option is 'warp plus')")
|
|
||||||
def configure_warp(all: bool, popular_sites: bool, domestic_sites: bool, block_adult_sites: bool, warp_option: str, warp_key: str):
|
|
||||||
try:
|
try:
|
||||||
cli_api.configure_warp(all, popular_sites, domestic_sites, block_adult_sites, warp_option, warp_key)
|
cli_api.configure_warp(all, popular_sites, domestic_sites, block_adult_sites)
|
||||||
click.echo('WARP configured successfully.')
|
click.echo('WARP configured successfully.')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
click.echo(f'{e}', err=True)
|
click.echo(f'{e}', err=True)
|
||||||
|
|
||||||
|
|
||||||
@cli.command('warp-status')
|
@cli.command('warp-status')
|
||||||
def warp_status():
|
def warp_status():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -29,24 +29,24 @@ class Command(Enum):
|
|||||||
REMOVE_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'remove_user.py')
|
REMOVE_USER = os.path.join(SCRIPT_DIR, 'hysteria2', 'remove_user.py')
|
||||||
SHOW_USER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'show_user_uri.py')
|
SHOW_USER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'show_user_uri.py')
|
||||||
WRAPPER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'wrapper_uri.py')
|
WRAPPER_URI = os.path.join(SCRIPT_DIR, 'hysteria2', 'wrapper_uri.py')
|
||||||
IP_ADD = os.path.join(SCRIPT_DIR, 'hysteria2', 'ip.sh')
|
IP_ADD = os.path.join(SCRIPT_DIR, 'hysteria2', 'ip.py')
|
||||||
MANAGE_OBFS = os.path.join(SCRIPT_DIR, 'hysteria2', 'manage_obfs.py')
|
MANAGE_OBFS = os.path.join(SCRIPT_DIR, 'hysteria2', 'manage_obfs.py')
|
||||||
MASQUERADE_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'masquerade.sh')
|
MASQUERADE_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'masquerade.sh')
|
||||||
TRAFFIC_STATUS = 'traffic.py' # won't be called directly (it's a python module)
|
TRAFFIC_STATUS = 'traffic.py' # won't be called directly (it's a python module)
|
||||||
UPDATE_GEO = os.path.join(SCRIPT_DIR, 'hysteria2', 'update_geo.py')
|
UPDATE_GEO = os.path.join(SCRIPT_DIR, 'hysteria2', 'update_geo.py')
|
||||||
LIST_USERS = os.path.join(SCRIPT_DIR, 'hysteria2', 'list_users.sh')
|
LIST_USERS = os.path.join(SCRIPT_DIR, 'hysteria2', 'list_users.sh')
|
||||||
SERVER_INFO = os.path.join(SCRIPT_DIR, 'hysteria2', 'server_info.sh')
|
SERVER_INFO = os.path.join(SCRIPT_DIR, 'hysteria2', 'server_info.py')
|
||||||
BACKUP_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'backup.sh')
|
BACKUP_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'backup.py')
|
||||||
RESTORE_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'restore.sh')
|
RESTORE_HYSTERIA2 = os.path.join(SCRIPT_DIR, 'hysteria2', 'restore.sh')
|
||||||
INSTALL_TELEGRAMBOT = os.path.join(SCRIPT_DIR, 'telegrambot', 'runbot.sh')
|
INSTALL_TELEGRAMBOT = os.path.join(SCRIPT_DIR, 'telegrambot', 'runbot.py')
|
||||||
SHELL_SINGBOX = os.path.join(SCRIPT_DIR, 'singbox', 'singbox_shell.sh')
|
SHELL_SINGBOX = os.path.join(SCRIPT_DIR, 'singbox', 'singbox_shell.sh')
|
||||||
SHELL_WEBPANEL = os.path.join(SCRIPT_DIR, 'webpanel', 'webpanel_shell.sh')
|
SHELL_WEBPANEL = os.path.join(SCRIPT_DIR, 'webpanel', 'webpanel_shell.sh')
|
||||||
INSTALL_NORMALSUB = os.path.join(SCRIPT_DIR, 'normalsub', 'normalsub.sh')
|
INSTALL_NORMALSUB = os.path.join(SCRIPT_DIR, 'normalsub', 'normalsub.sh')
|
||||||
INSTALL_TCP_BRUTAL = os.path.join(SCRIPT_DIR, 'tcp-brutal', 'install.sh')
|
INSTALL_TCP_BRUTAL = os.path.join(SCRIPT_DIR, 'tcp-brutal', 'install.sh')
|
||||||
INSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'install.sh')
|
INSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'install.py')
|
||||||
UNINSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'uninstall.sh')
|
UNINSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'uninstall.py')
|
||||||
CONFIGURE_WARP = os.path.join(SCRIPT_DIR, 'warp', 'configure.sh')
|
CONFIGURE_WARP = os.path.join(SCRIPT_DIR, 'warp', 'configure.py')
|
||||||
STATUS_WARP = os.path.join(SCRIPT_DIR, 'warp', 'status.sh')
|
STATUS_WARP = os.path.join(SCRIPT_DIR, 'warp', 'status.py')
|
||||||
SERVICES_STATUS = os.path.join(SCRIPT_DIR, 'services_status.sh')
|
SERVICES_STATUS = os.path.join(SCRIPT_DIR, 'services_status.sh')
|
||||||
VERSION = os.path.join(SCRIPT_DIR, 'hysteria2', 'version.py')
|
VERSION = os.path.join(SCRIPT_DIR, 'hysteria2', 'version.py')
|
||||||
LIMIT_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'limit.sh')
|
LIMIT_SCRIPT = os.path.join(SCRIPT_DIR, 'hysteria2', 'limit.sh')
|
||||||
@ -182,7 +182,7 @@ def change_hysteria2_sni(sni: str):
|
|||||||
def backup_hysteria2():
|
def backup_hysteria2():
|
||||||
'''Backups Hysteria configuration. Raises an exception on failure.'''
|
'''Backups Hysteria configuration. Raises an exception on failure.'''
|
||||||
try:
|
try:
|
||||||
run_cmd(['bash', Command.BACKUP_HYSTERIA2.value])
|
run_cmd(['python3', Command.BACKUP_HYSTERIA2.value])
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise Exception(f"Backup failed: {e}")
|
raise Exception(f"Backup failed: {e}")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -368,10 +368,12 @@ def traffic_status(no_gui=False, display_output=True):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Next Update:
|
||||||
# TODO: it's better to return json
|
# TODO: it's better to return json
|
||||||
|
# TODO: After json todo need fix Telegram Bot and WebPanel
|
||||||
def server_info() -> str | None:
|
def server_info() -> str | None:
|
||||||
'''Retrieves server information.'''
|
'''Retrieves server information.'''
|
||||||
return run_cmd(['bash', Command.SERVER_INFO.value])
|
return run_cmd(['python3', Command.SERVER_INFO.value])
|
||||||
|
|
||||||
|
|
||||||
def get_ip_address() -> tuple[str | None, str | None]:
|
def get_ip_address() -> tuple[str | None, str | None]:
|
||||||
@ -387,7 +389,7 @@ def add_ip_address():
|
|||||||
'''
|
'''
|
||||||
Adds IP addresses from the environment to the .configs.env file.
|
Adds IP addresses from the environment to the .configs.env file.
|
||||||
'''
|
'''
|
||||||
run_cmd(['bash', Command.IP_ADD.value, 'add'])
|
run_cmd(['python3', Command.IP_ADD.value, 'add'])
|
||||||
|
|
||||||
|
|
||||||
def edit_ip_address(ipv4: str, ipv6: str):
|
def edit_ip_address(ipv4: str, ipv6: str):
|
||||||
@ -402,9 +404,9 @@ def edit_ip_address(ipv4: str, ipv6: str):
|
|||||||
if not ipv4 and not ipv6:
|
if not ipv4 and not ipv6:
|
||||||
raise InvalidInputError('Error: --edit requires at least one of --ipv4 or --ipv6.')
|
raise InvalidInputError('Error: --edit requires at least one of --ipv4 or --ipv6.')
|
||||||
if ipv4:
|
if ipv4:
|
||||||
run_cmd(['bash', Command.IP_ADD.value, 'edit', '-4', ipv4])
|
run_cmd(['python3', Command.IP_ADD.value, 'edit', '-4', ipv4])
|
||||||
if ipv6:
|
if ipv6:
|
||||||
run_cmd(['bash', Command.IP_ADD.value, 'edit', '-6', ipv6])
|
run_cmd(['python3', Command.IP_ADD.value, 'edit', '-6', ipv6])
|
||||||
|
|
||||||
|
|
||||||
def update_geo(country: str):
|
def update_geo(country: str):
|
||||||
@ -434,56 +436,48 @@ def install_tcp_brutal():
|
|||||||
|
|
||||||
def install_warp():
|
def install_warp():
|
||||||
'''Installs WARP.'''
|
'''Installs WARP.'''
|
||||||
run_cmd(['bash', Command.INSTALL_WARP.value])
|
run_cmd(['python3', Command.INSTALL_WARP.value])
|
||||||
|
|
||||||
|
|
||||||
def uninstall_warp():
|
def uninstall_warp():
|
||||||
'''Uninstalls WARP.'''
|
'''Uninstalls WARP.'''
|
||||||
run_cmd(['bash', Command.UNINSTALL_WARP.value])
|
run_cmd(['python3', Command.UNINSTALL_WARP.value])
|
||||||
|
|
||||||
|
|
||||||
def configure_warp(all: bool, popular_sites: bool, domestic_sites: bool, block_adult_sites: bool, warp_option: str, warp_key: str):
|
def configure_warp(all: bool, popular_sites: bool, domestic_sites: bool, block_adult_sites: bool):
|
||||||
'''
|
'''
|
||||||
Configures WARP with various options.
|
Configures WARP with various options.
|
||||||
'''
|
'''
|
||||||
if warp_option == 'warp plus' and not warp_key:
|
|
||||||
raise InvalidInputError('Error: WARP Plus key is required when \'warp plus\' is selected.')
|
|
||||||
options = {
|
|
||||||
'all': 'true' if all else 'false',
|
|
||||||
'popular_sites': 'true' if popular_sites else 'false',
|
|
||||||
'domestic_sites': 'true' if domestic_sites else 'false',
|
|
||||||
'block_adult_sites': 'true' if block_adult_sites else 'false',
|
|
||||||
'warp_option': warp_option or '',
|
|
||||||
'warp_key': warp_key or ''
|
|
||||||
}
|
|
||||||
cmd_args = [
|
cmd_args = [
|
||||||
'bash', Command.CONFIGURE_WARP.value,
|
'python3', Command.CONFIGURE_WARP.value
|
||||||
options['all'],
|
|
||||||
options['popular_sites'],
|
|
||||||
options['domestic_sites'],
|
|
||||||
options['block_adult_sites'],
|
|
||||||
options['warp_option']
|
|
||||||
]
|
]
|
||||||
if options['warp_key']:
|
if all:
|
||||||
cmd_args.append(options['warp_key'])
|
cmd_args.append('--all')
|
||||||
|
if popular_sites:
|
||||||
|
cmd_args.append('--popular-sites')
|
||||||
|
if domestic_sites:
|
||||||
|
cmd_args.append('--domestic-sites')
|
||||||
|
if block_adult_sites:
|
||||||
|
cmd_args.append('--block-adult')
|
||||||
|
|
||||||
run_cmd(cmd_args)
|
run_cmd(cmd_args)
|
||||||
|
|
||||||
|
|
||||||
def warp_status() -> str | None:
|
def warp_status() -> str | None:
|
||||||
'''Checks the status of WARP.'''
|
'''Checks the status of WARP.'''
|
||||||
return run_cmd(['bash', Command.STATUS_WARP.value])
|
return run_cmd(['python3', Command.STATUS_WARP.value])
|
||||||
|
|
||||||
|
|
||||||
def start_telegram_bot(token: str, adminid: str):
|
def start_telegram_bot(token: str, adminid: str):
|
||||||
'''Starts the Telegram bot.'''
|
'''Starts the Telegram bot.'''
|
||||||
if not token or not adminid:
|
if not token or not adminid:
|
||||||
raise InvalidInputError('Error: Both --token and --adminid are required for the start action.')
|
raise InvalidInputError('Error: Both --token and --adminid are required for the start action.')
|
||||||
run_cmd(['bash', Command.INSTALL_TELEGRAMBOT.value, 'start', token, adminid])
|
run_cmd(['python3', Command.INSTALL_TELEGRAMBOT.value, 'start', token, adminid])
|
||||||
|
|
||||||
|
|
||||||
def stop_telegram_bot():
|
def stop_telegram_bot():
|
||||||
'''Stops the Telegram bot.'''
|
'''Stops the Telegram bot.'''
|
||||||
run_cmd(['bash', Command.INSTALL_TELEGRAMBOT.value, 'stop'])
|
run_cmd(['python3', Command.INSTALL_TELEGRAMBOT.value, 'stop'])
|
||||||
|
|
||||||
|
|
||||||
def start_singbox(domain: str, port: int):
|
def start_singbox(domain: str, port: int):
|
||||||
|
|||||||
27
core/scripts/hysteria2/backup.py
Normal file
27
core/scripts/hysteria2/backup.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
backup_dir = Path("/opt/hysbackup")
|
||||||
|
backup_file = backup_dir / f"hysteria_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
|
||||||
|
|
||||||
|
files_to_backup = [
|
||||||
|
Path("/etc/hysteria/ca.key"),
|
||||||
|
Path("/etc/hysteria/ca.crt"),
|
||||||
|
Path("/etc/hysteria/users.json"),
|
||||||
|
Path("/etc/hysteria/config.json"),
|
||||||
|
Path("/etc/hysteria/.configs.env"),
|
||||||
|
]
|
||||||
|
|
||||||
|
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(backup_file, 'w') as zipf:
|
||||||
|
for file_path in files_to_backup:
|
||||||
|
if file_path.exists():
|
||||||
|
zipf.write(file_path, arcname=file_path.name)
|
||||||
|
print("Backup successfully created")
|
||||||
|
except Exception as e:
|
||||||
|
print("Backup failed!", str(e))
|
||||||
@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
BACKUP_DIR="/opt/hysbackup"
|
|
||||||
BACKUP_FILE="$BACKUP_DIR/hysteria_backup_$(date +%Y%m%d_%H%M%S).zip"
|
|
||||||
|
|
||||||
if [ ! -d "$BACKUP_DIR" ]; then
|
|
||||||
mkdir -p "$BACKUP_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
FILES_TO_BACKUP=(
|
|
||||||
"/etc/hysteria/ca.key"
|
|
||||||
"/etc/hysteria/ca.crt"
|
|
||||||
"/etc/hysteria/users.json"
|
|
||||||
"/etc/hysteria/config.json"
|
|
||||||
"/etc/hysteria/.configs.env"
|
|
||||||
)
|
|
||||||
|
|
||||||
zip -j "$BACKUP_FILE" "${FILES_TO_BACKUP[@]}" >/dev/null
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Backup successfully created"
|
|
||||||
else
|
|
||||||
echo "Backup failed!"
|
|
||||||
fi
|
|
||||||
102
core/scripts/hysteria2/ip.py
Normal file
102
core/scripts/hysteria2/ip.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import CONFIG_ENV
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_env_file_exists():
|
||||||
|
if not CONFIG_ENV.exists():
|
||||||
|
print("CONFIG_ENV not found. Creating a new one...")
|
||||||
|
CONFIG_ENV.touch()
|
||||||
|
|
||||||
|
|
||||||
|
def update_config(key: str, value: str):
|
||||||
|
content = []
|
||||||
|
if CONFIG_ENV.exists():
|
||||||
|
with CONFIG_ENV.open("r") as f:
|
||||||
|
content = [line for line in f if not line.startswith(f"{key}=")]
|
||||||
|
content.append(f"{key}={value}\n")
|
||||||
|
with CONFIG_ENV.open("w") as f:
|
||||||
|
f.writelines(content)
|
||||||
|
|
||||||
|
|
||||||
|
def get_interface_addresses():
|
||||||
|
ipv4_address = ""
|
||||||
|
ipv6_address = ""
|
||||||
|
|
||||||
|
interfaces = subprocess.check_output(["ip", "-o", "link", "show"]).decode()
|
||||||
|
interfaces = [
|
||||||
|
line.split(": ")[1]
|
||||||
|
for line in interfaces.strip().splitlines()
|
||||||
|
if not re.match(r"^(lo|wgcf|warp)$", line.split(": ")[1])
|
||||||
|
]
|
||||||
|
|
||||||
|
for iface in interfaces:
|
||||||
|
try:
|
||||||
|
ipv4 = subprocess.check_output(["ip", "-o", "-4", "addr", "show", iface]).decode()
|
||||||
|
for line in ipv4.strip().splitlines():
|
||||||
|
addr = line.split()[3].split("/")[0]
|
||||||
|
if not re.match(r"^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1]))", addr):
|
||||||
|
ipv4_address = addr
|
||||||
|
break
|
||||||
|
ipv6 = subprocess.check_output(["ip", "-o", "-6", "addr", "show", iface]).decode()
|
||||||
|
for line in ipv6.strip().splitlines():
|
||||||
|
addr = line.split()[3].split("/")[0]
|
||||||
|
if not re.match(r"^(::1|fe80:)", addr):
|
||||||
|
ipv6_address = addr
|
||||||
|
break
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return ipv4_address, ipv6_address
|
||||||
|
|
||||||
|
|
||||||
|
def add_ips():
|
||||||
|
ensure_env_file_exists()
|
||||||
|
ipv4, ipv6 = get_interface_addresses()
|
||||||
|
|
||||||
|
update_config("IP4", ipv4 or "")
|
||||||
|
update_config("IP6", ipv6 or "")
|
||||||
|
|
||||||
|
print(f"Updated IP4={ipv4 or 'Not Found'}")
|
||||||
|
print(f"Updated IP6={ipv6 or 'Not Found'}")
|
||||||
|
|
||||||
|
|
||||||
|
def edit_ip(option: str, new_ip: str):
|
||||||
|
ensure_env_file_exists()
|
||||||
|
if option == "-4":
|
||||||
|
update_config("IP4", new_ip)
|
||||||
|
print(f"IP4 has been updated to {new_ip}.")
|
||||||
|
elif option == "-6":
|
||||||
|
update_config("IP6", new_ip)
|
||||||
|
print(f"IP6 has been updated to {new_ip}.")
|
||||||
|
else:
|
||||||
|
print("Invalid option. Use -4 for IPv4 or -6 for IPv6.")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: {add|edit -4|-6 <new_ip>}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
action = sys.argv[1]
|
||||||
|
|
||||||
|
if action == "add":
|
||||||
|
add_ips()
|
||||||
|
elif action == "edit" and len(sys.argv) == 4:
|
||||||
|
edit_ip(sys.argv[2], sys.argv[3])
|
||||||
|
else:
|
||||||
|
print("Usage: {add|edit -4|-6 <new_ip>}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,71 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
if [ ! -f "$CONFIG_ENV" ]; then
|
|
||||||
echo "CONFIG_ENV not found. Creating a new one..."
|
|
||||||
touch "$CONFIG_ENV"
|
|
||||||
fi
|
|
||||||
|
|
||||||
update_config() {
|
|
||||||
local key=$1
|
|
||||||
local value=$2
|
|
||||||
|
|
||||||
sed -i "/^$key=/d" "$CONFIG_ENV" 2>/dev/null
|
|
||||||
|
|
||||||
echo "$key=$value" >> "$CONFIG_ENV"
|
|
||||||
}
|
|
||||||
|
|
||||||
add_ips() {
|
|
||||||
ipv4_address=""
|
|
||||||
ipv6_address=""
|
|
||||||
|
|
||||||
interfaces=$(ip -o link show | awk -F': ' '{print $2}' | grep -vE '^(lo|wgcf|warp)$')
|
|
||||||
|
|
||||||
for interface in $interfaces; do
|
|
||||||
if ip addr show "$interface" > /dev/null 2>&1; then
|
|
||||||
ipv4=$(ip -o -4 addr show "$interface" | awk '{print $4}' | grep -vE '^(127\\.|10\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[0-1]))' | head -n 1 | cut -d/ -f1)
|
|
||||||
if [[ -z $ipv4_address && -n $ipv4 ]]; then
|
|
||||||
ipv4_address=$ipv4
|
|
||||||
fi
|
|
||||||
|
|
||||||
ipv6=$(ip -o -6 addr show "$interface" | awk '{print $4}' | grep -vE '^(::1|fe80:)' | head -n 1 | cut -d/ -f1)
|
|
||||||
if [[ -z $ipv6_address && -n $ipv6 ]]; then
|
|
||||||
ipv6_address=$ipv6
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
update_config "IP4" "${ipv4_address:-}"
|
|
||||||
update_config "IP6" "${ipv6_address:-}"
|
|
||||||
|
|
||||||
echo "Updated IP4=${ipv4_address:-Not Found}"
|
|
||||||
echo "Updated IP6=${ipv6_address:-Not Found}"
|
|
||||||
}
|
|
||||||
|
|
||||||
edit_ip() {
|
|
||||||
local type=$1
|
|
||||||
local new_ip=$2
|
|
||||||
|
|
||||||
if [[ $type == "-4" ]]; then
|
|
||||||
update_config "IP4" "$new_ip"
|
|
||||||
echo "IP4 has been updated to $new_ip."
|
|
||||||
elif [[ $type == "-6" ]]; then
|
|
||||||
update_config "IP6" "$new_ip"
|
|
||||||
echo "IP6 has been updated to $new_ip."
|
|
||||||
else
|
|
||||||
echo "Invalid option. Use -4 for IPv4 or -6 for IPv6."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
add)
|
|
||||||
add_ips
|
|
||||||
;;
|
|
||||||
edit)
|
|
||||||
edit_ip "$2" "$3"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 {add|edit -4|-6 <new_ip>}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
122
core/scripts/hysteria2/server_info.py
Normal file
122
core/scripts/hysteria2/server_info.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from hysteria2_api import Hysteria2Client
|
||||||
|
import time
|
||||||
|
from init_paths import *
|
||||||
|
from paths import *
|
||||||
|
|
||||||
|
|
||||||
|
def get_secret() -> str:
|
||||||
|
if not CONFIG_FILE.exists():
|
||||||
|
print("Error: config.json file not found!", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with CONFIG_FILE.open() as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
secret = data.get("trafficStats", {}).get("secret")
|
||||||
|
if not secret:
|
||||||
|
print("Error: secret not found in config.json!", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return secret
|
||||||
|
|
||||||
|
|
||||||
|
def convert_bytes(bytes_val: int) -> str:
|
||||||
|
units = [("TB", 1 << 40), ("GB", 1 << 30), ("MB", 1 << 20), ("KB", 1 << 10)]
|
||||||
|
for unit, factor in units:
|
||||||
|
if bytes_val >= factor:
|
||||||
|
return f"{bytes_val / factor:.2f} {unit}"
|
||||||
|
return f"{bytes_val} B"
|
||||||
|
|
||||||
|
|
||||||
|
def get_cpu_usage(interval: float = 0.1) -> float:
|
||||||
|
def read_cpu_times():
|
||||||
|
with open("/proc/stat") as f:
|
||||||
|
line = f.readline()
|
||||||
|
fields = list(map(int, line.strip().split()[1:]))
|
||||||
|
idle, total = fields[3], sum(fields)
|
||||||
|
return idle, total
|
||||||
|
|
||||||
|
idle1, total1 = read_cpu_times()
|
||||||
|
time.sleep(interval)
|
||||||
|
idle2, total2 = read_cpu_times()
|
||||||
|
|
||||||
|
idle_delta = idle2 - idle1
|
||||||
|
total_delta = total2 - total1
|
||||||
|
cpu_usage = 100.0 * (1 - idle_delta / total_delta) if total_delta else 0.0
|
||||||
|
return round(cpu_usage, 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_memory_usage() -> tuple[int, int]:
|
||||||
|
with open("/proc/meminfo") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
mem_total = int(next(line for line in lines if "MemTotal" in line).split()[1]) // 1024
|
||||||
|
mem_available = int(next(line for line in lines if "MemAvailable" in line).split()[1]) // 1024
|
||||||
|
mem_used = mem_total - mem_available
|
||||||
|
|
||||||
|
return mem_total, mem_used
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_online_user_count(secret: str) -> int:
|
||||||
|
try:
|
||||||
|
client = Hysteria2Client(
|
||||||
|
base_url=API_BASE_URL,
|
||||||
|
secret=secret
|
||||||
|
)
|
||||||
|
online_users = client.get_online_clients()
|
||||||
|
return sum(1 for user in online_users.values() if user.is_online)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting online users: {e}", file=sys.stderr)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_total_traffic() -> tuple[int, int]:
|
||||||
|
if not USERS_FILE.exists():
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
with USERS_FILE.open() as f:
|
||||||
|
users = json.load(f)
|
||||||
|
|
||||||
|
total_upload = 0
|
||||||
|
total_download = 0
|
||||||
|
|
||||||
|
for user_data in users.values():
|
||||||
|
total_upload += int(user_data.get("upload_bytes", 0) or 0)
|
||||||
|
total_download += int(user_data.get("download_bytes", 0) or 0)
|
||||||
|
|
||||||
|
return total_upload, total_download
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error parsing traffic data: {e}", file=sys.stderr)
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
secret = get_secret()
|
||||||
|
|
||||||
|
cpu_usage = get_cpu_usage()
|
||||||
|
mem_total, mem_used = get_memory_usage()
|
||||||
|
online_users = get_online_user_count(secret)
|
||||||
|
|
||||||
|
print(f"📈 CPU Usage: {cpu_usage}")
|
||||||
|
print(f"📋 Total RAM: {mem_total}MB")
|
||||||
|
print(f"💻 Used RAM: {mem_used}MB")
|
||||||
|
print(f"👥 Online Users: {online_users}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
total_upload, total_download = get_total_traffic()
|
||||||
|
|
||||||
|
print(f"🔼 Uploaded Traffic: {convert_bytes(total_upload)}")
|
||||||
|
print(f"🔽 Downloaded Traffic: {convert_bytes(total_download)}")
|
||||||
|
print(f"📊 Total Traffic: {convert_bytes(total_upload + total_download)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,63 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
get_secret() {
|
|
||||||
[ ! -f "$CONFIG_FILE" ] && { echo "Error: config.json file not found!" >&2; exit 1; }
|
|
||||||
|
|
||||||
local secret=$(jq -r '.trafficStats.secret' "$CONFIG_FILE")
|
|
||||||
|
|
||||||
[ "$secret" = "null" ] || [ -z "$secret" ] && {
|
|
||||||
echo "Error: secret not found in config.json!" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "$secret"
|
|
||||||
}
|
|
||||||
|
|
||||||
convert_bytes() {
|
|
||||||
local bytes=$1
|
|
||||||
if (( bytes < 1048576 )); then
|
|
||||||
printf "%.2f KB" "$(echo "scale=2; $bytes / 1024" | bc)"
|
|
||||||
elif (( bytes < 1073741824 )); then
|
|
||||||
printf "%.2f MB" "$(echo "scale=2; $bytes / 1048576" | bc)"
|
|
||||||
elif (( bytes < 1099511627776 )); then
|
|
||||||
printf "%.2f GB" "$(echo "scale=2; $bytes / 1073741824" | bc)"
|
|
||||||
else
|
|
||||||
printf "%.2f TB" "$(echo "scale=2; $bytes / 1099511627776" | bc)"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}')
|
|
||||||
|
|
||||||
mem_stats=$(free -m)
|
|
||||||
mem_total=$(echo "$mem_stats" | awk '/Mem:/ {print $2}')
|
|
||||||
mem_used=$(echo "$mem_stats" | awk '/Mem:/ {print $3}')
|
|
||||||
|
|
||||||
secret=$(get_secret)
|
|
||||||
online_users=$(curl -s -H "Authorization: $secret" "$ONLINE_API_URL")
|
|
||||||
online_user_count=$(echo "$online_users" | jq 'add // 0')
|
|
||||||
|
|
||||||
echo "📈 CPU Usage: $cpu_usage"
|
|
||||||
echo "📋 Total RAM: ${mem_total}MB"
|
|
||||||
echo "💻 Used RAM: ${mem_used}MB"
|
|
||||||
echo "👥 Online Users: $online_user_count"
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [ -f "$USERS_FILE" ]; then
|
|
||||||
read total_upload total_download <<< $(jq -r '
|
|
||||||
reduce .[] as $user (
|
|
||||||
{"up": 0, "down": 0};
|
|
||||||
.up += (($user.upload_bytes | numbers) // 0) |
|
|
||||||
.down += (($user.download_bytes | numbers) // 0)
|
|
||||||
) | "\(.up) \(.down)"' "$USERS_FILE" 2>/dev/null || echo "0 0")
|
|
||||||
|
|
||||||
total_upload=${total_upload:-0}
|
|
||||||
total_download=${total_download:-0}
|
|
||||||
|
|
||||||
echo "🔼 Uploaded Traffic: $(convert_bytes "$total_upload")"
|
|
||||||
echo "🔽 Downloaded Traffic: $(convert_bytes "$total_download")"
|
|
||||||
|
|
||||||
total_traffic=$((total_upload + total_download))
|
|
||||||
echo "📊 Total Traffic: $(convert_bytes "$total_traffic")"
|
|
||||||
fi
|
|
||||||
74
core/scripts/telegrambot/runbot.py
Normal file
74
core/scripts/telegrambot/runbot.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import TELEGRAM_ENV
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def update_env_file(api_token, admin_user_ids):
|
||||||
|
TELEGRAM_ENV.write_text(f"""API_TOKEN={api_token}
|
||||||
|
ADMIN_USER_IDS=[{admin_user_ids}]
|
||||||
|
""")
|
||||||
|
|
||||||
|
def create_service_file():
|
||||||
|
Path("/etc/systemd/system/hysteria-telegram-bot.service").write_text("""[Unit]
|
||||||
|
Description=Hysteria Telegram Bot
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/bash -c 'source /etc/hysteria/hysteria2_venv/bin/activate && /etc/hysteria/hysteria2_venv/bin/python /etc/hysteria/core/scripts/telegrambot/tbot.py'
|
||||||
|
WorkingDirectory=/etc/hysteria/core/scripts/telegrambot
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
""")
|
||||||
|
|
||||||
|
def start_service(api_token, admin_user_ids):
|
||||||
|
if subprocess.run(["systemctl", "is-active", "--quiet", "hysteria-telegram-bot.service"]).returncode == 0:
|
||||||
|
print("The hysteria-telegram-bot.service is already running.")
|
||||||
|
return
|
||||||
|
|
||||||
|
update_env_file(api_token, admin_user_ids)
|
||||||
|
create_service_file()
|
||||||
|
|
||||||
|
subprocess.run(["systemctl", "daemon-reload"])
|
||||||
|
subprocess.run(["systemctl", "enable", "hysteria-telegram-bot.service"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
subprocess.run(["systemctl", "start", "hysteria-telegram-bot.service"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
if subprocess.run(["systemctl", "is-active", "--quiet", "hysteria-telegram-bot.service"]).returncode == 0:
|
||||||
|
print("Hysteria bot setup completed. The service is now running.\n")
|
||||||
|
else:
|
||||||
|
print("Hysteria bot setup completed. The service failed to start.")
|
||||||
|
|
||||||
|
def stop_service():
|
||||||
|
subprocess.run(["systemctl", "stop", "hysteria-telegram-bot.service"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
subprocess.run(["systemctl", "disable", "hysteria-telegram-bot.service"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
TELEGRAM_ENV.unlink(missing_ok=True)
|
||||||
|
print("\nHysteria bot service stopped and disabled. .env file removed.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python3 runbot.py {start|stop} <API_TOKEN> <ADMIN_USER_IDS>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
action = sys.argv[1]
|
||||||
|
|
||||||
|
if action == "start":
|
||||||
|
if len(sys.argv) != 4:
|
||||||
|
print("Usage: python3 runbot.py start <API_TOKEN> <ADMIN_USER_IDS>")
|
||||||
|
sys.exit(1)
|
||||||
|
start_service(sys.argv[2], sys.argv[3])
|
||||||
|
elif action == "stop":
|
||||||
|
stop_service()
|
||||||
|
else:
|
||||||
|
print("Usage: python3 runbot.py {start|stop} <API_TOKEN> <ADMIN_USER_IDS>")
|
||||||
|
sys.exit(1)
|
||||||
@ -1,78 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
source /etc/hysteria/core/scripts/utils.sh
|
|
||||||
define_colors
|
|
||||||
|
|
||||||
update_env_file() {
|
|
||||||
local api_token=$1
|
|
||||||
local admin_user_ids=$2
|
|
||||||
|
|
||||||
cat <<EOL > /etc/hysteria/core/scripts/telegrambot/.env
|
|
||||||
API_TOKEN=$api_token
|
|
||||||
ADMIN_USER_IDS=[$admin_user_ids]
|
|
||||||
EOL
|
|
||||||
}
|
|
||||||
|
|
||||||
create_service_file() {
|
|
||||||
cat <<EOL > /etc/systemd/system/hysteria-telegram-bot.service
|
|
||||||
[Unit]
|
|
||||||
Description=Hysteria Telegram Bot
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/bin/bash -c 'source /etc/hysteria/hysteria2_venv/bin/activate && /etc/hysteria/hysteria2_venv/bin/python /etc/hysteria/core/scripts/telegrambot/tbot.py'
|
|
||||||
WorkingDirectory=/etc/hysteria/core/scripts/telegrambot
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOL
|
|
||||||
}
|
|
||||||
|
|
||||||
start_service() {
|
|
||||||
local api_token=$1
|
|
||||||
local admin_user_ids=$2
|
|
||||||
|
|
||||||
if systemctl is-active --quiet hysteria-telegram-bot.service; then
|
|
||||||
echo "The hysteria-telegram-bot.service is already running."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
update_env_file "$api_token" "$admin_user_ids"
|
|
||||||
create_service_file
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable hysteria-telegram-bot.service > /dev/null 2>&1
|
|
||||||
systemctl start hysteria-telegram-bot.service > /dev/null 2>&1
|
|
||||||
|
|
||||||
if systemctl is-active --quiet hysteria-telegram-bot.service; then
|
|
||||||
echo -e "${green}Hysteria bot setup completed. The service is now running. ${NC}"
|
|
||||||
echo -e "\n\n"
|
|
||||||
else
|
|
||||||
echo "Hysteria bot setup completed. The service failed to start."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_service() {
|
|
||||||
systemctl stop hysteria-telegram-bot.service > /dev/null 2>&1
|
|
||||||
systemctl disable hysteria-telegram-bot.service > /dev/null 2>&1
|
|
||||||
|
|
||||||
rm -f /etc/hysteria/core/scripts/telegrambot/.env
|
|
||||||
echo -e "\n"
|
|
||||||
|
|
||||||
echo "Hysteria bot service stopped and disabled. .env file removed."
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start_service "$2" "$3"
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop_service
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 {start|stop} <API_TOKEN> <ADMIN_USER_IDS>"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
define_colors
|
|
||||||
159
core/scripts/warp/configure.py
Normal file
159
core/scripts/warp/configure.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import *
|
||||||
|
|
||||||
|
def warp_configure_handler(all_traffic=False, popular_sites=False, domestic_sites=False, block_adult_sites=False):
|
||||||
|
"""
|
||||||
|
Configure WARP routing rules based on provided parameters
|
||||||
|
|
||||||
|
Args:
|
||||||
|
all_traffic (bool): Toggle WARP for all traffic
|
||||||
|
popular_sites (bool): Toggle WARP for popular sites (Google, Netflix, etc.)
|
||||||
|
domestic_sites (bool): Toggle between WARP and Reject for domestic sites
|
||||||
|
block_adult_sites (bool): Toggle blocking of adult content
|
||||||
|
"""
|
||||||
|
with open(CONFIG_FILE, 'r') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
modified = False
|
||||||
|
|
||||||
|
if all_traffic:
|
||||||
|
warp_all_active = any(rule == "warps(all)" for rule in config.get('acl', {}).get('inline', []))
|
||||||
|
|
||||||
|
if warp_all_active:
|
||||||
|
config['acl']['inline'] = [rule for rule in config['acl']['inline'] if rule != "warps(all)"]
|
||||||
|
print("Traffic configuration changed to Direct.")
|
||||||
|
modified = True
|
||||||
|
else:
|
||||||
|
if 'acl' not in config:
|
||||||
|
config['acl'] = {}
|
||||||
|
if 'inline' not in config['acl']:
|
||||||
|
config['acl']['inline'] = []
|
||||||
|
config['acl']['inline'].append("warps(all)")
|
||||||
|
print("Traffic configuration changed to WARP.")
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if popular_sites:
|
||||||
|
popular_rules = [
|
||||||
|
"warps(geoip:google)",
|
||||||
|
"warps(geosite:google)",
|
||||||
|
"warps(geosite:netflix)",
|
||||||
|
"warps(geosite:spotify)",
|
||||||
|
"warps(geosite:openai)",
|
||||||
|
"warps(geoip:openai)"
|
||||||
|
]
|
||||||
|
|
||||||
|
rule_exists = any(rule in config.get('acl', {}).get('inline', []) for rule in popular_rules)
|
||||||
|
|
||||||
|
if rule_exists:
|
||||||
|
config['acl']['inline'] = [rule for rule in config['acl']['inline']
|
||||||
|
if rule not in popular_rules]
|
||||||
|
print("WARP configuration for Google, OpenAI, etc. removed.")
|
||||||
|
modified = True
|
||||||
|
else:
|
||||||
|
if 'acl' not in config:
|
||||||
|
config['acl'] = {}
|
||||||
|
if 'inline' not in config['acl']:
|
||||||
|
config['acl']['inline'] = []
|
||||||
|
config['acl']['inline'].extend(popular_rules)
|
||||||
|
print("WARP configured for Google, OpenAI, etc.")
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if domestic_sites:
|
||||||
|
ir_site_rule = "warps(geosite:ir)"
|
||||||
|
ir_ip_rule = "warps(geoip:ir)"
|
||||||
|
reject_site_rule = "reject(geosite:ir)"
|
||||||
|
reject_ip_rule = "reject(geoip:ir)"
|
||||||
|
|
||||||
|
using_warp = (ir_site_rule in config.get('acl', {}).get('inline', []) and
|
||||||
|
ir_ip_rule in config.get('acl', {}).get('inline', []))
|
||||||
|
using_reject = (reject_site_rule in config.get('acl', {}).get('inline', []) and
|
||||||
|
reject_ip_rule in config.get('acl', {}).get('inline', []))
|
||||||
|
|
||||||
|
if 'acl' not in config:
|
||||||
|
config['acl'] = {}
|
||||||
|
if 'inline' not in config['acl']:
|
||||||
|
config['acl']['inline'] = []
|
||||||
|
|
||||||
|
if using_warp:
|
||||||
|
config['acl']['inline'] = [reject_site_rule if rule == ir_site_rule else
|
||||||
|
reject_ip_rule if rule == ir_ip_rule else
|
||||||
|
rule for rule in config['acl']['inline']]
|
||||||
|
print("Configuration changed to Reject for geosite:ir and geoip:ir.")
|
||||||
|
modified = True
|
||||||
|
elif using_reject:
|
||||||
|
config['acl']['inline'] = [ir_site_rule if rule == reject_site_rule else
|
||||||
|
ir_ip_rule if rule == reject_ip_rule else
|
||||||
|
rule for rule in config['acl']['inline']]
|
||||||
|
print("Configuration changed to Use WARP for geosite:ir and geoip:ir.")
|
||||||
|
modified = True
|
||||||
|
else:
|
||||||
|
config['acl']['inline'].extend([reject_site_rule, reject_ip_rule])
|
||||||
|
print("Added Reject configuration for geosite:ir and geoip:ir.")
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if block_adult_sites:
|
||||||
|
nsfw_rule = "reject(geosite:nsfw)"
|
||||||
|
|
||||||
|
blocked = nsfw_rule in config.get('acl', {}).get('inline', [])
|
||||||
|
|
||||||
|
if blocked:
|
||||||
|
config['acl']['inline'] = [rule for rule in config['acl']['inline']
|
||||||
|
if rule != nsfw_rule]
|
||||||
|
if 'resolver' not in config:
|
||||||
|
config['resolver'] = {}
|
||||||
|
if 'tls' not in config['resolver']:
|
||||||
|
config['resolver']['tls'] = {}
|
||||||
|
config['resolver']['tls']['addr'] = "1.1.1.1:853"
|
||||||
|
print("Adult content blocking removed and resolver updated.")
|
||||||
|
modified = True
|
||||||
|
else:
|
||||||
|
if 'acl' not in config:
|
||||||
|
config['acl'] = {}
|
||||||
|
if 'inline' not in config['acl']:
|
||||||
|
config['acl']['inline'] = []
|
||||||
|
config['acl']['inline'].append(nsfw_rule)
|
||||||
|
if 'resolver' not in config:
|
||||||
|
config['resolver'] = {}
|
||||||
|
if 'tls' not in config['resolver']:
|
||||||
|
config['resolver']['tls'] = {}
|
||||||
|
config['resolver']['tls']['addr'] = "1.1.1.3:853"
|
||||||
|
print("Adult content blocked and resolver updated.")
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if modified:
|
||||||
|
with open(CONFIG_FILE, 'w') as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(["python3", CLI_PATH, "restart-hysteria2"],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Warning: Failed to restart hysteria2")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Configure WARP settings")
|
||||||
|
parser.add_argument("--all", action="store_true", help="Toggle WARP for all traffic")
|
||||||
|
parser.add_argument("--popular-sites", action="store_true", help="Toggle WARP for popular sites")
|
||||||
|
parser.add_argument("--domestic-sites", action="store_true", help="Toggle between WARP and Reject for domestic sites")
|
||||||
|
parser.add_argument("--block-adult", action="store_true", help="Toggle blocking of adult content")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
warp_configure_handler(
|
||||||
|
all_traffic=args.all,
|
||||||
|
popular_sites=args.popular_sites,
|
||||||
|
domestic_sites=args.domestic_sites,
|
||||||
|
block_adult_sites=args.block_adult
|
||||||
|
)
|
||||||
@ -1,78 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
warp_configure_handler() {
|
|
||||||
local all=$1
|
|
||||||
local popular_sites=$2
|
|
||||||
local domestic_sites=$3
|
|
||||||
local block_adult_sites=$4
|
|
||||||
local warp_option=$5
|
|
||||||
local warp_key=$6
|
|
||||||
|
|
||||||
if [ "$all" == "true" ]; then
|
|
||||||
if [ "$(jq -r 'if .acl.inline | index("warps(all)") then "WARP active" else "Direct" end' "$CONFIG_FILE")" == "WARP active" ]; then
|
|
||||||
jq 'del(.acl.inline[] | select(. == "warps(all)"))' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Traffic configuration changed to Direct."
|
|
||||||
else
|
|
||||||
jq '.acl.inline += ["warps(all)"]' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Traffic configuration changed to WARP."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$popular_sites" == "true" ]; then
|
|
||||||
if [ "$(jq -r 'if (.acl.inline | index("warps(geoip:google)")) or (.acl.inline | index("warps(geosite:google)")) or (.acl.inline | index("warps(geosite:netflix)")) or (.acl.inline | index("warps(geosite:spotify)")) or (.acl.inline | index("warps(geosite:openai)")) or (.acl.inline | index("warps(geoip:openai)")) then "WARP active" else "Direct" end' "$CONFIG_FILE")" == "WARP active" ]; then
|
|
||||||
jq 'del(.acl.inline[] | select(. == "warps(geoip:google)" or . == "warps(geosite:google)" or . == "warps(geosite:netflix)" or . == "warps(geosite:spotify)" or . == "warps(geosite:openai)" or . == "warps(geoip:openai)"))' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "WARP configuration for Google, OpenAI, etc. removed."
|
|
||||||
else
|
|
||||||
jq '.acl.inline += ["warps(geoip:google)", "warps(geosite:google)", "warps(geosite:netflix)", "warps(geosite:spotify)", "warps(geosite:openai)", "warps(geoip:openai)"]' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "WARP configured for Google, OpenAI, etc."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$domestic_sites" == "true" ]; then
|
|
||||||
if [ "$(jq -r 'if (.acl.inline | index("warps(geosite:ir)")) and (.acl.inline | index("warps(geoip:ir)")) then "Use WARP" else "Reject" end' "$CONFIG_FILE")" == "Use WARP" ]; then
|
|
||||||
jq '(.acl.inline[] | select(. == "warps(geosite:ir)")) = "reject(geosite:ir)" | (.acl.inline[] | select(. == "warps(geoip:ir)")) = "reject(geoip:ir)"' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Configuration changed to Reject for geosite:ir and geoip:ir."
|
|
||||||
else
|
|
||||||
jq '(.acl.inline[] | select(. == "reject(geosite:ir)")) = "warps(geosite:ir)" | (.acl.inline[] | select(. == "reject(geoip:ir)")) = "warps(geoip:ir)"' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Configuration changed to Use WARP for geosite:ir and geoip:ir."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$block_adult_sites" == "true" ]; then
|
|
||||||
if [ "$(jq -r 'if .acl.inline | index("reject(geosite:nsfw)") then "Blocked" else "Not blocked" end' "$CONFIG_FILE")" == "Blocked" ]; then
|
|
||||||
jq 'del(.acl.inline[] | select(. == "reject(geosite:nsfw)"))' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
jq '.resolver.tls.addr = "1.1.1.1:853"' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Adult content blocking removed and resolver updated."
|
|
||||||
else
|
|
||||||
jq '.acl.inline += ["reject(geosite:nsfw)"]' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
jq '.resolver.tls.addr = "1.1.1.3:853"' "$CONFIG_FILE" > "${CONFIG_FILE}.temp" && mv "${CONFIG_FILE}.temp" "$CONFIG_FILE"
|
|
||||||
echo "Adult content blocked and resolver updated."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$warp_option" == "warp plus" ]; then
|
|
||||||
if [ -z "$warp_key" ]; then
|
|
||||||
echo "Error: WARP Plus key is required. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cd /etc/warp/ || { echo "Failed to change directory to /etc/warp/"; exit 1; }
|
|
||||||
|
|
||||||
WGCF_LICENSE_KEY="$warp_key" wgcf update
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Error: Failed to update WARP Plus configuration."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
elif [ "$warp_option" == "warp" ]; then
|
|
||||||
cd /etc/warp/ || { echo "Failed to change directory to /etc/warp/"; exit 1; }
|
|
||||||
rm wgcf-account.toml && yes | wgcf register
|
|
||||||
echo "WARP configured with a new account."
|
|
||||||
fi
|
|
||||||
|
|
||||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
warp_configure_handler "$1" "$2" "$3" "$4" "$5" "$6"
|
|
||||||
77
core/scripts/warp/install.py
Normal file
77
core/scripts/warp/install.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import *
|
||||||
|
|
||||||
|
WARP_DEVICE = "wgcf"
|
||||||
|
|
||||||
|
def is_service_active(service_name: str) -> bool:
|
||||||
|
return subprocess.run(["systemctl", "is-active", "--quiet", service_name]).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def install_warp():
|
||||||
|
print("Installing WARP...")
|
||||||
|
result = subprocess.run("bash <(curl -fsSL https://raw.githubusercontent.com/ReturnFI/Warp/main/warp.sh) wgx",
|
||||||
|
shell=True, executable="/bin/bash")
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def add_warp_outbound_to_config():
|
||||||
|
if not CONFIG_FILE.exists():
|
||||||
|
print(f"Error: Config file {CONFIG_FILE} not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(CONFIG_FILE, "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
outbounds = config.get("outbounds", [])
|
||||||
|
if any(outbound.get("name") == "warps" for outbound in outbounds):
|
||||||
|
print("WARP outbound already exists in the configuration.")
|
||||||
|
return
|
||||||
|
|
||||||
|
outbounds.append({
|
||||||
|
"name": "warps",
|
||||||
|
"type": "direct",
|
||||||
|
"direct": {
|
||||||
|
"mode": 4,
|
||||||
|
"bindDevice": WARP_DEVICE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
config["outbounds"] = outbounds
|
||||||
|
|
||||||
|
with open(CONFIG_FILE, "w") as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
|
||||||
|
print("WARP outbound added to config.json.")
|
||||||
|
|
||||||
|
|
||||||
|
def restart_hysteria():
|
||||||
|
subprocess.run(["python3", str(CLI_PATH), "restart-hysteria2"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
print("Hysteria2 restarted with updated configuration.")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
warp_service = f"wg-quick@{WARP_DEVICE}.service"
|
||||||
|
|
||||||
|
if is_service_active(warp_service):
|
||||||
|
print("WARP is already active. Checking configuration...")
|
||||||
|
add_warp_outbound_to_config()
|
||||||
|
restart_hysteria()
|
||||||
|
else:
|
||||||
|
if install_warp() and is_service_active(warp_service):
|
||||||
|
print("WARP installation successful.")
|
||||||
|
add_warp_outbound_to_config()
|
||||||
|
restart_hysteria()
|
||||||
|
else:
|
||||||
|
print("WARP installation failed.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
if systemctl is-active --quiet wg-quick@wgcf.service; then
|
|
||||||
echo "WARP is already active. Checking configuration..."
|
|
||||||
|
|
||||||
if [ -f "$CONFIG_FILE" ] && jq -e '.outbounds[] | select(.name == "warps")' "$CONFIG_FILE" > /dev/null 2>&1; then
|
|
||||||
echo "WARP outbound already exists in the configuration. No changes needed."
|
|
||||||
else
|
|
||||||
if [ -f "$CONFIG_FILE" ]; then
|
|
||||||
jq '.outbounds += [{"name": "warps", "type": "direct", "direct": {"mode": 4, "bindDevice": "wgcf"}}]' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
|
||||||
echo "WARP outbound added to config.json."
|
|
||||||
else
|
|
||||||
echo "Error: Config file $CONFIG_FILE not found."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Installing WARP..."
|
|
||||||
bash <(curl -fsSL https://raw.githubusercontent.com/ReturnFI/Warp/main/warp.sh) wgx
|
|
||||||
|
|
||||||
if systemctl is-active --quiet wg-quick@wgcf.service; then
|
|
||||||
echo "WARP installation successful."
|
|
||||||
|
|
||||||
if [ -f "$CONFIG_FILE" ]; then
|
|
||||||
if jq -e '.outbounds[] | select(.name == "warps")' "$CONFIG_FILE" > /dev/null 2>&1; then
|
|
||||||
echo "WARP outbound already exists in the configuration."
|
|
||||||
else
|
|
||||||
jq '.outbounds += [{"name": "warps", "type": "direct", "direct": {"mode": 4, "bindDevice": "wgcf"}}]' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
echo "WARP outbound added to config.json."
|
|
||||||
fi
|
|
||||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
|
||||||
echo "Hysteria2 restarted with updated configuration."
|
|
||||||
else
|
|
||||||
echo "Error: Config file $CONFIG_FILE not found."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "WARP installation failed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
74
core/scripts/warp/status.py
Normal file
74
core/scripts/warp/status.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import *
|
||||||
|
|
||||||
|
colors = {
|
||||||
|
"cyan": "\033[96m",
|
||||||
|
"green": "\033[92m",
|
||||||
|
"red": "\033[91m",
|
||||||
|
"purple": "\033[95m",
|
||||||
|
"end": "\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
def echo_status(label, is_active):
|
||||||
|
status = f"{colors['green']}Active{colors['end']}" if is_active else f"{colors['red']}Inactive{colors['end']}"
|
||||||
|
print(f"{colors['cyan']}{label}:{colors['end']} {status}")
|
||||||
|
|
||||||
|
def check_warp_configuration():
|
||||||
|
if not Path(CONFIG_FILE).exists():
|
||||||
|
print(f"{colors['red']}Error: Config file not found at {CONFIG_FILE}{colors['end']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(CONFIG_FILE, "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
acl_inline = config.get("acl", {}).get("inline", [])
|
||||||
|
|
||||||
|
def contains_warp(rule_prefixes):
|
||||||
|
return any(rule.startswith(prefix) for rule in acl_inline for prefix in rule_prefixes)
|
||||||
|
|
||||||
|
print("--------------------------------")
|
||||||
|
print(f"{colors['purple']}Current WARP Configuration:{colors['end']}")
|
||||||
|
|
||||||
|
echo_status(
|
||||||
|
"All traffic",
|
||||||
|
contains_warp(["warps(all)"])
|
||||||
|
)
|
||||||
|
|
||||||
|
echo_status(
|
||||||
|
"Popular sites (Google, Netflix, etc.)",
|
||||||
|
contains_warp([
|
||||||
|
"warps(geosite:google)",
|
||||||
|
"warps(geoip:google)",
|
||||||
|
"warps(geosite:netflix)",
|
||||||
|
"warps(geosite:spotify)",
|
||||||
|
"warps(geosite:openai)",
|
||||||
|
"warps(geoip:openai)"
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
echo_status(
|
||||||
|
"Domestic sites (geosite:ir, geoip:ir)",
|
||||||
|
contains_warp([
|
||||||
|
"warps(geosite:ir)",
|
||||||
|
"warps(geoip:ir)"
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
echo_status(
|
||||||
|
"Block adult content",
|
||||||
|
"reject(geosite:nsfw)" in acl_inline
|
||||||
|
)
|
||||||
|
|
||||||
|
print("--------------------------------")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
check_warp_configuration()
|
||||||
@ -1,34 +0,0 @@
|
|||||||
source /etc/hysteria/core/scripts/utils.sh
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
check_warp_configuration() {
|
|
||||||
echo "--------------------------------"
|
|
||||||
echo -e "${LPurple}Current WARP Configuration: ${NC}"
|
|
||||||
|
|
||||||
if jq -e '.acl.inline[]? | select(test("warps\\(all\\)"))' "$CONFIG_FILE" > /dev/null; then
|
|
||||||
echo -e "${cyan}All traffic:${NC} ${green}Active${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${cyan}All traffic:${NC} ${red}Inactive${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.acl.inline[]? | select(test("warps\\(geosite:google\\)")) or select(test("warps\\(geoip:google\\)")) or select(test("warps\\(geosite:netflix\\)")) or select(test("warps\\(geosite:spotify\\)")) or select(test("warps\\(geosite:openai\\)")) or select(test("warps\\(geoip:openai\\)"))' "$CONFIG_FILE" > /dev/null; then
|
|
||||||
echo -e "${cyan}Popular sites (Google, Netflix, etc.):${NC} ${green}Active${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${cyan}Popular sites (Google, Netflix, etc.):${NC} ${red}Inactive${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.acl.inline[]? | select(test("warps\\(geosite:ir\\)")) or select(test("warps\\(geoip:ir\\)"))' "$CONFIG_FILE" > /dev/null; then
|
|
||||||
echo -e "${cyan}Domestic sites (geosite:ir, geoip:ir):${NC} ${green}Active${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${cyan}Domestic sites (geosite:ir, geoip:ir):${NC} ${red}Inactive${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.acl.inline[]? | select(test("reject\\(geosite:nsfw\\)"))' "$CONFIG_FILE" > /dev/null; then
|
|
||||||
echo -e "${cyan}Block adult content:${NC} ${green}Active${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${cyan}Block adult content:${NC} ${red}Inactive${NC}"
|
|
||||||
fi
|
|
||||||
echo "--------------------------------"
|
|
||||||
}
|
|
||||||
define_colors
|
|
||||||
check_warp_configuration
|
|
||||||
117
core/scripts/warp/uninstall.py
Normal file
117
core/scripts/warp/uninstall.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
core_scripts_dir = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
if str(core_scripts_dir) not in sys.path:
|
||||||
|
sys.path.append(str(core_scripts_dir))
|
||||||
|
|
||||||
|
from paths import CONFIG_FILE, CLI_PATH
|
||||||
|
|
||||||
|
TEMP_CONFIG = Path("/etc/hysteria/config_temp.json")
|
||||||
|
|
||||||
|
|
||||||
|
def systemctl_active(service: str) -> bool:
|
||||||
|
return subprocess.run(["systemctl", "is-active", "--quiet", service]).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def run_shell(command: str):
|
||||||
|
subprocess.run(command, shell=True, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(path: Path):
|
||||||
|
if path.exists():
|
||||||
|
with path.open("r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
print(f"❌ Config file not found: {path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_config(config: dict, path: Path):
|
||||||
|
with path.open("w", encoding="utf-8") as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
shutil.move(str(path), str(CONFIG_FILE))
|
||||||
|
|
||||||
|
|
||||||
|
def reset_acl_inline(config: dict):
|
||||||
|
default = [
|
||||||
|
"reject(geosite:ir)", "reject(geoip:ir)",
|
||||||
|
"reject(geosite:category-ads-all)", "reject(geoip:private)",
|
||||||
|
"reject(geosite:google@ads)"
|
||||||
|
]
|
||||||
|
updated = []
|
||||||
|
for item in config.get("acl", {}).get("inline", []):
|
||||||
|
if item in [
|
||||||
|
"warps(all)", "warps(geoip:google)", "warps(geosite:google)",
|
||||||
|
"warps(geosite:netflix)", "warps(geosite:spotify)",
|
||||||
|
"warps(geosite:openai)", "warps(geoip:openai)"
|
||||||
|
]:
|
||||||
|
updated.append("direct")
|
||||||
|
elif item == "warps(geosite:ir)":
|
||||||
|
updated.append("reject(geosite:ir)")
|
||||||
|
elif item == "warps(geoip:ir)":
|
||||||
|
updated.append("reject(geoip:ir)")
|
||||||
|
else:
|
||||||
|
updated.append(item)
|
||||||
|
|
||||||
|
final_inline = default + [i for i in updated if i not in default and i != "direct"]
|
||||||
|
config["acl"]["inline"] = final_inline
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def remove_warp_outbound(config: dict):
|
||||||
|
config["outbounds"] = [
|
||||||
|
o for o in config.get("outbounds", [])
|
||||||
|
if not (
|
||||||
|
o.get("name") == "warps" and
|
||||||
|
o.get("type") == "direct" and
|
||||||
|
o.get("direct", {}).get("mode") == 4 and
|
||||||
|
o.get("direct", {}).get("bindDevice") == "wgcf"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def remove_porn_blocking(config: dict):
|
||||||
|
inline = config.get("acl", {}).get("inline", [])
|
||||||
|
if "reject(geosite:category-porn)" in inline:
|
||||||
|
config["acl"]["inline"] = [i for i in inline if i != "reject(geosite:category-porn)"]
|
||||||
|
print("🔒 Adult content blocking removed.")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def set_dns(config: dict):
|
||||||
|
config.setdefault("resolver", {}).setdefault("tls", {})["addr"] = "1.1.1.1:853"
|
||||||
|
print("🔧 DNS resolver changed to 1.1.1.1:853.")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def restart_hysteria():
|
||||||
|
subprocess.run(["python3", str(CLI_PATH), "restart-hysteria2"],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if systemctl_active("wg-quick@wgcf.service"):
|
||||||
|
print("🧹 Uninstalling WARP...")
|
||||||
|
run_shell('bash -c "bash <(curl -fsSL https://raw.githubusercontent.com/ReturnFI/Warp/main/warp.sh) dwg"')
|
||||||
|
config = load_config(CONFIG_FILE)
|
||||||
|
if config:
|
||||||
|
config = reset_acl_inline(config)
|
||||||
|
config = remove_warp_outbound(config)
|
||||||
|
config = remove_porn_blocking(config)
|
||||||
|
config = set_dns(config)
|
||||||
|
save_config(config, TEMP_CONFIG)
|
||||||
|
restart_hysteria()
|
||||||
|
print("✅ WARP uninstalled and configuration reset.")
|
||||||
|
else:
|
||||||
|
print("ℹ️ WARP is not active. Skipping uninstallation.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
|
||||||
|
|
||||||
if systemctl is-active --quiet wg-quick@wgcf.service; then
|
|
||||||
echo "Uninstalling WARP..."
|
|
||||||
bash <(curl -fsSL https://raw.githubusercontent.com/ReturnFI/Warp/main/warp.sh) dwg
|
|
||||||
|
|
||||||
if [ -f "$CONFIG_FILE" ]; then
|
|
||||||
default_config='["reject(geosite:ir)", "reject(geoip:ir)", "reject(geosite:category-ads-all)", "reject(geoip:private)", "reject(geosite:google@ads)"]'
|
|
||||||
|
|
||||||
jq --argjson default_config "$default_config" '
|
|
||||||
.acl.inline |= map(
|
|
||||||
if . == "warps(all)" or . == "warps(geoip:google)" or . == "warps(geosite:google)" or . == "warps(geosite:netflix)" or . == "warps(geosite:spotify)" or . == "warps(geosite:openai)" or . == "warps(geoip:openai)" then
|
|
||||||
"direct"
|
|
||||||
elif . == "warps(geosite:ir)" then
|
|
||||||
"reject(geosite:ir)"
|
|
||||||
elif . == "warps(geoip:ir)" then
|
|
||||||
"reject(geoip:ir)"
|
|
||||||
else
|
|
||||||
.
|
|
||||||
end
|
|
||||||
) | .acl.inline |= ($default_config + (. - $default_config | map(select(. != "direct"))))
|
|
||||||
' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
|
|
||||||
jq 'del(.outbounds[] | select(.name == "warps" and .type == "direct" and .direct.mode == 4 and .direct.bindDevice == "wgcf"))' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
|
|
||||||
if [ "$(jq -r 'if .acl.inline | index("reject(geosite:category-porn)") then "Blocked" else "Not blocked" end' "$CONFIG_FILE")" == "Blocked" ]; then
|
|
||||||
jq 'del(.acl.inline[] | select(. == "reject(geosite:category-porn)"))' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
echo "Adult content blocking removed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq '.resolver.tls.addr = "1.1.1.1:853"' "$CONFIG_FILE" > /etc/hysteria/config_temp.json && mv /etc/hysteria/config_temp.json "$CONFIG_FILE"
|
|
||||||
echo "DNS resolver address changed to 1.1.1.1:853."
|
|
||||||
|
|
||||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
|
||||||
echo "WARP uninstalled and configurations reset to default."
|
|
||||||
else
|
|
||||||
echo "Error: Config file $CONFIG_FILE not found."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "WARP is not active. Skipping uninstallation."
|
|
||||||
fi
|
|
||||||
@ -61,7 +61,7 @@ async def configure(body: ConfigureInputBody):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
cli_api.configure_warp(body.all, body.popular_sites, body.domestic_sites,
|
cli_api.configure_warp(body.all, body.popular_sites, body.domestic_sites,
|
||||||
body.block_adult_sites, body.warp_option, body.warp_key)
|
body.block_adult_sites)
|
||||||
return DetailResponse(detail='WARP configured successfully.')
|
return DetailResponse(detail='WARP configured successfully.')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=400, detail=f'Error: {str(e)}')
|
raise HTTPException(status_code=400, detail=f'Error: {str(e)}')
|
||||||
|
|||||||
@ -7,8 +7,6 @@ class ConfigureInputBody(BaseModel):
|
|||||||
popular_sites: bool = False
|
popular_sites: bool = False
|
||||||
domestic_sites: bool = False
|
domestic_sites: bool = False
|
||||||
block_adult_sites: bool = False
|
block_adult_sites: bool = False
|
||||||
warp_option: Literal['warp', 'warp plus', ''] = ''
|
|
||||||
warp_key: str = ''
|
|
||||||
|
|
||||||
|
|
||||||
class StatusResponse(BaseModel):
|
class StatusResponse(BaseModel):
|
||||||
|
|||||||
20
menu.sh
20
menu.sh
@ -389,10 +389,8 @@ warp_configure_handler() {
|
|||||||
echo "2. Use WARP for popular sites"
|
echo "2. Use WARP for popular sites"
|
||||||
echo "3. Use WARP for domestic sites"
|
echo "3. Use WARP for domestic sites"
|
||||||
echo "4. Block adult content"
|
echo "4. Block adult content"
|
||||||
echo "5. WARP (Plus) Profile"
|
echo "5. WARP Status Profile"
|
||||||
echo "6. WARP (Normal) Profile"
|
echo "6. Change IP address"
|
||||||
echo "7. WARP Status Profile"
|
|
||||||
echo "8. Change IP address"
|
|
||||||
echo "0. Cancel"
|
echo "0. Cancel"
|
||||||
|
|
||||||
read -p "Select an option: " option
|
read -p "Select an option: " option
|
||||||
@ -402,23 +400,13 @@ warp_configure_handler() {
|
|||||||
2) python3 $CLI_PATH configure-warp --popular-sites ;;
|
2) python3 $CLI_PATH configure-warp --popular-sites ;;
|
||||||
3) python3 $CLI_PATH configure-warp --domestic-sites ;;
|
3) python3 $CLI_PATH configure-warp --domestic-sites ;;
|
||||||
4) python3 $CLI_PATH configure-warp --block-adult-sites ;;
|
4) python3 $CLI_PATH configure-warp --block-adult-sites ;;
|
||||||
5)
|
5)
|
||||||
echo "Please enter your WARP Plus key:"
|
|
||||||
read -r warp_key
|
|
||||||
if [ -z "$warp_key" ]; then
|
|
||||||
echo "Error: WARP Plus key cannot be empty. Exiting."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
python3 $CLI_PATH configure-warp --warp-option "warp plus" --warp-key "$warp_key"
|
|
||||||
;;
|
|
||||||
6) python3 $CLI_PATH configure-warp --warp-option "warp" ;;
|
|
||||||
7)
|
|
||||||
ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me)
|
ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me)
|
||||||
cd /etc/warp/ && wgcf status
|
cd /etc/warp/ && wgcf status
|
||||||
echo
|
echo
|
||||||
echo -e "${yellow}Warp IP :${NC} ${cyan}$ip ${NC}" ;;
|
echo -e "${yellow}Warp IP :${NC} ${cyan}$ip ${NC}" ;;
|
||||||
|
|
||||||
8)
|
6)
|
||||||
old_ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me)
|
old_ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me)
|
||||||
echo "Current IP address: $old_ip"
|
echo "Current IP address: $old_ip"
|
||||||
echo "Restarting $service_name..."
|
echo "Restarting $service_name..."
|
||||||
|
|||||||
Reference in New Issue
Block a user