Merge branch 'ReturnFI:main' into ordev3

This commit is contained in:
Seyed Mahdi
2026-01-03 22:21:31 +03:30
committed by GitHub
15 changed files with 268 additions and 88 deletions

View File

@ -670,10 +670,14 @@ def stop_webpanel_decoy():
click.echo(f'{e}', err=True)
@cli.command('get-webpanel-url')
def get_web_panel_url():
@click.option('--url-only', is_flag=True, help='Only display the URL without additional text')
def get_web_panel_url(url_only: bool):
try:
url = cli_api.get_webpanel_url()
click.echo(f'Hysteria web panel is now running. The service is accessible on: {url}')
if url_only:
click.echo(url)
else:
click.echo(f'Hysteria web panel is now running. The service is accessible on: {url}')
except Exception as e:
click.echo(f'{e}', err=True)

View File

@ -160,13 +160,13 @@ def show_uri(args: argparse.Namespace) -> None:
if args.all or args.ip_version == 4:
if ip4 and ip4 != "None":
uri = generate_uri(args.username, auth_password, ip4, local_port,
local_obfs_password, local_sha256, local_sni, 4, local_insecure, f"{args.username}-IPv4")
local_obfs_password, local_sha256, local_sni, 4, local_insecure, "IPv4")
display_uri_and_qr(uri, "IPv4", args, terminal_width)
if args.all or args.ip_version == 6:
if ip6 and ip6 != "None":
uri = generate_uri(args.username, auth_password, ip6, local_port,
local_obfs_password, local_sha256, local_sni, 6, local_insecure, f"{args.username}-IPv6")
local_obfs_password, local_sha256, local_sni, 6, local_insecure, "IPv6")
display_uri_and_qr(uri, "IPv6", args, terminal_width)
for node in nodes:
@ -194,14 +194,14 @@ def show_uri(args: argparse.Namespace) -> None:
sni=node_sni,
ip_version=ip_v,
insecure=node_insecure,
fragment_tag=f"{args.username}-{node_name}"
fragment_tag=node_name
)
display_uri_and_qr(uri, f"Node: {node_name} (IPv{ip_v})", args, terminal_width)
if args.singbox and is_service_active("hysteria-singbox.service"):
domain, port = get_singbox_domain_and_port()
if domain and port:
print(f"\nSingbox Sublink:\nhttps://{domain}:{port}/sub/singbox/{args.username}/{args.ip_version}#{args.username}\n")
print(f"\nSingbox Sublink:\nhttps://{domain}:{port}/sub/singbox/{args.username}/{args.ip_version}#Hysteria2\n")
if args.normalsub and is_service_active("hysteria-normal-sub.service"):
domain, port, subpath = get_normalsub_domain_and_port()

View File

@ -87,9 +87,9 @@ def process_users(target_usernames: List[str]) -> List[Dict[str, Any]]:
user_output = {"username": username, "ipv4": None, "ipv6": None, "nodes": [], "normal_sub": None}
if ip4 and ip4 != "None":
user_output["ipv4"] = generate_uri(username, auth_password, ip4, default_port, base_uri_params, 4, f"{username}-IPv4")
user_output["ipv4"] = generate_uri(username, auth_password, ip4, default_port, base_uri_params, 4, "IPv4")
if ip6 and ip6 != "None":
user_output["ipv6"] = generate_uri(username, auth_password, ip6, default_port, base_uri_params, 6, f"{username}-IPv6")
user_output["ipv6"] = generate_uri(username, auth_password, ip6, default_port, base_uri_params, 6, "IPv6")
for node in nodes:
node_name = node.get("name")
@ -98,7 +98,7 @@ def process_users(target_usernames: List[str]) -> List[Dict[str, Any]]:
continue
ip_v = 6 if ':' in node_ip else 4
tag = f"{username}-{node_name}"
tag = node_name
node_port = str(node.get("port", default_port))
node_sni = node.get("sni", default_sni)
@ -117,7 +117,7 @@ def process_users(target_usernames: List[str]) -> List[Dict[str, Any]]:
user_output["nodes"].append({"name": node_name, "uri": uri})
if ns_domain and ns_port and ns_subpath:
user_output["normal_sub"] = f"https://{ns_domain}:{ns_port}/{ns_subpath}/{auth_password}#{username}"
user_output["normal_sub"] = f"https://{ns_domain}:{ns_port}/{ns_subpath}/{auth_password}#Hysteria2"
results.append(user_output)

View File

@ -308,15 +308,11 @@ class SingboxConfigGenerator:
print(f"Error during Singbox config generation from URI: {e}, URI: {uri}")
return None
return {
outbound_config = {
"type": "hysteria2",
"tag": unquote(parsed_url.fragment),
"server": server,
"server_port": server_port,
"obfs": {
"type": "salamander",
"password": obfs_password
},
"password": final_password,
"tls": {
"enabled": True,
@ -325,6 +321,14 @@ class SingboxConfigGenerator:
}
}
if obfs_password:
outbound_config["obfs"] = {
"type": "salamander",
"password": obfs_password
}
return outbound_config
def combine_configs(self, all_uris: List[str], username: str, fragment: str) -> Optional[Dict[str, Any]]:
if not all_uris:
return None

View File

@ -70,11 +70,19 @@
{
"outbounds": [
"auto",
"direct"
"direct",
"select"
],
"tag": "proxy",
"type": "selector"
},
{
"type": "selector",
"tag": "select",
"outbounds": [],
"interrupt_exist_connections": false
},
{
"interval": "10m",
"outbounds": [],

View File

@ -8,3 +8,5 @@ from .search import *
from .serverinfo import *
from .cpu import *
from .check_version import *
from .weburl import *
from .settings import *

View File

@ -4,5 +4,11 @@ def create_main_markup():
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
markup.row(' Add User', '🔍 Show User')
markup.row('🗑️ Delete User', '🖥️ Server Info')
markup.row('💾 Backup Server')
markup.row('💾 Backup Server', '⚙️ Settings')
return markup
def create_settings_markup():
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
markup.row('🔗 Get Webpanel URL')
markup.row('⬅️ Back')
return markup

View File

@ -0,0 +1,10 @@
from utils.command import *
from utils.common import create_main_markup, create_settings_markup
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == '⚙️ Settings')
def settings_menu_handler(message):
bot.send_message(message.chat.id, "⚙️ Settings Menu:", reply_markup=create_settings_markup())
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == '⬅️ Back')
def back_to_main_menu_handler(message):
bot.send_message(message.chat.id, "⬅️ Returning to Main Menu...", reply_markup=create_main_markup())

View File

@ -0,0 +1,15 @@
from utils.command import *
@bot.message_handler(func=lambda message: is_admin(message.from_user.id) and message.text == '🔗 Get Webpanel URL')
def get_webpanel_url_handler(message):
status_command = f"python3 {CLI_PATH} get-webpanel-services-status"
status_result = run_cli_command(status_command)
if "hysteria-webpanel.service: Inactive" in status_result:
bot.reply_to(message, "⚠️ The Webpanel service is currently inactive.")
return
command = f"python3 {CLI_PATH} get-webpanel-url --url-only"
result = run_cli_command(command)
bot.send_chat_action(message.chat.id, 'typing')
bot.reply_to(message, "🌐 Webpanel URL:\n" + result)

View File

@ -161,6 +161,7 @@
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="1000">1000</option>
</select>
</form>
</div>