#!/bin/bash source /etc/hysteria/core/scripts/utils.sh source /etc/hysteria/core/scripts/path.sh source /etc/hysteria/core/scripts/services_status.sh >/dev/null 2>&1 check_services() { for service in "${services[@]}"; do service_base_name=$(basename "$service" .service) display_name=$(echo "$service_base_name" | sed -E 's/([^-]+)-?/\u\1/g') if systemctl is-active --quiet "$service"; then echo -e "${NC}${display_name}:${green} Active${NC}" else echo -e "${NC}${display_name}:${red} Inactive${NC}" fi done } 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 SNI (default: bts.com): " sni sni=${sni:-bts.com} read -p "Enter the 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" --sni "$sni" cat < /etc/hysteria/.configs.env SNI=$sni EOF python3 $CLI_PATH ip-address } hysteria2_add_user_handler() { while true; do read -p "Enter the username: " username if [[ "$username" =~ ^[a-zA-Z0-9_-]+$ ]]; then if [[ -n $(python3 $CLI_PATH get-user -u "$username" 2>/dev/null) ]]; 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 local unlimited_arg="" while true; do read -p "Exempt user from IP limit checks (unlimited IP)? (y/n) [n]: " unlimited_choice case "$unlimited_choice" in y|Y) unlimited_arg="--unlimited"; break ;; n|N|"") break ;; *) echo -e "${red}Error:${NC} Please answer 'y' or 'n'." ;; esac done creation_date=$(date +%Y-%m-%d) python3 $CLI_PATH add-user --username "$username" --traffic-limit "$traffic_limit_GB" --expiration-days "$expiration_days" --creation-date "$creation_date" $unlimited_arg } hysteria2_edit_user_handler() { 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_input "Enter the username you want to edit: " '^[a-zA-Z0-9_-]+$' '' username user_exists_output=$(python3 $CLI_PATH get-user -u "$username" 2>&1) if [[ -z "$user_exists_output" ]]; then echo -e "${red}Error:${NC} User '$username' not found or an error occurred." return 1 fi prompt_for_input "Enter the new username (leave empty to keep the current username): " '^[a-zA-Z0-9_-]*$' '' new_username 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_input "Enter the new expiration days (leave empty to keep the current expiration days): " '^[0-9]*$' '' new_expiration_days while true; do read -p "Do you want to generate a new password? (y/n) [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 while true; do read -p "Do you want to generate a new creation date? (y/n) [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 local blocked_arg="" while true; do read -p "Change user block status? ([b]lock/[u]nblock/[s]kip) [s]: " block_user case "$block_user" in b|B) blocked_arg="--blocked"; break ;; u|U) blocked_arg="--unblocked"; break ;; s|S|"") break ;; *) echo -e "${red}Error:${NC} Please answer 'b', 'u', or 's'." ;; esac done local ip_limit_arg="" while true; do read -p "Change IP limit status? ([u]nlimited/[l]imited/[s]kip) [s]: " ip_limit_status case "$ip_limit_status" in u|U) ip_limit_arg="--unlimited-ip"; break ;; l|L) ip_limit_arg="--limited-ip"; break ;; s|S|"") break ;; *) echo -e "${red}Error:${NC} Please answer 'u', 'l', or 's'." ;; esac done 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 [[ -n "$blocked_arg" ]]; then args+=("$blocked_arg"); fi if [[ -n "$ip_limit_arg" ]]; then args+=("$ip_limit_arg"); fi 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" } 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 user_data=$(python3 "$CLI_PATH" get-user --username "$username" 2>/dev/null) if [[ $exit_code -ne 0 || -z "$user_data" ]]; then echo -e "${red}Error:${NC} User '$username' not found or invalid response." return 1 fi password=$(echo "$user_data" | jq -r '.password // "N/A"') max_download_bytes=$(echo "$user_data" | jq -r '.max_download_bytes // 0') upload_bytes=$(echo "$user_data" | jq -r '.upload_bytes // 0') download_bytes=$(echo "$user_data" | jq -r '.download_bytes // 0') account_creation_date=$(echo "$user_data" | jq -r '.account_creation_date // "N/A"') expiration_days=$(echo "$user_data" | jq -r '.expiration_days // 0') blocked=$(echo "$user_data" | jq -r '.blocked // false') status=$(echo "$user_data" | jq -r '.status // "N/A"') total_usage=$((upload_bytes + download_bytes)) max_download_gb=$(echo "scale=2; $max_download_bytes / 1073741824" | bc) upload_gb=$(echo "scale=2; $upload_bytes / 1073741824" | bc) download_gb=$(echo "scale=2; $download_bytes / 1073741824" | bc) total_usage_gb=$(echo "scale=2; $total_usage / 1073741824" | bc) expiration_date=$(date -d "$account_creation_date + $expiration_days days" +"%Y-%m-%d") current_date=$(date +"%Y-%m-%d") used_days=$(( ( $(date -d "$current_date" +%s) - $(date -d "$account_creation_date" +%s) ) / 86400 )) if [[ $used_days -gt $expiration_days ]]; then used_days=$expiration_days fi echo -e "${green}User Details:${NC}" echo -e "Username: $username" echo -e "Password: $password" echo -e "Total Traffic: $max_download_gb GB" echo -e "Total Usage: $total_usage_gb GB" echo -e "Time Expiration: $expiration_date ($used_days/$expiration_days Days)" echo -e "Blocked: $blocked" echo -e "Status: $status" } 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 user_count=$(echo "$users_json" | jq 'length') if [ "$user_count" -eq 0 ]; then echo -e "${red}Error:${NC} No users found." return 1 fi printf "%-20s %-20s %-15s %-20s %-30s %-10s %-15s %-15s %-15s %-10s\n" \ "Username" "Traffic(GB)" "Expiry(Days)" "Created" "Password" "Blocked" "Status" "Down(MB)" "Up(MB)" echo "$users_json" | jq -r '.[] | [.username, (if .max_download_bytes == 0 then "Unlimited" else (.max_download_bytes / 1073741824 | tostring) end), (if .expiration_days == 0 then "Never" else (.expiration_days | tostring) end), (.account_creation_date // "N/A"), .password, .blocked, .status, ((.download_bytes // 0) / 1048576 | floor), ((.upload_bytes // 0) / 1048576 | floor), .online_count] | @tsv' | \ while IFS=$'\t' read -r username traffic expiry created password blocked status down up online; do printf "%-20s %-20s %-15s %-20s %-30s %-10s %-15s %-15s %-15s %-10s\n" \ "$username" "$traffic" "$expiry" "$created" "$password" "$blocked" "$status" "$down" "$up" 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() { check_service_active() { systemctl is-active --quiet "$1" } 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 flags="" if check_service_active "hysteria-singbox.service"; then flags+=" -s" fi if check_service_active "hysteria-normal-sub.service"; then flags+=" -n" fi if [[ -n "$flags" ]]; then python3 $CLI_PATH show-user-uri -u "$username" -a -qr $flags else python3 $CLI_PATH show-user-uri -u "$username" -a -qr fi } 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" } hysteria2_change_sni_handler() { while true; do read -p "Enter the new SNI (e.g., example.com): " sni if [[ "$sni" =~ ^[a-zA-Z0-9.]+$ ]]; then break else echo -e "${red}Error:${NC} SNI can only contain letters, numbers, and dots." fi done python3 $CLI_PATH change-hysteria2-sni --sni "$sni" if systemctl is-active --quiet hysteria-singbox.service; then systemctl restart hysteria-singbox.service fi } edit_ips() { while true; do echo "======================================" echo " IP/Domain Address Manager " echo "======================================" echo "1. Change IPv4 or Domain" echo "2. Change IPv6 or Domain" echo "0. Back" echo "======================================" read -p "Enter your choice [0-2]: " choice case $choice in 1) read -p "Enter the new IPv4 address or domain: " new_ip4_or_domain if [[ $new_ip4_or_domain =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then if [[ $(echo "$new_ip4_or_domain" | awk -F. '{for (i=1;i<=NF;i++) if ($i>255) exit 1}') ]]; then echo "Error: Invalid IPv4 address. Values must be between 0 and 255." else python3 "$CLI_PATH" ip-address --edit -4 "$new_ip4_or_domain" echo "IPv4 address has been updated to $new_ip4_or_domain." fi elif [[ $new_ip4_or_domain =~ ^[a-zA-Z0-9.-]+$ ]] && [[ ! $new_ip4_or_domain =~ [/:] ]]; then python3 "$CLI_PATH" ip-address --edit -4 "$new_ip4_or_domain" echo "Domain has been updated to $new_ip4_or_domain." else echo "Error: Invalid IPv4 or domain format." fi break ;; 2) read -p "Enter the new IPv6 address or domain: " new_ip6_or_domain if [[ $new_ip6_or_domain =~ ^(([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$ ]]; then python3 "$CLI_PATH" ip-address --edit -6 "$new_ip6_or_domain" echo "IPv6 address has been updated to $new_ip6_or_domain." elif [[ $new_ip6_or_domain =~ ^[a-zA-Z0-9.-]+$ ]] && [[ ! $new_ip6_or_domain =~ [/:] ]]; then python3 "$CLI_PATH" ip-address --edit -6 "$new_ip6_or_domain" echo "Domain has been updated to $new_ip6_or_domain." else echo "Error: Invalid IPv6 or domain format." fi break ;; 0) break ;; *) echo "Invalid option. Please try again." break ;; esac echo "======================================" read -p "Press Enter to continue..." done } hysteria_upgrade(){ bash <(curl https://raw.githubusercontent.com/ReturnFI/Blitz/main/upgrade.sh) } warp_configure_handler() { local service_name="wg-quick@wgcf.service" if systemctl is-active --quiet "$service_name"; then echo -e "${cyan}=== WARP Status ===${NC}" status_json=$(python3 $CLI_PATH warp-status) all_traffic=$(echo "$status_json" | grep -o '"all_traffic_via_warp": *[^,}]*' | cut -d':' -f2 | tr -d ' "') popular_sites=$(echo "$status_json" | grep -o '"popular_sites_via_warp": *[^,}]*' | cut -d':' -f2 | tr -d ' "') domestic_sites_via_warp=$(echo "$status_json" | grep -o '"domestic_sites_via_warp": *[^,}]*' | cut -d':' -f2 | tr -d ' "') block_adult=$(echo "$status_json" | grep -o '"block_adult_content": *[^,}]*' | cut -d':' -f2 | tr -d ' "') display_status() { local label="$1" local status_val="$2" if [ "$status_val" = "true" ]; then echo -e " ${green}✓${NC} $label: ${green}Enabled${NC}" else echo -e " ${red}✗${NC} $label: ${red}Disabled${NC}" fi } display_status "All Traffic via WARP" "$all_traffic" display_status "Popular Sites via WARP" "$popular_sites" display_status "Domestic Sites via WARP" "$domestic_sites_via_warp" display_status "Block Adult Content" "$block_adult" echo -e "${cyan}==================${NC}" echo echo "Configure WARP Options (Toggle):" echo "1. All traffic via WARP" echo "2. Popular sites via WARP" echo "3. Domestic sites (WARP/Reject)" echo "4. Block adult content" echo "5. WARP Status Profile (IP etc.)" echo "6. Change WARP IP address" echo "7. Switch to WARP Plus" echo "8. Switch to Normal WARP" echo "0. Cancel" read -p "Select an option to toggle: " option case $option in 1) target_state=$([ "$all_traffic" = "true" ] && echo "off" || echo "on") python3 $CLI_PATH configure-warp --set-all "$target_state" ;; 2) target_state=$([ "$popular_sites" = "true" ] && echo "off" || echo "on") python3 $CLI_PATH configure-warp --set-popular-sites "$target_state" ;; 3) target_state=$([ "$domestic_sites_via_warp" = "true" ] && echo "off" || echo "on") python3 $CLI_PATH configure-warp --set-domestic-sites "$target_state" ;; 4) target_state=$([ "$block_adult" = "true" ] && echo "off" || echo "on") python3 $CLI_PATH configure-warp --set-block-adult-sites "$target_state" ;; 5) current_ip=$(python3 $CLI_PATH warp-status | grep -o '"ip": *"[^"]*"' | cut -d':' -f2- | tr -d '" ') if [ -z "$current_ip" ]; then current_ip=$(curl -s --interface wgcf --connect-timeout 1 http://v4.ident.me || echo "N/A") fi cd /etc/warp/ && wgcf status echo echo -e "${yellow}Warp IP:${NC} ${cyan}${current_ip}${NC}" ;; 6) old_ip=$(curl -s --interface wgcf --connect-timeout 1 http://v4.ident.me || echo "N/A") echo -e "${yellow}Current IP:${NC} ${cyan}$old_ip${NC}" echo "Restarting $service_name to attempt IP change..." systemctl restart "$service_name" echo -n "Waiting for service to restart" for i in {1..5}; do echo -n "." sleep 1 done echo new_ip=$(curl -s --interface wgcf --connect-timeout 1 http://v4.ident.me || echo "N/A") echo -e "${yellow}New IP:${NC} ${green}$new_ip${NC}" if [ "$old_ip" != "N/A" ] && [ "$new_ip" != "N/A" ] && [ "$old_ip" != "$new_ip" ]; then echo -e "${green}✓ IP address changed successfully${NC}" elif [ "$old_ip" = "$new_ip" ] && [ "$old_ip" != "N/A" ]; then echo -e "${yellow}⚠ IP address remained the same${NC}" else echo -e "${red}✗ Could not verify IP change.${NC}" fi ;; 7) echo -e "${yellow}Switching to WARP Plus...${NC}" read -p "Enter your WARP Plus license key: " warp_key if [ -z "$warp_key" ]; then echo -e "${red}Error: WARP Plus key is required.${NC}" else echo "Stopping WARP service..." systemctl stop "$service_name" 2>/dev/null cd /etc/warp/ || { echo -e "${red}Failed to change directory to /etc/warp/${NC}"; return 1; } echo "Updating WARP Plus configuration..." WGCF_LICENSE_KEY="$warp_key" wgcf update if [ $? -eq 0 ]; then echo "Starting WARP service..." systemctl start "$service_name" echo -e "${green}✓ Successfully switched to WARP Plus${NC}" python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1 else echo -e "${red}✗ Failed to update WARP Plus configuration${NC}" systemctl start "$service_name" fi fi ;; 8) echo -e "${yellow}Switching to Normal WARP...${NC}" echo "This will create a new WARP account. Continue? (y/N)" read -p "" confirm if [[ "$confirm" =~ ^[Yy]$ ]]; then echo "Stopping WARP service..." systemctl stop "$service_name" 2>/dev/null cd /etc/warp/ || { echo -e "${red}Failed to change directory to /etc/warp/${NC}"; return 1; } echo "Creating new WARP account..." rm -f wgcf-account.toml yes | wgcf register if [ $? -eq 0 ]; then echo "Starting WARP service..." systemctl start "$service_name" echo -e "${green}✓ Successfully switched to Normal WARP with new account${NC}" python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1 else echo -e "${red}✗ Failed to register new WARP account${NC}" systemctl start "$service_name" fi else echo -e "${yellow}Operation canceled${NC}" fi ;; 0) echo "WARP configuration canceled." ;; *) echo -e "${red}Invalid option. Please try again.${NC}" ;; esac else echo -e "${red}$service_name is not active. Please start the service before configuring WARP.${NC}" 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-telegram-bot.service; then echo "The hysteria-telegram-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() { echo -e "${red} Deprecated${NC}" } normalsub_handler() { while true; do echo -e "${cyan}1.${NC} Start Normal-Sub service" echo -e "${red}2.${NC} Stop Normal-Sub service" echo -e "${yellow}3.${NC} Change SUBPATH" echo "0. Back" read -p "Choose an option: " option case $option in 1) if systemctl is-active --quiet hysteria-normal-sub.service; then echo "The hysteria-normal-sub.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 normal-sub -a start -d "$domain" -p "$port" fi ;; 2) if ! systemctl is-active --quiet hysteria-normal-sub.service; then echo "The hysteria-normal-sub.service is already inactive." else python3 $CLI_PATH normal-sub -a stop fi ;; 3) if ! systemctl is-active --quiet hysteria-normal-sub.service; then echo "Error: The hysteria-normal-sub.service is not active. Start the service first." continue fi while true; do read -e -p "Enter new SUBPATH (Must include Uppercase, Lowercase, and Numbers): " subpath if [[ -z "$subpath" ]]; then echo "Error: SUBPATH cannot be empty. Please try again." elif ! [[ "$subpath" =~ [A-Z] ]] || ! [[ "$subpath" =~ [a-z] ]] || ! [[ "$subpath" =~ [0-9] ]]; then echo "Error: SUBPATH must include at least one uppercase letter, one lowercase letter, and one number." else python3 $CLI_PATH normal-sub -a edit_subpath -sp "$subpath" break fi done ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } webpanel_handler() { service_status=$(python3 "$CLI_PATH" get-webpanel-services-status) echo -e "${cyan}Services Status:${NC}" echo "$service_status" echo "" while true; do echo -e "${cyan}1.${NC} Start WebPanel service" echo -e "${red}2.${NC} Stop WebPanel service" echo -e "${cyan}3.${NC} Get WebPanel URL" echo -e "${cyan}4.${NC} Show API Token" echo -e "${yellow}5.${NC} Reset WebPanel Credentials" echo -e "${yellow}6.${NC} Change Domain/Port" echo -e "${yellow}7.${NC} Change Root Path" echo -e "${yellow}8.${NC} Change Session Expiration" echo "0. Back" read -p "Choose an option: " option case $option in 1) if systemctl is-active --quiet hysteria-webpanel.service; then echo "The hysteria-webpanel.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 while true; do read -e -p "Enter the admin username: " admin_username if [ -z "$admin_username" ]; then echo "Admin username cannot be empty. Please try again." else break fi done while true; do read -sp "Enter the admin password: " admin_password echo "" if [ -z "$admin_password" ]; then echo "Admin password cannot be empty. Please try again." continue fi local check_password read -sp "Enter the admin password again: " check_password echo "" if [ -z "$check_password" ]; then echo "Admin password cannot be empty. Please try again." continue fi if [ "$check_password" == "$admin_password" ]; then echo "Password is set!" break fi echo "Passwords did NOT match. Please try again." done python3 $CLI_PATH webpanel -a start -d "$domain" -p "$port" -au "$admin_username" -ap "$admin_password" fi ;; 2) if ! systemctl is-active --quiet hysteria-webpanel.service; then echo "The hysteria-webpanel.service is already inactive." else python3 $CLI_PATH webpanel -a stop fi ;; 3) url=$(python3 $CLI_PATH get-webpanel-url) echo "-------------------------------" echo "$url" echo "-------------------------------" ;; 4) api_token=$(python3 $CLI_PATH get-webpanel-api-token) echo "-------------------------------" echo "$api_token" echo "-------------------------------" ;; 5) if ! systemctl is-active --quiet hysteria-webpanel.service; then echo -e "${red}WebPanel service is not running. Cannot reset credentials.${NC}" else read -e -p "Enter new admin username (leave blank to keep current): " new_username read -e -p "Enter new admin password (leave blank to keep current): " new_password echo if [ -z "$new_username" ] && [ -z "$new_password" ]; then echo -e "${yellow}No changes specified. Aborting.${NC}" else local cmd_args=("-u" "$new_username") if [ -n "$new_password" ]; then cmd_args+=("-p" "$new_password") fi if [ -z "$new_username" ]; then cmd_args=() if [ -n "$new_password" ]; then cmd_args+=("-p" "$new_password") fi fi echo "Attempting to reset credentials..." python3 "$CLI_PATH" reset-webpanel-creds "${cmd_args[@]}" fi fi ;; 6) if ! systemctl is-active --quiet hysteria-webpanel.service; then echo -e "${red}WebPanel service is not running. Cannot perform this action.${NC}" else read -e -p "Enter new domain (leave blank to keep current): " new_domain read -e -p "Enter new port (leave blank to keep current): " new_port if [ -z "$new_domain" ] && [ -z "$new_port" ]; then echo -e "${yellow}No changes specified. Aborting.${NC}" else local cmd_args=() if [ -n "$new_domain" ]; then cmd_args+=("--domain" "$new_domain") fi if [ -n "$new_port" ]; then cmd_args+=("--port" "$new_port") fi echo "Attempting to change domain/port..." python3 "$CLI_PATH" change-webpanel-domain-port "${cmd_args[@]}" fi fi ;; 7) if ! systemctl is-active --quiet hysteria-webpanel.service; then echo -e "${red}WebPanel service is not running. Cannot perform this action.${NC}" else read -e -p "Enter new root path (leave blank for random): " new_root_path local cmd_args=() if [ -n "$new_root_path" ]; then cmd_args+=("--path" "$new_root_path") fi echo "Attempting to change root path..." python3 "$CLI_PATH" change-webpanel-root "${cmd_args[@]}" fi ;; 8) if ! systemctl is-active --quiet hysteria-webpanel.service; then echo -e "${red}WebPanel service is not running. Cannot perform this action.${NC}" else while true; do read -e -p "Enter new session expiration in minutes: " new_minutes if [[ "$new_minutes" =~ ^[0-9]+$ ]]; then break else echo -e "${red}Error:${NC} Please enter a valid number." fi done echo "Attempting to change session expiration..." python3 "$CLI_PATH" change-webpanel-exp --minutes "$new_minutes" fi ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } obfs_handler() { while true; do echo -e "${cyan}1.${NC} Remove Obfs" echo -e "${red}2.${NC} Generating new Obfs" echo "0. Back" read -p "Choose an option: " option case $option in 1) python3 $CLI_PATH manage_obfs -r ;; 2) status=$(python3 $CLI_PATH masquerade -s) if [[ "$status" == "Enabled" ]]; then echo -e "${red}Error:${NC} Cannot use Obfs when masquerade is enabled." else python3 $CLI_PATH manage_obfs -g fi ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } geo_update_handler() { echo "Configure Geo Update Options:" echo "1. Update Iran Geo Files" echo "2. Update China Geo Files" echo "3. Update Russia Geo Files" echo "4. Check Current Geo Files" echo "0. Cancel" read -p "Select an option: " option case $option in 1) echo "Updating Iran Geo Files..." python3 $CLI_PATH update-geo --country iran ;; 2) echo "Updating China Geo Files..." python3 $CLI_PATH update-geo --country china ;; 3) echo "Updating Russia Geo Files..." python3 $CLI_PATH update-geo --country russia ;; 4) echo "Current Geo Files Information:" echo "--------------------------" if [ -f "/etc/hysteria/geosite.dat" ]; then echo "GeoSite File:" ls -lh /etc/hysteria/geosite.dat echo "Last modified: $(stat -c %y /etc/hysteria/geosite.dat)" else echo "GeoSite file not found!" fi echo if [ -f "/etc/hysteria/geoip.dat" ]; then echo "GeoIP File:" ls -lh /etc/hysteria/geoip.dat echo "Last modified: $(stat -c %y /etc/hysteria/geoip.dat)" else echo "GeoIP file not found!" fi ;; 0) echo "Geo update configuration canceled." ;; *) echo "Invalid option. Please try again." ;; esac } masquerade_handler() { while true; do status=$(python3 $CLI_PATH masquerade -s) echo "--------------------------" if [ "$status" == "Enabled" ]; then echo -e "Masquerade Status: ${green}${status}${NC}" else echo -e "Masquerade Status: ${red}${status}${NC}" fi echo "--------------------------" echo -e "${cyan}1.${NC} Enable Masquerade" echo -e "${cyan}2.${NC} Remove Masquerade" echo "0. Back" read -p "Choose an option: " option case $option in 1) python3 $CLI_PATH masquerade -e ;; 2) python3 $CLI_PATH masquerade -r ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } ip_limit_handler() { while true; do echo -e "${cyan}1.${NC} Start IP Limiter Service" echo -e "${red}2.${NC} Stop IP Limiter Service" echo -e "${yellow}3.${NC} Change IP Limiter Configuration" echo "0. Back" read -p "Choose an option: " option case $option in 1) if systemctl is-active --quiet hysteria-ip-limit.service; then echo "The hysteria-ip-limit.service is already active." else while true; do read -e -p "Enter Block Duration (seconds, default: 60): " block_duration block_duration=${block_duration:-60} if ! [[ "$block_duration" =~ ^[0-9]+$ ]]; then echo "Invalid Block Duration. Please enter a number." else break fi done while true; do read -e -p "Enter Max IPs per User (default: 1): " max_ips max_ips=${max_ips:-1} if ! [[ "$max_ips" =~ ^[0-9]+$ ]]; then echo "Invalid Max IPs. Please enter a number." else break fi done python3 $CLI_PATH config-ip-limit --block-duration "$block_duration" --max-ips "$max_ips" python3 $CLI_PATH start-ip-limit fi ;; 2) if ! systemctl is-active --quiet hysteria-ip-limit.service; then echo "The hysteria-ip-limit.service is already inactive." else python3 $CLI_PATH stop-ip-limit fi ;; 3) block_duration="" max_ips="" updated=false while true; do read -e -p "Enter New Block Duration (seconds, current: $(grep '^BLOCK_DURATION=' /etc/hysteria/.configs.env | cut -d'=' -f2), leave empty to keep current): " input_block_duration if [[ -n "$input_block_duration" ]] && ! [[ "$input_block_duration" =~ ^[0-9]+$ ]]; then echo "Invalid Block Duration. Please enter a number or leave empty." else if [[ -n "$input_block_duration" ]]; then block_duration="$input_block_duration" updated=true fi break fi done while true; do read -e -p "Enter New Max IPs per User (current: $(grep '^MAX_IPS=' /etc/hysteria/.configs.env | cut -d'=' -f2), leave empty to keep current): " input_max_ips if [[ -n "$input_max_ips" ]] && ! [[ "$input_max_ips" =~ ^[0-9]+$ ]]; then echo "Invalid Max IPs. Please enter a number or leave empty." else if [[ -n "$input_max_ips" ]]; then max_ips="$input_max_ips" updated=true fi break fi done if [[ "$updated" == "true" ]]; then python3 $CLI_PATH config-ip-limit --block-duration "$block_duration" --max-ips "$max_ips" else echo "No changes to IP Limiter configuration were provided." fi ;; 0) break ;; *) echo "Invalid option. Please try again." ;; esac done } display_main_menu() { clear tput setaf 7 ; tput setab 4 ; tput bold echo -e "◇────────────────🚀 Welcome To Blitz Panel 🚀─────────────────◇" tput sgr0 echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" printf "\033[0;32m• OS: \033[0m%-25s \033[0;32m• ARCH: \033[0m%-25s\n" "$OS" "$ARCH" printf "\033[0;32m• ISP: \033[0m%-25s \033[0;32m• CPU: \033[0m%-25s\n" "$ISP" "$CPU" printf "\033[0;32m• IP: \033[0m%-25s \033[0;32m• RAM: \033[0m%-25s\n" "$IP" "$RAM" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" check_core_version check_version echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${yellow} ☼ Services Status ☼ ${NC}" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" check_services 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}" } 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 } display_hysteria2_menu() { clear echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -e "${yellow} ☼ Blitz 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}" } 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_handler ;; 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 } 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(${red}Deprecated${NC})" echo -e "${green}[7] ${NC}↝ Normal-SUB SubLink" echo -e "${green}[8] ${NC}↝ Web Panel" echo -e "${cyan}[9] ${NC}↝ Change Port Hysteria2" echo -e "${cyan}[10] ${NC}↝ Change SNI Hysteria2" echo -e "${cyan}[11] ${NC}↝ Manage OBFS" echo -e "${cyan}[12] ${NC}↝ Change IPs(4-6)" echo -e "${cyan}[13] ${NC}↝ Update geo Files" echo -e "${cyan}[14] ${NC}↝ Manage Masquerade" echo -e "${cyan}[15] ${NC}↝ Restart Hysteria2" echo -e "${cyan}[16] ${NC}↝ Update Core Hysteria2" echo -e "${cyan}[17] ${NC}↝ IP Limiter Menu" echo -e "${red}[18] ${NC}↝ Uninstall Hysteria2" echo -e "${red}[0] ${NC}↝ Back to Main Menu" echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}" echo -ne "${yellow}➜ Enter your option: ${NC}" } 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) normalsub_handler ;; 8) webpanel_handler ;; 9) hysteria2_change_port_handler ;; 10) hysteria2_change_sni_handler ;; 11) obfs_handler ;; 12) edit_ips ;; 13) geo_update_handler ;; 14) masquerade_handler ;; 15) python3 $CLI_PATH restart-hysteria2 ;; 16) python3 $CLI_PATH update-hysteria2 ;; 17) ip_limit_handler ;; 18) python3 $CLI_PATH uninstall-hysteria2 ;; 0) return ;; *) echo "Invalid option. Please try again." ;; esac echo read -rp "Press Enter to continue..." done } define_colors main_menu