Merge branch 'ReturnFI:main' into ordev3
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -160,4 +160,5 @@ cython_debug/
|
||||
#.idea/
|
||||
|
||||
hysteria2_venv/
|
||||
.vscode/
|
||||
# .vscode/
|
||||
# !.vscode/settings.json
|
||||
|
||||
172
.vscode/settings.json
vendored
Normal file
172
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
{
|
||||
"workbench.colorTheme": "Default Dark+",
|
||||
"workbench.colorCustomizations": {
|
||||
"editor.background": "#252b3d",
|
||||
"editor.foreground": "#dde1eb",
|
||||
"activityBar.background": "#2f3648",
|
||||
"activityBar.foreground": "#FF6B35",
|
||||
"activityBar.activeBorder": "#FF6B35",
|
||||
"activityBar.inactiveForeground": "#a8afc0",
|
||||
"sideBar.background": "#2f3648",
|
||||
"sideBar.foreground": "#dde1eb",
|
||||
"sideBar.border": "#3a4158",
|
||||
"sideBarTitle.foreground": "#FFA500",
|
||||
"statusBar.background": "#2f3648",
|
||||
"statusBar.foreground": "#58C4DC",
|
||||
"statusBar.noFolderBackground": "#2f3648",
|
||||
"statusBar.debuggingBackground": "#FF6347",
|
||||
"statusBar.debuggingForeground": "#ffffff",
|
||||
"titleBar.activeBackground": "#252b3d",
|
||||
"titleBar.activeForeground": "#dde1eb",
|
||||
"titleBar.inactiveBackground": "#252b3d",
|
||||
"titleBar.inactiveForeground": "#a8afc0",
|
||||
"titleBar.border": "#3a4158",
|
||||
"editorGroupHeader.tabsBackground": "#252b3d",
|
||||
"editorGroupHeader.tabsBorder": "#3a4158",
|
||||
"tab.activeBackground": "#2f3648",
|
||||
"tab.activeForeground": "#FFA500",
|
||||
"tab.inactiveBackground": "#252b3d",
|
||||
"tab.inactiveForeground": "#a8afc0",
|
||||
"tab.border": "#3a4158",
|
||||
"tab.activeBorder": "#FF6B35",
|
||||
"tab.activeBorderTop": "#FF6B35",
|
||||
"editorLineNumber.foreground": "#8891a4",
|
||||
"editorLineNumber.activeForeground": "#58C4DC",
|
||||
"editorCursor.foreground": "#FFA500",
|
||||
"editor.selectionBackground": "#4a6589",
|
||||
"editor.selectionHighlightBackground": "#4a658960",
|
||||
"editor.lineHighlightBackground": "#2f364860",
|
||||
"editorBracketMatch.background": "#4a658960",
|
||||
"editorBracketMatch.border": "#FFA500",
|
||||
"editorWidget.background": "#2f3648",
|
||||
"editorSuggestWidget.background": "#2f3648",
|
||||
"editorSuggestWidget.selectedBackground": "#4a6589",
|
||||
"editorSuggestWidget.highlightForeground": "#FFA500",
|
||||
"input.background": "#252b3d",
|
||||
"input.border": "#3a4158",
|
||||
"input.foreground": "#dde1eb",
|
||||
"inputOption.activeBorder": "#58C4DC",
|
||||
"dropdown.background": "#2f3648",
|
||||
"dropdown.border": "#3a4158",
|
||||
"button.background": "#3FB950",
|
||||
"button.foreground": "#ffffff",
|
||||
"button.hoverBackground": "#4ec55e",
|
||||
"list.activeSelectionBackground": "#4a6589",
|
||||
"list.activeSelectionForeground": "#ffffff",
|
||||
"list.inactiveSelectionBackground": "#4a658960",
|
||||
"list.hoverBackground": "#4a658950",
|
||||
"list.focusBackground": "#4a6589",
|
||||
"terminal.foreground": "#dde1eb",
|
||||
"terminal.ansiGreen": "#3FB950",
|
||||
"terminal.ansiYellow": "#D29922",
|
||||
"terminal.ansiBlue": "#58A6FF",
|
||||
"terminal.ansiMagenta": "#BC8CFF",
|
||||
"terminal.ansiCyan": "#39C5CF",
|
||||
"terminal.ansiRed": "#FF6347",
|
||||
"gitDecoration.modifiedResourceForeground": "#D29922",
|
||||
"gitDecoration.addedResourceForeground": "#3FB950",
|
||||
"gitDecoration.deletedResourceForeground": "#FF6347",
|
||||
"gitDecoration.untrackedResourceForeground": "#39C5CF",
|
||||
"badge.background": "#FF6B35",
|
||||
"badge.foreground": "#ffffff",
|
||||
"notifications.background": "#2f3648",
|
||||
"notifications.foreground": "#dde1eb",
|
||||
"notifications.border": "#3a4158",
|
||||
"notificationLink.foreground": "#58A6FF",
|
||||
"panel.background": "#252b3d",
|
||||
"panel.border": "#3a4158",
|
||||
"panelTitle.activeForeground": "#FFA500",
|
||||
"panelTitle.inactiveForeground": "#a8afc0",
|
||||
"panelTitle.activeBorder": "#FF6B35"
|
||||
},
|
||||
"editor.tokenColorCustomizations": {
|
||||
"textMateRules": [
|
||||
{
|
||||
"scope": "comment",
|
||||
"settings": {
|
||||
"foreground": "#8891a4",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["keyword", "storage.type", "storage.modifier"],
|
||||
"settings": {
|
||||
"foreground": "#FF6347"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["string", "string.quoted"],
|
||||
"settings": {
|
||||
"foreground": "#3FB950"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["constant.numeric", "constant.language"],
|
||||
"settings": {
|
||||
"foreground": "#BC8CFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["variable", "support.variable"],
|
||||
"settings": {
|
||||
"foreground": "#58A6FF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.function", "support.function"],
|
||||
"settings": {
|
||||
"foreground": "#D29922"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.type", "entity.name.class", "support.class"],
|
||||
"settings": {
|
||||
"foreground": "#39C5CF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.tag"],
|
||||
"settings": {
|
||||
"foreground": "#3FB950"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.other.attribute-name"],
|
||||
"settings": {
|
||||
"foreground": "#39C5CF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["support.type.property-name"],
|
||||
"settings": {
|
||||
"foreground": "#58A6FF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["meta.import", "meta.export"],
|
||||
"settings": {
|
||||
"foreground": "#BC8CFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["punctuation.definition.tag"],
|
||||
"settings": {
|
||||
"foreground": "#a8afc0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"editor.fontFamily": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, 'Courier New', monospace",
|
||||
"editor.fontSize": 13.5,
|
||||
"editor.lineHeight": 1.6,
|
||||
"editor.fontLigatures": true,
|
||||
"editor.fontWeight": "400",
|
||||
"editor.letterSpacing": 0.5,
|
||||
"editor.cursorBlinking": "expand",
|
||||
"editor.cursorSmoothCaretAnimation": "on",
|
||||
"editor.smoothScrolling": true,
|
||||
"workbench.list.smoothScrolling": true,
|
||||
"editor.minimap.enabled": false,
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": "active"
|
||||
}
|
||||
89
changelog
89
changelog
@ -1,79 +1,36 @@
|
||||
# 📦 ** Release 2.5.0 — ACL & WARP fixes, safer password generation, service controls 🚀**
|
||||
# 🔖 ** release(2.5.1): Telegram UX improvements, web panel access & outbound enhancements 🚀 **
|
||||
|
||||
*Released: 2025-12-19*
|
||||
*Released: 2026-01-02*
|
||||
|
||||
## ✨ New Features
|
||||
|
||||
### 🧠 **Core & Service Management**
|
||||
* 🤖 **Telegram Bot**: Added settings menu
|
||||
* 🌐 **Web Panel URL**:
|
||||
|
||||
* 🔍 Added **Hysteria2 core version detection** and validation
|
||||
* 📊 Display **core version** directly in the web panel
|
||||
* 🔁 Added **Restart Hysteria2** button in the web UI with proper fetch handling
|
||||
* 🔌 New API endpoint to **restart Hysteria2 service** programmatically
|
||||
* Added service status check before retrieving web panel URL
|
||||
* New handlers to retrieve and display web panel URL
|
||||
* 🖥️ **CLI**: Added option to display *only* the web panel URL
|
||||
* 🔀 **Outbounds**: Introduced new `select` outbound type(SingBox)
|
||||
* 👥 **Users**: Added option to display up to **1000 users** in selection dropdown
|
||||
* ⏳ **Expiration**: Added validation for expiration days and improved UI
|
||||
|
||||
### 🌍 **Geo & ACL Rule Management**
|
||||
### 🐛 Fixes
|
||||
|
||||
* 🗺️ Enhanced **Geo file update process** with intelligent ACL rule handling
|
||||
* 🧩 Added **geo rule detection & auto-update** for ACL configurations
|
||||
* 🧹 Removed redundant `geoip` / `geosite` rejection rules
|
||||
* 🚫 Removed speedtest-related rejection from ACL rules
|
||||
* 🛡️ Prevent enabling **Masquerade** when **OBFS** is active
|
||||
* ⚙️ Conditionally generate **sing-box OBFS** configuration(SingBox)
|
||||
|
||||
### 🛡️ **Security & Password Handling**
|
||||
### ♻️ Refactors & Improvements
|
||||
|
||||
* 🔐 Replaced `pwgen` with **Python `secrets` module** for secure password generation
|
||||
* 🔑 Improved password generation for:
|
||||
* 🧭 Introduced settings submenu structure in Telegram bot
|
||||
* 🔗 Removed username from URI fragments
|
||||
* 💾 Streamlined upgrade backup process and removed geo data download
|
||||
|
||||
* add-user
|
||||
* bulk users
|
||||
* internal password utilities
|
||||
* 🧪 Removed implicit password auto-generation where not required
|
||||
### 🔧 Dependencies
|
||||
|
||||
### 🧰 **CLI Improvements**
|
||||
* ⬆️ **FastAPI** bumped to `0.128.0`
|
||||
* ⬆️ **psutil** bumped to `7.2.1`
|
||||
* ⬆️ **pillow** bumped to `12.1.0`
|
||||
|
||||
* 📤 Added `run_cmd_and_stream` for **real-time command output streaming**
|
||||
* ⚙️ Improved error messages during **Hysteria2 installation**
|
||||
* 🔁 Updated install scripts to return meaningful output messages
|
||||
* 🔒 Replaced `pwgen` with **openssl** for:
|
||||
|
||||
* obfs password generation
|
||||
* normalsub path generation
|
||||
|
||||
### 🌐 **Network & IP Detection**
|
||||
|
||||
* 🌍 Added **ip.sb fallback** for public IP detection to improve reliability
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Fixes & Improvements
|
||||
|
||||
### 👤 **User & Username Handling**
|
||||
|
||||
* ✅ Allowed **underscores (`_`) in usernames**
|
||||
* 🧼 Improved validation regex and error handling
|
||||
* 🔧 Updated `remove-user` command syntax
|
||||
* 🧠 Improved user handler robustness
|
||||
|
||||
### 🎭 **Masquerade & OBFS**
|
||||
|
||||
* 🚫 Prevented **OBFS usage when masquerade is enabled** to avoid conflicts
|
||||
|
||||
### 🧹 **Scheduler & System Stability**
|
||||
|
||||
* 🔇 Suppressed noisy `systemctl` output during scheduler setup
|
||||
* 🧯 Improved rule-checking logic for WARP configuration status
|
||||
|
||||
### 🧼 **Uninstall & Cleanup**
|
||||
|
||||
* ♻️ Improved WARP uninstall flow:
|
||||
|
||||
* Better config loading & error handling
|
||||
* Automatic **ACL rule reset** after uninstall
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dependency Updates
|
||||
|
||||
* ⬆️ **FastAPI** → 0.124.4
|
||||
* ⬆️ **Certbot** → 5.2.2
|
||||
* ⬆️ **python-multipart** → 0.0.21
|
||||
### ❤️ Credits
|
||||
|
||||
Special thanks to **@MiliAxe** and **@SeyedHashtag** for their valuable contributions 🙌
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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": [],
|
||||
|
||||
@ -8,3 +8,5 @@ from .search import *
|
||||
from .serverinfo import *
|
||||
from .cpu import *
|
||||
from .check_version import *
|
||||
from .weburl import *
|
||||
from .settings import *
|
||||
@ -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
|
||||
10
core/scripts/telegrambot/utils/settings.py
Normal file
10
core/scripts/telegrambot/utils/settings.py
Normal 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())
|
||||
15
core/scripts/telegrambot/utils/weburl.py
Normal file
15
core/scripts/telegrambot/utils/weburl.py
Normal 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)
|
||||
@ -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>
|
||||
|
||||
@ -6,7 +6,7 @@ aiofiles==25.1.0
|
||||
click==8.3.1
|
||||
|
||||
# Utilities
|
||||
psutil==7.1.3
|
||||
psutil==7.2.1
|
||||
python-dotenv==1.2.1
|
||||
schedule==1.2.2
|
||||
requests==2.32.5
|
||||
@ -16,7 +16,7 @@ typing_extensions==4.15.0
|
||||
pyTelegramBotAPI==4.29.1
|
||||
qrcode==8.2
|
||||
pypng==0.20220715.0
|
||||
pillow==12.0.0
|
||||
pillow==12.1.0
|
||||
|
||||
# Cache / misc
|
||||
propcache==0.4.1
|
||||
@ -28,7 +28,7 @@ pymongo==4.15.5
|
||||
hysteria2-api==0.1.3
|
||||
|
||||
# Web panel (FastAPI stack)
|
||||
fastapi==0.124.4
|
||||
fastapi==0.128.0
|
||||
Jinja2==3.1.6
|
||||
python-multipart==0.0.21
|
||||
hypercorn==0.18.0
|
||||
|
||||
Reference in New Issue
Block a user