Compare commits

..

54 Commits

Author SHA1 Message Date
Aron Yang
22c233f0cd Fix TUN mode cleanup on Linux/macOS (#8202) 2025-10-26 11:09:23 +08:00
JieXu
b2d6282755 Remove AppImage. Update package.sh (#8201)
* Refactor AppRun script generation in packaging

* Update minimum kernel version requirement to 6.13

* Update minimum kernel version requirement to 5.14

* Revise runtime dependencies with version constraints

Updated runtime dependencies for package-rhel.sh to include version constraints and additional requirements.

* Modify package dependencies in package-debian.sh

Updated package dependencies to include libc6, fontconfig, coreutils, and bash.

* Remove AppImage packaging and upload steps

Removed AppImage packaging and upload steps from the workflow.

* Delete package-appimage.sh

* Simplify environment checks in Utils.cs

Removed checks for APPIMAGE environment variable and mount path.

* Update v2rayN.slnx

* Remove package scripts from v2rayN solution

Removed references to package-appimage.sh and pkg2appimage.yml from the solution file.
2025-10-26 10:13:49 +08:00
2dust
c8d89e3dce Adjusted the items in the configuration right-click menu 2025-10-25 20:10:42 +08:00
DHR60
d3b1810eab Update Directory.Packages.props (#8191) 2025-10-25 10:27:31 +08:00
JieXu
51409a3e28 Add French support | Ajouter le support du français | 添加法语支持 (#8186)
* Add files via upload

* Add files via upload

* Update Global.cs

* Add French resource file to project

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Delete v2rayN/AmazTool/Resx/Resource.fr.resx
2025-10-24 19:38:40 +08:00
2dust
1a0f50a41e up 7.15.6
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
2025-10-24 14:22:35 +08:00
2dust
83d4a9c18e Update Directory.Packages.props 2025-10-24 14:22:05 +08:00
2dust
b4c20e7b81 Bug fix
https://github.com/2dust/v2rayN/discussions/8168
2025-10-23 19:03:16 +08:00
DHR60
7c76308c93 Fix (#8180) 2025-10-23 17:58:02 +08:00
DHR60
f28fa31c14 Fix tcp dns (#8179) 2025-10-23 17:57:47 +08:00
DHR60
bbedc4dbb1 Fix (#8175) 2025-10-23 09:10:21 +08:00
DHR60
ecf42cb85d Fix dns (#8174) 2025-10-23 09:09:26 +08:00
2dust
e4701d6703 Add one-click test of real connection delay 2025-10-22 19:54:24 +08:00
mlds23
54a47d00a3 更新繁體中文翻譯 (#8166) 2025-10-22 17:00:19 +08:00
DHR60
964572817b Fix (#8161) 2025-10-22 09:07:53 +08:00
2dust
10358064dc up 7.15.5 2025-10-21 20:52:02 +08:00
2dust
6a19896915 Optimize the desktop version icon 2025-10-21 20:00:02 +08:00
DHR60
07e173eab1 Bootstrap DNS (#8160)
Also fix the handling of IPv6 domains
2025-10-21 17:28:48 +08:00
JieXu
91bca3a7ae Update Bug report (#8157)
* Update package-rhel.sh

* Update package-rhel.sh

* Update 01_bug_report.yml

* Fix formatting in bug report issue template

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update bug report template to remove checkmark

Removed the checkmark from the bug report template.

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Update bug report template header formatting

* Update 01_bug_report.yml

* Update 01_bug_report.yml

* Fix placeholder text in bug report template

* Update 01_bug_report.yml

* Create config.yml for issue templates

Add issue template configuration with contact links.

* Update config.yml

* Update config.yml

* Update config.yml
2025-10-21 09:29:52 +08:00
2dust
20260412a7 Remove Enable Security Protocol TLS v1.3 (subscription/update)
The TLS version is automatically negotiated by the operating system stack; by default it selects the highest version supported by both endpoints.
2025-10-20 20:17:13 +08:00
2dust
bca030002f Adjust some texts in Simplified Chinese. Please wait for other languages. 2025-10-19 19:43:31 +08:00
2dust
479bf8e037 Adjusted the items in the configuration right-click menu 2025-10-19 16:55:21 +08:00
2dust
cb5069bcfc Optimize and improve GlobalUsings 2025-10-19 14:13:26 +08:00
2dust
eb1339f2f5 Optimize and improve GlobalUsings 2025-10-19 13:54:40 +08:00
2dust
b66bfabd21 Optimize and improve GlobalUsings 2025-10-19 11:42:32 +08:00
2dust
3555d861ae Optimization and improvement 2025-10-19 11:05:28 +08:00
2dust
f39bc6d3b0 up 7.15.4 2025-10-17 20:59:27 +08:00
2dust
0c0ecc359b Fix
https://github.com/2dust/v2rayN/issues/8129
2025-10-16 12:18:14 +08:00
2dust
68713e7b77 MB/s 2025-10-15 19:46:10 +08:00
2dust
899b3fc97b up 7.15.3 2025-10-14 19:52:42 +08:00
DHR60
a1490d0ac1 Update singbox_fakeip_filter (#8117) 2025-10-12 09:59:30 +08:00
DHR60
b23f49ffce Remove unnecessary settings (#8107) 2025-10-11 19:22:26 +08:00
2dust
9a9e28e494 Update GlobalHotKeys 2025-10-10 19:25:54 +08:00
2dust
65ee5eb510 Fix,remove NaiveproxyFmt HysteriaFmt ,adjust ClashFmt
https://github.com/2dust/v2rayN/issues/8102
2025-10-10 17:12:45 +08:00
DHR60
1f42d32e1a Fix Freedom Resolver (#8100) 2025-10-10 16:58:18 +08:00
2dust
f2ed8c1d6b up 7.15.2 2025-10-09 20:29:45 +08:00
2dust
308b216d1b Adjust ActionPrecheckManager 2025-10-09 20:29:25 +08:00
2dust
c713f5c8f5 Update Directory.Packages.props 2025-10-09 20:22:41 +08:00
2dust
6771eb25d1 Adjust ActionPrecheckManager 2025-10-09 20:22:35 +08:00
2dust
91af50f99a Optimize code ,add IsGroupType extension. Adjust EConfigType 2025-10-08 17:13:54 +08:00
2dust
a559586e71 Code clean 2025-10-08 15:48:51 +08:00
2dust
929520775d Bug fix 2025-10-08 15:48:45 +08:00
2dust
4eaf31bbf8 Fix
https://github.com/2dust/v2rayN/issues/8092
2025-10-08 14:36:47 +08:00
2dust
1607525539 Optimize the ruletype UI 2025-10-08 14:12:16 +08:00
DHR60
31b5b4ca0c Add rule type selection to routing rules (#8007)
* Add rule type selection to routing rules

* Use enum

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-10-08 10:40:26 +08:00
2dust
64c7fea2bc Update Directory.Packages.props 2025-10-07 19:17:35 +08:00
2dust
f76fd364a2 Rename ProfileGroupItem.ParentIndexId to IndexId
Because ProfileGroupItem is an extension of ProfileItem, it is better to name the fields the same way.
2025-10-07 14:01:36 +08:00
2dust
0a1d6db9d1 Add cycle check for AddGroupServerViewModel 2025-10-07 13:54:31 +08:00
2dust
7a750a127e Rename ActionPrecheckService 2025-10-07 13:53:33 +08:00
2dust
fce4a7b74c Optimization and improvement, tray, etc.
https://github.com/2dust/v2rayN/pull/8083
2025-10-07 11:16:20 +08:00
DHR60
fec7353703 PreCheck (#7902)
* PreCheck

* Fix
2025-10-07 10:03:20 +08:00
Weheal
40c90d5b3b Fix: AutoHideStartup's bug of displaying window before hiding it. (#8083)
* Fix: AutoHideStartup's bug of displaying window before hiding it.

* Disable AutoHideStartup for Linux

* Revert "Disable AutoHideStartup for Linux"

This reverts commit 09f27e345575f9044c035a7fea055a83f5136fa2.

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-10-07 10:02:53 +08:00
2dust
9c58fec8d4 Bug fix 2025-10-05 19:55:52 +08:00
DHR60
11343a30fd Multi profile (#7929)
* Multi Profile

* VM and wpf

* avalonia

* Fix right click not working

* Exclude specific profile types from selection

* Rename

* Add Policy Group support

* Add generate policy group

* Adjust UI

* Add Proxy Chain support

* Fix

* Add fallback support

* Add PolicyGroup include other Group support

* Add group in traffic splitting support

* Avoid duplicate tags

* Refactor

* Adjust chained proxy, actual outbound is at the top

Based on actual network flow instead of data packets

* Add helper function

* Refactor

* Add chain selection control to group outbounds

* Avoid self-reference

* Fix

* Improves Tun2Socks address handling

* Avoids circular dependency in profile groups

Adds cycle detection to prevent infinite loops when evaluating profile groups.

This ensures that profile group configurations don't result in stack overflow errors when groups reference each other, directly or indirectly.

* Fix

* Fix

* Update ProfileGroupItem.cs

* Refactor

* Remove unnecessary checks

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-10-05 16:27:34 +08:00
212 changed files with 6301 additions and 1991 deletions

View File

@@ -3,6 +3,18 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
title: "[Bug]: " title: "[Bug]: "
labels: ["bug"] labels: ["bug"]
body: body:
- type: markdown
attributes:
value: |
### 报告 Bug 前请务必确认以下事项:
> ** **
> **✓ 已自行排除服务器端问题。**
> **✓ 已升级到最新客户端版本。**
> **✓ 已通过搜索确认没有人提出过相同问题。**
> **✓ 已确认自己的电脑系统环境是受支持的。**
---
- type: input - type: input
id: "os-version" id: "os-version"
attributes: attributes:
@@ -10,6 +22,7 @@ body:
description: "操作系统和版本" description: "操作系统和版本"
validations: validations:
required: true required: true
- type: input - type: input
id: "expectation" id: "expectation"
attributes: attributes:
@@ -17,6 +30,7 @@ body:
description: "描述你认为应该发生什么" description: "描述你认为应该发生什么"
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: "describe-the-bug" id: "describe-the-bug"
attributes: attributes:
@@ -24,22 +38,34 @@ body:
description: "描述实际发生了什么" description: "描述实际发生了什么"
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: "reproduction-method" id: "reproduction-method"
attributes: attributes:
label: "复现方法" label: "复现方法"
description: "在BUG出现前执行了哪些操作" description: "在BUG出现前执行了哪些操作"
placeholder: 标序号 placeholder: "标序号"
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: "log" id: "gui-log"
attributes: attributes:
label: "日志信息" label: "软件日志"
description: "位置在软件当前目录下的guiLogs" description: "位置在软件当前目录下的guiLogs"
placeholder: 在日志开始和结束位置粘贴冒号后的内容``` placeholder: "在日志开始和结束位置粘贴冒号后的内容到这:"
validations: validations:
required: true required: true
- type: textarea
id: "core-log"
attributes:
label: "内核日志"
description: "位置在软件主界面的信息框内"
placeholder: "在信息框内鼠标右键复制全部信息粘贴在这:"
validations:
required: true
- type: textarea - type: textarea
id: "more" id: "more"
attributes: attributes:
@@ -47,6 +73,7 @@ body:
description: "可选" description: "可选"
validations: validations:
required: false required: false
- type: checkboxes - type: checkboxes
id: "latest-version" id: "latest-version"
attributes: attributes:
@@ -55,6 +82,7 @@ body:
options: options:
- label: - label:
required: true required: true
- type: checkboxes - type: checkboxes
id: "issues" id: "issues"
attributes: attributes:
@@ -63,3 +91,12 @@ body:
options: options:
- label: - label:
required: true required: true
- type: checkboxes
id: "system-version"
attributes:
label: "我确认系统版本是受支持的"
description: "否则请切换后尝试"
options:
- label:
required: true

9
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
blank_issues_enabled: false
contact_links:
- name: Discussions / 讨论区
url: https://github.com/2dust/v2rayN/discussions
about: 使用问题或需要帮助请前往 Discussions。
- name: Wiki / 使用说明
url: https://github.com/2dust/v2rayN/wiki
about: 查看常见问题和使用文档。

View File

@@ -68,21 +68,6 @@ jobs:
file_glob: true file_glob: true
prerelease: true prerelease: true
- name: Package AppImage
if: github.event.inputs.release_tag != ''
run: |
chmod a+x package-appimage.sh
./package-appimage.sh
- name: Upload AppImage to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.AppImage
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
# release zip archive # release zip archive
- name: Package release zip archive - name: Package release zip archive
if: github.event.inputs.release_tag != '' if: github.event.inputs.release_tag != ''

View File

@@ -1,67 +0,0 @@
#!/bin/bash
set -euo pipefail
# Install deps
sudo apt update -y
sudo apt install -y libfuse2 wget file
# Get tools
wget -qO appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool
# x86_64 AppDir
APPDIR_X64="AppDir-x86_64"
rm -rf "$APPDIR_X64"
mkdir -p "$APPDIR_X64/usr/lib/v2rayN" "$APPDIR_X64/usr/bin" "$APPDIR_X64/usr/share/applications" "$APPDIR_X64/usr/share/pixmaps"
cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun"
chmod +x "$APPDIR_X64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN"
cat > "$APPDIR_X64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_X64/v2rayN.desktop" "$APPDIR_X64/usr/share/applications/v2rayN.desktop"
ARCH=x86_64 ./appimagetool "$APPDIR_X64" "v2rayN-${OutputArch}.AppImage"
file "v2rayN-${OutputArch}.AppImage" | grep -q 'x86-64'
# aarch64 AppDir
APPDIR_ARM64="AppDir-aarch64"
rm -rf "$APPDIR_ARM64"
mkdir -p "$APPDIR_ARM64/usr/lib/v2rayN" "$APPDIR_ARM64/usr/bin" "$APPDIR_ARM64/usr/share/applications" "$APPDIR_ARM64/usr/share/pixmaps"
cp -rf "$OutputPathArm64"/* "$APPDIR_ARM64/usr/lib/v2rayN" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_ARM64/AppRun"
chmod +x "$APPDIR_ARM64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN"
cat > "$APPDIR_ARM64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_ARM64/v2rayN.desktop" "$APPDIR_ARM64/usr/share/applications/v2rayN.desktop"
# aarch64 runtime
wget -qO runtime-aarch64 https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-aarch64
chmod +x runtime-aarch64
# build aarch64 AppImage
ARCH=aarch64 ./appimagetool --runtime-file ./runtime-aarch64 "$APPDIR_ARM64" "v2rayN-${OutputArchArm}.AppImage"
file "v2rayN-${OutputArchArm}.AppImage" | grep -q 'ARM aarch64'

View File

@@ -28,7 +28,7 @@ Package: v2rayN
Version: $Version Version: $Version
Architecture: $Arch2 Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN Maintainer: https://github.com/2dust/v2rayN
Depends: desktop-file-utils, xdg-utils Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1)
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF EOF

View File

@@ -614,8 +614,13 @@ ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL) # Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils Requires: glibc >= 2.34
Requires: fontconfig >= 2.13.1
Requires: desktop-file-utils >= 0.26
Requires: xdg-utils >= 1.1.3
Requires: coreutils >= 8.32
Requires: bash >= 5.1
%description %description
v2rayN Linux for Red Hat Enterprise Linux v2rayN Linux for Red Hat Enterprise Linux

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.15.1</Version> <Version>7.15.6</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -6,23 +6,23 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" /> <PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.7" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" /> <PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageVersion Include="CliWrap" Version="3.9.0" /> <PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" /> <PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" /> <PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" /> <PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.6.0" /> <PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="20.4.1" /> <PackageVersion Include="ReactiveUI" Version="22.2.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" /> <PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" /> <PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />
<PackageVersion Include="NLog" Version="6.0.4" /> <PackageVersion Include="NLog" Version="6.0.5" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />

View File

@@ -1,5 +1,3 @@
using ReactiveUI;
namespace ServiceLib.Base; namespace ServiceLib.Base;
public class MyReactiveObject : ReactiveObject public class MyReactiveObject : ReactiveObject

View File

@@ -1,6 +1,3 @@
using System.Collections.Concurrent;
using System.Reflection;
namespace ServiceLib.Common; namespace ServiceLib.Common;
public static class EmbedUtils public static class EmbedUtils

View File

@@ -84,4 +84,14 @@ public static class Extension
{ {
return source.Concat(new[] { string.Empty }).ToList(); return source.Concat(new[] { string.Empty }).ToList();
} }
public static bool IsGroupType(this EConfigType configType)
{
return configType is EConfigType.PolicyGroup or EConfigType.ProxyChain;
}
public static bool IsComplexType(this EConfigType configType)
{
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
}
} }

View File

@@ -1,6 +1,5 @@
using System.Formats.Tar; using System.Formats.Tar;
using System.IO.Compression; using System.IO.Compression;
using System.Text;
namespace ServiceLib.Common; namespace ServiceLib.Common;

View File

@@ -1,8 +1,3 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Common; namespace ServiceLib.Common;
public class JsonUtils public class JsonUtils

View File

@@ -1,5 +1,3 @@
using System.Diagnostics;
namespace ServiceLib.Common; namespace ServiceLib.Common;
public static class ProcUtils public static class ProcUtils

View File

@@ -1,13 +1,5 @@
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal; using System.Security.Principal;
using System.Text;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
@@ -363,6 +355,110 @@ public class Utils
return userHostsMap; return userHostsMap;
} }
/// <summary>
/// Parse a possibly non-standard URL into scheme, domain, port, and path.
/// If parsing fails, the entire input is returned as domain, and others are empty or zero.
/// </summary>
/// <param name="url">Input URL or string</param>
/// <returns>(domain, scheme, port, path)</returns>
public static (string domain, string scheme, int port, string path) ParseUrl(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return ("", "", 0, "");
}
// 1. First, try to parse using the standard Uri class.
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && !string.IsNullOrEmpty(uri.Host))
{
var scheme = uri.Scheme;
var domain = uri.Host;
var port = uri.IsDefaultPort ? 0 : uri.Port;
var path = uri.PathAndQuery;
return (domain, scheme, port, path);
}
// 2. Try to handle more general cases with a regular expression, including non-standard schemes.
// This regex captures the scheme (optional), authority (host+port), and path (optional).
var match = Regex.Match(url, @"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):/{2,})?([^/?#]+)([^?#]*)?.*$");
if (match.Success)
{
var scheme = match.Groups[1].Value;
var authority = match.Groups[2].Value;
var path = match.Groups[3].Value;
// Remove userinfo from the authority part.
var atIndex = authority.LastIndexOf('@');
if (atIndex > 0)
{
authority = authority.Substring(atIndex + 1);
}
var (domain, port) = ParseAuthority(authority);
// If the parsed domain is empty, it means the authority part is malformed, so trigger the fallback.
if (!string.IsNullOrEmpty(domain))
{
return (domain, scheme, port, path);
}
}
// 3. If all of the above fails, execute the final fallback strategy.
return (url, "", 0, "");
}
/// <summary>
/// Helper function to parse domain and port from the authority part, with correct handling for IPv6.
/// </summary>
private static (string domain, int port) ParseAuthority(string authority)
{
if (string.IsNullOrEmpty(authority))
{
return ("", 0);
}
var port = 0;
var domain = authority;
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]"))
{
int closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
{
// Port exists
var portStr = authority.Substring(closingBracketIndex + 2);
if (int.TryParse(portStr, out var portNum))
{
port = portNum;
}
domain = authority.Substring(0, closingBracketIndex + 1);
}
else
{
// No port
domain = authority;
}
}
else // Handle IPv4 or domain names
{
var lastColonIndex = authority.LastIndexOf(':');
// Ensure there are digits after the colon and that this colon is not part of an IPv6 address.
if (lastColonIndex > 0 && lastColonIndex < authority.Length - 1 && authority.Substring(lastColonIndex + 1).All(char.IsDigit))
{
var portStr = authority.Substring(lastColonIndex + 1);
if (int.TryParse(portStr, out var portNum))
{
port = portNum;
domain = authority.Substring(0, lastColonIndex);
}
}
}
return (domain, port);
}
#endregion #endregion
#region #region
@@ -898,11 +994,6 @@ public class Utils
return false; return false;
} }
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath(); var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? ""; var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/'); var p = baseDir.Replace('\\', '/');
@@ -912,11 +1003,6 @@ public class Utils
return false; return false;
} }
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase)) if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;

View File

@@ -1,5 +1,3 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32; using Microsoft.Win32;
namespace ServiceLib.Common; namespace ServiceLib.Common;

View File

@@ -12,5 +12,7 @@ public enum EConfigType
TUIC = 8, TUIC = 8,
WireGuard = 9, WireGuard = 9,
HTTP = 10, HTTP = 10,
Anytls = 11 Anytls = 11,
PolicyGroup = 101,
ProxyChain = 102,
} }

View File

@@ -2,8 +2,9 @@ namespace ServiceLib.Enums;
public enum EMultipleLoad public enum EMultipleLoad
{ {
LeastPing,
Fallback,
Random, Random,
RoundRobin, RoundRobin,
LeastPing,
LeastLoad LeastLoad
} }

View File

@@ -0,0 +1,8 @@
namespace ServiceLib.Enums;
public enum ERuleType
{
ALL = 0,
Routing = 1,
DNS = 2,
}

View File

@@ -5,5 +5,6 @@ public enum ESpeedActionType
Tcping, Tcping,
Realping, Realping,
Speedtest, Speedtest,
Mixedtest Mixedtest,
FastRealping
} }

View File

@@ -23,6 +23,7 @@ public enum EViewAction
RoutingRuleDetailsWindow, RoutingRuleDetailsWindow,
AddServerWindow, AddServerWindow,
AddServer2Window, AddServer2Window,
AddGroupServerWindow,
DNSSettingWindow, DNSSettingWindow,
RoutingSettingWindow, RoutingSettingWindow,
OptionSettingWindow, OptionSettingWindow,

View File

@@ -1,5 +1,3 @@
using System.Reactive;
namespace ServiceLib.Events; namespace ServiceLib.Events;
public static class AppEvents public static class AppEvents

View File

@@ -1,5 +1,3 @@
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
namespace ServiceLib.Events; namespace ServiceLib.Events;

View File

@@ -50,6 +50,7 @@ public class Global
public const string DirectTag = "direct"; public const string DirectTag = "direct";
public const string BlockTag = "block"; public const string BlockTag = "block";
public const string DnsTag = "dns-module"; public const string DnsTag = "dns-module";
public const string BalancerTagSuffix = "-round";
public const string StreamSecurity = "tls"; public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality"; public const string StreamSecurityReality = "reality";
public const string Loopback = "127.0.0.1"; public const string Loopback = "127.0.0.1";
@@ -83,8 +84,7 @@ public class Global
public const string SingboxDirectDNSTag = "direct_dns"; public const string SingboxDirectDNSTag = "direct_dns";
public const string SingboxRemoteDNSTag = "remote_dns"; public const string SingboxRemoteDNSTag = "remote_dns";
public const string SingboxOutboundResolverTag = "outbound_resolver"; public const string SingboxLocalDNSTag = "local_local";
public const string SingboxFinalResolverTag = "final_resolver";
public const string SingboxHostsDNSTag = "hosts_dns"; public const string SingboxHostsDNSTag = "hosts_dns";
public const string SingboxFakeDNSTag = "fake_dns"; public const string SingboxFakeDNSTag = "fake_dns";
@@ -315,6 +315,8 @@ public class Global
EConfigType.HTTP, EConfigType.HTTP,
]; ];
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
public static readonly List<string> DomainStrategies = public static readonly List<string> DomainStrategies =
[ [
AsIs, AsIs,
@@ -425,6 +427,7 @@ public class Global
"zh-Hant", "zh-Hant",
"en", "en",
"fa-Ir", "fa-Ir",
"fr",
"ru", "ru",
"hu" "hu"
]; ];

View File

@@ -1,14 +1,36 @@
global using System.Collections.Concurrent;
global using System.Diagnostics;
global using System.Net;
global using System.Net.NetworkInformation;
global using System.Net.Sockets;
global using System.Reactive;
global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using System.Reflection;
global using System.Runtime.InteropServices;
global using System.Security.Cryptography;
global using System.Text;
global using System.Text.Encodings.Web;
global using System.Text.Json;
global using System.Text.Json.Nodes;
global using System.Text.Json.Serialization;
global using System.Text.RegularExpressions;
global using DynamicData;
global using DynamicData.Binding;
global using ReactiveUI;
global using ReactiveUI.Fody.Helpers;
global using ServiceLib.Base; global using ServiceLib.Base;
global using ServiceLib.Common; global using ServiceLib.Common;
global using ServiceLib.Enums; global using ServiceLib.Enums;
global using ServiceLib.Events; global using ServiceLib.Events;
global using ServiceLib.Handler; global using ServiceLib.Handler;
global using ServiceLib.Handler.Fmt;
global using ServiceLib.Handler.SysProxy;
global using ServiceLib.Helper; global using ServiceLib.Helper;
global using ServiceLib.Manager; global using ServiceLib.Manager;
global using ServiceLib.Handler.Fmt;
global using ServiceLib.Services;
global using ServiceLib.Services.Statistics;
global using ServiceLib.Services.CoreConfig;
global using ServiceLib.Models; global using ServiceLib.Models;
global using ServiceLib.Resx; global using ServiceLib.Resx;
global using ServiceLib.Handler.SysProxy; global using ServiceLib.Services;
global using ServiceLib.Services.CoreConfig;
global using ServiceLib.Services.Statistics;
global using SQLite;

View File

@@ -1,5 +1,4 @@
using System.Security.Principal; using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler; namespace ServiceLib.Handler;

View File

@@ -1,5 +1,4 @@
using System.Data; using System.Data;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler; namespace ServiceLib.Handler;
@@ -113,10 +112,8 @@ public static class ConfigHandler
config.ConstItem ??= new ConstItem(); config.ConstItem ??= new ConstItem();
config.SimpleDNSItem ??= InitBuiltinSimpleDNS(); config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
if (config.SimpleDNSItem.GlobalFakeIp is null) config.SimpleDNSItem.GlobalFakeIp ??= true;
{ config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
config.SimpleDNSItem.GlobalFakeIp = true;
}
config.SpeedTestItem ??= new(); config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10) if (config.SpeedTestItem.SpeedTestTimeout < 10)
@@ -357,6 +354,11 @@ public static class ConfigHandler
{ {
} }
} }
else if (profileItem.ConfigType.IsGroupType())
{
var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId);
await AddGroupServerCommon(config, profileItem, profileGroupItem, true);
}
else else
{ {
await AddServerCommon(config, profileItem, true); await AddServerCommon(config, profileItem, true);
@@ -1074,6 +1076,37 @@ public static class ConfigHandler
return 0; return 0;
} }
public static async Task<int> AddGroupServerCommon(Config config, ProfileItem profileItem, ProfileGroupItem profileGroupItem, bool toFile = true)
{
var maxSort = -1;
if (profileItem.IndexId.IsNullOrEmpty())
{
profileItem.IndexId = Utils.GetGuid(false);
maxSort = ProfileExManager.Instance.GetMaxSort();
}
var groupType = profileItem.ConfigType == EConfigType.ProxyChain ? EConfigType.ProxyChain.ToString() : profileGroupItem.MultipleLoad.ToString();
profileItem.Address = $"{profileItem.CoreType}-{groupType}";
if (maxSort > 0)
{
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
}
if (toFile)
{
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
if (profileGroupItem != null)
{
profileGroupItem.IndexId = profileItem.IndexId;
await ProfileGroupItemManager.Instance.SaveItemAsync(profileGroupItem);
}
else
{
ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(profileItem.IndexId);
await ProfileGroupItemManager.Instance.SaveTo();
}
}
return 0;
}
/// <summary> /// <summary>
/// Compare two profile items to determine if they represent the same server /// Compare two profile items to determine if they represent the same server
/// Used for deduplication and server matching /// Used for deduplication and server matching
@@ -1145,7 +1178,7 @@ public static class ConfigHandler
} }
/// <summary> /// <summary>
/// Create a custom server that combines multiple servers for load balancing /// Create a group server that combines multiple servers for load balancing
/// Generates a configuration file that references multiple servers /// Generates a configuration file that references multiple servers
/// </summary> /// </summary>
/// <param name="config">Current configuration</param> /// <param name="config">Current configuration</param>
@@ -1153,45 +1186,55 @@ public static class ConfigHandler
/// <param name="coreType">Core type to use (Xray or sing_box)</param> /// <param name="coreType">Core type to use (Xray or sing_box)</param>
/// <param name="multipleLoad">Load balancing algorithm</param> /// <param name="multipleLoad">Load balancing algorithm</param>
/// <returns>Result object with success state and data</returns> /// <returns>Result object with success state and data</returns>
public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad) public static async Task<RetResult> AddGroupServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad, string? subId)
{ {
var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName); var result = new RetResult();
var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, multipleLoad); var indexId = Utils.GetGuid(false);
if (result.Success != true) var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
{
return result;
}
if (!File.Exists(configPath)) var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} ";
{
return result;
}
var profileItem = await AppManager.Instance.GetProfileItem(indexId) ?? new();
profileItem.IndexId = indexId;
if (coreType == ECoreType.Xray) if (coreType == ECoreType.Xray)
{ {
profileItem.Remarks = multipleLoad switch remark += multipleLoad switch
{ {
EMultipleLoad.Random => ResUI.menuSetDefaultMultipleServerXrayRandom, EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
EMultipleLoad.RoundRobin => ResUI.menuSetDefaultMultipleServerXrayRoundRobin, EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
EMultipleLoad.LeastPing => ResUI.menuSetDefaultMultipleServerXrayLeastPing, EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
EMultipleLoad.LeastLoad => ResUI.menuSetDefaultMultipleServerXrayLeastLoad, EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
_ => ResUI.menuSetDefaultMultipleServerXrayRoundRobin, EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
_ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
}; };
} }
else if (coreType == ECoreType.sing_box) else if (coreType == ECoreType.sing_box)
{ {
profileItem.Remarks = ResUI.menuSetDefaultMultipleServerSingBoxLeastPing; remark += multipleLoad switch
{
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
_ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
};
} }
profileItem.Address = Global.CoreMultipleLoadConfigFileName; var profile = new ProfileItem
profileItem.ConfigType = EConfigType.Custom; {
profileItem.CoreType = coreType; IndexId = indexId,
CoreType = coreType,
await AddServerCommon(config, profileItem, true); ConfigType = EConfigType.PolicyGroup,
Remarks = remark,
IsSub = false
};
if (!subId.IsNullOrEmpty())
{
profile.Subid = subId;
}
var profileGroup = new ProfileGroupItem
{
ChildItems = childProfileIndexId,
MultipleLoad = multipleLoad,
IndexId = indexId,
};
var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
result.Success = ret == 0;
result.Data = indexId; result.Data = indexId;
return result; return result;
} }
@@ -1209,12 +1252,21 @@ public static class ConfigHandler
ProfileItem? itemSocks = null; ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun) if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
{ {
var tun2SocksAddress = node.Address;
if (node.ConfigType.IsGroupType())
{
var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
if (lstAddresses.Count > 0)
{
tun2SocksAddress = Utils.List2String(lstAddresses);
}
}
itemSocks = new ProfileItem() itemSocks = new ProfileItem()
{ {
CoreType = ECoreType.sing_box, CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS, ConfigType = EConfigType.SOCKS,
Address = Global.Loopback, Address = Global.Loopback,
SpiderX = node.Address, // Tun2SocksAddress SpiderX = tun2SocksAddress, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
}; };
} }
@@ -1431,15 +1483,6 @@ public static class ConfigHandler
{ {
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks); profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
} }
if (profileItem is null)
{
profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
}
//Is naiveproxy configuration
if (profileItem is null)
{
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
}
if (profileItem is null || profileItem.Address.IsNullOrEmpty()) if (profileItem is null || profileItem.Address.IsNullOrEmpty())
{ {
return -1; return -1;
@@ -2229,8 +2272,7 @@ public static class ConfigHandler
BlockBindingQuery = true, BlockBindingQuery = true,
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(), RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
SingboxOutboundsResolveDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), BootstrapDNS = Global.DomainPureIPDNSAddress.FirstOrDefault(),
SingboxFinalResolveDNS = Global.DomainPureIPDNSAddress.FirstOrDefault()
}; };
} }

View File

@@ -1,5 +1,3 @@
using System.Net;
namespace ServiceLib.Handler; namespace ServiceLib.Handler;
public static class ConnectionHandler public static class ConnectionHandler

View File

@@ -132,24 +132,4 @@ public static class CoreConfigHandler
await File.WriteAllTextAsync(fileName, result.Data.ToString()); await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result; return result;
} }
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
} }

View File

@@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
if (Contains(strData, "external-controller", "-port", "proxies")) if (Contains(strData, "rules", "-port", "proxies"))
{ {
var fileName = WriteAllText(strData, "yaml"); var fileName = WriteAllText(strData, "yaml");

View File

@@ -63,24 +63,6 @@ public class Hysteria2Fmt : BaseFmt
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
} }
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.hysteria,
Address = fileName,
Remarks = subRemarks ?? "hysteria_custom"
};
return profileItem;
}
return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks) public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{ {
if (Contains(strData, "server", "auth", "up", "down", "listen")) if (Contains(strData, "server", "auth", "up", "down", "listen"))

View File

@@ -1,22 +0,0 @@
namespace ServiceLib.Handler.Fmt;
public class NaiveproxyFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.naiveproxy,
Address = fileName,
Remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
}
}

View File

@@ -1,5 +1,3 @@
using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt;
public class ShadowsocksFmt : BaseFmt public class ShadowsocksFmt : BaseFmt

View File

@@ -1,4 +1,3 @@
using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption; using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy; namespace ServiceLib.Handler.SysProxy;

View File

@@ -1,4 +1,3 @@
using System.Net;
using Downloader; using Downloader;
namespace ServiceLib.Helper; namespace ServiceLib.Helper;

View File

@@ -1,8 +1,5 @@
using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Text;
namespace ServiceLib.Helper; namespace ServiceLib.Helper;

View File

@@ -1,5 +1,4 @@
using System.Collections; using System.Collections;
using SQLite;
namespace ServiceLib.Helper; namespace ServiceLib.Helper;

View File

@@ -0,0 +1,282 @@
namespace ServiceLib.Manager;
/// <summary>
/// Centralized pre-checks before sensitive actions (set active profile, generate config, etc.).
/// </summary>
public class ActionPrecheckManager(Config config)
{
private static readonly Lazy<ActionPrecheckManager> _instance = new(() => new ActionPrecheckManager(AppManager.Instance.Config));
public static ActionPrecheckManager Instance => _instance.Value;
private readonly Config _config = config;
public async Task<List<string>> Check(string? indexId)
{
if (indexId.IsNullOrEmpty())
{
return [ResUI.PleaseSelectServer];
}
var item = await AppManager.Instance.GetProfileItem(indexId);
if (item is null)
{
return [ResUI.PleaseSelectServer];
}
return await Check(item);
}
public async Task<List<string>> Check(ProfileItem? item)
{
if (item is null)
{
return [ResUI.PleaseSelectServer];
}
var errors = new List<string>();
errors.AddRange(await ValidateCurrentNodeAndCoreSupport(item));
errors.AddRange(await ValidateRelatedNodesExistAndValid(item));
return errors;
}
private async Task<List<string>> ValidateCurrentNodeAndCoreSupport(ProfileItem item)
{
if (item.ConfigType == EConfigType.Custom)
{
return [];
}
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
return await ValidateNodeAndCoreSupport(item, coreType);
}
private async Task<List<string>> ValidateNodeAndCoreSupport(ProfileItem item, ECoreType? coreType = null)
{
var errors = new List<string>();
coreType ??= AppManager.Instance.GetCoreType(item, item.ConfigType);
if (item.ConfigType is EConfigType.Custom)
{
errors.Add(string.Format(ResUI.NotSupportProtocol, item.ConfigType.ToString()));
return errors;
}
if (!item.IsComplex())
{
if (item.Address.IsNullOrEmpty())
{
errors.Add(string.Format(ResUI.InvalidProperty, "Address"));
return errors;
}
if (item.Port is <= 0 or >= 65536)
{
errors.Add(string.Format(ResUI.InvalidProperty, "Port"));
return errors;
}
switch (item.ConfigType)
{
case EConfigType.VMess:
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
break;
case EConfigType.VLESS:
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
if (!Global.Flows.Contains(item.Flow))
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
break;
case EConfigType.Shadowsocks:
if (item.Id.IsNullOrEmpty())
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
break;
}
if (item.ConfigType is EConfigType.VLESS or EConfigType.Trojan
&& item.StreamSecurity == Global.StreamSecurityReality
&& item.PublicKey.IsNullOrEmpty())
{
errors.Add(string.Format(ResUI.InvalidProperty, "PublicKey"));
}
if (errors.Count > 0)
{
return errors;
}
}
if (item.ConfigType.IsGroupType())
{
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
if (group is null || group.ChildItems.IsNullOrEmpty())
{
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
return errors;
}
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
if (hasCycle)
{
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
return errors;
}
foreach (var child in Utils.String2List(group.ChildItems))
{
var childErrors = new List<string>();
if (child.IsNullOrEmpty())
{
continue;
}
var childItem = await AppManager.Instance.GetProfileItem(child);
if (childItem is null)
{
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
continue;
}
if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain)
{
childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks));
continue;
}
childErrors.AddRange(await ValidateNodeAndCoreSupport(childItem, coreType));
errors.AddRange(childErrors);
}
return errors;
}
var net = item.GetNetwork() ?? item.Network;
if (coreType == ECoreType.sing_box)
{
// sing-box does not support xhttp / kcp
// sing-box does not support transports like ws/http/httpupgrade/etc. when the node is not vmess/trojan/vless
if (net is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
{
errors.Add(string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net));
return errors;
}
if (item.ConfigType is not (EConfigType.VMess or EConfigType.VLESS or EConfigType.Trojan))
{
if (net is nameof(ETransport.ws) or nameof(ETransport.http) or nameof(ETransport.h2) or nameof(ETransport.quic) or nameof(ETransport.httpupgrade))
{
errors.Add(string.Format(ResUI.CoreNotSupportProtocolTransport, nameof(ECoreType.sing_box), item.ConfigType.ToString(), net));
return errors;
}
}
}
else if (coreType is ECoreType.Xray)
{
// Xray core does not support these protocols
if (!Global.XraySupportConfigType.Contains(item.ConfigType)
&& !item.IsComplex())
{
errors.Add(string.Format(ResUI.CoreNotSupportProtocol, nameof(ECoreType.Xray), item.ConfigType.ToString()));
return errors;
}
}
return errors;
}
private async Task<List<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
{
var errors = new List<string>();
errors.AddRange(await ValidateProxyChainedNodeExistAndValid(item));
errors.AddRange(await ValidateRoutingNodeExistAndValid(item));
return errors;
}
private async Task<List<string>> ValidateProxyChainedNodeExistAndValid(ProfileItem? item)
{
var errors = new List<string>();
if (item is null)
{
return errors;
}
// prev node and next node
var subItem = await AppManager.Instance.GetSubItem(item.Subid);
if (subItem is null)
{
return errors;
}
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
await CollectProxyChainedNodeValidation(prevNode, subItem.PrevProfile, coreType, errors);
await CollectProxyChainedNodeValidation(nextNode, subItem.NextProfile, coreType, errors);
return errors;
}
private async Task CollectProxyChainedNodeValidation(ProfileItem? node, string tag, ECoreType coreType, List<string> errors)
{
if (node is not null)
{
var nodeErrors = await ValidateNodeAndCoreSupport(node, coreType);
errors.AddRange(nodeErrors.Select(s => ResUI.ProxyChainedPrefix + s));
}
else if (tag.IsNotEmpty())
{
errors.Add(ResUI.ProxyChainedPrefix + string.Format(ResUI.NodeTagNotExist, tag));
}
}
private async Task<List<string>> ValidateRoutingNodeExistAndValid(ProfileItem? item)
{
var errors = new List<string>();
if (item is null)
{
return errors;
}
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing == null)
{
return errors;
}
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var ruleItem in rules ?? [])
{
if (!ruleItem.Enabled)
{
continue;
}
var outboundTag = ruleItem.OutboundTag;
if (outboundTag.IsNullOrEmpty() || Global.OutboundTags.Contains(outboundTag))
{
continue;
}
var tagItem = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
if (tagItem is null)
{
errors.Add(ResUI.RoutingRuleOutboundPrefix + string.Format(ResUI.NodeTagNotExist, outboundTag));
continue;
}
var tagErrors = await ValidateNodeAndCoreSupport(tagItem, coreType);
errors.AddRange(tagErrors.Select(s => ResUI.RoutingRuleOutboundPrefix + s));
}
return errors;
}
}

View File

@@ -64,6 +64,7 @@ public sealed class AppManager
SQLiteHelper.Instance.CreateTable<ProfileExItem>(); SQLiteHelper.Instance.CreateTable<ProfileExItem>();
SQLiteHelper.Instance.CreateTable<DNSItem>(); SQLiteHelper.Instance.CreateTable<DNSItem>();
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>(); SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
return true; return true;
} }
@@ -207,6 +208,15 @@ public sealed class AppManager
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks); return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
} }
public async Task<ProfileGroupItem?> GetProfileGroupItem(string indexId)
{
if (indexId.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
}
public async Task<List<RoutingItem>?> RoutingItems() public async Task<List<RoutingItem>?> RoutingItems()
{ {
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync(); return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();

View File

@@ -1,4 +1,3 @@
using System.Text;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
@@ -35,7 +34,7 @@ public class CoreAdminManager
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine("#!/bin/bash"); sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
sb.AppendLine($"sudo -S {cmdLine}"); sb.AppendLine($"exec sudo -S -- {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var procService = new ProcessService( var procService = new ProcessService(

View File

@@ -95,7 +95,7 @@ public class CoreManager
public async Task<ProcessService?> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<ProcessService?> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray; var coreType = selecteds.Any(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName); var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);

View File

@@ -1,6 +1,3 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;
public class PacManager public class PacManager

View File

@@ -1,5 +1,3 @@
using System.Collections.Concurrent;
//using System.Reactive.Linq; //using System.Reactive.Linq;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;

View File

@@ -0,0 +1,284 @@
namespace ServiceLib.Manager;
public class ProfileGroupItemManager
{
private static readonly Lazy<ProfileGroupItemManager> _instance = new(() => new());
private ConcurrentDictionary<string, ProfileGroupItem> _items = new();
public static ProfileGroupItemManager Instance => _instance.Value;
private static readonly string _tag = "ProfileGroupItemManager";
private ProfileGroupItemManager()
{
}
public async Task Init()
{
await InitData();
}
// Read-only getters: do not create or mark dirty
public bool TryGet(string indexId, out ProfileGroupItem? item)
{
item = null;
if (string.IsNullOrWhiteSpace(indexId))
{
return false;
}
return _items.TryGetValue(indexId, out item);
}
public ProfileGroupItem? GetOrDefault(string indexId)
{
return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )");
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
}
private ProfileGroupItem AddProfileGroupItem(string indexId)
{
var profileGroupItem = new ProfileGroupItem()
{
IndexId = indexId,
ChildItems = string.Empty,
MultipleLoad = EMultipleLoad.LeastPing
};
_items[indexId] = profileGroupItem;
return profileGroupItem;
}
private ProfileGroupItem GetProfileGroupItem(string indexId)
{
if (string.IsNullOrEmpty(indexId))
{
indexId = Utils.GetGuid(false);
}
return _items.GetOrAdd(indexId, AddProfileGroupItem);
}
public async Task ClearAll()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
_items.Clear();
}
public async Task SaveTo()
{
try
{
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
var existsMap = lstExists.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!);
var lstInserts = new List<ProfileGroupItem>();
var lstUpdates = new List<ProfileGroupItem>();
foreach (var item in _items.Values)
{
if (string.IsNullOrEmpty(item.IndexId))
{
continue;
}
if (existsMap.ContainsKey(item.IndexId))
{
lstUpdates.Add(item);
}
else
{
lstInserts.Add(item);
}
}
try
{
if (lstInserts.Count > 0)
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public ProfileGroupItem GetOrCreateAndMarkDirty(string indexId)
{
return GetProfileGroupItem(indexId);
}
public async ValueTask DisposeAsync()
{
await SaveTo();
}
public async Task SaveItemAsync(ProfileGroupItem item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
if (string.IsNullOrWhiteSpace(item.IndexId))
{
throw new ArgumentException("IndexId required", nameof(item));
}
_items[item.IndexId] = item;
try
{
var lst = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().Where(t => t.IndexId == item.IndexId).ToListAsync();
if (lst != null && lst.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(new List<ProfileGroupItem> { item });
}
else
{
await SQLiteHelper.Instance.InsertAllAsync(new List<ProfileGroupItem> { item });
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
#region Helper
public static bool HasCycle(string? indexId)
{
return HasCycle(indexId, new HashSet<string>(), new HashSet<string>());
}
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
{
if (indexId.IsNullOrEmpty())
return false;
if (stack.Contains(indexId))
return true;
if (visited.Contains(indexId))
return false;
visited.Add(indexId);
stack.Add(indexId);
try
{
Instance.TryGet(indexId, out var groupItem);
if (groupItem == null || groupItem.ChildItems.IsNullOrEmpty())
{
return false;
}
var childIds = Utils.String2List(groupItem.ChildItems)
.Where(p => !string.IsNullOrEmpty(p))
.ToList();
if (childIds == null)
{
return false;
}
foreach (var child in childIds)
{
if (HasCycle(child, visited, stack))
{
return true;
}
}
return false;
}
finally
{
stack.Remove(indexId);
}
}
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
{
Instance.TryGet(indexId, out var profileGroupItem);
if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
{
return (new List<ProfileItem>(), profileGroupItem);
}
var items = await GetChildProfileItems(profileGroupItem);
return (items, profileGroupItem);
}
public static async Task<List<ProfileItem>> GetChildProfileItems(ProfileGroupItem? group)
{
if (group == null || group.ChildItems.IsNullOrEmpty())
{
return new();
}
var childProfiles = (await Task.WhenAll(
Utils.String2List(group.ChildItems)
.Where(p => !p.IsNullOrEmpty())
.Select(AppManager.Instance.GetProfileItem)
))
.Where(p =>
p != null &&
p.IsValid() &&
p.ConfigType != EConfigType.Custom
)
.ToList();
return childProfiles;
}
public static async Task<HashSet<string>> GetAllChildDomainAddresses(string indexId)
{
// include grand children
var childAddresses = new HashSet<string>();
if (!Instance.TryGet(indexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
return childAddresses;
var childIds = Utils.String2List(groupItem.ChildItems);
foreach (var childId in childIds)
{
var childNode = await AppManager.Instance.GetProfileItem(childId);
if (childNode == null)
continue;
if (!childNode.IsComplex())
{
childAddresses.Add(childNode.Address);
}
else if (childNode.ConfigType.IsGroupType())
{
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
foreach (var addr in subAddresses)
{
childAddresses.Add(addr);
}
}
}
return childAddresses;
}
#endregion Helper
}

View File

@@ -26,15 +26,29 @@ public class TaskManager
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
//Execute once 1 minute //Execute once 1 minute
await UpdateTaskRunSubscription(); try
{
await UpdateTaskRunSubscription();
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - UpdateTaskRunSubscription", ex);
}
//Execute once 20 minute //Execute once 20 minute
if (numOfExecuted % 20 == 0) if (numOfExecuted % 20 == 0)
{ {
//Logging.SaveLog("Execute save config"); //Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(_config); try
await ProfileExManager.Instance.SaveTo(); {
await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo();
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - SaveConfig", ex);
}
} }
//Execute once 1 hour //Execute once 1 hour
@@ -46,8 +60,14 @@ public class TaskManager
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1)); FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1)); FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour try
await UpdateTaskRunGeo(numOfExecuted / 60); {
await UpdateTaskRunGeo(numOfExecuted / 60);
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - UpdateTaskRunGeo", ex);
}
} }
numOfExecuted++; numOfExecuted++;

View File

@@ -1,4 +1,3 @@
using System.Net;
using WebDav; using WebDav;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;

View File

@@ -1,6 +1,3 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models; namespace ServiceLib.Models;
public class CheckUpdateModel : ReactiveObject public class CheckUpdateModel : ReactiveObject

View File

@@ -1,6 +1,3 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -71,7 +71,6 @@ public class GUIItem
public bool DisplayRealTimeSpeed { get; set; } public bool DisplayRealTimeSpeed { get; set; }
public bool KeepOlderDedupl { get; set; } public bool KeepOlderDedupl { get; set; }
public int AutoUpdateInterval { get; set; } public int AutoUpdateInterval { get; set; }
public bool EnableSecurityProtocolTls13 { get; set; }
public int TrayMenuServersLimit { get; set; } = 20; public int TrayMenuServersLimit { get; set; } = 20;
public bool EnableHWA { get; set; } = false; public bool EnableHWA { get; set; } = false;
public bool EnableLog { get; set; } = true; public bool EnableLog { get; set; } = true;
@@ -264,8 +263,7 @@ public class SimpleDNSItem
public bool? BlockBindingQuery { get; set; } public bool? BlockBindingQuery { get; set; }
public string? DirectDNS { get; set; } public string? DirectDNS { get; set; }
public string? RemoteDNS { get; set; } public string? RemoteDNS { get; set; }
public string? SingboxOutboundsResolveDNS { get; set; } public string? BootstrapDNS { get; set; }
public string? SingboxFinalResolveDNS { get; set; }
public string? RayStrategy4Freedom { get; set; } public string? RayStrategy4Freedom { get; set; }
public string? SingboxStrategy4Direct { get; set; } public string? SingboxStrategy4Direct { get; set; }
public string? SingboxStrategy4Proxy { get; set; } public string? SingboxStrategy4Proxy { get; set; }

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models;
public class GitHubReleaseAsset public class GitHubReleaseAsset

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -0,0 +1,12 @@
namespace ServiceLib.Models;
[Serializable]
public class ProfileGroupItem
{
[PrimaryKey]
public string IndexId { get; set; }
public string ChildItems { get; set; }
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
}

View File

@@ -1,6 +1,3 @@
using ReactiveUI;
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]
@@ -32,18 +29,21 @@ public class ProfileItem : ReactiveObject
public string GetSummary() public string GetSummary()
{ {
var summary = $"[{(ConfigType).ToString()}] "; var summary = $"[{(ConfigType).ToString()}] ";
var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.'); if (IsComplex())
var addr = arrAddr.Length switch
{ {
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}", summary += $"[{CoreType.ToString()}]{Remarks}";
> 1 => $"***{arrAddr.Last()}", }
_ => Address else
};
summary += ConfigType switch
{ {
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}", var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
_ => $"{Remarks}({addr}:{Port})" var addr = arrAddr.Length switch
}; {
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
> 1 => $"***{arrAddr.Last()}",
_ => Address
};
summary += $"{Remarks}({addr}:{Port})";
}
return summary; return summary;
} }
@@ -61,6 +61,51 @@ public class ProfileItem : ReactiveObject
return Network.TrimEx(); return Network.TrimEx();
} }
public bool IsComplex()
{
return ConfigType.IsComplexType();
}
public bool IsValid()
{
if (IsComplex())
return true;
if (Address.IsNullOrEmpty() || Port is <= 0 or >= 65536)
return false;
switch (ConfigType)
{
case EConfigType.VMess:
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
return false;
break;
case EConfigType.VLESS:
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
return false;
if (!Global.Flows.Contains(Flow))
return false;
break;
case EConfigType.Shadowsocks:
if (Id.IsNullOrEmpty())
return false;
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
return false;
break;
}
if ((ConfigType is EConfigType.VLESS or EConfigType.Trojan)
&& StreamSecurity == Global.StreamSecurityReality
&& PublicKey.IsNullOrEmpty())
{
return false;
}
return true;
}
#endregion function #endregion function
[PrimaryKey] [PrimaryKey]

View File

@@ -1,5 +1,3 @@
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -15,4 +15,5 @@ public class RulesItem
public List<string>? Process { get; set; } public List<string>? Process { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public string? Remarks { get; set; } public string? Remarks { get; set; }
public ERuleType? RuleType { get; set; }
} }

View File

@@ -7,4 +7,5 @@ public class RulesItemModel : RulesItem
public string Ips { get; set; } public string Ips { get; set; }
public string Domains { get; set; } public string Domains { get; set; }
public string Protocols { get; set; } public string Protocols { get; set; }
public string RuleTypeName { get; set; }
} }

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models;
public class SingboxConfig public class SingboxConfig
@@ -145,6 +143,7 @@ public class Outbound4Sbox : BaseServer4Sbox
public string? plugin_opts { get; set; } public string? plugin_opts { get; set; }
public List<string>? outbounds { get; set; } public List<string>? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; } public bool? interrupt_exist_connections { get; set; }
public int? tolerance { get; set; }
} }
public class Endpoints4Sbox : BaseServer4Sbox public class Endpoints4Sbox : BaseServer4Sbox

View File

@@ -1,5 +1,3 @@
using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models;
public class V2rayConfig public class V2rayConfig
@@ -217,6 +215,7 @@ public class Dns4Ray
public class DnsServer4Ray public class DnsServer4Ray
{ {
public string? address { get; set; } public string? address { get; set; }
public int? port { get; set; }
public List<string>? domains { get; set; } public List<string>? domains { get; set; }
public bool? skipFallback { get; set; } public bool? skipFallback { get; set; }
public List<string>? expectedIPs { get; set; } public List<string>? expectedIPs { get; set; }

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models;
/// <summary> /// <summary>

View File

@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。 // (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI { public class ResUI {
@@ -114,6 +114,33 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Core &apos;{0}&apos; does not support network type &apos;{1}&apos;. 的本地化字符串。
/// </summary>
public static string CoreNotSupportNetwork {
get {
return ResourceManager.GetString("CoreNotSupportNetwork", resourceCulture);
}
}
/// <summary>
/// 查找类似 Core &apos;{0}&apos; does not support protocol &apos;{1}&apos;. 的本地化字符串。
/// </summary>
public static string CoreNotSupportProtocol {
get {
return ResourceManager.GetString("CoreNotSupportProtocol", resourceCulture);
}
}
/// <summary>
/// 查找类似 Core &apos;{0}&apos; does not support protocol &apos;{1}&apos; when using transport &apos;{2}&apos;. 的本地化字符串。
/// </summary>
public static string CoreNotSupportProtocolTransport {
get {
return ResourceManager.GetString("CoreNotSupportProtocolTransport", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。 /// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
/// </summary> /// </summary>
@@ -267,6 +294,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Group &apos;{0}&apos; is empty. Please add at least one node. 的本地化字符串。
/// </summary>
public static string GroupEmpty {
get {
return ResourceManager.GetString("GroupEmpty", resourceCulture);
}
}
/// <summary>
/// 查找类似 {0} Group cannot reference itself or have a circular reference 的本地化字符串。
/// </summary>
public static string GroupSelfReference {
get {
return ResourceManager.GetString("GroupSelfReference", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 This is not the correct configuration, please check 的本地化字符串。 /// 查找类似 This is not the correct configuration, please check 的本地化字符串。
/// </summary> /// </summary>
@@ -294,6 +339,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 The {0} property is invalid, please check. 的本地化字符串。
/// </summary>
public static string InvalidProperty {
get {
return ResourceManager.GetString("InvalidProperty", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Invalid address (URL) 的本地化字符串。 /// 查找类似 Invalid address (URL) 的本地化字符串。
/// </summary> /// </summary>
@@ -538,7 +592,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Speed (M/s) 的本地化字符串。 /// 查找类似 Speed (MB/s) 的本地化字符串。
/// </summary> /// </summary>
public static string LvTestSpeed { public static string LvTestSpeed {
get { get {
@@ -672,6 +726,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Add Child Configuration 的本地化字符串。
/// </summary>
public static string menuAddChildServer {
get {
return ResourceManager.GetString("menuAddChildServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。 /// 查找类似 Add a custom configuration Configuration 的本地化字符串。
/// </summary> /// </summary>
@@ -699,6 +762,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Add Policy Group Configuration 的本地化字符串。
/// </summary>
public static string menuAddPolicyGroupServer {
get {
return ResourceManager.GetString("menuAddPolicyGroupServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Add Proxy Chain Configuration 的本地化字符串。
/// </summary>
public static string menuAddProxyChainServer {
get {
return ResourceManager.GetString("menuAddProxyChainServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。 /// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。
/// </summary> /// </summary>
@@ -942,6 +1023,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Test real delay 的本地化字符串。
/// </summary>
public static string menuFastRealPing {
get {
return ResourceManager.GetString("menuFastRealPing", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Full Config Template Setting 的本地化字符串。 /// 查找类似 Full Config Template Setting 的本地化字符串。
/// </summary> /// </summary>
@@ -951,6 +1041,78 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Generate Policy Group from Multiple Profiles 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServer {
get {
return ResourceManager.GetString("menuGenGroupMultipleServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxFallback {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxLeastPing {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayFallback {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastLoad {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastLoad", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastPing {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRandom {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRandom", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRoundRobin {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRoundRobin", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Global Hotkey Setting 的本地化字符串。 /// 查找类似 Global Hotkey Setting 的本地化字符串。
/// </summary> /// </summary>
@@ -1320,6 +1482,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Remove Child Configuration 的本地化字符串。
/// </summary>
public static string menuRemoveChildServer {
get {
return ResourceManager.GetString("menuRemoveChildServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Remove duplicate Configurations 的本地化字符串。 /// 查找类似 Remove duplicate Configurations 的本地化字符串。
/// </summary> /// </summary>
@@ -1473,6 +1644,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Server List 的本地化字符串。
/// </summary>
public static string menuServerList {
get {
return ResourceManager.GetString("menuServerList", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Configurations 的本地化字符串。 /// 查找类似 Configurations 的本地化字符串。
/// </summary> /// </summary>
@@ -1482,60 +1662,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Multi-Configuration to custom configuration 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServer {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServerSingBoxLeastPing {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServerSingBoxLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServerXrayLeastLoad {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastLoad", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServerXrayLeastPing {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServerXrayRandom {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRandom", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServerXrayRoundRobin {
get {
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRoundRobin", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Set as active Configuration (Enter) 的本地化字符串。 /// 查找类似 Set as active Configuration (Enter) 的本地化字符串。
/// </summary> /// </summary>
@@ -1941,6 +2067,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Node alias &apos;{0}&apos; does not exist. 的本地化字符串。
/// </summary>
public static string NodeTagNotExist {
get {
return ResourceManager.GetString("NodeTagNotExist", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Non-VMess or SS protocol 的本地化字符串。 /// 查找类似 Non-VMess or SS protocol 的本地化字符串。
/// </summary> /// </summary>
@@ -1968,6 +2103,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Not support protocol &apos;{0}&apos;. 的本地化字符串。
/// </summary>
public static string NotSupportProtocol {
get {
return ResourceManager.GetString("NotSupportProtocol", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Scan completed, no valid QR code found 的本地化字符串。 /// 查找类似 Scan completed, no valid QR code found 的本地化字符串。
/// </summary> /// </summary>
@@ -1995,6 +2139,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Please Add At Least One Configuration 的本地化字符串。
/// </summary>
public static string PleaseAddAtLeastOneServer {
get {
return ResourceManager.GetString("PleaseAddAtLeastOneServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Please fill Remarks 的本地化字符串。 /// 查找类似 Please fill Remarks 的本地化字符串。
/// </summary> /// </summary>
@@ -2040,6 +2193,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Policy group: 的本地化字符串。
/// </summary>
public static string PolicyGroupPrefix {
get {
return ResourceManager.GetString("PolicyGroupPrefix", resourceCulture);
}
}
/// <summary>
/// 查找类似 Proxy chained: 的本地化字符串。
/// </summary>
public static string ProxyChainedPrefix {
get {
return ResourceManager.GetString("ProxyChainedPrefix", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。 /// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。
/// </summary> /// </summary>
@@ -2103,6 +2274,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Routing rule outbound: 的本地化字符串。
/// </summary>
public static string RoutingRuleOutboundPrefix {
get {
return ResourceManager.GetString("RoutingRuleOutboundPrefix", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Run as Admin 的本地化字符串。 /// 查找类似 Run as Admin 的本地化字符串。
/// </summary> /// </summary>
@@ -2338,7 +2518,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Prevent domain-based routing rules from failing 的本地化字符串。 /// 查找类似 Block ECH and HTTP/3 availability checks when enabled 的本地化字符串。
/// </summary> /// </summary>
public static string TbBlockSVCBHTTPSQueriesTips { public static string TbBlockSVCBHTTPSQueriesTips {
get { get {
@@ -2346,6 +2526,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Bootstrap DNS 的本地化字符串。
/// </summary>
public static string TbBootstrapDNS {
get {
return ResourceManager.GetString("TbBootstrapDNS", resourceCulture);
}
}
/// <summary>
/// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
/// </summary>
public static string TbBootstrapDNSTips {
get {
return ResourceManager.GetString("TbBootstrapDNSTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Browse 的本地化字符串。 /// 查找类似 Browse 的本地化字符串。
/// </summary> /// </summary>
@@ -2373,6 +2571,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Policy Group 的本地化字符串。
/// </summary>
public static string TbConfigTypePolicyGroup {
get {
return ResourceManager.GetString("TbConfigTypePolicyGroup", resourceCulture);
}
}
/// <summary>
/// 查找类似 Proxy Chain 的本地化字符串。
/// </summary>
public static string TbConfigTypeProxyChain {
get {
return ResourceManager.GetString("TbConfigTypeProxyChain", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Confirm 的本地化字符串。 /// 查找类似 Confirm 的本地化字符串。
/// </summary> /// </summary>
@@ -2418,6 +2634,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 V2ray Custom DNS 的本地化字符串。
/// </summary>
public static string TbCustomDnsRay {
get {
return ResourceManager.GetString("TbCustomDnsRay", resourceCulture);
}
}
/// <summary>
/// 查找类似 sing-box Custom DNS 的本地化字符串。
/// </summary>
public static string TbCustomDnsSingbox {
get {
return ResourceManager.GetString("TbCustomDnsSingbox", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Display GUI 的本地化字符串。 /// 查找类似 Display GUI 的本地化字符串。
/// </summary> /// </summary>
@@ -2526,6 +2760,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Fallback 的本地化字符串。
/// </summary>
public static string TbFallback {
get {
return ResourceManager.GetString("TbFallback", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Fingerprint 的本地化字符串。 /// 查找类似 Fingerprint 的本地化字符串。
/// </summary> /// </summary>
@@ -2643,6 +2886,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Most Stable 的本地化字符串。
/// </summary>
public static string TbLeastLoad {
get {
return ResourceManager.GetString("TbLeastLoad", resourceCulture);
}
}
/// <summary>
/// 查找类似 Lowest Latency 的本地化字符串。
/// </summary>
public static string TbLeastPing {
get {
return ResourceManager.GetString("TbLeastPing", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Address (IPv4, IPv6) 的本地化字符串。 /// 查找类似 Address (IPv4, IPv6) 的本地化字符串。
/// </summary> /// </summary>
@@ -2697,6 +2958,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Policy Group Type 的本地化字符串。
/// </summary>
public static string TbPolicyGroupType {
get {
return ResourceManager.GetString("TbPolicyGroupType", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Port 的本地化字符串。 /// 查找类似 Port 的本地化字符串。
/// </summary> /// </summary>
@@ -2769,6 +3039,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Random 的本地化字符串。
/// </summary>
public static string TbRandom {
get {
return ResourceManager.GetString("TbRandom", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 v2ray Full Config Template 的本地化字符串。 /// 查找类似 v2ray Full Config Template 的本地化字符串。
/// </summary> /// </summary>
@@ -2805,6 +3084,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Via proxy — please ensure remote availability 的本地化字符串。
/// </summary>
public static string TbRemoteDNSTips {
get {
return ResourceManager.GetString("TbRemoteDNSTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Camouflage domain(host) 的本地化字符串。 /// 查找类似 Camouflage domain(host) 的本地化字符串。
/// </summary> /// </summary>
@@ -2832,6 +3120,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Round Robin 的本地化字符串。
/// </summary>
public static string TbRoundRobin {
get {
return ResourceManager.GetString("TbRoundRobin", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。 /// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
/// </summary> /// </summary>
@@ -2914,11 +3211,20 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Bootstrap DNS (sing-box) 的本地化字符串。 /// 查找类似 Rule Type 的本地化字符串。
/// </summary> /// </summary>
public static string TbSBBootstrapDNS { public static string TbRuleType {
get { get {
return ResourceManager.GetString("TbSBBootstrapDNS", resourceCulture); return ResourceManager.GetString("TbRuleType", resourceCulture);
}
}
/// <summary>
/// 查找类似 You can set separate rules for Routing and DNS, or select &quot;ALL&quot; to apply to both 的本地化字符串。
/// </summary>
public static string TbRuleTypeTips {
get {
return ResourceManager.GetString("TbRuleTypeTips", resourceCulture);
} }
} }
@@ -2931,15 +3237,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
/// </summary>
public static string TbSBFallbackDNSResolve {
get {
return ResourceManager.GetString("TbSBFallbackDNSResolve", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 sing-box Full Config Template 的本地化字符串。 /// 查找类似 sing-box Full Config Template 的本地化字符串。
/// </summary> /// </summary>
@@ -2958,24 +3255,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Resolve Outbound Domains 的本地化字符串。
/// </summary>
public static string TbSBOutboundDomainResolve {
get {
return ResourceManager.GetString("TbSBOutboundDomainResolve", resourceCulture);
}
}
/// <summary>
/// 查找类似 Outbound DNS Resolution (sing-box) 的本地化字符串。
/// </summary>
public static string TbSBOutboundsResolverDNS {
get {
return ResourceManager.GetString("TbSBOutboundsResolverDNS", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。 /// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
/// </summary> /// </summary>
@@ -3111,24 +3390,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 V2ray DNS settings 的本地化字符串。
/// </summary>
public static string TbSettingsCoreDns {
get {
return ResourceManager.GetString("TbSettingsCoreDns", resourceCulture);
}
}
/// <summary>
/// 查找类似 sing-box DNS settings 的本地化字符串。
/// </summary>
public static string TbSettingsCoreDnsSingbox {
get {
return ResourceManager.GetString("TbSettingsCoreDnsSingbox", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Core: KCP settings 的本地化字符串。 /// 查找类似 Core: KCP settings 的本地化字符串。
/// </summary> /// </summary>
@@ -3408,6 +3669,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 If the system does not have a tray function, please do not enable it 的本地化字符串。
/// </summary>
public static string TbSettingsHide2TrayWhenCloseTip {
get {
return ResourceManager.GetString("TbSettingsHide2TrayWhenCloseTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Hysteria Max bandwidth (Up/Down) 的本地化字符串。 /// 查找类似 Hysteria Max bandwidth (Up/Down) 的本地化字符串。
/// </summary> /// </summary>
@@ -3723,15 +3993,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Enable Security Protocol TLS v1.3 (subscription/update) 的本地化字符串。
/// </summary>
public static string TbSettingsTLS13 {
get {
return ResourceManager.GetString("TbSettingsTLS13", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。 /// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
/// </summary> /// </summary>
@@ -4005,9 +4266,9 @@ namespace ServiceLib.Resx {
/// <summary> /// <summary>
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。 /// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
/// </summary> /// </summary>
public static string TbXrayFreedomResolveStrategy { public static string TbXrayFreedomStrategy {
get { get {
return ResourceManager.GetString("TbXrayFreedomResolveStrategy", resourceCulture); return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture);
} }
} }

View File

@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>هسته: تنظیمات اولیه</value> <value>هسته: تنظیمات اولیه</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>تنظیمات V2ray DNS</value> <value>V2ray Custom DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>هسته: تنظیمات KCP</value> <value>هسته: تنظیمات KCP</value>
@@ -747,9 +747,6 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>تنظیمات پراکسی سیستم</value> <value>تنظیمات پراکسی سیستم</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>فعال کردن پروتکل امنیتی TLS نسخه 1.3 (اشتراک/به‌روزرسانی)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value> <value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
</data> </data>
@@ -892,7 +889,7 @@
<value>تاخیر (میلی‌ثانیه)</value> <value>تاخیر (میلی‌ثانیه)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>سرعت (M/s)</value> <value>سرعت (MB/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Core اجرا نشد، لطفاً گزارش را ببینید</value> <value>Core اجرا نشد، لطفاً گزارش را ببینید</value>
@@ -1011,8 +1008,8 @@
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>تنظیمات DNS</value> <value>تنظیمات DNS</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>تنظیمات DNS sing-box</value> <value>sing-box Custom DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value> <value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
@@ -1377,22 +1374,22 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>مخفی و پورت می شود، با کاما (،) جدا می شود</value> <value>مخفی و پورت می شود، با کاما (،) جدا می شود</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>چند سرور به پیکربندی سفارشی</value> <value>Generate Policy Group from Multiple Profiles</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>چند سرور تصادفی توسط Xray</value> <value>چند سرور تصادفی توسط Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>چند سرور RoundRobin توسط Xray</value> <value>چند سرور RoundRobin توسط Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>چند سرور LeastPing توسط Xray</value> <value>چند سرور LeastPing توسط Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>چند سرور LeastLoad توسط Xray</value> <value>چند سرور LeastLoad توسط Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>LeastPing چند سرور توسط sing-box</value> <value>LeastPing چند سرور توسط sing-box</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
@@ -1419,19 +1416,10 @@
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value> <value>Domestic DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>Outbound DNS Resolution (sing-box)</value> <value>Via proxy — please ensure remote availability</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Resolve Outbound Domains</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
@@ -1471,7 +1459,7 @@
<value>Custom DNS Enabled, This Page's Settings Invalid</value> <value>Custom DNS Enabled, This Page's Settings Invalid</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Prevent domain-based routing rules from failing</value> <value>Block ECH and HTTP/3 availability checks when enabled</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Please fill in the correct config template</value> <value>Please fill in the correct config template</value>
@@ -1512,4 +1500,103 @@
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value> <value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>Proxy chained: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>Routing rule outbound: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>Policy group: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>Node alias '{0}' does not exist.</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>Group '{0}' is empty. Please add at least one node.</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>The {0} property is invalid, please check.</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} Group cannot reference itself or have a circular reference</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>Not support protocol '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>If the system does not have a tray function, please do not enable it</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root> </root>

File diff suppressed because it is too large Load Diff

View File

@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Core: alapbeállítások</value> <value>Core: alapbeállítások</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>V2ray DNS beállítások</value> <value>V2ray Custom DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP beállítások</value> <value>Core: KCP beállítások</value>
@@ -747,9 +747,6 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>Rendszerproxy beállítások</value> <value>Rendszerproxy beállítások</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Biztonsági protokoll TLS v1.3 engedélyezése (előfizetés/frissítés)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tálca jobb egérgombos menü konfigurációk megjelenítési limitje</value> <value>Tálca jobb egérgombos menü konfigurációk megjelenítési limitje</value>
</data> </data>
@@ -892,7 +889,7 @@
<value>Késleltetés (ms)</value> <value>Késleltetés (ms)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>Sebesség (M/s)</value> <value>Sebesség (MB/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Nem sikerült futtatni a Core-t, kérjük, ellenőrizze a prompt információt</value> <value>Nem sikerült futtatni a Core-t, kérjük, ellenőrizze a prompt információt</value>
@@ -1011,8 +1008,8 @@
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS beállítások</value> <value>DNS beállítások</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box DNS beállítások</value> <value>sing-box Custom DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value> <value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
@@ -1377,22 +1374,22 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>A portot lefedi, vesszővel (,) elválasztva</value> <value>A portot lefedi, vesszővel (,) elválasztva</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Több konfiguráció egyéni konfigurációra</value> <value>Generate Policy Group from Multiple Profiles</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Több konfiguráció véletlenszerűen Xray szerint</value> <value>Több konfiguráció véletlenszerűen Xray szerint</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Több konfiguráció RoundRobin Xray szerint</value> <value>Több konfiguráció RoundRobin Xray szerint</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Több konfiguráció legkisebb pinggel Xray szerint</value> <value>Több konfiguráció legkisebb pinggel Xray szerint</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Több konfiguráció legkisebb terheléssel Xray szerint</value> <value>Több konfiguráció legkisebb terheléssel Xray szerint</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Több konfiguráció legkisebb pinggel sing-box szerint</value> <value>Több konfiguráció legkisebb pinggel sing-box szerint</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
@@ -1419,19 +1416,10 @@
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value> <value>Domestic DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>Outbound DNS Resolution (sing-box)</value> <value>Via proxy — please ensure remote availability</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Resolve Outbound Domains</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
@@ -1471,7 +1459,7 @@
<value>Custom DNS Enabled, This Page's Settings Invalid</value> <value>Custom DNS Enabled, This Page's Settings Invalid</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Prevent domain-based routing rules from failing</value> <value>Block ECH and HTTP/3 availability checks when enabled</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Please fill in the correct config template</value> <value>Please fill in the correct config template</value>
@@ -1512,4 +1500,103 @@
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value> <value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>Proxy chained: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>Routing rule outbound: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>Policy group: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>Node alias '{0}' does not exist.</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>Group '{0}' is empty. Please add at least one node.</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>The {0} property is invalid, please check.</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} Group cannot reference itself or have a circular reference</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>Not support protocol '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>If the system does not have a tray function, please do not enable it</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root> </root>

View File

@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Core: basic settings</value> <value>Core: basic settings</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>V2ray DNS settings</value> <value>V2ray Custom DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP settings</value> <value>Core: KCP settings</value>
@@ -747,9 +747,6 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>System proxy settings</value> <value>System proxy settings</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Enable Security Protocol TLS v1.3 (subscription/update)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tray right-click menu Configurations display limit</value> <value>Tray right-click menu Configurations display limit</value>
</data> </data>
@@ -892,7 +889,7 @@
<value>Delay (ms)</value> <value>Delay (ms)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>Speed (M/s)</value> <value>Speed (MB/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Failed to run Core, please check the prompt information</value> <value>Failed to run Core, please check the prompt information</value>
@@ -1011,8 +1008,8 @@
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS Settings</value> <value>DNS Settings</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box DNS settings</value> <value>sing-box Custom DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Please fill in DNS Structure, Click to view the document</value> <value>Please fill in DNS Structure, Click to view the document</value>
@@ -1377,22 +1374,22 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value> <value>Will cover the port, separate with commas (,)</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Multi-Configuration to custom configuration</value> <value>Generate Policy Group from Multiple Profiles</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Multi-Configuration Random by Xray</value> <value>Multi-Configuration Random by Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Multi-Configuration RoundRobin by Xray</value> <value>Multi-Configuration RoundRobin by Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Multi-Configuration LeastPing by Xray</value> <value>Multi-Configuration LeastPing by Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Multi-Configuration LeastLoad by Xray</value> <value>Multi-Configuration LeastLoad by Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Multi-Configuration LeastPing by sing-box</value> <value>Multi-Configuration LeastPing by sing-box</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
@@ -1419,19 +1416,10 @@
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value> <value>Domestic DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>Outbound DNS Resolution (sing-box)</value> <value>Via proxy — please ensure remote availability</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Resolve Outbound Domains</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value> <value>xray Freedom Resolution Strategy</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
@@ -1471,7 +1459,7 @@
<value>Custom DNS Enabled, This Page's Settings Invalid</value> <value>Custom DNS Enabled, This Page's Settings Invalid</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Prevent domain-based routing rules from failing</value> <value>Block ECH and HTTP/3 availability checks when enabled</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Please fill in the correct config template</value> <value>Please fill in the correct config template</value>
@@ -1512,4 +1500,103 @@
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value> <value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>Proxy chained: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>Routing rule outbound: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>Policy group: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>Node alias '{0}' does not exist.</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>Group '{0}' is empty. Please add at least one node.</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>The {0} property is invalid, please check.</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} Group cannot reference itself or have a circular reference</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>Not support protocol '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>If the system does not have a tray function, please do not enable it</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root> </root>

View File

@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Ядро: базовые настройки</value> <value>Ядро: базовые настройки</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>Настройки DNS V2ray</value> <value>V2ray Custom DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Ядро: настройки KCP</value> <value>Ядро: настройки KCP</value>
@@ -747,9 +747,6 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>Настройки системного прокси</value> <value>Настройки системного прокси</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Включить протокол безопасности TLS v1.3 (обновление подписки)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Лимит серверов в меню трея</value> <value>Лимит серверов в меню трея</value>
</data> </data>
@@ -1011,8 +1008,8 @@
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>Настройки DNS</value> <value>Настройки DNS</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>Настройки DNS sing-box</value> <value>sing-box Custom DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value> <value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
@@ -1377,22 +1374,22 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Заменит указанный порт, перечисляйте через запятую (,)</value> <value>Заменит указанный порт, перечисляйте через запятую (,)</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>От мультиконфигурации к пользовательской конфигурации</value> <value>Generate Policy Group from Multiple Profiles</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Случайный (Xray)</value> <value>Случайный (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Круговой (Xray)</value> <value>Круговой (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value> <value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Минимальная нагрузка (Xray)</value> <value>Минимальная нагрузка (Xray)</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value> <value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
@@ -1419,19 +1416,10 @@
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>Внутренний DNS</value> <value>Внутренний DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>Резолвер DNS для исходящих (sing-box)</value> <value>Via proxy — please ensure remote availability</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Разрешать домены для исходящих соединений</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>Стратегия резолвинга Freedom (Xray)</value> <value>Стратегия резолвинга Freedom (Xray)</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
@@ -1471,7 +1459,7 @@
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value> <value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Предотвращает сбои доменных правил маршрутизации</value> <value>Block ECH and HTTP/3 availability checks when enabled</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Пожалуйста, заполните корректный шаблон конфигурации</value> <value>Пожалуйста, заполните корректный шаблон конфигурации</value>
@@ -1512,4 +1500,103 @@
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value> <value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>Proxy chained: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>Routing rule outbound: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>Policy group: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>Node alias '{0}' does not exist.</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>Group '{0}' is empty. Please add at least one node.</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>The {0} property is invalid, please check.</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} Group cannot reference itself or have a circular reference</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>Not support protocol '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>If the system does not have a tray function, please do not enable it</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root> </root>

View File

@@ -121,7 +121,7 @@
<value>导出分享链接至剪贴板成功</value> <value>导出分享链接至剪贴板成功</value>
</data> </data>
<data name="CheckServerSettings" xml:space="preserve"> <data name="CheckServerSettings" xml:space="preserve">
<value>请先检查配置文件设置</value> <value>请先检查设置</value>
</data> </data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve"> <data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>配置格式不正确</value> <value>配置格式不正确</value>
@@ -133,7 +133,7 @@
<value>下载开始...</value> <value>下载开始...</value>
</data> </data>
<data name="FailedConversionConfiguration" xml:space="preserve"> <data name="FailedConversionConfiguration" xml:space="preserve">
<value>转换配置文件失败</value> <value>转换配置失败</value>
</data> </data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve"> <data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>生成默认配置文件失败</value> <value>生成默认配置文件失败</value>
@@ -142,10 +142,10 @@
<value>获取默认配置失败</value> <value>获取默认配置失败</value>
</data> </data>
<data name="FailedImportedCustomServer" xml:space="preserve"> <data name="FailedImportedCustomServer" xml:space="preserve">
<value>导入自定义配置文件失败</value> <value>导入自定义配置失败</value>
</data> </data>
<data name="FailedReadConfiguration" xml:space="preserve"> <data name="FailedReadConfiguration" xml:space="preserve">
<value>读取配置文件失败</value> <value>读取配置失败</value>
</data> </data>
<data name="FillCorrectServerPort" xml:space="preserve"> <data name="FillCorrectServerPort" xml:space="preserve">
<value>请填写正确格式的端口</value> <value>请填写正确格式的端口</value>
@@ -265,13 +265,13 @@
<value>请选择协议</value> <value>请选择协议</value>
</data> </data>
<data name="PleaseSelectServer" xml:space="preserve"> <data name="PleaseSelectServer" xml:space="preserve">
<value>请先选择配置文件</value> <value>请先选择配置</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>配置文件去重完成。原数量: {0},现数量: {1}。</value> <value>配置去重完成。原数量: {0},现数量: {1}。</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>是否确定移除配置文件</value> <value>是否确定移除?</value>
</data> </data>
<data name="SaveClientConfigurationIn" xml:space="preserve"> <data name="SaveClientConfigurationIn" xml:space="preserve">
<value>客户端配置文件保存在:{0}</value> <value>客户端配置文件保存在:{0}</value>
@@ -283,10 +283,10 @@
<value>配置成功。 {0}</value> <value>配置成功。 {0}</value>
</data> </data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve"> <data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>成功导入自定义配置文件</value> <value>成功导入自定义配置</value>
</data> </data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve"> <data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>成功从剪贴板导入 {0} 个配置文件</value> <value>成功从剪贴板导入 {0} 个配置</value>
</data> </data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve"> <data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>扫描导入分享链接成功</value> <value>扫描导入分享链接成功</value>
@@ -385,7 +385,7 @@
<value>所有</value> <value>所有</value>
</data> </data>
<data name="FillServerAddressCustom" xml:space="preserve"> <data name="FillServerAddressCustom" xml:space="preserve">
<value>请浏览导入配置文件配置</value> <value>请浏览导入配置</value>
</data> </data>
<data name="Speedtesting" xml:space="preserve"> <data name="Speedtesting" xml:space="preserve">
<value>测试中...</value> <value>测试中...</value>
@@ -397,7 +397,7 @@
<value>本地</value> <value>本地</value>
</data> </data>
<data name="MsgServerTitle" xml:space="preserve"> <data name="MsgServerTitle" xml:space="preserve">
<value>配置文件过滤器,按回车执行</value> <value>过滤器,按回车执行</value>
</data> </data>
<data name="menuCheckUpdate" xml:space="preserve"> <data name="menuCheckUpdate" xml:space="preserve">
<value>检查更新</value> <value>检查更新</value>
@@ -427,7 +427,7 @@
<value>路由设置</value> <value>路由设置</value>
</data> </data>
<data name="menuServers" xml:space="preserve"> <data name="menuServers" xml:space="preserve">
<value>配置文件</value> <value>配置</value>
</data> </data>
<data name="menuSetting" xml:space="preserve"> <data name="menuSetting" xml:space="preserve">
<value>设置</value> <value>设置</value>
@@ -478,55 +478,55 @@
<value>扫描屏幕上的二维码 (Ctrl+S)</value> <value>扫描屏幕上的二维码 (Ctrl+S)</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>克隆所选配置文件</value> <value>克隆所选</value>
</data> </data>
<data name="menuRemoveDuplicateServer" xml:space="preserve"> <data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>移除重复的配置文件</value> <value>移除重复</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>移除所选配置文件 (多选) (Delete)</value> <value>移除所选 (多选) (Delete)</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>设为活动配置文件 (Enter)</value> <value>设为活动 (Enter)</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服务统计数据</value> <value>清除所有服务统计数据</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>测试配置文件真连接延迟 (多选) (Ctrl+R)</value> <value>测试真连接延迟 (多选) (Ctrl+R)</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>按测试结果排序</value> <value>按测试结果排序</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>测试配置文件速度 (多选) (Ctrl+T)</value> <value>测试速度 (多选) (Ctrl+T)</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>测试配置文件延迟 Tcping (多选) (Ctrl+O)</value> <value>测试延迟 Tcping (多选) (Ctrl+O)</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>导出所选配置文件完整配置</value> <value>导出所选完整配置</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value> <value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>添加自定义配置文件</value> <value>添加自定义配置</value>
</data> </data>
<data name="menuAddShadowsocksServer" xml:space="preserve"> <data name="menuAddShadowsocksServer" xml:space="preserve">
<value>添加 [Shadowsocks] 配置文件</value> <value>添加 [Shadowsocks]</value>
</data> </data>
<data name="menuAddSocksServer" xml:space="preserve"> <data name="menuAddSocksServer" xml:space="preserve">
<value>添加 [SOCKS] 配置文件</value> <value>添加 [SOCKS] </value>
</data> </data>
<data name="menuAddTrojanServer" xml:space="preserve"> <data name="menuAddTrojanServer" xml:space="preserve">
<value>添加 [Trojan] 配置文件</value> <value>添加 [Trojan] </value>
</data> </data>
<data name="menuAddVlessServer" xml:space="preserve"> <data name="menuAddVlessServer" xml:space="preserve">
<value>添加 [VLESS] 配置文件</value> <value>添加 [VLESS] </value>
</data> </data>
<data name="menuAddVmessServer" xml:space="preserve"> <data name="menuAddVmessServer" xml:space="preserve">
<value>添加 [VMess] 配置文件</value> <value>添加 [VMess] </value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>全选 (Ctrl+A)</value> <value>全选 (Ctrl+A)</value>
@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Core: 基础设置</value> <value>Core: 基础设置</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>v2ray DNS 设置</value> <value>v2ray 自定义 DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP 设置</value> <value>Core: KCP 设置</value>
@@ -691,7 +691,7 @@
<value>Outbound Freedom domainStrategy</value> <value>Outbound Freedom domainStrategy</value>
</data> </data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve"> <data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>自动调整配置文件列宽在更新订阅后</value> <value>自动调整配置列宽在更新订阅后</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>检查 Pre-Release 更新 (请谨慎启用)</value> <value>检查 Pre-Release 更新 (请谨慎启用)</value>
@@ -700,7 +700,7 @@
<value>例外</value> <value>例外</value>
</data> </data>
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号 (;) 分隔。</value> <value>例外:对于下列字符开头的地址,不使用代理配置。使用分号 (;) 分隔。</value>
</data> </data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>显示实时速度 (需重启)</value> <value>显示实时速度 (需重启)</value>
@@ -747,11 +747,8 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>系统代理设置</value> <value>系统代理设置</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>启用安全协议 TLS v1.3 (订阅/检查更新)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>托盘右键菜单配置文件展示数量限制</value> <value>托盘右键菜单配置展示数量限制</value>
</data> </data>
<data name="TbSettingsUdpEnabled" xml:space="preserve"> <data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>开启 UDP</value> <value>开启 UDP</value>
@@ -784,7 +781,7 @@
<value>Pac 模式</value> <value>Pac 模式</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>分享配置文件 (Ctrl+F)</value> <value>分享 (Ctrl+F)</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>路由</value> <value>路由</value>
@@ -892,7 +889,7 @@
<value>延迟 (ms)</value> <value>延迟 (ms)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>速度 (M/s)</value> <value>速度 (MB/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>运行 Core 失败,请查看提示信息</value> <value>运行 Core 失败,请查看提示信息</value>
@@ -916,7 +913,7 @@
<value>移至订阅分组</value> <value>移至订阅分组</value>
</data> </data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve"> <data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>启用配置文件拖放排序 (需重启)</value> <value>启用配置拖放排序 (需重启)</value>
</data> </data>
<data name="TbAutoRefresh" xml:space="preserve"> <data name="TbAutoRefresh" xml:space="preserve">
<value>自动刷新</value> <value>自动刷新</value>
@@ -925,10 +922,10 @@
<value>跳过测试</value> <value>跳过测试</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>编辑配置文件 (Ctrl+D)</value> <value>编辑 (Ctrl+D)</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主界面双击设为活动配置文件</value> <value>主界面双击设为活动</value>
</data> </data>
<data name="SpeedtestingCompleted" xml:space="preserve"> <data name="SpeedtestingCompleted" xml:space="preserve">
<value>测试完成</value> <value>测试完成</value>
@@ -1008,8 +1005,8 @@
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS 设置</value> <value>DNS 设置</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box DNS 设置</value> <value>sing-box 自定义 DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>请填写 DNS JSON 结构,点击查看文档</value> <value>请填写 DNS JSON 结构,点击查看文档</value>
@@ -1033,7 +1030,7 @@
<value>Domain</value> <value>Domain</value>
</data> </data>
<data name="menuAddHysteria2Server" xml:space="preserve"> <data name="menuAddHysteria2Server" xml:space="preserve">
<value>添加 [Hysteria2] 配置文件</value> <value>添加 [Hysteria2] </value>
</data> </data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve"> <data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大带宽 (Up/Dw)</value> <value>Hysteria 最大带宽 (Up/Dw)</value>
@@ -1042,19 +1039,19 @@
<value>使用系统 hosts</value> <value>使用系统 hosts</value>
</data> </data>
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>添加 [TUIC] 配置文件</value> <value>添加 [TUIC] </value>
</data> </data>
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>拥塞控制算法</value> <value>拥塞控制算法</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>前置代理配置文件别名</value> <value>前置代理配置别名</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>落地代理配置文件別名</value> <value>落地代理配置別名</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>请确保配置文件别名存在并唯一</value> <value>请确保配置别名存在并唯一</value>
</data> </data>
<data name="TbSettingsTunAutoRoute" xml:space="preserve"> <data name="TbSettingsTunAutoRoute" xml:space="preserve">
<value>自动路由</value> <value>自动路由</value>
@@ -1075,7 +1072,7 @@
<value>启用 IPv6</value> <value>启用 IPv6</value>
</data> </data>
<data name="menuAddWireguardServer" xml:space="preserve"> <data name="menuAddWireguardServer" xml:space="preserve">
<value>添加 [WireGuard] 配置文件</value> <value>添加 [WireGuard] </value>
</data> </data>
<data name="TbPrivateKey" xml:space="preserve"> <data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value> <value>PrivateKey</value>
@@ -1108,7 +1105,7 @@
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve"> <data name="menuAddHttpServer" xml:space="preserve">
<value>添加 [HTTP] 配置文件</value> <value>添加 [HTTP] </value>
</data> </data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve"> <data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>和分组前置代理冲突</value> <value>和分组前置代理冲突</value>
@@ -1198,13 +1195,13 @@
<value>延迟测试</value> <value>延迟测试</value>
</data> </data>
<data name="menuProxiesDelaytestPart" xml:space="preserve"> <data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>当前部分节点延迟测试</value> <value>当前部分延迟测试</value>
</data> </data>
<data name="menuProxiesReload" xml:space="preserve"> <data name="menuProxiesReload" xml:space="preserve">
<value>刷新</value> <value>刷新</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>设为活动节点 (Enter)</value> <value>设为活动 (Enter)</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Outbound 默认解析策略</value> <value>Outbound 默认解析策略</value>
@@ -1222,7 +1219,7 @@
<value>导出分享链接至剪贴板 (多选) Base64 编码</value> <value>导出分享链接至剪贴板 (多选) Base64 编码</value>
</data> </data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve"> <data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>导出所选配置文件完整配置至剪贴板</value> <value>导出所选完整配置至剪贴板</value>
</data> </data>
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>显示或隐藏主界面</value> <value>显示或隐藏主界面</value>
@@ -1339,7 +1336,7 @@
<value>多线程测试时的并发数量</value> <value>多线程测试时的并发数量</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:对于下列地址不使用代理配置文件。使用逗号 (,) 分隔。</value> <value>例外:对于下列地址不使用代理配置。使用逗号 (,) 分隔。</value>
</data> </data>
<data name="TbSettingsDestOverride" xml:space="preserve"> <data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探测类型</value> <value>流量探测类型</value>
@@ -1374,32 +1371,32 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>会覆盖端口,多组时用逗号 (,) 隔开</value> <value>会覆盖端口,多组时用逗号 (,) 隔开</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多配置文件产生自定义配置 (多选)</value> <value>多选生成策略组</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多配置文件随机 Xray</value> <value>多随机 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多配置文件负载均衡 Xray</value> <value>多负载均衡 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多配置文件最低延迟 Xray</value> <value>多最低延迟 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多配置文件最稳定 Xray</value> <value>多最稳定 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多配置文件最低延迟 sing-box</value> <value>多最低延迟 sing-box</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>导出配置文件</value> <value>导出</value>
</data> </data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve"> <data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>当前连接信息测试地址</value> <value>当前连接信息测试地址</value>
</data> </data>
<data name="TbRuleOutboundTagTip" xml:space="preserve"> <data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填写配置文件别名,请确保存在并唯一</value> <value>可以填写配置别名,请确保存在并唯一</value>
</data> </data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve"> <data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密码错误,请重试。</value> <value>密码错误,请重试。</value>
@@ -1408,7 +1405,7 @@
<value>Mldsa65Verify</value> <value>Mldsa65Verify</value>
</data> </data>
<data name="menuAddAnytlsServer" xml:space="preserve"> <data name="menuAddAnytlsServer" xml:space="preserve">
<value>添加 [Anytls] 配置文件</value> <value>添加 [Anytls] </value>
</data> </data>
<data name="TbRemoteDNS" xml:space="preserve"> <data name="TbRemoteDNS" xml:space="preserve">
<value>远程 DNS</value> <value>远程 DNS</value>
@@ -1416,19 +1413,10 @@
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>直连 DNS</value> <value>直连 DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>出站 DNS 解析sing-box</value> <value>通过代理,请确保远程可用</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>解析出站域名</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>解析 DNS 服务器域名,需指定为 IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray freedom 解析策略</value> <value>xray freedom 解析策略</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
@@ -1468,7 +1456,7 @@
<value>自定义 DNS 已启用,此页面配置将无效</value> <value>自定义 DNS 已启用,此页面配置将无效</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>避免域名分流规则失效</value> <value>开启后将阻止 ECH 和 HTTP/3 可用性查询</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>请填写正确的配置模板</value> <value>请填写正确的配置模板</value>
@@ -1504,9 +1492,108 @@
<value>开始解析和处理订阅内容</value> <value>开始解析和处理订阅内容</value>
</data> </data>
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>选择配置文件</value> <value>选择配置</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value> <value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>请至少添加一个配置</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>策略组</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>链式代理</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>最低延迟</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>随机</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>负载均衡</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>最稳定</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>策略组类型</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>添加策略组</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>添加链式代理</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>添加子项</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>删除子项</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>子项列表</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>故障转移</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多选故障转移 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多选故障转移 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>核心 '{0}' 在使用传输方式 '{2}' 时不支持协议 '{1}'。</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>核心 '{0}' 不支持协议 '{1}'。</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>代理链: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>路由规则出站: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>策略组: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>别名 '{0}' 不存在。</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>组“{0}”为空。请至少添加一个配置。</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>{0}属性无效,请检查</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} 分组不能引用自身或循环引用</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>不支持协议 '{0}'。</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>如果系统没有托盘功能,请不要开启</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>规则类型</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>可对 Routing 和 DNS 单独设定规则ALL 则都生效</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>解析 DNS 服务器域名,需指定为 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一键测试真连接延迟</value>
</data>
</root> </root>

View File

@@ -121,7 +121,7 @@
<value>匯出分享連結至剪貼簿成功</value> <value>匯出分享連結至剪貼簿成功</value>
</data> </data>
<data name="CheckServerSettings" xml:space="preserve"> <data name="CheckServerSettings" xml:space="preserve">
<value>請先檢查設定檔設定</value> <value>請先檢查設定</value>
</data> </data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve"> <data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>設定格式不正確</value> <value>設定格式不正確</value>
@@ -133,7 +133,7 @@
<value>下載開始...</value> <value>下載開始...</value>
</data> </data>
<data name="FailedConversionConfiguration" xml:space="preserve"> <data name="FailedConversionConfiguration" xml:space="preserve">
<value>轉換設定失敗</value> <value>轉換設定失敗</value>
</data> </data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve"> <data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>生成預設設定檔失敗</value> <value>生成預設設定檔失敗</value>
@@ -142,10 +142,10 @@
<value>獲取預設設定失敗</value> <value>獲取預設設定失敗</value>
</data> </data>
<data name="FailedImportedCustomServer" xml:space="preserve"> <data name="FailedImportedCustomServer" xml:space="preserve">
<value>匯入自訂設定設定檔失敗</value> <value>匯入自訂設定失敗</value>
</data> </data>
<data name="FailedReadConfiguration" xml:space="preserve"> <data name="FailedReadConfiguration" xml:space="preserve">
<value>讀取設定失敗</value> <value>讀取設定失敗</value>
</data> </data>
<data name="FillCorrectServerPort" xml:space="preserve"> <data name="FillCorrectServerPort" xml:space="preserve">
<value>請填寫正確格式的埠</value> <value>請填寫正確格式的埠</value>
@@ -265,13 +265,13 @@
<value>請選擇協定</value> <value>請選擇協定</value>
</data> </data>
<data name="PleaseSelectServer" xml:space="preserve"> <data name="PleaseSelectServer" xml:space="preserve">
<value>請先選擇設定</value> <value>請先選擇設定</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>設定檔去重完成。原數量: {0},現數量: {1}。</value> <value>去重完成。原數量: {0},現數量: {1}。</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>是否確定移除設定檔</value> <value>是否確定移除?</value>
</data> </data>
<data name="SaveClientConfigurationIn" xml:space="preserve"> <data name="SaveClientConfigurationIn" xml:space="preserve">
<value>用戶端設定檔儲存在:{0}</value> <value>用戶端設定檔儲存在:{0}</value>
@@ -283,10 +283,10 @@
<value>設定成功。{0}</value> <value>設定成功。{0}</value>
</data> </data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve"> <data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>成功匯入自訂設定設定檔</value> <value>成功匯入自訂節點</value>
</data> </data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve"> <data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>成功從剪貼簿匯入 {0} 個設定檔</value> <value>成功從剪貼簿匯入 {0} 個節點</value>
</data> </data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve"> <data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>掃描匯入分享連結成功</value> <value>掃描匯入分享連結成功</value>
@@ -385,7 +385,7 @@
<value>所有</value> <value>所有</value>
</data> </data>
<data name="FillServerAddressCustom" xml:space="preserve"> <data name="FillServerAddressCustom" xml:space="preserve">
<value>請瀏覽匯入設定檔設定</value> <value>請瀏覽匯入設定</value>
</data> </data>
<data name="Speedtesting" xml:space="preserve"> <data name="Speedtesting" xml:space="preserve">
<value>測試中...</value> <value>測試中...</value>
@@ -397,7 +397,7 @@
<value>本機</value> <value>本機</value>
</data> </data>
<data name="MsgServerTitle" xml:space="preserve"> <data name="MsgServerTitle" xml:space="preserve">
<value>設定檔過濾,按 Enter 執行</value> <value>過濾,按 Enter 執行</value>
</data> </data>
<data name="menuCheckUpdate" xml:space="preserve"> <data name="menuCheckUpdate" xml:space="preserve">
<value>檢查更新</value> <value>檢查更新</value>
@@ -478,55 +478,55 @@
<value>掃描螢幕上的二維碼 (Ctrl+S)</value> <value>掃描螢幕上的二維碼 (Ctrl+S)</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>複製所選設定檔</value> <value>複製所選</value>
</data> </data>
<data name="menuRemoveDuplicateServer" xml:space="preserve"> <data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>移除重複的設定檔</value> <value>移除重複</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>移除所選設定檔 (多選) (Delete)</value> <value>移除所選 (多選) (Delete)</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>設為活動設定檔 (Enter)</value> <value>設為活動 (Enter)</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服務統計資料</value> <value>清除所有服務統計資料</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>測試設定檔真連線延遲 (多選) (Ctrl+R)</value> <value>測試真連線延遲 (多選) (Ctrl+R)</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>按測試結果排序</value> <value>按測試結果排序</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>測試設定檔速度 (多選) (Ctrl+T)</value> <value>測試速度 (多選) (Ctrl+T)</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>測試設定檔延遲 Tcping (多選) (Ctrl+O)</value> <value>測試延遲 Tcping (多選) (Ctrl+O)</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>匯出所選設定檔完整設定</value> <value>匯出所選完整設定</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value> <value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>新增自訂設定設定檔</value> <value>新增自訂節點</value>
</data> </data>
<data name="menuAddShadowsocksServer" xml:space="preserve"> <data name="menuAddShadowsocksServer" xml:space="preserve">
<value>新增 [Shadowsocks] 設定檔</value> <value>新增 [Shadowsocks] 節點</value>
</data> </data>
<data name="menuAddSocksServer" xml:space="preserve"> <data name="menuAddSocksServer" xml:space="preserve">
<value>新增 [SOCKS] 設定檔</value> <value>新增 [SOCKS] 節點</value>
</data> </data>
<data name="menuAddTrojanServer" xml:space="preserve"> <data name="menuAddTrojanServer" xml:space="preserve">
<value>新增 [Trojan] 設定檔</value> <value>新增 [Trojan] 節點</value>
</data> </data>
<data name="menuAddVlessServer" xml:space="preserve"> <data name="menuAddVlessServer" xml:space="preserve">
<value>新增 [VLESS] 設定檔</value> <value>新增 [VLESS] 節點</value>
</data> </data>
<data name="menuAddVmessServer" xml:space="preserve"> <data name="menuAddVmessServer" xml:space="preserve">
<value>新增 [VMess] 設定檔</value> <value>新增 [VMess] 節點</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>全選 (Ctrl+A)</value> <value>全選 (Ctrl+A)</value>
@@ -675,8 +675,8 @@
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Core: 基礎設定</value> <value>Core: 基礎設定</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbCustomDnsRay" xml:space="preserve">
<value>V2ray DNS 設定</value> <value>v2ray 自訂 DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP 設定</value> <value>Core: KCP 設定</value>
@@ -691,7 +691,7 @@
<value>Outbound Freedom domainStrategy</value> <value>Outbound Freedom domainStrategy</value>
</data> </data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve"> <data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>在更新訂閱後自動調整設定檔列寬</value> <value>在更新訂閱後自動調整列寬</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value> <value>檢查 Pre-Release 更新 (請謹慎啟用)</value>
@@ -700,7 +700,7 @@
<value>例外</value> <value>例外</value>
</data> </data>
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號 (;) 分隔。</value> <value>例外:對於下列字元開頭的位址,不使用代理。使用分號 (;) 分隔。</value>
</data> </data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve"> <data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>顯示即時速度(需重啟)</value> <value>顯示即時速度(需重啟)</value>
@@ -747,11 +747,8 @@
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>系統代理設定</value> <value>系統代理設定</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>啟用安全協定 TLS v1.3 (訂閱/檢查更新)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>工具列右鍵選單設定展示數量限制</value> <value>工具列右鍵選單設定展示數量限制</value>
</data> </data>
<data name="TbSettingsUdpEnabled" xml:space="preserve"> <data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>開啟 UDP</value> <value>開啟 UDP</value>
@@ -784,7 +781,7 @@
<value>PAC 模式</value> <value>PAC 模式</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>分享設定檔 (Ctrl+F)</value> <value>分享 (Ctrl+F)</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>路由</value> <value>路由</value>
@@ -886,13 +883,13 @@
<value>請勿將代理伺服器用於本機Intranet位址</value> <value>請勿將代理伺服器用於本機Intranet位址</value>
</data> </data>
<data name="menuMixedTestServer" xml:space="preserve"> <data name="menuMixedTestServer" xml:space="preserve">
<value>一鍵多執行緒測試延遲速度 (Ctrl+E)</value> <value>一鍵延遲速度測試 (Ctrl+E)</value>
</data> </data>
<data name="LvTestDelay" xml:space="preserve"> <data name="LvTestDelay" xml:space="preserve">
<value>延遲 (ms)</value> <value>延遲 (ms)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>速度 (M/s)</value> <value>速度 (MB/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>執行 Core 失敗,請查看提示訊息</value> <value>執行 Core 失敗,請查看提示訊息</value>
@@ -916,7 +913,7 @@
<value>移至訂閱分組</value> <value>移至訂閱分組</value>
</data> </data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve"> <data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>啟動設定檔拖放排序 (需重啟)</value> <value>啟拖放排序 (需重啟)</value>
</data> </data>
<data name="TbAutoRefresh" xml:space="preserve"> <data name="TbAutoRefresh" xml:space="preserve">
<value>自動重新整理</value> <value>自動重新整理</value>
@@ -925,10 +922,10 @@
<value>跳過測試</value> <value>跳過測試</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>編輯設定檔 (Ctrl+D)</value> <value>編輯 (Ctrl+D)</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主介面輕按兩下設為活動設定檔</value> <value>主介面輕按兩下設為活動</value>
</data> </data>
<data name="SpeedtestingCompleted" xml:space="preserve"> <data name="SpeedtestingCompleted" xml:space="preserve">
<value>測試完成</value> <value>測試完成</value>
@@ -946,7 +943,7 @@
<value>目前字型 (需重啟)</value> <value>目前字型 (需重啟)</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts啟設定</value> <value>複製字型 TTF/TTC 檔案到目錄 guiFonts新啟動後生效</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac 連接埠 = +3Xray API 連接埠 = +4mihomo API 連接埠 = +5</value> <value>Pac 連接埠 = +3Xray API 連接埠 = +4mihomo API 連接埠 = +5</value>
@@ -1006,10 +1003,10 @@
<value>不需要轉換時請留空</value> <value>不需要轉換時請留空</value>
</data> </data>
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS 設定</value> <value>DNS設定</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box DNS 設定</value> <value>sing-box 自訂 DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>請填寫 DNS JSON 結構,點擊查看檔案</value> <value>請填寫 DNS JSON 結構,點擊查看檔案</value>
@@ -1033,7 +1030,7 @@
<value>Domain</value> <value>Domain</value>
</data> </data>
<data name="menuAddHysteria2Server" xml:space="preserve"> <data name="menuAddHysteria2Server" xml:space="preserve">
<value>添加 [Hysteria2] 設定檔</value> <value>新增 [Hysteria2] 節點</value>
</data> </data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve"> <data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大頻寬 (Up/Dw)</value> <value>Hysteria 最大頻寬 (Up/Dw)</value>
@@ -1042,19 +1039,19 @@
<value>使用系統 hosts</value> <value>使用系統 hosts</value>
</data> </data>
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>新增 [TUIC] 設定檔</value> <value>新增 [TUIC] 節點</value>
</data> </data>
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>擁塞控制算法</value> <value>擁塞控制算法</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>前置代理設定檔別名</value> <value>前置代理節點別名</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>落地代理設定檔別名</value> <value>落地代理節點別名</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>請確保設定檔別名存在並且唯一</value> <value>請確保節點別名存在並且唯一</value>
</data> </data>
<data name="TbSettingsTunAutoRoute" xml:space="preserve"> <data name="TbSettingsTunAutoRoute" xml:space="preserve">
<value>自動路由</value> <value>自動路由</value>
@@ -1075,7 +1072,7 @@
<value>啟用 IPv6</value> <value>啟用 IPv6</value>
</data> </data>
<data name="menuAddWireguardServer" xml:space="preserve"> <data name="menuAddWireguardServer" xml:space="preserve">
<value>添加 [WireGuard] 設定檔</value> <value>新增 [WireGuard] 節點</value>
</data> </data>
<data name="TbPrivateKey" xml:space="preserve"> <data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value> <value>PrivateKey</value>
@@ -1108,7 +1105,7 @@
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve"> <data name="menuAddHttpServer" xml:space="preserve">
<value>新增 [HTTP] 設定檔</value> <value>新增 [HTTP] 節點</value>
</data> </data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve"> <data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>和分組前置代理衝突</value> <value>和分組前置代理衝突</value>
@@ -1222,13 +1219,13 @@
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value> <value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
</data> </data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve"> <data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>匯出所選設定檔完整設定至剪貼簿</value> <value>匯出所選完整設定至剪貼簿</value>
</data> </data>
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>顯示或隱藏主介面</value> <value>顯示或隱藏主介面</value>
</data> </data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂設定的 Socks 連接埠</value> <value>自訂 Socks 連接埠</value>
</data> </data>
<data name="menuBackupAndRestore" xml:space="preserve"> <data name="menuBackupAndRestore" xml:space="preserve">
<value>備份和還原</value> <value>備份和還原</value>
@@ -1312,7 +1309,7 @@
<value>請不要使用不安全的 HTTP 協定訂閱位址</value> <value>請不要使用不安全的 HTTP 協定訂閱位址</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動設定</value> <value>安裝字體到系統中,選擇或填入字體名稱,重新啟動後生效</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value> <value>是否確定退出?</value>
@@ -1339,7 +1336,7 @@
<value>多執行緒測試時的並發數量</value> <value>多執行緒測試時的並發數量</value>
</data> </data>
<data name="TbSettingsExceptionTip2" xml:space="preserve"> <data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:對於下列位址不使用代理設定檔,使用逗號 (,) 分隔。</value> <value>例外:對於下列位址不使用代理,使用逗號 (,) 分隔。</value>
</data> </data>
<data name="TbSettingsDestOverride" xml:space="preserve"> <data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探測類型</value> <value>流量探測類型</value>
@@ -1374,32 +1371,32 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value> <value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多設定檔產生自訂配置 (多選)</value> <value>多選生成策略組</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多設定檔隨機 Xray</value> <value>多隨機 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多設定檔負載平衡 Xray</value> <value>多負載平衡 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多設定檔最低延遲 Xray</value> <value>多最低延遲 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve"> <data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多設定檔最穩定 Xray</value> <value>多最穩定 Xray</value>
</data> </data>
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve"> <data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多設定檔最低延遲 sing-box</value> <value>多最低延遲 sing-box</value>
</data> </data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>匯出設定檔</value> <value>匯出</value>
</data> </data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve"> <data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>目前連接資訊測試地址</value> <value>目前連接資訊測試地址</value>
</data> </data>
<data name="TbRuleOutboundTagTip" xml:space="preserve"> <data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填寫設定檔別名,請確保存在並唯一</value> <value>可以填寫節點別名,請確保存在並唯一</value>
</data> </data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve"> <data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密碼錯誤,請重試。</value> <value>密碼錯誤,請重試。</value>
@@ -1408,105 +1405,195 @@
<value>Mldsa65Verify</value> <value>Mldsa65Verify</value>
</data> </data>
<data name="menuAddAnytlsServer" xml:space="preserve"> <data name="menuAddAnytlsServer" xml:space="preserve">
<value>新增 [Anytls] 設定檔</value> <value>新增 [Anytls] 節點</value>
</data> </data>
<data name="TbRemoteDNS" xml:space="preserve"> <data name="TbRemoteDNS" xml:space="preserve">
<value>Remote DNS</value> <value>遠程 DNS</value>
</data> </data>
<data name="TbDomesticDNS" xml:space="preserve"> <data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value> <value>直連 DNS</value>
</data> </data>
<data name="TbSBOutboundsResolverDNS" xml:space="preserve"> <data name="TbRemoteDNSTips" xml:space="preserve">
<value>Outbound DNS Resolution (sing-box)</value> <value>通过代理,请确保远程可用</value>
</data> </data>
<data name="TbSBOutboundDomainResolve" xml:space="preserve"> <data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>Resolve Outbound Domains</value> <value>xray freedom 解析策略</value>
</data>
<data name="TbSBBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS (sing-box)</value>
</data>
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value>
</data> </data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve"> <data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box Direct Resolution Strategy</value> <value>sing-box 直連解析策略</value>
</data> </data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve"> <data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box Remote Resolution Strategy</value> <value>sing-box 遠程解析策略</value>
</data> </data>
<data name="TbAddCommonDNSHosts" xml:space="preserve"> <data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value> <value>新增常用 DNS Hosts</value>
</data> </data>
<data name="TbFakeIP" xml:space="preserve"> <data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value> <value>FakeIP</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
<value>Block SVCB and HTTPS Queries</value> <value>阻止 SVCB HTTPS 查詢</value>
</data> </data>
<data name="TbDNSHostsConfig" xml:space="preserve"> <data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value> <value>DNS Hosts“網域名稱1 ip1 ip2” 一行一個)</value>
</data> </data>
<data name="ThBasicDNSSettings" xml:space="preserve"> <data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value> <value>DNS 基礎設定</value>
</data> </data>
<data name="ThAdvancedDNSSettings" xml:space="preserve"> <data name="ThAdvancedDNSSettings" xml:space="preserve">
<value>Advanced DNS Settings</value> <value>DNS 進階設定</value>
</data> </data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve"> <data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Regional Domain IPs</value> <value>校驗相應地區域名 IP</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value> <value>配置後,會對相應地區域名(如 geosite:cn的返回 IP 進行校驗,僅返回期望 IP</value>
</data> </data>
<data name="TbCustomDNSEnable" xml:space="preserve"> <data name="TbCustomDNSEnable" xml:space="preserve">
<value>Enable Custom DNS</value> <value>啟用自訂 DNS</value>
</data> </data>
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve"> <data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
<value>Custom DNS Enabled, This Page's Settings Invalid</value> <value>自訂 DNS 已啟用,此頁面配置將無效</value>
</data> </data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve"> <data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Prevent domain-based routing rules from failing</value> <value>開啟後將阻止 ECH 和 HTTP/3 可用性查詢</value>
</data> </data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve"> <data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Please fill in the correct config template</value> <value>請填寫正確的配置範本</value>
</data> </data>
<data name="menuFullConfigTemplate" xml:space="preserve"> <data name="menuFullConfigTemplate" xml:space="preserve">
<value>Full Config Template Setting</value> <value>完整配置範本設定</value>
</data> </data>
<data name="TbFullConfigTemplateEnable" xml:space="preserve"> <data name="TbFullConfigTemplateEnable" xml:space="preserve">
<value>Enable Full Config Template</value> <value>啟用完整配置範本</value>
</data> </data>
<data name="TbRayFullConfigTemplate" xml:space="preserve"> <data name="TbRayFullConfigTemplate" xml:space="preserve">
<value>v2ray Full Config Template</value> <value>v2ray 完整配置範本</value>
</data> </data>
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve"> <data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document</value> <value>僅添加出站配置,routing.balancers routing.rules.outboundTag,點擊查看文檔</value>
</data> </data>
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
<value>Do Not Add Non-Proxy Protocol Outbound</value> <value>不添加非代理協定出站</value>
</data> </data>
<data name="TbSetUpstreamProxyDetour" xml:space="preserve"> <data name="TbSetUpstreamProxyDetour" xml:space="preserve">
<value>Set Upstream Proxy Tag</value> <value>設定上游代理 tag</value>
</data> </data>
<data name="TbSBFullConfigTemplate" xml:space="preserve"> <data name="TbSBFullConfigTemplate" xml:space="preserve">
<value>sing-box Full Config Template</value> <value>sing-box 完整配置範本</value>
</data> </data>
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve"> <data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
<value>Add Outbound and Endpoint Config Only, Click to view the document</value> <value>僅添加出站和端點配置,點擊查看文檔</value>
</data> </data>
<data name="TbFullConfigTemplateDesc" xml:space="preserve"> <data name="TbFullConfigTemplateDesc" xml:space="preserve">
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value> <value>此功能供高級用戶和有特殊需求的用戶使用。 啟用此功能後,將忽略 Core 的基礎設定DNS 設定 ,路由設定。你需要保證系統代理的埠和流量統計等功能的配置正確,一切都由你來設定。</value>
</data> </data>
<data name="MsgStartParsingSubscription" xml:space="preserve"> <data name="MsgStartParsingSubscription" xml:space="preserve">
<value>開始解析和處理訂閱內容</value> <value>開始解析和處理訂閱內容</value>
</data> </data>
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value> <value>選擇節點</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value> <value>默認全域生效,內置 FakeIP 過濾,僅在 sing-box 中生效</value>
</data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>請至少添加一個節點</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>策略組</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>鏈式代理</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>最低延遲</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>隨機</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>負載均衡</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>最穩定</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>策略組類型</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>添加策略組</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>添加鏈式代理</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>添加子項</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>刪除子項</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>子項清單</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>容錯移轉</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多選容錯移轉 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多選容錯移轉 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支援網路類型 '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>核心 '{0}' 在使用傳輸方式 '{2}' 時不支援協定 '{1}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>核心 '{0}' 不支援協定 '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>代理鏈: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>路由規則出站: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>策略組: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>別名 '{0}' 不存在。</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>組“{0}”為空.請至少添加一個配置。</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>{0}屬性無效,請檢查</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} 分組不能引用自身或循環引用</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>不支援協定 '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>如果系統沒有託盤功能,請不要開啟</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>規則類型</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>可對 Routing 和 DNS 單獨設定規則ALL 則都生效</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>解析 DNS 伺服器網域名稱,需指定為 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一鍵測試真連線延遲</value>
</data> </data>
</root> </root>

View File

@@ -28,15 +28,15 @@ fi
kill_children() { kill_children() {
local parent=$1 local parent=$1
local children=$(ps -o pid --no-headers --ppid "$parent") local children=$(ps -o pid --no-headers --ppid "$parent")
# Output information about processes being terminated # Output information about processes being terminated
echo "Processing children of PID: $parent..." echo "Processing children of PID: $parent..."
# Process each child # Process each child
for child in $children; do for child in $children; do
# Recursively find and kill child's children first # Recursively find and kill child's children first
kill_children "$child" kill_children "$child"
# Force kill the child process # Force kill the child process
echo "Terminating child process: $child" echo "Terminating child process: $child"
kill -9 "$child" 2>/dev/null || true kill -9 "$child" 2>/dev/null || true
@@ -47,6 +47,18 @@ echo "============================================"
echo "Starting termination of process $PID and all its children" echo "Starting termination of process $PID and all its children"
echo "============================================" echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_children
if ps -p $PID > /dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its children and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all child processes # Find and kill all child processes
kill_children "$PID" kill_children "$PID"

View File

@@ -42,6 +42,20 @@ echo "============================================"
echo "Starting termination of process $PID and all its descendants" echo "Starting termination of process $PID and all its descendants"
echo "============================================" echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_descendants
# Use the macOS-native 'kill -0' check
if kill -0 $PID 2>/dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its descendants and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all descendant processes # Find and kill all descendant processes
kill_descendants "$PID" kill_descendants "$PID"

View File

@@ -1,4 +1,4 @@
{ {
"domain": [ "domain": [
"amobile.music.tc.qq.com", "amobile.music.tc.qq.com",
"api-jooxtt.sanook.com", "api-jooxtt.sanook.com",
@@ -28,6 +28,7 @@
"swdownload.apple.com", "swdownload.apple.com",
"swquery.apple.com", "swquery.apple.com",
"swscan.apple.com", "swscan.apple.com",
"turn.cloudflare.com",
"trackercdn.kugou.com", "trackercdn.kugou.com",
"xnotify.xboxlive.com" "xnotify.xboxlive.com"
], ],

View File

@@ -57,6 +57,9 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.fr.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx"> <EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
@@ -79,4 +82,4 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,3 @@
using System.Net;
using System.Net.NetworkInformation;
namespace ServiceLib.Services.CoreConfig; namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigSingboxService(Config config) public partial class CoreConfigSingboxService(Config config)
@@ -16,7 +13,7 @@ public partial class CoreConfigSingboxService(Config config)
try try
{ {
if (node == null if (node == null
|| node.Port <= 0) || !node.IsValid())
{ {
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;
@@ -29,6 +26,18 @@ public partial class CoreConfigSingboxService(Config config)
ret.Msg = ResUI.InitialConfiguration; ret.Msg = ResUI.InitialConfiguration;
if (node.ConfigType.IsGroupType())
{
switch (node.ConfigType)
{
case EConfigType.PolicyGroup:
return await GenerateClientMultipleLoadConfig(node);
case EConfigType.ProxyChain:
return await GenerateClientChainConfig(node);
}
}
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
if (result.IsNullOrEmpty()) if (result.IsNullOrEmpty())
{ {
@@ -142,12 +151,9 @@ public partial class CoreConfigSingboxService(Config config)
continue; continue;
} }
var item = await AppManager.Instance.GetProfileItem(it.IndexId); var item = await AppManager.Instance.GetProfileItem(it.IndexId);
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) if (item is null || item.IsComplex() || !item.IsValid())
{ {
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) continue;
{
continue;
}
} }
//find unused port //find unused port
@@ -187,27 +193,6 @@ public partial class CoreConfigSingboxService(Config config)
singboxConfig.inbounds.Add(inbound); singboxConfig.inbounds.Add(inbound);
//outbound //outbound
if (item is null)
{
continue;
}
if (item.ConfigType == EConfigType.Shadowsocks
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
{
continue;
}
if (item.ConfigType == EConfigType.VLESS
&& !Global.Flows.Contains(item.Flow))
{
continue;
}
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
&& item.StreamSecurity == Global.StreamSecurityReality
&& item.PublicKey.IsNullOrEmpty())
{
continue;
}
var server = await GenServer(item); var server = await GenServer(item);
if (server is null) if (server is null)
{ {
@@ -246,7 +231,7 @@ public partial class CoreConfigSingboxService(Config config)
} }
singboxConfig.route.default_domain_resolver = new() singboxConfig.route.default_domain_resolver = new()
{ {
server = Global.SingboxFinalResolverTag server = Global.SingboxLocalDNSTag,
}; };
ret.Success = true; ret.Success = true;
@@ -266,7 +251,8 @@ public partial class CoreConfigSingboxService(Config config)
var ret = new RetResult(); var ret = new RetResult();
try try
{ {
if (node is not { Port: > 0 }) if (node == null
|| !node.IsValid())
{ {
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;
@@ -318,7 +304,7 @@ public partial class CoreConfigSingboxService(Config config)
} }
singboxConfig.route.default_domain_resolver = new() singboxConfig.route.default_domain_resolver = new()
{ {
server = Global.SingboxFinalResolverTag server = Global.SingboxLocalDNSTag,
}; };
singboxConfig.route.rules.Clear(); singboxConfig.route.rules.Clear();
@@ -344,7 +330,7 @@ public partial class CoreConfigSingboxService(Config config)
} }
} }
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds) public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
{ {
var ret = new RetResult(); var ret = new RetResult();
try try
@@ -371,56 +357,77 @@ public partial class CoreConfigSingboxService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
singboxConfig.outbounds.RemoveAt(0);
await GenLog(singboxConfig); await GenLog(singboxConfig);
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenRouting(singboxConfig);
await GenExperimental(singboxConfig);
singboxConfig.outbounds.RemoveAt(0);
var proxyProfiles = new List<ProfileItem>(); var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
foreach (var it in selecteds) if (groupRet != 0)
{
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
{
continue;
}
if (it.Port <= 0)
{
continue;
}
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
if (item is null)
{
continue;
}
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
}
if (item.ConfigType == EConfigType.Shadowsocks
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
{
continue;
}
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
{
continue;
}
//outbound
proxyProfiles.Add(item);
}
if (proxyProfiles.Count <= 0)
{ {
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
await GenOutboundsList(proxyProfiles, singboxConfig);
await GenRouting(singboxConfig);
await GenExperimental(singboxConfig);
await GenDns(null, singboxConfig);
await ConvertGeo2Ruleset(singboxConfig);
ret.Success = true;
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
{
var ret = new RetResult();
try
{
if (_config == null)
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
ret.Msg = ResUI.InitialConfiguration;
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
if (singboxConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
singboxConfig.outbounds.RemoveAt(0);
await GenLog(singboxConfig);
await GenInbounds(singboxConfig);
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
if (groupRet != 0)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenRouting(singboxConfig);
await GenExperimental(singboxConfig);
await GenDns(null, singboxConfig); await GenDns(null, singboxConfig);
await ConvertGeo2Ruleset(singboxConfig); await ConvertGeo2Ruleset(singboxConfig);

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Nodes;
namespace ServiceLib.Services.CoreConfig; namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigSingboxService public partial class CoreConfigSingboxService

View File

@@ -43,7 +43,7 @@ public partial class CoreConfigSingboxService
}); });
} }
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag); await GenOutboundDnsRule(node, singboxConfig);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -58,16 +58,12 @@ public partial class CoreConfigSingboxService
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS); var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
directDns.tag = Global.SingboxDirectDNSTag; directDns.tag = Global.SingboxDirectDNSTag;
directDns.domain_resolver = Global.SingboxFinalResolverTag; directDns.domain_resolver = Global.SingboxLocalDNSTag;
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS); var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
remoteDns.tag = Global.SingboxRemoteDNSTag; remoteDns.tag = Global.SingboxRemoteDNSTag;
remoteDns.detour = Global.ProxyTag; remoteDns.detour = Global.ProxyTag;
remoteDns.domain_resolver = Global.SingboxFinalResolverTag; remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS);
resolverDns.tag = Global.SingboxOutboundResolverTag;
resolverDns.domain_resolver = Global.SingboxFinalResolverTag;
var hostsDns = new Server4Sbox var hostsDns = new Server4Sbox
{ {
@@ -112,10 +108,6 @@ public partial class CoreConfigSingboxService
{ {
remoteDns.domain_resolver = Global.SingboxHostsDNSTag; remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
} }
if (resolverDns.server == host.Key)
{
resolverDns.domain_resolver = Global.SingboxHostsDNSTag;
}
if (directDns.server == host.Key) if (directDns.server == host.Key)
{ {
directDns.domain_resolver = Global.SingboxHostsDNSTag; directDns.domain_resolver = Global.SingboxHostsDNSTag;
@@ -126,7 +118,6 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.servers ??= new List<Server4Sbox>(); singboxConfig.dns.servers ??= new List<Server4Sbox>();
singboxConfig.dns.servers.Add(remoteDns); singboxConfig.dns.servers.Add(remoteDns);
singboxConfig.dns.servers.Add(directDns); singboxConfig.dns.servers.Add(directDns);
singboxConfig.dns.servers.Add(resolverDns);
singboxConfig.dns.servers.Add(hostsDns); singboxConfig.dns.servers.Add(hostsDns);
// fake ip // fake ip
@@ -147,8 +138,8 @@ public partial class CoreConfigSingboxService
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem) private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
{ {
var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS); var finalDns = ParseDnsAddress(simpleDNSItem.BootstrapDNS);
finalDns.tag = Global.SingboxFinalResolverTag; finalDns.tag = Global.SingboxLocalDNSTag;
singboxConfig.dns ??= new Dns4Sbox(); singboxConfig.dns ??= new Dns4Sbox();
singboxConfig.dns.servers ??= new List<Server4Sbox>(); singboxConfig.dns.servers ??= new List<Server4Sbox>();
singboxConfig.dns.servers.Add(finalDns); singboxConfig.dns.servers.Add(finalDns);
@@ -253,6 +244,11 @@ public partial class CoreConfigSingboxService
continue; continue;
} }
if (item.RuleType == ERuleType.Routing)
{
continue;
}
var rule = new Rule4Sbox(); var rule = new Rule4Sbox();
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule)); var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
if (validDomains <= 0) if (validDomains <= 0)
@@ -337,7 +333,7 @@ public partial class CoreConfigSingboxService
await GenDnsDomainsLegacyCompatible(singboxConfig, item); await GenDnsDomainsLegacyCompatible(singboxConfig, item);
} }
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag); await GenOutboundDnsRule(node, singboxConfig);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -346,16 +342,17 @@ public partial class CoreConfigSingboxService
return 0; return 0;
} }
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
{ {
var dns4Sbox = singboxConfig.dns ?? new(); var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= []; dns4Sbox.servers ??= [];
dns4Sbox.rules ??= []; dns4Sbox.rules ??= [];
var tag = Global.SingboxFinalResolverTag; var tag = Global.SingboxLocalDNSTag;
var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress;
var localDnsServer = ParseDnsAddress(localDnsAddress); var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
var localDnsServer = ParseDnsAddress(finalDnsAddress);
localDnsServer.tag = tag; localDnsServer.tag = tag;
dns4Sbox.servers.Add(localDnsServer); dns4Sbox.servers.Add(localDnsServer);
@@ -364,19 +361,19 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0); return await Task.FromResult(0);
} }
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
{ {
var dns4Sbox = singboxConfig.dns ?? new(); var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= []; dns4Sbox.servers ??= [];
dns4Sbox.rules ??= []; dns4Sbox.rules ??= [];
var tag = Global.SingboxFinalResolverTag; var tag = Global.SingboxLocalDNSTag;
dns4Sbox.servers.Add(new() dns4Sbox.servers.Add(new()
{ {
tag = tag, tag = tag,
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
detour = Global.DirectTag, detour = Global.DirectTag,
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, strategy = string.IsNullOrEmpty(dnsItem?.DomainStrategy4Freedom) ? null : dnsItem?.DomainStrategy4Freedom,
}); });
dns4Sbox.rules.Insert(0, new() dns4Sbox.rules.Insert(0, new()
{ {
@@ -407,23 +404,26 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0); return await Task.FromResult(0);
} }
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server) private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig)
{ {
if (node == null) if (node == null)
{ {
return 0; return 0;
} }
var domain = string.Empty; List<string> domain = new();
if (Utils.IsDomain(node.Address)) // normal outbound if (Utils.IsDomain(node.Address)) // normal outbound
{ {
domain = node.Address; domain.Add(node.Address);
} }
else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress
{ {
domain = node.SpiderX; domain.AddRange(Utils.String2List(node.SpiderX)
.Where(Utils.IsDomain)
.Distinct()
.ToList());
} }
if (domain.IsNullOrEmpty()) if (domain.Count == 0)
{ {
return 0; return 0;
} }
@@ -431,8 +431,8 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.rules ??= new List<Rule4Sbox>(); singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{ {
server = server, server = Global.SingboxLocalDNSTag,
domain = [domain], domain = domain,
}); });
return await Task.FromResult(0); return await Task.FromResult(0);
@@ -454,79 +454,41 @@ public partial class CoreConfigSingboxService
return server; return server;
} }
if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) var (domain, scheme, port, path) = Utils.ParseUrl(addressFirst);
if (scheme.Equals("dhcp", StringComparison.OrdinalIgnoreCase))
{ {
var interface_name = addressFirst.Substring(7);
server.type = "dhcp"; server.type = "dhcp";
server.Interface = interface_name == "auto" ? null : interface_name; if ((!domain.IsNullOrEmpty()) && domain != "auto")
{
server.server = domain;
}
return server; return server;
} }
if (!addressFirst.Contains("://")) if (scheme.IsNullOrEmpty())
{ {
// udp dns // udp dns
server.type = "udp"; server.type = "udp";
server.server = addressFirst;
return server;
} }
else
try
{ {
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); // server.type = scheme.ToLower();
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
var uri = new Uri(addressFirst); // remove "+local" suffix
server.server = uri.Host; // TODO: "+local" suffix decide server.detour = "direct" ?
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
if (!uri.IsDefaultPort)
{
server.server_port = uri.Port;
}
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
{
server.path = uri.AbsolutePath;
}
} }
catch (UriFormatException)
server.server = domain;
if (port != 0)
{ {
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); server.server_port = port;
if (protocolEndIndex > 0) }
{ if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(path) && path != "/")
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); {
var remaining = addressFirst.Substring(protocolEndIndex + 3); server.path = path;
var portIndex = remaining.IndexOf(':');
var pathIndex = remaining.IndexOf('/');
if (portIndex > 0)
{
server.server = remaining.Substring(0, portIndex);
var portPart = pathIndex > portIndex
? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1)
: remaining.Substring(portIndex + 1);
if (int.TryParse(portPart, out var parsedPort))
{
server.server_port = parsedPort;
}
}
else if (pathIndex > 0)
{
server.server = remaining.Substring(0, pathIndex);
}
else
{
server.server = remaining;
}
if (pathIndex > 0 && (server.type == "https" || server.type == "h3"))
{
server.path = remaining.Substring(pathIndex);
}
}
} }
return server; return server;
} }
} }

View File

@@ -179,13 +179,21 @@ public partial class CoreConfigSingboxService
if (node.ConfigType == EConfigType.WireGuard) if (node.ConfigType == EConfigType.WireGuard)
{ {
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound); var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
await GenEndpoint(node, endpoint); var ret = await GenEndpoint(node, endpoint);
if (ret != 0)
{
return null;
}
return endpoint; return endpoint;
} }
else else
{ {
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(node, outbound); var ret = await GenOutbound(node, outbound);
if (ret != 0)
{
return null;
}
return outbound; return outbound;
} }
} }
@@ -196,6 +204,54 @@ public partial class CoreConfigSingboxService
return await Task.FromResult<BaseServer4Sbox?>(null); return await Task.FromResult<BaseServer4Sbox?>(null);
} }
private async Task<int> GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
{
try
{
if (!node.ConfigType.IsGroupType())
{
return -1;
}
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
if (hasCycle)
{
return -1;
}
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
return -1;
}
switch (node.ConfigType)
{
case EConfigType.PolicyGroup:
if (ignoreOriginChain)
{
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
}
else
{
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
}
break;
case EConfigType.ProxyChain:
await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
break;
default:
break;
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult(0);
}
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
@@ -238,7 +294,7 @@ public partial class CoreConfigSingboxService
var tls = new Tls4Sbox() var tls = new Tls4Sbox()
{ {
enabled = true, enabled = true,
record_fragment = _config.CoreBasicItem.EnableFragment, record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
server_name = server_name, server_name = server_name,
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(), alpn = node.GetAlpn(),
@@ -410,7 +466,7 @@ public partial class CoreConfigSingboxService
return 0; return 0;
} }
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig) private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{ {
try try
{ {
@@ -438,6 +494,29 @@ public partial class CoreConfigSingboxService
{ {
index++; index++;
if (node.ConfigType.IsGroupType())
{
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
continue;
}
var childBaseTagName = $"{baseTagName}-{index}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
if (ret == 0)
{
proxyTags.Add(childBaseTagName);
}
continue;
}
// Handle proxy chain // Handle proxy chain
string? prevTag = null; string? prevTag = null;
var currentServer = await GenServer(node); var currentServer = await GenServer(node);
@@ -450,7 +529,7 @@ public partial class CoreConfigSingboxService
var subItem = await AppManager.Instance.GetSubItem(node.Subid); var subItem = await AppManager.Instance.GetSubItem(node.Subid);
// current proxy // current proxy
currentServer.tag = $"{Global.ProxyTag}-{index}"; currentServer.tag = $"{baseTagName}-{index}";
proxyTags.Add(currentServer.tag); proxyTags.Add(currentServer.tag);
if (!node.Subid.IsNullOrEmpty()) if (!node.Subid.IsNullOrEmpty())
@@ -467,7 +546,7 @@ public partial class CoreConfigSingboxService
{ {
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound); var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
await GenOutbound(prevNode, prevOutbound); await GenOutbound(prevNode, prevOutbound);
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; prevTag = $"prev-{baseTagName}-{++prevIndex}";
prevOutbound.tag = prevTag; prevOutbound.tag = prevTag;
prevOutbounds.Add(prevOutbound); prevOutbounds.Add(prevOutbound);
} }
@@ -508,16 +587,21 @@ public partial class CoreConfigSingboxService
var outUrltest = new Outbound4Sbox var outUrltest = new Outbound4Sbox
{ {
type = "urltest", type = "urltest",
tag = $"{Global.ProxyTag}-auto", tag = $"{baseTagName}-auto",
outbounds = proxyTags, outbounds = proxyTags,
interrupt_exist_connections = false, interrupt_exist_connections = false,
}; };
if (multipleLoad == EMultipleLoad.Fallback)
{
outUrltest.tolerance = 5000;
}
// Add selector outbound (manual selection) // Add selector outbound (manual selection)
var outSelector = new Outbound4Sbox var outSelector = new Outbound4Sbox
{ {
type = "selector", type = "selector",
tag = Global.ProxyTag, tag = baseTagName,
outbounds = JsonUtils.DeepCopy(proxyTags), outbounds = JsonUtils.DeepCopy(proxyTags),
interrupt_exist_connections = false, interrupt_exist_connections = false,
}; };
@@ -529,12 +613,12 @@ public partial class CoreConfigSingboxService
} }
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds // Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
resultOutbounds.AddRange(prevOutbounds); var serverList = new List<BaseServer4Sbox>();
resultOutbounds.AddRange(singboxConfig.outbounds); serverList = serverList.Concat(prevOutbounds)
singboxConfig.outbounds = resultOutbounds; .Concat(resultOutbounds)
singboxConfig.endpoints ??= new List<Endpoints4Sbox>(); .Concat(resultEndpoints)
resultEndpoints.AddRange(singboxConfig.endpoints); .ToList();
singboxConfig.endpoints = resultEndpoints; await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -574,4 +658,163 @@ public partial class CoreConfigSingboxService
} }
return null; return null;
} }
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{
var resultOutbounds = new List<Outbound4Sbox>();
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
var proxyTags = new List<string>(); // For selector and urltest outbounds
for (var i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
if (node == null)
continue;
if (node.ConfigType.IsGroupType())
{
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
continue;
}
var childBaseTagName = $"{baseTagName}-{i + 1}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
if (ret == 0)
{
proxyTags.Add(childBaseTagName);
}
continue;
}
var server = await GenServer(node);
if (server is null)
{
break;
}
server.tag = baseTagName + (i + 1).ToString();
if (server is Endpoints4Sbox endpoint)
{
resultEndpoints.Add(endpoint);
}
else if (server is Outbound4Sbox outbound)
{
resultOutbounds.Add(outbound);
}
proxyTags.Add(server.tag);
}
// Add urltest outbound (auto selection based on latency)
if (proxyTags.Count > 0)
{
var outUrltest = new Outbound4Sbox
{
type = "urltest",
tag = $"{baseTagName}-auto",
outbounds = proxyTags,
interrupt_exist_connections = false,
};
if (multipleLoad == EMultipleLoad.Fallback)
{
outUrltest.tolerance = 5000;
}
// Add selector outbound (manual selection)
var outSelector = new Outbound4Sbox
{
type = "selector",
tag = baseTagName,
outbounds = JsonUtils.DeepCopy(proxyTags),
interrupt_exist_connections = false,
};
outSelector.outbounds.Insert(0, outUrltest.tag);
// Insert these at the beginning
resultOutbounds.Insert(0, outUrltest);
resultOutbounds.Insert(0, outSelector);
}
var serverList = new List<BaseServer4Sbox>();
serverList = serverList.Concat(resultOutbounds)
.Concat(resultEndpoints)
.ToList();
await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
return await Task.FromResult(0);
}
private async Task<int> GenChainOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag)
{
// Based on actual network flow instead of data packets
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
var resultOutbounds = new List<Outbound4Sbox>();
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
for (var i = 0; i < nodesReverse.Count; i++)
{
var node = nodesReverse[i];
var server = await GenServer(node);
if (server is null)
{
break;
}
if (i == 0)
{
server.tag = baseTagName;
}
else
{
server.tag = baseTagName + i.ToString();
}
if (i != nodesReverse.Count - 1)
{
server.detour = baseTagName + (i + 1).ToString();
}
if (server is Endpoints4Sbox endpoint)
{
resultEndpoints.Add(endpoint);
}
else if (server is Outbound4Sbox outbound)
{
resultOutbounds.Add(outbound);
}
}
var serverList = new List<BaseServer4Sbox>();
serverList = serverList.Concat(resultOutbounds)
.Concat(resultEndpoints)
.ToList();
await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
return await Task.FromResult(0);
}
private async Task<int> AddRangeOutbounds(List<BaseServer4Sbox> servers, SingboxConfig singboxConfig, bool prepend = true)
{
try
{
if (servers is null || servers.Count <= 0)
{
return 0;
}
var outbounds = servers.Where(s => s is Outbound4Sbox).Cast<Outbound4Sbox>().ToList();
var endpoints = servers.Where(s => s is Endpoints4Sbox).Cast<Endpoints4Sbox>().ToList();
singboxConfig.endpoints ??= new();
if (prepend)
{
singboxConfig.outbounds.InsertRange(0, outbounds);
singboxConfig.endpoints.InsertRange(0, endpoints);
}
else
{
singboxConfig.outbounds.AddRange(outbounds);
singboxConfig.endpoints.AddRange(endpoints);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult(0);
}
} }

View File

@@ -9,13 +9,13 @@ public partial class CoreConfigSingboxService
singboxConfig.route.final = Global.ProxyTag; singboxConfig.route.final = Global.ProxyTag;
var item = _config.SimpleDNSItem; var item = _config.SimpleDNSItem;
var defaultDomainResolverTag = Global.SingboxOutboundResolverTag; var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct; var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (rawDNSItem != null && rawDNSItem.Enabled == true) if (rawDNSItem != null && rawDNSItem.Enabled == true)
{ {
defaultDomainResolverTag = Global.SingboxFinalResolverTag; defaultDomainResolverTag = Global.SingboxLocalDNSTag;
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom; directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
} }
singboxConfig.route.default_domain_resolver = new() singboxConfig.route.default_domain_resolver = new()
@@ -136,13 +136,21 @@ public partial class CoreConfigSingboxService
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item1 in rules ?? []) foreach (var item1 in rules ?? [])
{ {
if (item1.Enabled) if (!item1.Enabled)
{ {
await GenRoutingUserRule(item1, singboxConfig); continue;
if (item1.Ip != null && item1.Ip.Count > 0) }
{
ipRules.Add(item1); if (item1.RuleType == ERuleType.DNS)
} {
continue;
}
await GenRoutingUserRule(item1, singboxConfig);
if (item1.Ip?.Count > 0)
{
ipRules.Add(item1);
} }
} }
} }
@@ -368,26 +376,38 @@ public partial class CoreConfigSingboxService
} }
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag); var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
if (node == null if (node == null
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType)) || (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
&& !node.ConfigType.IsGroupType()))
{ {
return Global.ProxyTag; return Global.ProxyTag;
} }
var tag = Global.ProxyTag + node.IndexId.ToString(); var tag = $"{node.IndexId}-{Global.ProxyTag}";
if (singboxConfig.outbounds.Any(o => o.tag == tag) if (singboxConfig.outbounds.Any(o => o.tag == tag)
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag))) || (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
{ {
return tag; return tag;
} }
if (node.ConfigType.IsGroupType())
{
var ret = await GenGroupOutbound(node, singboxConfig, tag);
if (ret == 0)
{
return tag;
}
return Global.ProxyTag;
}
var server = await GenServer(node); var server = await GenServer(node);
if (server is null) if (server is null)
{ {
return Global.ProxyTag; return Global.ProxyTag;
} }
server.tag = Global.ProxyTag + node.IndexId.ToString(); server.tag = tag;
if (server is Endpoints4Sbox endpoint) if (server is Endpoints4Sbox endpoint)
{ {
singboxConfig.endpoints ??= new(); singboxConfig.endpoints ??= new();

View File

@@ -1,6 +1,3 @@
using System.Net;
using System.Net.NetworkInformation;
namespace ServiceLib.Services.CoreConfig; namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService(Config config) public partial class CoreConfigV2rayService(Config config)
@@ -16,7 +13,7 @@ public partial class CoreConfigV2rayService(Config config)
try try
{ {
if (node == null if (node == null
|| node.Port <= 0) || !node.IsValid())
{ {
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;
@@ -30,6 +27,18 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration; ret.Msg = ResUI.InitialConfiguration;
if (node.ConfigType.IsGroupType())
{
switch (node.ConfigType)
{
case EConfigType.PolicyGroup:
return await GenerateClientMultipleLoadConfig(node);
case EConfigType.ProxyChain:
return await GenerateClientChainConfig(node);
}
}
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (result.IsNullOrEmpty()) if (result.IsNullOrEmpty())
{ {
@@ -71,7 +80,7 @@ public partial class CoreConfigV2rayService(Config config)
} }
} }
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad) public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
{ {
var ret = new RetResult(); var ret = new RetResult();
@@ -99,70 +108,50 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
v2rayConfig.outbounds.RemoveAt(0);
await GenLog(v2rayConfig); await GenLog(v2rayConfig);
await GenInbounds(v2rayConfig); await GenInbounds(v2rayConfig);
await GenRouting(v2rayConfig);
await GenDns(null, v2rayConfig);
await GenStatistic(v2rayConfig);
v2rayConfig.outbounds.RemoveAt(0);
var proxyProfiles = new List<ProfileItem>(); var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
foreach (var it in selecteds) if (groupRet != 0)
{
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
{
continue;
}
if (it.Port <= 0)
{
continue;
}
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
if (item is null)
{
continue;
}
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
}
if (item.ConfigType == EConfigType.Shadowsocks
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
{
continue;
}
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
{
continue;
}
//outbound
proxyProfiles.Add(item);
}
if (proxyProfiles.Count <= 0)
{ {
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; return ret;
} }
await GenOutboundsList(proxyProfiles, v2rayConfig);
//add balancers await GenRouting(v2rayConfig);
await GenBalancer(v2rayConfig, multipleLoad); await GenDns(null, v2rayConfig);
await GenStatistic(v2rayConfig);
var balancer = v2rayConfig.routing.balancers.First(); var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
//add rule //add rule
var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList(); var rules = v2rayConfig.routing.rules;
if (rules?.Count > 0) if (rules?.Count > 0 && ((v2rayConfig.routing.balancers?.Count ?? 0) > 0))
{ {
var balancerTagSet = v2rayConfig.routing.balancers
.Select(b => b.tag)
.ToHashSet();
foreach (var rule in rules) foreach (var rule in rules)
{ {
rule.outboundTag = null; if (rule.outboundTag == null)
rule.balancerTag = balancer.tag; continue;
if (balancerTagSet.Contains(rule.outboundTag))
{
rule.balancerTag = rule.outboundTag;
rule.outboundTag = null;
continue;
}
var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
if (balancerTagSet.Contains(outboundWithSuffix))
{
rule.balancerTag = outboundWithSuffix;
rule.outboundTag = null;
}
} }
} }
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
@@ -170,7 +159,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new() v2rayConfig.routing.rules.Add(new()
{ {
ip = ["0.0.0.0/0", "::/0"], ip = ["0.0.0.0/0", "::/0"],
balancerTag = balancer.tag, balancerTag = defaultBalancerTag,
type = "field" type = "field"
}); });
} }
@@ -179,7 +168,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new() v2rayConfig.routing.rules.Add(new()
{ {
network = "tcp,udp", network = "tcp,udp",
balancerTag = balancer.tag, balancerTag = defaultBalancerTag,
type = "field" type = "field"
}); });
} }
@@ -197,6 +186,63 @@ public partial class CoreConfigV2rayService(Config config)
} }
} }
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
{
var ret = new RetResult();
try
{
if (_config == null)
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
ret.Msg = ResUI.InitialConfiguration;
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
if (v2rayConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
v2rayConfig.outbounds.RemoveAt(0);
await GenLog(v2rayConfig);
await GenInbounds(v2rayConfig);
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
if (groupRet != 0)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenRouting(v2rayConfig);
await GenDns(null, v2rayConfig);
await GenStatistic(v2rayConfig);
ret.Success = true;
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds) public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
{ {
var ret = new RetResult(); var ret = new RetResult();
@@ -255,12 +301,9 @@ public partial class CoreConfigV2rayService(Config config)
continue; continue;
} }
var item = await AppManager.Instance.GetProfileItem(it.IndexId); var item = await AppManager.Instance.GetProfileItem(it.IndexId);
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS) if (item is null || item.IsComplex() || !item.IsValid())
{ {
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id)) continue;
{
continue;
}
} }
//find unused port //find unused port
@@ -289,28 +332,6 @@ public partial class CoreConfigV2rayService(Config config)
it.Port = port; it.Port = port;
it.AllowTest = true; it.AllowTest = true;
//outbound
if (item is null)
{
continue;
}
if (item.ConfigType == EConfigType.Shadowsocks
&& !Global.SsSecuritiesInXray.Contains(item.Security))
{
continue;
}
if (item.ConfigType == EConfigType.VLESS
&& !Global.Flows.Contains(item.Flow))
{
continue;
}
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
&& item.StreamSecurity == Global.StreamSecurityReality
&& item.PublicKey.IsNullOrEmpty())
{
continue;
}
//inbound //inbound
Inbounds4Ray inbound = new() Inbounds4Ray inbound = new()
{ {
@@ -321,6 +342,7 @@ public partial class CoreConfigV2rayService(Config config)
inbound.tag = inbound.protocol + inbound.port.ToString(); inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
//outbound
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(item, outbound); await GenOutbound(item, outbound);
outbound.tag = Global.ProxyTag + inbound.port.ToString(); outbound.tag = Global.ProxyTag + inbound.port.ToString();
@@ -354,7 +376,8 @@ public partial class CoreConfigV2rayService(Config config)
var ret = new RetResult(); var ret = new RetResult();
try try
{ {
if (node is not { Port: > 0 }) if (node == null
|| !node.IsValid())
{ {
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;

View File

@@ -2,34 +2,87 @@ namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService public partial class CoreConfigV2rayService
{ {
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad) private async Task<int> GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{ {
if (multipleLoad == EMultipleLoad.LeastPing) // Collect all existing subject selectors from both observatories
var subjectSelectors = new List<string>();
subjectSelectors.AddRange(v2rayConfig.burstObservatory?.subjectSelector ?? []);
subjectSelectors.AddRange(v2rayConfig.observatory?.subjectSelector ?? []);
// Case 1: exact match already exists -> nothing to do
if (subjectSelectors.Any(baseTagName.StartsWith))
return await Task.FromResult(0);
// Case 2: prefix match exists -> reuse it and move to the first position
var matched = subjectSelectors.FirstOrDefault(s => s.StartsWith(baseTagName));
if (matched is not null)
{ {
var observatory = new Observatory4Ray baseTagName = matched;
if (v2rayConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
{ {
subjectSelector = [Global.ProxyTag], v2rayConfig.burstObservatory.subjectSelector.Remove(baseTagName);
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl, v2rayConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
probeInterval = "3m", }
enableConcurrency = true,
}; if (v2rayConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
v2rayConfig.observatory = observatory; {
v2rayConfig.observatory.subjectSelector.Remove(baseTagName);
v2rayConfig.observatory.subjectSelector.Insert(0, baseTagName);
}
return await Task.FromResult(0);
} }
else if (multipleLoad == EMultipleLoad.LeastLoad)
// Case 3: need to create or insert based on multipleLoad type
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
{ {
var burstObservatory = new BurstObservatory4Ray if (v2rayConfig.burstObservatory is null)
{ {
subjectSelector = [Global.ProxyTag], // Create new burst observatory with default ping config
pingConfig = new() v2rayConfig.burstObservatory = new BurstObservatory4Ray
{ {
destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl, subjectSelector = [baseTagName],
interval = "5m", pingConfig = new()
timeout = "30s", {
sampling = 2, destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
} interval = "5m",
}; timeout = "30s",
v2rayConfig.burstObservatory = burstObservatory; sampling = 2,
}
};
}
else
{
v2rayConfig.burstObservatory.subjectSelector ??= new();
v2rayConfig.burstObservatory.subjectSelector.Add(baseTagName);
}
} }
else if (multipleLoad is EMultipleLoad.LeastPing)
{
if (v2rayConfig.observatory is null)
{
// Create new observatory with default probe config
v2rayConfig.observatory = new Observatory4Ray
{
subjectSelector = [baseTagName],
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
probeInterval = "3m",
enableConcurrency = true,
};
}
else
{
v2rayConfig.observatory.subjectSelector ??= new();
v2rayConfig.observatory.subjectSelector.Add(baseTagName);
}
}
return await Task.FromResult(0);
}
private async Task<string> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
{
var strategyType = multipleLoad switch var strategyType = multipleLoad switch
{ {
EMultipleLoad.Random => "random", EMultipleLoad.Random => "random",
@@ -38,13 +91,22 @@ public partial class CoreConfigV2rayService
EMultipleLoad.LeastLoad => "leastLoad", EMultipleLoad.LeastLoad => "leastLoad",
_ => "roundRobin", _ => "roundRobin",
}; };
var balancerTag = $"{selector}{Global.BalancerTagSuffix}";
var balancer = new BalancersItem4Ray var balancer = new BalancersItem4Ray
{ {
selector = [Global.ProxyTag], selector = [selector],
strategy = new() { type = strategyType }, strategy = new()
tag = $"{Global.ProxyTag}-round", {
type = strategyType,
settings = new()
{
expected = 1,
},
},
tag = balancerTag,
}; };
v2rayConfig.routing.balancers = [balancer]; v2rayConfig.routing.balancers ??= new();
return await Task.FromResult(0); v2rayConfig.routing.balancers.Add(balancer);
return await Task.FromResult(balancerTag);
} }
} }

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Nodes;
namespace ServiceLib.Services.CoreConfig; namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService public partial class CoreConfigV2rayService
@@ -88,8 +86,8 @@ public partial class CoreConfigV2rayService
} }
} }
// Handle outbounds - append instead of override var customOutboundsNode = new JsonArray();
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
foreach (var outbound in v2rayConfig.outbounds) foreach (var outbound in v2rayConfig.outbounds)
{ {
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
@@ -99,14 +97,30 @@ public partial class CoreConfigV2rayService
continue; continue;
} }
} }
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty))) else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
{ {
outbound.streamSettings ??= new StreamSettings4Ray(); var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
outbound.streamSettings.sockopt ??= new Sockopt4Ray(); ?? outbound.settings?.vnext?.FirstOrDefault()?.address
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; ?? string.Empty;
if (!Utils.IsPrivateNetwork(outboundAddress))
{
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
}
} }
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
} }
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
{
foreach (var outbound in templateOutbounds)
{
customOutboundsNode.Add(outbound?.DeepClone());
}
}
fullConfigTemplateNode["outbounds"] = customOutboundsNode; fullConfigTemplateNode["outbounds"] = customOutboundsNode;
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));

View File

@@ -1,7 +1,3 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Services.CoreConfig; namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService public partial class CoreConfigV2rayService
@@ -83,9 +79,23 @@ public partial class CoreConfigV2rayService
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null) static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{ {
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
int? portFinal = null;
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = domain;
portFinal = port > 0 ? port : null;
}
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = scheme + "://" + domain;
portFinal = port > 0 ? port : null;
}
var dnsServer = new DnsServer4Ray var dnsServer = new DnsServer4Ray
{ {
address = dnsAddress, address = domainFinal,
port = portFinal,
skipFallback = true, skipFallback = true,
domains = domains.Count > 0 ? domains : null, domains = domains.Count > 0 ? domains : null,
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
@@ -107,6 +117,35 @@ public partial class CoreConfigV2rayService
var expectedIPs = new List<string>(); var expectedIPs = new List<string>();
var regionNames = new HashSet<string>(); var regionNames = new HashSet<string>();
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.FirstOrDefault());
var dnsServerDomains = new List<string>();
foreach (var dns in directDNSAddress)
{
var (domain, _, _, _) = Utils.ParseUrl(dns);
if (domain == "localhost")
{
continue;
}
if (Utils.IsDomain(domain))
{
dnsServerDomains.Add($"full:{domain}");
}
}
foreach (var dns in remoteDNSAddress)
{
var (domain, _, _, _) = Utils.ParseUrl(dns);
if (domain == "localhost")
{
continue;
}
if (Utils.IsDomain(domain))
{
dnsServerDomains.Add($"full:{domain}");
}
}
dnsServerDomains = dnsServerDomains.Distinct().ToList();
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
{ {
expectedIPs = simpleDNSItem.DirectExpectedIPs expectedIPs = simpleDNSItem.DirectExpectedIPs
@@ -142,6 +181,11 @@ public partial class CoreConfigV2rayService
continue; continue;
} }
if (item.RuleType == ERuleType.Routing)
{
continue;
}
foreach (var domain in item.Domain) foreach (var domain in item.Domain)
{ {
if (domain.StartsWith('#')) if (domain.StartsWith('#'))
@@ -216,6 +260,10 @@ public partial class CoreConfigV2rayService
AddDnsServers(remoteDNSAddress, proxyGeositeList); AddDnsServers(remoteDNSAddress, proxyGeositeList);
AddDnsServers(directDNSAddress, directGeositeList); AddDnsServers(directDNSAddress, directGeositeList);
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
if (dnsServerDomains.Count > 0)
{
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
}
var useDirectDns = rules?.LastOrDefault() is { } lastRule var useDirectDns = rules?.LastOrDefault() is { } lastRule
&& lastRule.OutboundTag == Global.DirectTag && lastRule.OutboundTag == Global.DirectTag
@@ -349,7 +397,7 @@ public partial class CoreConfigV2rayService
return 0; return 0;
} }
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dnsItem)
{ {
if (node == null) if (node == null)
{ {
@@ -388,7 +436,7 @@ public partial class CoreConfigV2rayService
{ {
var dnsServer = new DnsServer4Ray() var dnsServer = new DnsServer4Ray()
{ {
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
skipFallback = true, skipFallback = true,
domains = domainList domains = domainList
}; };

View File

@@ -480,6 +480,60 @@ public partial class CoreConfigV2rayService
return 0; return 0;
} }
private async Task<int> GenGroupOutbound(ProfileItem node, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
{
try
{
if (!node.ConfigType.IsGroupType())
{
return -1;
}
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
if (hasCycle)
{
return -1;
}
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
return -1;
}
switch (node.ConfigType)
{
case EConfigType.PolicyGroup:
if (ignoreOriginChain)
{
await GenOutboundsList(childProfiles, v2rayConfig, baseTagName);
}
else
{
await GenOutboundsListWithChain(childProfiles, v2rayConfig, baseTagName);
}
break;
case EConfigType.ProxyChain:
await GenChainOutboundsList(childProfiles, v2rayConfig, baseTagName);
break;
default:
break;
}
//add balancers
if (node.ConfigType == EConfigType.PolicyGroup)
{
await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return await Task.FromResult(0);
}
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig) private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
{ {
//fragment proxy //fragment proxy
@@ -552,7 +606,7 @@ public partial class CoreConfigV2rayService
return 0; return 0;
} }
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig) private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
{ {
try try
{ {
@@ -577,6 +631,25 @@ public partial class CoreConfigV2rayService
{ {
index++; index++;
if (node.ConfigType.IsGroupType())
{
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
continue;
}
var childBaseTagName = $"{baseTagName}-{index}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
continue;
}
// Handle proxy chain // Handle proxy chain
string? prevTag = null; string? prevTag = null;
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
@@ -590,7 +663,7 @@ public partial class CoreConfigV2rayService
// current proxy // current proxy
await GenOutbound(node, currentOutbound); await GenOutbound(node, currentOutbound);
currentOutbound.tag = $"{Global.ProxyTag}-{index}"; currentOutbound.tag = $"{baseTagName}-{index}";
if (!node.Subid.IsNullOrEmpty()) if (!node.Subid.IsNullOrEmpty())
{ {
@@ -606,7 +679,7 @@ public partial class CoreConfigV2rayService
{ {
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(prevNode, prevOutbound); await GenOutbound(prevNode, prevOutbound);
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}"; prevTag = $"prev-{baseTagName}-{++prevIndex}";
prevOutbound.tag = prevTag; prevOutbound.tag = prevTag;
prevOutbounds.Add(prevOutbound); prevOutbounds.Add(prevOutbound);
} }
@@ -628,9 +701,17 @@ public partial class CoreConfigV2rayService
} }
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds // Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
resultOutbounds.AddRange(prevOutbounds); if (baseTagName == Global.ProxyTag)
resultOutbounds.AddRange(v2rayConfig.outbounds); {
v2rayConfig.outbounds = resultOutbounds; resultOutbounds.AddRange(prevOutbounds);
resultOutbounds.AddRange(v2rayConfig.outbounds);
v2rayConfig.outbounds = resultOutbounds;
}
else
{
v2rayConfig.outbounds.AddRange(prevOutbounds);
v2rayConfig.outbounds.AddRange(resultOutbounds);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -692,4 +773,110 @@ public partial class CoreConfigV2rayService
} }
return null; return null;
} }
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
{
var resultOutbounds = new List<Outbounds4Ray>();
for (var i = 0; i < nodes.Count; i++)
{
var node = nodes[i];
if (node == null)
continue;
if (node.ConfigType.IsGroupType())
{
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
if (childProfiles.Count <= 0)
{
continue;
}
var childBaseTagName = $"{baseTagName}-{i + 1}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
continue;
}
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (txtOutbound.IsNullOrEmpty())
{
break;
}
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
var result = await GenOutbound(node, outbound);
if (result != 0)
{
break;
}
outbound.tag = baseTagName + (i + 1).ToString();
resultOutbounds.Add(outbound);
}
if (baseTagName == Global.ProxyTag)
{
resultOutbounds.AddRange(v2rayConfig.outbounds);
v2rayConfig.outbounds = resultOutbounds;
}
else
{
v2rayConfig.outbounds.AddRange(resultOutbounds);
}
return await Task.FromResult(0);
}
private async Task<int> GenChainOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
{
// Based on actual network flow instead of data packets
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
var resultOutbounds = new List<Outbounds4Ray>();
for (var i = 0; i < nodesReverse.Count; i++)
{
var node = nodesReverse[i];
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (txtOutbound.IsNullOrEmpty())
{
break;
}
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
var result = await GenOutbound(node, outbound);
if (result != 0)
{
break;
}
if (i == 0)
{
outbound.tag = baseTagName;
}
else
{
// avoid v2ray observe
outbound.tag = "chain-" + baseTagName + i.ToString();
}
if (i != nodesReverse.Count - 1)
{
outbound.streamSettings.sockopt = new()
{
dialerProxy = "chain-" + baseTagName + (i + 1).ToString()
};
}
resultOutbounds.Add(outbound);
}
if (baseTagName == Global.ProxyTag)
{
resultOutbounds.AddRange(v2rayConfig.outbounds);
v2rayConfig.outbounds = resultOutbounds;
}
else
{
v2rayConfig.outbounds.AddRange(resultOutbounds);
}
return await Task.FromResult(0);
}
} }

View File

@@ -20,11 +20,18 @@ public partial class CoreConfigV2rayService
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item in rules) foreach (var item in rules)
{ {
if (item.Enabled) if (!item.Enabled)
{ {
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item)); continue;
await GenRoutingUserRule(item2, v2rayConfig);
} }
if (item.RuleType == ERuleType.DNS)
{
continue;
}
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
await GenRoutingUserRule(item2, v2rayConfig);
} }
} }
} }
@@ -125,18 +132,30 @@ public partial class CoreConfigV2rayService
} }
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag); var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
if (node == null if (node == null
|| !Global.XraySupportConfigType.Contains(node.ConfigType)) || (!Global.XraySupportConfigType.Contains(node.ConfigType)
&& !node.ConfigType.IsGroupType()))
{ {
return Global.ProxyTag; return Global.ProxyTag;
} }
var tag = Global.ProxyTag + node.IndexId.ToString(); var tag = $"{node.IndexId}-{Global.ProxyTag}";
if (v2rayConfig.outbounds.Any(p => p.tag == tag)) if (v2rayConfig.outbounds.Any(p => p.tag == tag))
{ {
return tag; return tag;
} }
if (node.ConfigType.IsGroupType())
{
var ret = await GenGroupOutbound(node, v2rayConfig, tag);
if (ret == 0)
{
return tag;
}
return Global.ProxyTag;
}
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(node, outbound); await GenOutbound(node, outbound);

View File

@@ -1,6 +1,4 @@
using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Sockets;
namespace ServiceLib.Services; namespace ServiceLib.Services;
@@ -19,8 +17,6 @@ public class DownloadService
{ {
try try
{ {
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var progress = new Progress<string>(); var progress = new Progress<string>();
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}"); progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
@@ -44,7 +40,6 @@ public class DownloadService
{ {
try try
{ {
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}")); UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
var progress = new Progress<double>(); var progress = new Progress<double>();
@@ -71,7 +66,6 @@ public class DownloadService
public async Task<string?> UrlRedirectAsync(string url, bool blProxy) public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
{ {
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webRequestHandler = new SocketsHttpHandler var webRequestHandler = new SocketsHttpHandler
{ {
AllowAutoRedirect = false, AllowAutoRedirect = false,
@@ -141,7 +135,6 @@ public class DownloadService
{ {
try try
{ {
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webProxy = await GetWebProxy(blProxy); var webProxy = await GetWebProxy(blProxy);
var client = new HttpClient(new SocketsHttpHandler() var client = new HttpClient(new SocketsHttpHandler()
{ {
@@ -186,8 +179,6 @@ public class DownloadService
{ {
try try
{ {
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webProxy = await GetWebProxy(blProxy); var webProxy = await GetWebProxy(blProxy);
if (userAgent.IsNullOrEmpty()) if (userAgent.IsNullOrEmpty())
@@ -238,17 +229,4 @@ public class DownloadService
return false; return false;
} }
} }
private static void SetSecurityProtocol(bool enableSecurityProtocolTls13)
{
if (enableSecurityProtocolTls13)
{
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
}
else
{
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
}
ServicePointManager.DefaultConnectionLimit = 256;
}
} }

View File

@@ -1,6 +1,3 @@
using System.Diagnostics;
using System.Text;
namespace ServiceLib.Services; namespace ServiceLib.Services;
public class ProcessService : IDisposable public class ProcessService : IDisposable

View File

@@ -1,8 +1,3 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
namespace ServiceLib.Services; namespace ServiceLib.Services;
public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc) public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc)
@@ -64,7 +59,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
var lstSelected = new List<ServerTestItem>(); var lstSelected = new List<ServerTestItem>();
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType.IsComplexType())
{ {
continue; continue;
} }
@@ -116,10 +111,6 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
List<Task> tasks = []; List<Task> tasks = [];
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
try try
@@ -199,10 +190,6 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
{ {
continue; continue;
} }
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
await DoRealPing(it); await DoRealPing(it);
@@ -216,7 +203,10 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
} }
finally finally
{ {
await processService?.StopAsync(); if (processService != null)
{
await processService?.StopAsync();
}
} }
return true; return true;
} }
@@ -233,10 +223,6 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue; continue;
} }
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
await concurrencySemaphore.WaitAsync(); await concurrencySemaphore.WaitAsync();
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
@@ -272,7 +258,10 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
} }
finally finally
{ {
await processService?.StopAsync(); if (processService != null)
{
await processService?.StopAsync();
}
concurrencySemaphore.Release(); concurrencySemaphore.Release();
} }
})); }));
@@ -345,7 +334,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
{ {
List<List<ServerTestItem>> lstTest = new(); List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList(); var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
var lst2 = lstSelected.Where(t => Global.SingboxSupportConfigType.Contains(t.ConfigType) && !Global.XraySupportConfigType.Contains(t.ConfigType)).ToList(); var lst2 = lstSelected.Where(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++) for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{ {

View File

@@ -1,5 +1,4 @@
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text;
namespace ServiceLib.Services.Statistics; namespace ServiceLib.Services.Statistics;

View File

@@ -1,6 +1,3 @@
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace ServiceLib.Services; namespace ServiceLib.Services;
public class UpdateService public class UpdateService

View File

@@ -0,0 +1,233 @@
namespace ServiceLib.ViewModels;
public class AddGroupServerViewModel : MyReactiveObject
{
[Reactive]
public ProfileItem SelectedSource { get; set; }
[Reactive]
public ProfileItem SelectedChild { get; set; }
[Reactive]
public IList<ProfileItem> SelectedChildren { get; set; }
[Reactive]
public string? CoreType { get; set; }
[Reactive]
public string? PolicyGroupType { get; set; }
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public AddGroupServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppManager.Instance.Config;
_updateView = updateView;
var canEditRemove = this.WhenAnyValue(
x => x.SelectedChild,
SelectedChild => SelectedChild != null && !SelectedChild.Remarks.IsNullOrEmpty());
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ChildRemoveAsync();
}, canEditRemove);
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Top);
}, canEditRemove);
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Up);
}, canEditRemove);
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Down);
}, canEditRemove);
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Bottom);
}, canEditRemove);
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SaveServerAsync();
});
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();
ProfileGroupItemManager.Instance.TryGet(profileItem.IndexId, out var profileGroup);
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
{
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
EMultipleLoad.Fallback => ResUI.TbFallback,
EMultipleLoad.Random => ResUI.TbRandom,
EMultipleLoad.RoundRobin => ResUI.TbRoundRobin,
EMultipleLoad.LeastLoad => ResUI.TbLeastLoad,
_ => ResUI.TbLeastPing,
};
_ = Init();
}
public async Task Init()
{
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
if (childItemMulti != null)
{
var childIndexIds = childItemMulti.ChildItems.IsNullOrEmpty() ? new List<string>() : Utils.String2List(childItemMulti.ChildItems);
foreach (var item in childIndexIds)
{
var child = await AppManager.Instance.GetProfileItem(item);
if (child == null)
{
continue;
}
ChildItemsObs.Add(child);
}
}
}
public async Task ChildRemoveAsync()
{
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
foreach (var it in SelectedChildren ?? [SelectedChild])
{
if (it != null)
{
ChildItemsObs.Remove(it);
}
}
await Task.CompletedTask;
}
public async Task MoveServer(EMove eMove)
{
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
var index = ChildItemsObs.IndexOf(SelectedChild);
if (index < 0)
{
return;
}
var selectedChild = JsonUtils.DeepCopy(SelectedChild);
switch (eMove)
{
case EMove.Top:
if (index == 0)
{
return;
}
ChildItemsObs.RemoveAt(index);
ChildItemsObs.Insert(0, selectedChild);
break;
case EMove.Up:
if (index == 0)
{
return;
}
ChildItemsObs.RemoveAt(index);
ChildItemsObs.Insert(index - 1, selectedChild);
break;
case EMove.Down:
if (index == ChildItemsObs.Count - 1)
{
return;
}
ChildItemsObs.RemoveAt(index);
ChildItemsObs.Insert(index + 1, selectedChild);
break;
case EMove.Bottom:
if (index == ChildItemsObs.Count - 1)
{
return;
}
ChildItemsObs.RemoveAt(index);
ChildItemsObs.Add(selectedChild);
break;
default:
break;
}
await Task.CompletedTask;
}
private async Task SaveServerAsync()
{
var remarks = SelectedSource.Remarks;
if (remarks.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
return;
}
if (ChildItemsObs.Count == 0)
{
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
return;
}
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? ECoreType.Xray : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
if (SelectedSource.CoreType is not (ECoreType.Xray or ECoreType.sing_box) ||
SelectedSource.ConfigType is not (EConfigType.ProxyChain or EConfigType.PolicyGroup))
{
return;
}
var childIndexIds = new List<string>();
foreach (var item in ChildItemsObs)
{
if (item.IndexId.IsNullOrEmpty())
{
continue;
}
childIndexIds.Add(item.IndexId);
}
var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId);
profileGroup.ChildItems = Utils.List2String(childIndexIds);
profileGroup.MultipleLoad = PolicyGroupType switch
{
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
_ => EMultipleLoad.LeastPing,
};
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
if (hasCycle)
{
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
return;
}
if (await ConfigHandler.AddGroupServerCommon(_config, SelectedSource, profileGroup, true) == 0)
{
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
_updateView?.Invoke(EViewAction.CloseWindow, null);
}
else
{
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
}
}
}

View File

@@ -1,7 +1,3 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class AddServer2ViewModel : MyReactiveObject public class AddServer2ViewModel : MyReactiveObject

View File

@@ -1,7 +1,3 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class AddServerViewModel : MyReactiveObject public class AddServerViewModel : MyReactiveObject

View File

@@ -1,7 +1,3 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class BackupAndRestoreViewModel : MyReactiveObject public class BackupAndRestoreViewModel : MyReactiveObject

View File

@@ -1,11 +1,3 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class CheckUpdateViewModel : MyReactiveObject public class CheckUpdateViewModel : MyReactiveObject

View File

@@ -1,11 +1,3 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
public class ClashConnectionsViewModel : MyReactiveObject public class ClashConnectionsViewModel : MyReactiveObject

View File

@@ -1,11 +1,4 @@
using System.Reactive;
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using static ServiceLib.Models.ClashProviders; using static ServiceLib.Models.ClashProviders;
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;

Some files were not shown because too many files have changed in this diff Show More