@ -9,7 +9,12 @@
|
|||||||
```shell
|
```shell
|
||||||
bash <(curl https://raw.githubusercontent.com/ReturnFI/Hysteria2/main/menu.sh)
|
bash <(curl https://raw.githubusercontent.com/ReturnFI/Hysteria2/main/menu.sh)
|
||||||
```
|
```
|
||||||
بعد از نصب کافیه از دستور `hys2` برای اجرای اسکریپت Hysteria2 استفاده کنید و نیازی به اجرا دستور نصب نیست.
|
بعد از نصب کافیه از دستور `hys2` برای اجرای اسکریپت Hysteria2 استفاده کنید و نیازی به اجرا دوباره دستور نصب نیست.
|
||||||
|
|
||||||
|
### دستور آپدیت:
|
||||||
|
```shell
|
||||||
|
bash <(curl https://raw.githubusercontent.com/ReturnFI/Hysteria2/main/upgrade.sh)
|
||||||
|
```
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|||||||
@ -17,6 +17,10 @@ After installation, simply use the command `hys2` to run the Hysteria2 script.
|
|||||||
|
|
||||||
There is no need to execute the installation command again.
|
There is no need to execute the installation command again.
|
||||||
|
|
||||||
|
### Upgrade command :
|
||||||
|
```shell
|
||||||
|
bash <(curl https://raw.githubusercontent.com/ReturnFI/Hysteria2/main/upgrade.sh)
|
||||||
|
```
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|||||||
51
core/cli.py
51
core/cli.py
@ -12,7 +12,7 @@ import validator
|
|||||||
|
|
||||||
|
|
||||||
SCRIPT_DIR = '/etc/hysteria/core/scripts'
|
SCRIPT_DIR = '/etc/hysteria/core/scripts'
|
||||||
DEBUG = True
|
DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
class Command(Enum):
|
class Command(Enum):
|
||||||
@ -30,6 +30,7 @@ class Command(Enum):
|
|||||||
TRAFFIC_STATUS = 'traffic.py' # won't be call directly (it's a python module)
|
TRAFFIC_STATUS = 'traffic.py' # won't be call directly (it's a python module)
|
||||||
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.sh')
|
||||||
|
INSTALL_TELEGRAMBOT = os.path.join(SCRIPT_DIR, 'telegrambot', 'runbot.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.sh')
|
||||||
UNINSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'uninstall.sh')
|
UNINSTALL_WARP = os.path.join(SCRIPT_DIR, 'warp', 'uninstall.sh')
|
||||||
@ -42,11 +43,14 @@ def run_cmd(command: list[str]):
|
|||||||
Runs a command and returns the output.
|
Runs a command and returns the output.
|
||||||
Could raise subprocess.CalledProcessError
|
Could raise subprocess.CalledProcessError
|
||||||
'''
|
'''
|
||||||
if DEBUG and Command.GET_USER.value not in command and Command.LIST_USERS.value not in command:
|
|
||||||
|
# if the command is GET_USER or LIST_USERS we don't print the debug-command and just print the output
|
||||||
|
if DEBUG and not (Command.GET_USER.value in command or Command.LIST_USERS.value in command):
|
||||||
print(' '.join(command))
|
print(' '.join(command))
|
||||||
|
|
||||||
result = subprocess.check_output(command, shell=False)
|
result = subprocess.check_output(command, shell=False)
|
||||||
if DEBUG:
|
|
||||||
print(result.decode().strip())
|
print(result.decode().strip())
|
||||||
|
|
||||||
|
|
||||||
def generate_password() -> str:
|
def generate_password() -> str:
|
||||||
@ -174,17 +178,27 @@ def edit_user(username: str, new_username: str, new_traffic_limit: int, new_expi
|
|||||||
run_cmd(command_args)
|
run_cmd(command_args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ cli.command('remove-user')
|
@ cli.command('remove-user')
|
||||||
@ click.option('--username', '-u', required=True, help='Username for the user to remove', type=str)
|
@ click.option('--username', '-u', required=True, help='Username for the user to remove', type=str)
|
||||||
def remove_user(username: str):
|
def remove_user(username: str):
|
||||||
run_cmd(['bash', Command.REMOVE_USER.value, username])
|
run_cmd(['bash', Command.REMOVE_USER.value, username])
|
||||||
|
|
||||||
|
|
||||||
@ cli.command('show-user-uri')
|
@cli.command('show-user-uri')
|
||||||
@ click.option('--username', '-u', required=True, help='Username for the user to show the URI', type=str)
|
@click.option('--username', '-u', required=True, help='Username for the user to show the URI', type=str)
|
||||||
def show_user_uri(username: str):
|
@click.option('--qrcode', '-qr', is_flag=True, help='Generate QR code for the URI')
|
||||||
run_cmd(['bash', Command.SHOW_USER_URI.value, username])
|
@click.option('--ipv', '-ip', type=click.IntRange(4, 6), default=4, help='IP version (4 or 6)')
|
||||||
|
@click.option('--all', '-a', is_flag=True, help='Show both IPv4 and IPv6 URIs and generate QR codes for both if requested')
|
||||||
|
def show_user_uri(username: str, qrcode: bool, ipv: int, all: bool):
|
||||||
|
command_args = ['bash', Command.SHOW_USER_URI.value, '-u', username]
|
||||||
|
if qrcode:
|
||||||
|
command_args.append('-qr')
|
||||||
|
if all:
|
||||||
|
command_args.append('-a')
|
||||||
|
else:
|
||||||
|
command_args.extend(['-ip', str(ipv)])
|
||||||
|
|
||||||
|
run_cmd(command_args)
|
||||||
|
|
||||||
|
|
||||||
@ cli.command('traffic-status')
|
@ cli.command('traffic-status')
|
||||||
@ -196,6 +210,7 @@ def traffic_status():
|
|||||||
def list_users():
|
def list_users():
|
||||||
run_cmd(['bash', Command.LIST_USERS.value])
|
run_cmd(['bash', Command.LIST_USERS.value])
|
||||||
|
|
||||||
|
|
||||||
@cli.command('server-info')
|
@cli.command('server-info')
|
||||||
def server_info():
|
def server_info():
|
||||||
output = run_cmd(['bash', Command.SERVER_INFO.value])
|
output = run_cmd(['bash', Command.SERVER_INFO.value])
|
||||||
@ -233,11 +248,25 @@ def configure_warp(all: bool, popular_sites: bool, domestic_sites: bool, block_a
|
|||||||
"domestic_sites": domestic_sites,
|
"domestic_sites": domestic_sites,
|
||||||
"block_adult_sites": block_adult_sites
|
"block_adult_sites": block_adult_sites
|
||||||
}
|
}
|
||||||
|
|
||||||
options = {k: 'true' if v else 'false' for k, v in options.items()}
|
options = {k: 'true' if v else 'false' for k, v in options.items()}
|
||||||
run_cmd(['bash', Command.CONFIGURE_WARP.value,
|
run_cmd(['bash', Command.CONFIGURE_WARP.value,
|
||||||
options['all'], options['popular_sites'], options['domestic_sites'], options['block_adult_sites']])
|
options['all'], options['popular_sites'], options['domestic_sites'], options['block_adult_sites']])
|
||||||
|
|
||||||
|
@cli.command('telegram')
|
||||||
|
@click.option('--action', '-a', required=True, help='Action to perform: start or stop', type=click.Choice(['start', 'stop'], case_sensitive=False))
|
||||||
|
@click.option('--token', '-t', required=False, help='Token for running the telegram bot', type=str)
|
||||||
|
@click.option('--adminid', '-aid', required=False, help='Telegram admins ID for running the telegram bot', type=str)
|
||||||
|
def telegram(action: str, token: str, adminid: str):
|
||||||
|
if action == 'start':
|
||||||
|
if not token or not adminid:
|
||||||
|
print("Error: Both --token and --adminid are required for the start action.")
|
||||||
|
return
|
||||||
|
admin_ids = f'{adminid}'
|
||||||
|
run_cmd(['bash', Command.INSTALL_TELEGRAMBOT.value, 'start', token, admin_ids])
|
||||||
|
elif action == 'stop':
|
||||||
|
run_cmd(['bash', Command.INSTALL_TELEGRAMBOT.value, 'stop'])
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Source the path.sh script to load the necessary variables
|
# Source the path.sh script to load the necessary variables
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
source /etc/hysteria/core/scripts/path.sh
|
||||||
source /etc/hysteria/core/scripts/utils.sh
|
# source /etc/hysteria/core/scripts/utils.sh
|
||||||
define_colors
|
# define_colors
|
||||||
|
|
||||||
# Function to add a new user to the configuration
|
# Function to add a new user to the configuration
|
||||||
add_user() {
|
add_user() {
|
||||||
@ -55,7 +55,7 @@ add_user() {
|
|||||||
|
|
||||||
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
python3 "$CLI_PATH" restart-hysteria2 > /dev/null 2>&1
|
||||||
|
|
||||||
echo -e "${green}User $username added successfully.${NC}"
|
echo -e "User $username added successfully."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Call the function with the provided arguments
|
# Call the function with the provided arguments
|
||||||
|
|||||||
@ -1,57 +1,86 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Source the path.sh script to load the configuration variables
|
|
||||||
source /etc/hysteria/core/scripts/path.sh
|
source /etc/hysteria/core/scripts/path.sh
|
||||||
|
|
||||||
# Function to show URI if Hysteria2 is installed and active
|
|
||||||
show_uri() {
|
show_uri() {
|
||||||
if [ -f "$USERS_FILE" ]; then
|
if [ -f "$USERS_FILE" ]; then
|
||||||
if systemctl is-active --quiet hysteria-server.service; then
|
if systemctl is-active --quiet hysteria-server.service; then
|
||||||
# Check if the username is provided as an argument
|
local username
|
||||||
if [ -z "$1" ]; then
|
local generate_qrcode=false
|
||||||
echo "Usage: $0 <username>"
|
local ip_version=4
|
||||||
|
local show_all=false
|
||||||
|
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-u|--username) username="$2"; shift ;;
|
||||||
|
-qr|--qrcode) generate_qrcode=true ;;
|
||||||
|
-ip) ip_version="$2"; shift ;;
|
||||||
|
-a|--all) show_all=true ;;
|
||||||
|
*) echo "Unknown parameter passed: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
echo "Usage: $0 -u <username> [-qr] [-ip <4|6>] [-a]"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
username=$1
|
|
||||||
|
|
||||||
# Validate the username
|
|
||||||
if jq -e "has(\"$username\")" "$USERS_FILE" > /dev/null; then
|
if jq -e "has(\"$username\")" "$USERS_FILE" > /dev/null; then
|
||||||
# Get the selected user's details
|
|
||||||
authpassword=$(jq -r ".\"$username\".password" "$USERS_FILE")
|
authpassword=$(jq -r ".\"$username\".password" "$USERS_FILE")
|
||||||
port=$(jq -r '.listen' "$CONFIG_FILE" | cut -d':' -f2)
|
port=$(jq -r '.listen' "$CONFIG_FILE" | cut -d':' -f2)
|
||||||
sha256=$(jq -r '.tls.pinSHA256' "$CONFIG_FILE")
|
sha256=$(jq -r '.tls.pinSHA256' "$CONFIG_FILE")
|
||||||
obfspassword=$(jq -r '.obfs.salamander.password' "$CONFIG_FILE")
|
obfspassword=$(jq -r '.obfs.salamander.password' "$CONFIG_FILE")
|
||||||
|
|
||||||
# Get IP addresses
|
generate_uri() {
|
||||||
IP=$(curl -s -4 ip.gs)
|
local ip_version=$1
|
||||||
IP6=$(curl -s -6 ip.gs)
|
local ip=$2
|
||||||
|
if [ "$ip_version" -eq 4 ]; then
|
||||||
|
echo "hy2://$username%3A$authpassword@$ip:$port?obfs=salamander&obfs-password=$obfspassword&pinSHA256=$sha256&insecure=1&sni=bts.com#$username-IPv4"
|
||||||
|
elif [ "$ip_version" -eq 6 ]; then
|
||||||
|
echo "hy2://$username%3A$authpassword@[$ip]:$port?obfs=salamander&obfs-password=$obfspassword&pinSHA256=$sha256&insecure=1&sni=bts.com#$username-IPv6"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Construct URI
|
if [ "$show_all" = true ]; then
|
||||||
URI="hy2://$username%3A$authpassword@$IP:$port?obfs=salamander&obfs-password=$obfspassword&pinSHA256=$sha256&insecure=1&sni=bts.com#$username-IPv4"
|
IP=$(curl -s -4 ip.gs)
|
||||||
URI6="hy2://$username%3A$authpassword@[$IP6]:$port?obfs=salamander&obfs-password=$obfspassword&pinSHA256=$sha256&insecure=1&sni=bts.com#$username-IPv6"
|
URI=$(generate_uri 4 "$IP")
|
||||||
|
IP6=$(curl -s -6 ip.gs)
|
||||||
|
URI6=$(generate_uri 6 "$IP6")
|
||||||
|
echo -e "\nIPv4:\n$URI\n"
|
||||||
|
echo -e "\nIPv6:\n$URI6\n"
|
||||||
|
else
|
||||||
|
if [ "$ip_version" -eq 4 ]; then
|
||||||
|
IP=$(curl -s -4 ip.gs)
|
||||||
|
URI=$(generate_uri 4 "$IP")
|
||||||
|
echo -e "\nIPv4:\n$URI\n"
|
||||||
|
elif [ "$ip_version" -eq 6 ]; then
|
||||||
|
IP6=$(curl -s -6 ip.gs)
|
||||||
|
URI6=$(generate_uri 6 "$IP6")
|
||||||
|
echo -e "\nIPv6:\n$URI6\n"
|
||||||
|
else
|
||||||
|
echo "Invalid IP version. Use 4 for IPv4 or 6 for IPv6."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Generate QR codes
|
if [ "$generate_qrcode" = true ]; then
|
||||||
qr1=$(echo -n "$URI" | qrencode -t UTF8 -s 3 -m 2)
|
cols=$(tput cols)
|
||||||
qr2=$(echo -n "$URI6" | qrencode -t UTF8 -s 3 -m 2)
|
if [ -n "$URI" ]; then
|
||||||
|
qr1=$(echo -n "$URI" | qrencode -t UTF8 -s 3 -m 2)
|
||||||
# Display QR codes and URIs
|
echo -e "\nIPv4 QR Code:\n"
|
||||||
cols=$(tput cols)
|
echo "$qr1" | while IFS= read -r line; do
|
||||||
echo -e "\nIPv4:\n"
|
printf "%*s\n" $(( (${#line} + cols) / 2)) "$line"
|
||||||
echo "$qr1" | while IFS= read -r line; do
|
done
|
||||||
printf "%*s\n" $(( (${#line} + cols) / 2)) "$line"
|
fi
|
||||||
done
|
if [ -n "$URI6" ]; then
|
||||||
|
qr2=$(echo -n "$URI6" | qrencode -t UTF8 -s 3 -m 2)
|
||||||
echo -e "\nIPv6:\n"
|
echo -e "\nIPv6 QR Code:\n"
|
||||||
echo "$qr2" | while IFS= read -r line; do
|
echo "$qr2" | while IFS= read -r line; do
|
||||||
printf "%*s\n" $(( (${#line} + cols) / 2)) "$line"
|
printf "%*s\n" $(( (${#line} + cols) / 2)) "$line"
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
echo
|
fi
|
||||||
echo "IPv4: $URI"
|
|
||||||
echo
|
|
||||||
echo "IPv6: $URI6"
|
|
||||||
echo
|
|
||||||
else
|
else
|
||||||
echo "Invalid username. Please try again."
|
echo "Invalid username. Please try again."
|
||||||
fi
|
fi
|
||||||
@ -63,5 +92,4 @@ show_uri() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Call the function with the provided username argument
|
show_uri "$@"
|
||||||
show_uri "$1"
|
|
||||||
|
|||||||
@ -1,28 +1,37 @@
|
|||||||
source /etc/hysteria/core/scripts/path.sh
|
source /etc/hysteria/core/scripts/path.sh
|
||||||
|
|
||||||
echo "Uninstalling Hysteria2..."
|
echo "Uninstalling Hysteria2..."
|
||||||
#sleep 1
|
|
||||||
echo "Running uninstallation script..."
|
echo "Running uninstallation script..."
|
||||||
bash <(curl -fsSL https://get.hy2.sh/) --remove >/dev/null 2>&1
|
bash <(curl -fsSL https://get.hy2.sh/) --remove >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
echo "Removing WARP"
|
echo "Removing WARP"
|
||||||
python3 $CLI_PATH uninstall-warp
|
python3 $CLI_PATH uninstall-warp
|
||||||
|
|
||||||
echo "Removing Hysteria folder..."
|
echo "Removing Hysteria folder..."
|
||||||
rm -rf /etc/hysteria >/dev/null 2>&1
|
rm -rf /etc/hysteria >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
echo "Deleting hysteria user..."
|
echo "Deleting hysteria user..."
|
||||||
userdel -r hysteria >/dev/null 2>&1
|
userdel -r hysteria >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
echo "Removing systemd service files..."
|
echo "Removing systemd service files..."
|
||||||
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server.service >/dev/null 2>&1
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server.service >/dev/null 2>&1
|
||||||
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server@*.service >/dev/null 2>&1
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server@*.service >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
echo "Reloading systemd daemon..."
|
echo "Reloading systemd daemon..."
|
||||||
systemctl daemon-reload >/dev/null 2>&1
|
systemctl daemon-reload >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
echo "Removing cron jobs..."
|
echo "Removing cron jobs..."
|
||||||
(crontab -l | grep -v "python3 /etc/hysteria/core/cli.py traffic-status" | crontab -) >/dev/null 2>&1
|
(crontab -l | grep -v "python3 /etc/hysteria/core/cli.py traffic-status" | crontab -) >/dev/null 2>&1
|
||||||
(crontab -l | grep -v "/etc/hysteria/core/scripts/hysteria2/kick.sh" | crontab -) >/dev/null 2>&1
|
(crontab -l | grep -v "/etc/hysteria/core/scripts/hysteria2/kick.sh" | crontab -) >/dev/null 2>&1
|
||||||
#sleep 1
|
|
||||||
|
echo "Removing alias 'hys2' from .bashrc..."
|
||||||
|
sed -i '/alias hys2=.*\/etc\/hysteria\/menu.sh/d' ~/.bashrc
|
||||||
|
|
||||||
|
echo "Stopping hysteria-bot.service..."
|
||||||
|
systemctl stop hysteria-bot.service >/dev/null 2>&1
|
||||||
|
echo "Disabling hysteria-bot.service..."
|
||||||
|
systemctl disable hysteria-bot.service >/dev/null 2>&1
|
||||||
|
|
||||||
echo "Hysteria2 uninstalled!"
|
echo "Hysteria2 uninstalled!"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
89
core/scripts/telegrambot/runbot.sh
Normal file
89
core/scripts/telegrambot/runbot.sh
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
source /etc/hysteria/core/scripts/utils.sh
|
||||||
|
define_colors
|
||||||
|
install_dependencies() {
|
||||||
|
echo "Installing dependencies from /etc/hysteria/requirements.txt..."
|
||||||
|
if ! pip3 install -r /etc/hysteria/requirements.txt > /dev/null 2>&1; then
|
||||||
|
echo "Error: Failed to install dependencies. Please check the requirements file and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${green}Dependencies installed successfully. ${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
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-bot.service
|
||||||
|
[Unit]
|
||||||
|
Description=Hysteria Telegram Bot
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/python3 /etc/hysteria/core/scripts/telegrambot/tbot.py
|
||||||
|
WorkingDirectory=/etc/hysteria/core/scripts/telegrambot
|
||||||
|
Restart=always
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOL
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local api_token=$1
|
||||||
|
local admin_user_ids=$2
|
||||||
|
|
||||||
|
if systemctl is-active --quiet hysteria-bot.service; then
|
||||||
|
echo "The hysteria-bot.service is already running."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_dependencies
|
||||||
|
update_env_file "$api_token" "$admin_user_ids"
|
||||||
|
create_service_file
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable hysteria-bot.service > /dev/null 2>&1
|
||||||
|
systemctl start hysteria-bot.service > /dev/null 2>&1
|
||||||
|
|
||||||
|
if systemctl is-active --quiet hysteria-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-bot.service > /dev/null 2>&1
|
||||||
|
systemctl disable hysteria-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
|
||||||
234
core/scripts/telegrambot/tbot.py
Normal file
234
core/scripts/telegrambot/tbot.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import telebot
|
||||||
|
import subprocess
|
||||||
|
import qrcode
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from telebot import types
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
API_TOKEN = os.getenv('API_TOKEN')
|
||||||
|
ADMIN_USER_IDS = json.loads(os.getenv('ADMIN_USER_IDS'))
|
||||||
|
CLI_PATH = '/etc/hysteria/core/cli.py'
|
||||||
|
bot = telebot.TeleBot(API_TOKEN)
|
||||||
|
|
||||||
|
def run_cli_command(command):
|
||||||
|
try:
|
||||||
|
result = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return result.decode('utf-8').strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return f'Error: {e.output.decode("utf-8")}'
|
||||||
|
|
||||||
|
def create_main_markup():
|
||||||
|
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||||
|
markup.row('Show User', 'Add User')
|
||||||
|
markup.row('Delete User', 'Server Info')
|
||||||
|
return markup
|
||||||
|
|
||||||
|
def is_admin(user_id):
|
||||||
|
return user_id in ADMIN_USER_IDS
|
||||||
|
|
||||||
|
@bot.message_handler(commands=['start'])
|
||||||
|
def send_welcome(message):
|
||||||
|
if is_admin(message.from_user.id):
|
||||||
|
markup = create_main_markup()
|
||||||
|
bot.reply_to(message, "Welcome to the User Management Bot!", reply_markup=markup)
|
||||||
|
else:
|
||||||
|
bot.reply_to(message, "Unauthorized access. You do not have permission to use this bot.")
|
||||||
|
|
||||||
|
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == 'Add User')
|
||||||
|
def add_user(message):
|
||||||
|
msg = bot.reply_to(message, "Enter username:")
|
||||||
|
bot.register_next_step_handler(msg, process_add_user_step1)
|
||||||
|
|
||||||
|
def process_add_user_step1(message):
|
||||||
|
username = message.text.strip()
|
||||||
|
if username == "":
|
||||||
|
bot.reply_to(message, "Username cannot be empty. Please enter a valid username.")
|
||||||
|
return
|
||||||
|
|
||||||
|
command = f"python3 {CLI_PATH} list-users"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
try:
|
||||||
|
users = json.loads(result)
|
||||||
|
if username in users:
|
||||||
|
bot.reply_to(message, f"Username '{username}' already exists. Please choose a different username.")
|
||||||
|
return
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
bot.reply_to(message, "Error retrieving user list. Please try again later.")
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = bot.reply_to(message, "Enter traffic limit (GB):")
|
||||||
|
bot.register_next_step_handler(msg, process_add_user_step2, username)
|
||||||
|
|
||||||
|
def process_add_user_step2(message, username):
|
||||||
|
try:
|
||||||
|
traffic_limit = int(message.text.strip())
|
||||||
|
msg = bot.reply_to(message, "Enter expiration days:")
|
||||||
|
bot.register_next_step_handler(msg, process_add_user_step3, username, traffic_limit)
|
||||||
|
except ValueError:
|
||||||
|
bot.reply_to(message, "Invalid traffic limit. Please enter a number.")
|
||||||
|
|
||||||
|
def process_add_user_step3(message, username, traffic_limit):
|
||||||
|
try:
|
||||||
|
expiration_days = int(message.text.strip())
|
||||||
|
command = f"python3 {CLI_PATH} add-user -u {username} -t {traffic_limit} -e {expiration_days}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
except ValueError:
|
||||||
|
bot.reply_to(message, "Invalid expiration days. Please enter a number.")
|
||||||
|
|
||||||
|
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == 'Show User')
|
||||||
|
def show_user(message):
|
||||||
|
msg = bot.reply_to(message, "Enter username:")
|
||||||
|
bot.register_next_step_handler(msg, process_show_user)
|
||||||
|
|
||||||
|
def process_show_user(message):
|
||||||
|
username = message.text.strip()
|
||||||
|
command = f"python3 {CLI_PATH} get-user -u {username}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
|
||||||
|
if "Error" in result or "Invalid" in result:
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
else:
|
||||||
|
user_details = json.loads(result)
|
||||||
|
formatted_details = (
|
||||||
|
f"Name: {username}\n"
|
||||||
|
f"Traffic limit: {user_details['max_download_bytes'] / (1024 ** 3):.2f} GB\n"
|
||||||
|
f"Days: {user_details['expiration_days']}\n"
|
||||||
|
f"Account Creation: {user_details['account_creation_date']}\n"
|
||||||
|
f"Blocked: {user_details['blocked']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
qr_command = f"python3 {CLI_PATH} show-user-uri -u {username} -ip 4"
|
||||||
|
qr_result = run_cli_command(qr_command)
|
||||||
|
|
||||||
|
if "Error" in qr_result or "Invalid" in qr_result:
|
||||||
|
bot.reply_to(message, qr_result)
|
||||||
|
return
|
||||||
|
uri_v4 = qr_result.split('\n')[-1].strip()
|
||||||
|
|
||||||
|
qr_v4 = qrcode.make(uri_v4)
|
||||||
|
bio_v4 = io.BytesIO()
|
||||||
|
qr_v4.save(bio_v4, 'PNG')
|
||||||
|
bio_v4.seek(0)
|
||||||
|
|
||||||
|
markup = types.InlineKeyboardMarkup(row_width=2)
|
||||||
|
markup.add(types.InlineKeyboardButton("Edit Username", callback_data=f"edit_username:{username}"),
|
||||||
|
types.InlineKeyboardButton("Edit Traffic Limit", callback_data=f"edit_traffic:{username}"))
|
||||||
|
markup.add(types.InlineKeyboardButton("Edit Expiration Days", callback_data=f"edit_expiration:{username}"),
|
||||||
|
types.InlineKeyboardButton("Renew Password", callback_data=f"renew_password:{username}"))
|
||||||
|
markup.add(types.InlineKeyboardButton("Renew Creation Date", callback_data=f"renew_creation:{username}"),
|
||||||
|
types.InlineKeyboardButton("Block User", callback_data=f"block_user:{username}"))
|
||||||
|
|
||||||
|
bot.send_photo(message.chat.id, bio_v4, caption=f"User Details:\n{formatted_details}\n\nIPv4 URI: {uri_v4}", reply_markup=markup)
|
||||||
|
|
||||||
|
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == 'Server Info')
|
||||||
|
def server_info(message):
|
||||||
|
command = f"python3 {CLI_PATH} server-info"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
|
||||||
|
@bot.callback_query_handler(func=lambda call: call.data.startswith('edit_') or call.data.startswith('renew_') or call.data.startswith('block_'))
|
||||||
|
def handle_edit_callback(call):
|
||||||
|
action, username = call.data.split(':')
|
||||||
|
if action == 'edit_username':
|
||||||
|
msg = bot.send_message(call.message.chat.id, f"Enter new username for {username}:")
|
||||||
|
bot.register_next_step_handler(msg, process_edit_username, username)
|
||||||
|
elif action == 'edit_traffic':
|
||||||
|
msg = bot.send_message(call.message.chat.id, f"Enter new traffic limit (GB) for {username}:")
|
||||||
|
bot.register_next_step_handler(msg, process_edit_traffic, username)
|
||||||
|
elif action == 'edit_expiration':
|
||||||
|
msg = bot.send_message(call.message.chat.id, f"Enter new expiration days for {username}:")
|
||||||
|
bot.register_next_step_handler(msg, process_edit_expiration, username)
|
||||||
|
elif action == 'renew_password':
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} -rp"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.send_message(call.message.chat.id, result)
|
||||||
|
elif action == 'renew_creation':
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} -rc"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.send_message(call.message.chat.id, result)
|
||||||
|
elif action == 'block_user':
|
||||||
|
markup = types.InlineKeyboardMarkup()
|
||||||
|
markup.add(types.InlineKeyboardButton("True", callback_data=f"confirm_block:{username}:true"),
|
||||||
|
types.InlineKeyboardButton("False", callback_data=f"confirm_block:{username}:false"))
|
||||||
|
bot.send_message(call.message.chat.id, f"Set block status for {username}:", reply_markup=markup)
|
||||||
|
|
||||||
|
@bot.callback_query_handler(func=lambda call: call.data.startswith('confirm_block:'))
|
||||||
|
def handle_block_confirmation(call):
|
||||||
|
_, username, block_status = call.data.split(':')
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} {'-b' if block_status == 'true' else ''}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.send_message(call.message.chat.id, result)
|
||||||
|
|
||||||
|
def process_edit_username(message, username):
|
||||||
|
new_username = message.text.strip()
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} -nu {new_username}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
|
||||||
|
def process_edit_traffic(message, username):
|
||||||
|
try:
|
||||||
|
new_traffic_limit = int(message.text.strip())
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} -nt {new_traffic_limit}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
except ValueError:
|
||||||
|
bot.reply_to(message, "Invalid traffic limit. Please enter a number.")
|
||||||
|
|
||||||
|
def process_edit_expiration(message, username):
|
||||||
|
try:
|
||||||
|
new_expiration_days = int(message.text.strip())
|
||||||
|
command = f"python3 {CLI_PATH} edit-user -u {username} -ne {new_expiration_days}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
except ValueError:
|
||||||
|
bot.reply_to(message, "Invalid expiration days. Please enter a number.")
|
||||||
|
|
||||||
|
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == 'Delete User')
|
||||||
|
def delete_user(message):
|
||||||
|
msg = bot.reply_to(message, "Enter username:")
|
||||||
|
bot.register_next_step_handler(msg, process_delete_user)
|
||||||
|
|
||||||
|
def process_delete_user(message):
|
||||||
|
username = message.text.strip()
|
||||||
|
command = f"python3 {CLI_PATH} remove-user -u {username}"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
bot.reply_to(message, result)
|
||||||
|
|
||||||
|
@bot.inline_handler(lambda query: is_admin(query.from_user.id))
|
||||||
|
def handle_inline_query(query):
|
||||||
|
command = f"python3 {CLI_PATH} list-users"
|
||||||
|
result = run_cli_command(command)
|
||||||
|
try:
|
||||||
|
users = json.loads(result)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
bot.answer_inline_query(query.id, results=[], switch_pm_text="Error retrieving users.", switch_pm_user_id=query.from_user.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
query_text = query.query.lower()
|
||||||
|
results = []
|
||||||
|
for username, details in users.items():
|
||||||
|
if query_text in username.lower():
|
||||||
|
title = f"Username: {username}"
|
||||||
|
description = f"Traffic Limit: {details['max_download_bytes'] / (1024 ** 3):.2f} GB, Expiration Days: {details['expiration_days']}"
|
||||||
|
results.append(types.InlineQueryResultArticle(
|
||||||
|
id=username,
|
||||||
|
title=title,
|
||||||
|
description=description,
|
||||||
|
input_message_content=types.InputTextMessageContent(
|
||||||
|
message_text=f"Name: {username}\n"
|
||||||
|
f"Traffic limit: {details['max_download_bytes'] / (1024 ** 3):.2f} GB\n"
|
||||||
|
f"Days: {details['expiration_days']}\n"
|
||||||
|
f"Account Creation: {details['account_creation_date']}\n"
|
||||||
|
f"Blocked: {details['blocked']}"
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
bot.answer_inline_query(query.id, results)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
bot.polling(none_stop=True)
|
||||||
@ -19,4 +19,36 @@ get_system_info() {
|
|||||||
IP=$(echo "$IP_API_DATA" | jq -r '.ip')
|
IP=$(echo "$IP_API_DATA" | jq -r '.ip')
|
||||||
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}')
|
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "%"}')
|
||||||
RAM=$(free -m | awk 'NR==2{printf "%.2f%%", $3*100/$2 }')
|
RAM=$(free -m | awk 'NR==2{printf "%.2f%%", $3*100/$2 }')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version_greater_equal() {
|
||||||
|
IFS='.' read -r -a local_version_parts <<< "$1"
|
||||||
|
IFS='.' read -r -a latest_version_parts <<< "$2"
|
||||||
|
|
||||||
|
for ((i=0; i<${#local_version_parts[@]}; i++)); do
|
||||||
|
if [[ -z ${latest_version_parts[i]} ]]; then
|
||||||
|
latest_version_parts[i]=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((10#${local_version_parts[i]} > 10#${latest_version_parts[i]})); then
|
||||||
|
return 0
|
||||||
|
elif ((10#${local_version_parts[i]} < 10#${latest_version_parts[i]})); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
check_version() {
|
||||||
|
local_version=$(cat /etc/hysteria/VERSION)
|
||||||
|
latest_version=$(curl -s https://raw.githubusercontent.com/ReturnFI/Hysteria2/Dev/VERSION)
|
||||||
|
|
||||||
|
if version_greater_equal "$local_version" "$latest_version"; then
|
||||||
|
echo -e "Panel Version: ${cyan}$local_version${NC}"
|
||||||
|
else
|
||||||
|
echo -e "Panel Version: ${cyan}$local_version${NC}"
|
||||||
|
echo -e "Latest Version: ${cyan}$latest_version${NC}"
|
||||||
|
echo -e "${red}Please update your panel.${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
79
menu.sh
79
menu.sh
@ -28,7 +28,11 @@ hysteria2_add_user_handler() {
|
|||||||
read -p "Enter the username: " username
|
read -p "Enter the username: " username
|
||||||
|
|
||||||
if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then
|
if [[ "$username" =~ ^[a-zA-Z0-9]+$ ]]; then
|
||||||
break
|
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
|
else
|
||||||
echo -e "${red}Error:${NC} Username can only contain letters and numbers."
|
echo -e "${red}Error:${NC} Username can only contain letters and numbers."
|
||||||
fi
|
fi
|
||||||
@ -150,7 +154,7 @@ hysteria2_get_user_handler() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Run the command and suppress error output
|
# Run the command and suppress error output
|
||||||
if ! python3 "$CLI_PATH" get-user --username "$username" > /dev/null 2>&1; then
|
if ! python3 "$CLI_PATH" get-user --username "$username" 2>/dev/null; then
|
||||||
echo -e "${red}Error:${NC} User '$username' not found."
|
echo -e "${red}Error:${NC} User '$username' not found."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -196,7 +200,7 @@ hysteria2_show_user_uri_handler() {
|
|||||||
echo -e "${red}Error:${NC} Username can only contain letters and numbers."
|
echo -e "${red}Error:${NC} Username can only contain letters and numbers."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
python3 $CLI_PATH show-user-uri --username "$username"
|
python3 $CLI_PATH show-user-uri --username "$username" -a -qr
|
||||||
}
|
}
|
||||||
|
|
||||||
hysteria2_change_port_handler() {
|
hysteria2_change_port_handler() {
|
||||||
@ -238,6 +242,56 @@ warp_configure_handler() {
|
|||||||
fi
|
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 -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 -p "Enter the admin IDs (comma-separated): " admin_ids
|
||||||
|
if [ -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)
|
||||||
|
if ! systemctl is-active --quiet hysteria-bot.service; then
|
||||||
|
echo "The hysteria-bot.service is already inactive."
|
||||||
|
else
|
||||||
|
python3 $CLI_PATH telegram -a stop
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
0)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid option. Please try again."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Function to display the main menu
|
# Function to display the main menu
|
||||||
display_main_menu() {
|
display_main_menu() {
|
||||||
clear
|
clear
|
||||||
@ -250,6 +304,9 @@ display_main_menu() {
|
|||||||
|
|
||||||
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
||||||
|
|
||||||
|
check_version
|
||||||
|
|
||||||
|
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
||||||
echo -e "${yellow} ☼ Main Menu ☼ ${NC}"
|
echo -e "${yellow} ☼ Main Menu ☼ ${NC}"
|
||||||
|
|
||||||
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
||||||
@ -293,7 +350,7 @@ display_hysteria2_menu() {
|
|||||||
echo -e "${cyan}[3] ${NC}↝ Edit User"
|
echo -e "${cyan}[3] ${NC}↝ Edit User"
|
||||||
echo -e "${cyan}[4] ${NC}↝ Remove User"
|
echo -e "${cyan}[4] ${NC}↝ Remove User"
|
||||||
echo -e "${cyan}[5] ${NC}↝ Get User"
|
echo -e "${cyan}[5] ${NC}↝ Get User"
|
||||||
echo -e "${cyan}[6] ${NC}↝ List Users (WIP)"
|
echo -e "${cyan}[6] ${NC}↝ List Users"
|
||||||
echo -e "${cyan}[7] ${NC}↝ Check Traffic Status"
|
echo -e "${cyan}[7] ${NC}↝ Check Traffic Status"
|
||||||
echo -e "${cyan}[8] ${NC}↝ Show User URI"
|
echo -e "${cyan}[8] ${NC}↝ Show User URI"
|
||||||
|
|
||||||
@ -339,9 +396,10 @@ display_advance_menu() {
|
|||||||
echo -e "${green}[2] ${NC}↝ Install WARP"
|
echo -e "${green}[2] ${NC}↝ Install WARP"
|
||||||
echo -e "${cyan}[3] ${NC}↝ Configure WARP"
|
echo -e "${cyan}[3] ${NC}↝ Configure WARP"
|
||||||
echo -e "${red}[4] ${NC}↝ Uninstall WARP"
|
echo -e "${red}[4] ${NC}↝ Uninstall WARP"
|
||||||
echo -e "${cyan}[5] ${NC}↝ Change Port Hysteria2"
|
echo -e "${green}[5] ${NC}↝ Telegram Bot"
|
||||||
echo -e "${cyan}[6] ${NC}↝ Update Core Hysteria2"
|
echo -e "${cyan}[6] ${NC}↝ Change Port Hysteria2"
|
||||||
echo -e "${red}[7] ${NC}↝ Uninstall Hysteria2"
|
echo -e "${cyan}[7] ${NC}↝ Update Core Hysteria2"
|
||||||
|
echo -e "${red}[8] ${NC}↝ Uninstall Hysteria2"
|
||||||
echo -e "${red}[0] ${NC}↝ Back to Main Menu"
|
echo -e "${red}[0] ${NC}↝ Back to Main Menu"
|
||||||
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
echo -e "${LPurple}◇──────────────────────────────────────────────────────────────────────◇${NC}"
|
||||||
echo -ne "${yellow}➜ Enter your option: ${NC}"
|
echo -ne "${yellow}➜ Enter your option: ${NC}"
|
||||||
@ -359,9 +417,10 @@ advance_menu() {
|
|||||||
2) python3 $CLI_PATH install-warp ;;
|
2) python3 $CLI_PATH install-warp ;;
|
||||||
3) warp_configure_handler ;;
|
3) warp_configure_handler ;;
|
||||||
4) python3 $CLI_PATH uninstall-warp ;;
|
4) python3 $CLI_PATH uninstall-warp ;;
|
||||||
5) hysteria2_change_port_handler ;;
|
5) telegram_bot_handler ;;
|
||||||
6) python3 $CLI_PATH update-hysteria2 ;;
|
6) hysteria2_change_port_handler ;;
|
||||||
7) python3 $CLI_PATH uninstall-hysteria2 ;;
|
7) python3 $CLI_PATH update-hysteria2 ;;
|
||||||
|
8) python3 $CLI_PATH uninstall-hysteria2 ;;
|
||||||
0) return ;;
|
0) return ;;
|
||||||
*) echo "Invalid option. Please try again." ;;
|
*) echo "Invalid option. Please try again." ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
106
modify.py
106
modify.py
@ -1,106 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
users_file_path = '/etc/hysteria/users/users.json'
|
|
||||||
|
|
||||||
def colors():
|
|
||||||
green='\033[0;32m'
|
|
||||||
cyan='\033[0;36m'
|
|
||||||
red='\033[0;31m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
return green, cyan, red, NC
|
|
||||||
|
|
||||||
def load_users():
|
|
||||||
with open(users_file_path, 'r') as file:
|
|
||||||
return json.load(file)
|
|
||||||
|
|
||||||
def save_users(users):
|
|
||||||
with open(users_file_path, 'w') as file:
|
|
||||||
json.dump(users, file, indent=4)
|
|
||||||
|
|
||||||
def generate_password():
|
|
||||||
result = subprocess.run(['pwgen', '-s', '32', '1'], capture_output=True, text=True)
|
|
||||||
return result.stdout.strip()
|
|
||||||
|
|
||||||
def list_users(users):
|
|
||||||
green, cyan, red, NC = colors()
|
|
||||||
print(f"{green}List of Users{NC}\n")
|
|
||||||
for i, user in enumerate(users.keys(), start=1):
|
|
||||||
print(f"{cyan}{i}. {user}{NC}")
|
|
||||||
|
|
||||||
def edit_user(users):
|
|
||||||
green, cyan, red, NC = colors()
|
|
||||||
list_users(users)
|
|
||||||
try:
|
|
||||||
choice = int(input(f"{green}Enter the number of the user to edit:{NC} "))
|
|
||||||
username = list(users.keys())[choice - 1]
|
|
||||||
except (ValueError, IndexError):
|
|
||||||
print(f"{green}Invalid choice.{NC}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"{green}Editing user: {cyan}{username}{NC}")
|
|
||||||
|
|
||||||
change_password = input(f"Change password? (current: {users[username]['password']}) [y/N]: ").lower() == 'y'
|
|
||||||
if change_password:
|
|
||||||
new_password = generate_password()
|
|
||||||
users[username]['password'] = new_password
|
|
||||||
print(f"{green}New password: {cyan}{new_password}{NC}")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
current_max_download_gb = users[username]['max_download_bytes'] // (1024 ** 3)
|
|
||||||
max_download_gb = input(f"Enter new max download bytes in GB (current: {current_max_download_gb} GB, press Enter to keep current): ")
|
|
||||||
if max_download_gb.strip() == '':
|
|
||||||
break
|
|
||||||
elif max_download_gb.isdigit():
|
|
||||||
users[username]['max_download_bytes'] = int(max_download_gb) * (1024 ** 3)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print(f"{red}Invalid input. Please enter a valid number or press Enter to keep current.{NC}")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
expiration_days = input(f"Enter new expiration days (current: {users[username]['expiration_days']}, press Enter to keep current): ")
|
|
||||||
if expiration_days.strip() == '':
|
|
||||||
break
|
|
||||||
elif expiration_days.isdigit():
|
|
||||||
users[username]['expiration_days'] = int(expiration_days)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print(f"{red}Invalid input. Please enter a valid number or press Enter to keep current.{NC}")
|
|
||||||
|
|
||||||
blocked = input(f"Blocked? (current: {users[username]['blocked']}) [true/false]: ").lower()
|
|
||||||
if blocked:
|
|
||||||
users[username]['blocked'] = blocked == 'true'
|
|
||||||
|
|
||||||
change_date = input(f"Change account creation date to today? (current: {users[username]['account_creation_date']}) [y/N]: ").lower() == 'y'
|
|
||||||
if change_date:
|
|
||||||
users[username]['account_creation_date'] = datetime.today().strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
def main():
|
|
||||||
green, cyan, red, NC = colors()
|
|
||||||
|
|
||||||
if not os.path.exists(users_file_path):
|
|
||||||
print(f"{red}File {users_file_path} does not exist.{NC}")
|
|
||||||
return
|
|
||||||
|
|
||||||
users = load_users()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
print(f"{green}1. Edit user{NC}")
|
|
||||||
print(f"{red}2. Exit{NC}")
|
|
||||||
choice = input(f"{NC}Enter your choice: {NC}")
|
|
||||||
|
|
||||||
if choice == "1":
|
|
||||||
edit_user(users)
|
|
||||||
save_users(users)
|
|
||||||
elif choice == "2":
|
|
||||||
print(f"{red}Exiting...{NC}")
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print(f"{NC}Invalid choice. Please try again.{NC}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pyTelegramBotAPI
|
||||||
|
qrcode
|
||||||
|
python-dotenv
|
||||||
|
requests
|
||||||
53
upgrade.sh
Normal file
53
upgrade.sh
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
|
||||||
|
FILES=(
|
||||||
|
"/etc/hysteria/ca.key"
|
||||||
|
"/etc/hysteria/ca.crt"
|
||||||
|
"/etc/hysteria/users.json"
|
||||||
|
"/etc/hysteria/traffic_data.json"
|
||||||
|
"/etc/hysteria/config.json"
|
||||||
|
"/etc/hysteria/core/scripts/telegrambot/.env"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "Backing up files to $TEMP_DIR"
|
||||||
|
for FILE in "${FILES[@]}"; do
|
||||||
|
cp "$FILE" "$TEMP_DIR"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Removing /etc/hysteria directory"
|
||||||
|
rm -rf /etc/hysteria/
|
||||||
|
|
||||||
|
echo "Cloning Hysteria2 repository"
|
||||||
|
git clone https://github.com/ReturnFI/Hysteria2 /etc/hysteria
|
||||||
|
|
||||||
|
echo "Downloading geosite.dat and geoip.dat"
|
||||||
|
wget -O /etc/hysteria/geosite.dat https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/release/geosite.dat >/dev/null 2>&1
|
||||||
|
wget -O /etc/hysteria/geoip.dat https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/release/geoip.dat >/dev/null 2>&1
|
||||||
|
|
||||||
|
echo "Restoring backup files"
|
||||||
|
for FILE in "${FILES[@]}"; do
|
||||||
|
cp "$TEMP_DIR/$(basename $FILE)" "$FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Setting ownership and permissions for ca.key and ca.crt"
|
||||||
|
chown hysteria:hysteria /etc/hysteria/ca.key /etc/hysteria/ca.crt
|
||||||
|
chmod 640 /etc/hysteria/ca.key /etc/hysteria/ca.crt
|
||||||
|
|
||||||
|
echo "Restarting hysteria-server.service"
|
||||||
|
systemctl restart hysteria-server.service
|
||||||
|
|
||||||
|
echo "Setting execute permissions for user.sh and kick.sh"
|
||||||
|
chmod +x /etc/hysteria/core/scripts/hysteria2/user.sh
|
||||||
|
chmod +x /etc/hysteria/core/scripts/hysteria2/kick.sh
|
||||||
|
|
||||||
|
echo "Checking hysteria-server.service status"
|
||||||
|
if systemctl is-active --quiet hysteria-server.service; then
|
||||||
|
echo "Upgrade completed successfully"
|
||||||
|
else
|
||||||
|
echo "Upgrade failed: hysteria-server.service is not active"
|
||||||
|
fi
|
||||||
|
cd /etc/hysteria
|
||||||
|
chmod +x menu.sh
|
||||||
|
./menu.sh
|
||||||
Reference in New Issue
Block a user