#!/bin/bash source /etc/hysteria/core/scripts/utils.sh source /etc/hysteria/core/scripts/path.sh # OPTION HANDLERS (ONLY NEEDED ONE) hysteria2_install_handler() { if systemctl is-active --quiet hysteria-server.service; then echo "The hysteria-server.service is currently active." echo "If you need to update the core, please use the 'Update Core' option." return fi while true; do read -p "Enter the new port number you want to use: " port if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then echo "Invalid port number. Please enter a number between 1 and 65535." else break fi done python3 $CLI_PATH install-hysteria2 --port "$port" } hysteria2_add_user_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then if python3 $CLI_PATH get-user --username "$username" > /dev/null 2>&1; then echo -e "${red}Error:${NC} Username already exists. Please choose another username." else break fi else echo -e "${red}Error:${NC} Username can only contain letters and numbers." fi done read -p "Enter the traffic limit (in GB): " traffic_limit_GB read -p "Enter the expiration days: " expiration_days password=$(pwgen -s 32 1) creation_date=$(date +%Y-%m-%d) python3 $CLI_PATH add-user --username "$username" --traffic-limit "$traffic_limit_GB" --expiration-days "$expiration_days" --password "$password" --creation-date "$creation_date" } hysteria2_edit_user() { # Function to prompt for user input with validation prompt_for_input() { local prompt_message="$1" local validation_regex="$2" local default_value="$3" local input_variable_name="$4" while true; do read -p "$prompt_message" input if [[ -z "$input" ]]; then input="$default_value" fi if [[ "$input" =~ $validation_regex ]]; then eval "$input_variable_name='$input'" break else echo -e "${red}Error:${NC} Invalid input. Please try again." fi done } # Prompt for username prompt_for_input "Enter the username you want to edit: " '^[a-zA-Z0-9]+$' '' username # Check if user exists if ! python3 $CLI_PATH get-user --username "$username" > /dev/null 2>&1; then echo -e "${red}Error:${NC} User '$username' not found." return 1 fi # Prompt for new username prompt_for_input "Enter the new username (leave empty to keep the current username): " '^[a-zA-Z0-9]*$' '' new_username # Prompt for new traffic limit prompt_for_input "Enter the new traffic limit (in GB) (leave empty to keep the current limit): " '^[0-9]*$' '' new_traffic_limit_GB # Prompt for new expiration days prompt_for_input "Enter the new expiration days (leave empty to keep the current expiration days): " '^[0-9]*$' '' new_expiration_days # Determine if we need to renew password while true; do read -p "Do you want to generate a new password? (y/n): " renew_password case "$renew_password" in y|Y) renew_password=true; break ;; n|N) renew_password=false; break ;; *) echo -e "${red}Error:${NC} Please answer 'y' or 'n'." ;; esac done # Determine if we need to renew creation date while true; do read -p "Do you want to generate a new creation date? (y/n): " renew_creation_date case "$renew_creation_date" in y|Y) renew_creation_date=true; break ;; n|N) renew_creation_date=false; break ;; *) echo -e "${red}Error:${NC} Please answer 'y' or 'n'." ;; esac done # Determine if user should be blocked while true; do read -p "Do you want to block the user? (y/n): " block_user case "$block_user" in y|Y) blocked=true; break ;; n|N) blocked=false; break ;; *) echo -e "${red}Error:${NC} Please answer 'y' or 'n'." ;; esac done # Construct the arguments for the edit-user command args=() if [[ -n "$new_username" ]]; then args+=("--new-username" "$new_username"); fi if [[ -n "$new_traffic_limit_GB" ]]; then args+=("--new-traffic-limit" "$new_traffic_limit_GB"); fi if [[ -n "$new_expiration_days" ]]; then args+=("--new-expiration-days" "$new_expiration_days"); fi if [[ "$renew_password" == "true" ]]; then args+=("--renew-password"); fi if [[ "$renew_creation_date" == "true" ]]; then args+=("--renew-creation-date"); fi if [[ "$blocked" == "true" ]]; then args+=("--blocked"); fi # Call the edit-user script with the constructed arguments python3 $CLI_PATH edit-user --username "$username" "${args[@]}" } hysteria2_remove_user_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then break else echo -e "${red}Error:${NC} Username can only contain letters and numbers." fi done python3 $CLI_PATH remove-user --username "$username" } hysteria2_get_user_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then break else echo -e "${red}Error:${NC} Username can only contain letters and numbers." fi done # Run the command and suppress error output if ! python3 "$CLI_PATH" get-user --username "$username" 2>/dev/null; then echo -e "${red}Error:${NC} User '$username' not found." return 1 fi } hysteria2_list_users_handler() { users_json=$(python3 $CLI_PATH list-users 2>/dev/null) if [ $? -ne 0 ] || [ -z "$users_json" ]; then echo -e "${red}Error:${NC} Failed to list users." return 1 fi # Extract keys (usernames) from JSON users_keys=$(echo "$users_json" | jq -r 'keys[]') if [ -z "$users_keys" ]; then echo -e "${red}Error:${NC} No users found." return 1 fi # Print headers printf "%-20s %-20s %-15s %-20s %-30s %-10s\n" "Username" "Traffic Limit (GB)" "Expiration (Days)" "Creation Date" "Password" "Blocked" # Print user details for key in $users_keys; do echo "$users_json" | jq -r --arg key "$key" ' "\($key) \(.[$key].max_download_bytes / 1073741824) \(.[$key].expiration_days) \(.[$key].account_creation_date) \(.[$key].password) \(.[$key].blocked)"' | \ while IFS= read -r line; do IFS=' ' read -r username traffic_limit expiration_date creation_date password blocked <<< "$line" printf "%-20s %-20s %-15s %-20s %-30s %-10s\n" "$username" "$traffic_limit" "$expiration_date" "$creation_date" "$password" "$blocked" done done } hysteria2_reset_user_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then break else echo -e "${red}Error:${NC} Username can only contain letters and numbers." fi done python3 $CLI_PATH reset-user --username "$username" } hysteria2_show_user_uri_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then break else echo -e "${red}Error:${NC} Username can only contain letters and numbers." fi done python3 $CLI_PATH show-user-uri --username "$username" -a -qr } hysteria2_change_port_handler() { while true; do read -p "Enter the new port number you want to use: " port if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then echo "Invalid port number. Please enter a number between 1 and 65535." else break fi done python3 $CLI_PATH change-hysteria2-port --port "$port" } hysteria_upgrade(){ bash <(curl https://raw.githubusercontent.com/ReturnFI/Hysteria2/main/upgrade.sh) } warp_configure_handler() { local service_name="wg-quick@wgcf.service" if systemctl is-active --quiet "$service_name"; then echo "Configure WARP Options:" echo "1. Use WARP for all traffic" echo "2. Use WARP for popular sites" echo "3. Use WARP for domestic sites" echo "4. Block adult content" echo "5. WARP (Plus) Profile" echo "6. WARP (Normal) Profile" echo "7. WARP Status Profile" echo "8. Change IP address" echo "0. Cancel" read -p "Select an option: " option case $option in 1) python3 $CLI_PATH configure-warp --all ;; 2) python3 $CLI_PATH configure-warp --popular-sites ;; 3) python3 $CLI_PATH configure-warp --domestic-sites ;; 4) python3 $CLI_PATH configure-warp --block-adult-sites ;; 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) cd /etc/warp/ && wgcf status echo echo -e "${yellow}Warp IP :${NC} ${cyan}$ip ${NC}" ;; 8) old_ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me) echo "Current IP address: $old_ip" echo "Restarting $service_name..." systemctl restart "$service_name" sleep 5 new_ip=$(curl -s --interface wgcf --connect-timeout 0.5 http://v4.ident.me) echo "New IP address: $new_ip" ;; 0) echo "WARP configuration canceled." ;; *) echo "Invalid option. Please try again." ;; esac else echo "$service_name is not active. Please start the service before configuring WARP." fi } telegram_bot_handler() { while true; do echo -e "${cyan}1.${NC} Start Telegram bot service" echo -e "${red}2.${NC} Stop Telegram bot service" echo "0. Back" read -p "Choose an option: " option case $option in 1) if systemctl is-active --quiet hysteria-bot.service; then echo "The hysteria-bot.service is already active." else while true; do read -e -p "Enter the Telegram bot token: " token if [ -z "$token" ]; then echo "Token cannot be empty. Please try again." else break fi done while true; do read -e -p "Enter the admin IDs (comma-separated): " admin_ids if [[ ! "$admin_ids" =~ ^[0-9,]+$ ]]; then echo "Admin IDs can only contain numbers and commas. Please try again." elif [ -z "$admin_ids" ]; then echo "Admin IDs cannot be empty. Please try again." else break fi done python3 $CLI_PATH telegram -a start -t "$token" -aid "$admin_ids" fi ;; 2) python3 $CLI_PATH telegram -a stop ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } singbox_handler() { while true; do echo -e "${cyan}1.${NC} Start Singbox service" echo -e "${red}2.${NC} Stop Singbox service" echo "0. Back" read -p "Choose an option: " option case $option in 1) if systemctl is-active --quiet singbox.service; then echo "The singbox.service is already active." else while true; do read -e -p "Enter the domain name for the SSL certificate: " domain if [ -z "$domain" ]; then echo "Domain name cannot be empty. Please try again." else break fi done while true; do read -e -p "Enter the port number for the service: " port if [ -z "$port" ]; then echo "Port number cannot be empty. Please try again." elif ! [[ "$port" =~ ^[0-9]+$ ]]; then echo "Port must be a number. Please try again." else break fi done python3 $CLI_PATH singbox -a start -d "$domain" -p "$port" fi ;; 2) if ! systemctl is-active --quiet singbox.service; then echo "The singbox.service is already inactive." else python3 $CLI_PATH singbox -a stop fi ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } # Function to display the main menu display_main_menu() { clear tput setaf 7 ; tput setab 4 ; tput bold ; printf '%40s%s%-12s\n' "◇───────────ㅤ🚀ㅤWelcome To Hysteria2 Managementㅤ🚀ㅤ───────────◇" ; tput sgr0 echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${green}• OS: ${NC}$OS ${green}• ARCH: ${NC}$ARCH" echo -e "${green}• ISP: ${NC}$ISP ${green}• CPU: ${NC}$CPU" echo -e "${green}• IP: ${NC}$IP ${green}• RAM: ${NC}$RAM" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" check_version echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${yellow} ☼ Main Menu ☼ ${NC}" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${green}[1] ${NC}↝ Hysteria2 Menu" echo -e "${cyan}[2] ${NC}↝ Advance Menu" echo -e "${cyan}[3] ${NC}↝ Update Panel" echo -e "${red}[0] ${NC}↝ Exit" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -ne "${yellow}➜ Enter your option: ${NC}" } # Function to handle main menu options main_menu() { clear local choice while true; do get_system_info display_main_menu read -r choice case $choice in 1) hysteria2_menu ;; 2) advance_menu ;; 3) hysteria_upgrade ;; 0) exit 0 ;; *) echo "Invalid option. Please try again." ;; esac echo read -rp "Press Enter to continue..." done } # Function to display the Hysteria2 menu display_hysteria2_menu() { clear echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${yellow} ☼ Hysteria2 Menu ☼ ${NC}" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${green}[1] ${NC}↝ Install and Configure Hysteria2" echo -e "${cyan}[2] ${NC}↝ Add User" echo -e "${cyan}[3] ${NC}↝ Edit User" echo -e "${cyan}[4] ${NC}↝ Reset User" echo -e "${cyan}[5] ${NC}↝ Remove User" echo -e "${cyan}[6] ${NC}↝ Get User" echo -e "${cyan}[7] ${NC}↝ List Users" echo -e "${cyan}[8] ${NC}↝ Check Traffic Status" echo -e "${cyan}[9] ${NC}↝ Show User URI" echo -e "${red}[0] ${NC}↝ Back to Main Menu" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -ne "${yellow}➜ Enter your option: ${NC}" } # Function to handle Hysteria2 menu options hysteria2_menu() { clear local choice while true; do get_system_info display_hysteria2_menu read -r choice case $choice in 1) hysteria2_install_handler ;; 2) hysteria2_add_user_handler ;; 3) hysteria2_edit_user ;; 4) hysteria2_reset_user_handler ;; 5) hysteria2_remove_user_handler ;; 6) hysteria2_get_user_handler ;; 7) hysteria2_list_users_handler ;; 8) python3 $CLI_PATH traffic-status ;; 9) hysteria2_show_user_uri_handler ;; 0) return ;; *) echo "Invalid option. Please try again." ;; esac echo read -rp "Press Enter to continue..." done } # Function to get Advance menu display_advance_menu() { clear echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${yellow} ☼ Advance Menu ☼ ${NC}" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${green}[1] ${NC}↝ Install TCP Brutal" echo -e "${green}[2] ${NC}↝ Install WARP" echo -e "${cyan}[3] ${NC}↝ Configure WARP" echo -e "${red}[4] ${NC}↝ Uninstall WARP" echo -e "${green}[5] ${NC}↝ Telegram Bot" echo -e "${green}[6] ${NC}↝ SingBox SubLink" echo -e "${cyan}[7] ${NC}↝ Change Port Hysteria2" echo -e "${cyan}[8] ${NC}↝ Update Core Hysteria2" echo -e "${red}[9] ${NC}↝ Uninstall Hysteria2" echo -e "${red}[0] ${NC}↝ Back to Main Menu" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -ne "${yellow}➜ Enter your option: ${NC}" } # Function to handle Advance menu options advance_menu() { clear local choice while true; do display_advance_menu read -r choice case $choice in 1) python3 $CLI_PATH install-tcp-brutal ;; 2) python3 $CLI_PATH install-warp ;; 3) warp_configure_handler ;; 4) python3 $CLI_PATH uninstall-warp ;; 5) telegram_bot_handler ;; 6) singbox_handler ;; 7) hysteria2_change_port_handler ;; 8) python3 $CLI_PATH update-hysteria2 ;; 9) python3 $CLI_PATH uninstall-hysteria2 ;; 0) return ;; *) echo "Invalid option. Please try again." ;; esac echo read -rp "Press Enter to continue..." done } # Main function to run the script define_colors main_menu