Compare commits

...

175 Commits

Author SHA1 Message Date
2dust
3f0f895424 up 7.10.4 2025-03-06 17:35:10 +08:00
2dust
07a3bdc618 AI-optimized code 2025-03-06 16:36:39 +08:00
2dust
7e348c196e AI-optimized code 2025-03-06 14:53:25 +08:00
2dust
51e5885e76 AI-optimized code 2025-03-06 14:42:21 +08:00
2dust
50d7912f62 AI-optimized code 2025-03-06 14:34:31 +08:00
2dust
3869148fc8 AI-optimized code 2025-03-06 14:30:02 +08:00
2dust
a0af4fb30c Update DownloadService.cs
AI-optimized code
2025-03-06 14:08:38 +08:00
2dust
c374b8565b Update SpeedtestService.cs
AI-optimized code
2025-03-06 14:08:29 +08:00
2dust
7e8b405555 AI-optimized code 2025-03-06 12:21:21 +08:00
2dust
c3439c5abe AI-optimized code 2025-03-06 11:10:55 +08:00
2dust
d4a8787356 AI-optimized code 2025-03-06 10:57:39 +08:00
2dust
23b27575a0 AI-optimized code 2025-03-06 10:48:18 +08:00
2dust
8d8a887c42 Update HttpClientHelper.cs
AI-optimized code
2025-03-06 10:47:57 +08:00
2dust
1229c967ba Update SemanticVersion.cs
AI-optimized code
2025-03-06 10:47:31 +08:00
2dust
d35f65f86d Update Utils.cs
AI-optimized code
2025-03-06 10:47:06 +08:00
2dust
0a8ce0f961 Update ProxySettingWindows.cs
AI-optimized code
2025-03-06 10:46:50 +08:00
2dust
8092481d26 Update Job.cs
AI-optimized code
2025-03-06 10:46:41 +08:00
2dust
764014e49a Replace all Utils.ToInt(variable) with variable.ToInt() 2025-03-05 20:26:26 +08:00
2dust
71cc6d7a88 Replace all Utils.IsNullOrEmpty(variable) with variable.IsNullOrEmpty() 2025-03-05 19:44:49 +08:00
2dust
f3af831cf2 Replace all Utils.IsNotEmpty(variable) with variable.IsNotEmpty() 2025-03-05 16:42:43 +08:00
2dust
78fde575d7 up 7.10.3 2025-03-04 17:03:26 +08:00
2dust
6e5af34877 Revert "If the update fails during the upgrade, the update will be retried."
This reverts commit 9748fbb076.
2025-03-04 17:02:05 +08:00
2dust
8d1853e991 up 7.10.2 2025-03-04 15:11:05 +08:00
DHR60
859299c712 Add kcp DNS masquerade support (#6852)
* Add kcp DNS masquerade support

* Update V2rayConfig.cs

* Update CoreConfigV2rayService.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-03-04 10:32:07 +08:00
2dust
7fbb0013b0 Optimizing Task Code 2025-03-03 16:57:55 +08:00
2dust
837cfbd03b Optimize UI prompts 2025-03-03 14:36:30 +08:00
2dust
cdc5d72cfa Update CoreConfigSingboxService.cs 2025-03-03 14:36:01 +08:00
DHR60
8dcf5c5b90 Add Hy2 Port hopping URI support (#6848) 2025-03-03 14:11:53 +08:00
2dust
67fe6ac3d8 Optimizing Task Code, add unified processing of scheduled tasks 2025-03-03 12:20:58 +08:00
2dust
438eaba4d5 up 7.10.1 2025-03-02 10:30:08 +08:00
2dust
3c8baa99d5 Update Directory.Packages.props 2025-03-02 10:29:43 +08:00
2dust
e70658f311 Add Hy2 Port hopping for sing-box 1.11+
https://github.com/2dust/v2rayN/issues/6772
2025-03-01 21:13:37 +08:00
2dust
2dd10cf5a1 Optimize QrcodeView 2025-03-01 19:56:52 +08:00
2dust
96781a784b git submodule update --remote 2025-03-01 15:29:54 +08:00
2dust
9748fbb076 If the update fails during the upgrade, the update will be retried. 2025-03-01 14:23:43 +08:00
2dust
aa5e4378ab Update AutoStartupHandler.cs 2025-03-01 14:14:07 +08:00
2dust
a7de149fd7 up 7.10.0 2025-02-28 09:46:43 +08:00
2dust
ae38be36f5 Checkout submodules 2025-02-27 20:53:29 +08:00
2dust
a20e989211 Update build-windows-desktop.yml 2025-02-27 20:50:52 +08:00
2dust
579f47ba0d Create GlobalHotKeys 2025-02-27 20:25:20 +08:00
2dust
24cad87954 Bug fix
https://github.com/2dust/v2rayN/issues/6812
2025-02-27 20:12:53 +08:00
2dust
84d72cd110 Use project to implement Windows global hotkey
https://github.com/2dust/GlobalHotKeys
2025-02-27 20:12:07 +08:00
2dust
c0cd46a5aa Optimize HotkeyHandler 2025-02-27 17:50:54 +08:00
2dust
98613c43ca Fix tun mtu setting 2025-02-26 20:46:31 +08:00
2dust
555960e210 Optimize and improve code 2025-02-26 17:01:57 +08:00
2dust
a18ae5582b Jump to the selected item when refreshing the server list
https://github.com/2dust/v2rayN/issues/6800
2025-02-26 16:36:36 +08:00
2dust
6d6894591c Update Global.cs 2025-02-26 15:51:47 +08:00
2dust
984b97fc14 Optimize latency and IP address information testing
If the first test fails, it will be tested again after 500ms.
2025-02-26 14:30:10 +08:00
2dust
a7f35d4495 Windows desktop version add global hotkey function 2025-02-26 10:52:36 +08:00
2dust
add92cfa7c Update desktop global hotkey setting 2025-02-25 16:14:50 +08:00
2dust
6079e76be5 Improved global hotkey setting 2025-02-25 14:26:12 +08:00
2dust
166c7cb2f5 Fix window title 2025-02-25 14:15:54 +08:00
nayeko
dbd3ca44c2 Fix Package AppImage script (#6794)
Co-authored-by: nayeko <nayeko@users.noreply.github.com>
2025-02-25 10:06:33 +08:00
dependabot[bot]
ae495dde54 Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#6797)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-25 09:46:08 +08:00
2dust
4ae25b2f34 Improved global hotkey setting 2025-02-24 19:53:38 +08:00
2dust
cdfb621c59 Update MsgView.axaml 2025-02-24 19:07:05 +08:00
2dust
29e8df7d2e When set the linux system proxy, use shell scripts instead of command lines 2025-02-24 11:02:51 +08:00
2dust
72ff947d95 Bug fix for ProxySettingOSX 2025-02-23 19:59:01 +08:00
2dust
3edaac5739 When set the macOS system proxy, use shell scripts instead of command lines 2025-02-23 19:52:13 +08:00
2dust
5777a97119 Create proxy_set_osx_sh 2025-02-23 19:41:34 +08:00
2dust
aeddbc1dcc CreateLinuxShellFile in the binConfigs folder 2025-02-23 17:19:45 +08:00
2dust
2f3e409487 Add linux bash param 2025-02-23 16:59:22 +08:00
2dust
aa133bb50b Update SpeedtestService.cs 2025-02-23 16:34:40 +08:00
2dust
3bc79a4ba1 Update 01_bug_report.yml 2025-02-23 12:05:51 +08:00
2dust
064a04fbad up 7.9.3 2025-02-21 17:47:30 +08:00
2dust
39ed13cf8a Create build-all.yml 2025-02-21 17:38:47 +08:00
2dust
a1edacb196 Update UpdateService.cs 2025-02-21 17:37:36 +08:00
2dust
c9f79e4b47 Added update function for Country.mmdb, geoip-only-cn-private.dat, geoip.metadb
https://github.com/2dust/v2rayN/issues/6752
2025-02-21 15:38:11 +08:00
2dust
40c1498226 When testing speed, skip items with incorrect latency 2025-02-21 14:33:43 +08:00
2dust
390061f9bd Fix the desktop UI bug 2025-02-21 12:40:45 +08:00
2dust
42324a2c9e Adjust controls margin 2025-02-20 18:34:25 +08:00
2dust
b9b4ca6360 The desktop version use SemiFontFamilyRegular, Fall back to built-in fonts 2025-02-20 16:23:46 +08:00
2dust
565697bc0b The desktop version unified icon size 2025-02-20 14:32:38 +08:00
2dust
2e6c82c851 Adjust UI
https://github.com/2dust/v2rayN/issues/6751
2025-02-20 14:07:12 +08:00
2dust
ee75dd37af The desktop version add wrap to the subscription group 2025-02-20 11:08:28 +08:00
2dust
4859dcda08 up 7.9.2 2025-02-19 21:10:47 +08:00
2dust
2e962e555d Use AutoCompleteBox instead of editable Combobox for desktop
Thanks to https://github.com/irihitech/Ursa.Avalonia
2025-02-19 17:41:58 +08:00
2dust
ab73e5acb2 Micro-adjustment interface 2025-02-19 17:39:47 +08:00
2dust
4e3e5ce130 Add CanUserReorderColumns for desktop 2025-02-19 15:03:29 +08:00
2dust
bb8eef3bf5 Removed the Hide to tray when closing the window feature for Windows version
https://github.com/2dust/v2rayN/issues/6726
2025-02-19 10:42:31 +08:00
2dust
eee87ded29 Fix cache.db storage location
https://github.com/2dust/v2rayN/issues/6731
2025-02-19 10:06:47 +08:00
2dust
e0f005bd96 up 7.9.1 2025-02-18 18:58:19 +08:00
2dust
2cacc372ad Optimize desktop DataGrid RowHeight adjusts with font size 2025-02-18 18:48:29 +08:00
2dust
0b1b681655 Add system proxy pac to the Windows desktop version 2025-02-18 14:38:08 +08:00
2dust
deafd73306 Optimize RemoveInvalidServerResult 2025-02-17 20:00:28 +08:00
2dust
317a5da120 up build 2025-02-17 14:38:26 +08:00
bonjour
071cefc511 Revert "Fix Package AppImage script (#6681)" (#6714)
This reverts commit 41cc260b5c.
2025-02-17 13:59:36 +08:00
2dust
32a5cc8aa3 Check for avalonia desktop windows version 2025-02-17 12:27:00 +08:00
2dust
c41378a085 Update build-windows-desktop.yml 2025-02-17 12:24:55 +08:00
2dust
6910e03ef4 up 7.9.0 2025-02-16 20:34:14 +08:00
2dust
5060a358db Clear resx 2025-02-16 19:12:58 +08:00
2dust
b3e9a957c4 Remove invalid by test results 2025-02-16 15:07:09 +08:00
2dust
f6dbfc2dac up PackageVersion 2025-02-16 15:05:10 +08:00
2dust
2966a34e63 Update Directory.Build.props 2025-02-16 11:15:32 +08:00
2dust
50959951ae The number of concurrent during multi-test 2025-02-15 20:27:20 +08:00
2dust
c2e1cf7bdb Fix windows TaskbarIcon 2025-02-15 17:26:20 +08:00
2dust
51ac7cc8be Improved and optimized speedtest 2025-02-15 11:21:35 +08:00
2dust
3144f1d1c2 Remove SpeedTestPageSize 2025-02-14 15:00:23 +08:00
2dust
a176e7b912 Optimize the prompt function of speed test
Save friendly prompt information during speed test.
2025-02-14 14:55:43 +08:00
2dust
1198ec0f74 Improved and optimized speedtest
When testing server speed, start the Core for each server and generate the same configuration files as when using the server.
Add a folder binConfigs to store temporary configuration files for Core.
2025-02-13 19:46:51 +08:00
2dust
4104964e38 up 7.8.3 2025-02-12 20:04:29 +08:00
2dust
3bbd1edf06 Update V2rayConfig.cs 2025-02-12 19:18:59 +08:00
lyranico
41cc260b5c Fix Package AppImage script (#6681) 2025-02-12 14:33:02 +08:00
2dust
6cd5063c9b Fix call core file
https://github.com/2dust/v2rayN/issues/6680
2025-02-12 11:23:22 +08:00
2dust
e544df6d01 up 7.8.2 2025-02-11 16:30:16 +08:00
2dust
f952d2383c Remove IncludeNativeLibrariesForSelfExtract 2025-02-11 10:16:19 +08:00
2dust
8a29e147d3 Added Enable Mixin parameter 2025-02-10 20:04:47 +08:00
Alphax-Hue3682
8a19128e7f Update Persian translate (#6651)
* Update Persian translate

* Update ResUI.fa-Ir.resx
2025-02-10 17:40:21 +08:00
2dust
7dc9fbd8ff Improved and optimized speedtest 2025-02-10 10:08:03 +08:00
2dust
31a179e647 Code clean 2025-02-09 20:17:56 +08:00
2dust
c3fdfcc4bd up 7.8.1 2025-02-08 16:49:59 +08:00
2dust
7ea8fae2da Improve test logic
Retest the failed part of the test and call it recursively.
Remove the number of batches that are automatically divided when testing parameters.
2025-02-08 16:43:40 +08:00
2dust
67ffa810d3 Optimize code and remove Action 2025-02-06 15:28:51 +08:00
2dust
ba2a636dd2 Fixed the system language judgment during the first run
https://github.com/2dust/v2rayN/issues/6638
2025-02-06 14:36:38 +08:00
2dust
d471336994 Adjust App Exit function
https://github.com/2dust/v2rayN/issues/6634
2025-02-05 17:31:13 +08:00
2dust
7e6482fdff Adjust hysteria core run arguments
https://github.com/2dust/v2rayN/issues/6635
2025-02-05 17:14:56 +08:00
2dust
a8eba93ffd up 7.8.0 2025-02-04 14:30:42 +08:00
2dust
4ffe595db6 Code clean 2025-02-04 14:30:28 +08:00
2dust
885587e551 Update Directory.Packages.props 2025-02-04 14:19:56 +08:00
2dust
e986dc189e Fix ProcUtils NoAssociatedProcess issue 2025-02-04 10:30:33 +08:00
2dust
bccab41c8f Update Directory.Build.props 2025-02-04 10:28:49 +08:00
2dust
79a0538ca0 Improve core info 2025-02-03 20:19:20 +08:00
2dust
e6b27d17e4 Add support for overtls custom configuration
https://github.com/ShadowsocksR-Live/overtls
2025-02-03 15:43:26 +08:00
2dust
2a0012824a Add support for brook custom configuration
https://github.com/txthinking/brook
https://github.com/txthinking/brook/issues/1372
2025-02-03 15:06:21 +08:00
2dust
9d92be99ee Improve core running arguments 2025-02-03 14:48:53 +08:00
2dust
6914831d30 Code clean 2025-02-02 17:48:59 +08:00
2dust
19d83be6de Fix Package AppImage script 2025-02-01 19:10:54 +08:00
2dust
ce7303bd0d up 7.7.1 2025-02-01 14:16:54 +08:00
2dust
4c3318ac86 Revert "Update MainWindow.axaml.cs"
This reverts commit 4d95d7d8c0.
2025-02-01 14:11:12 +08:00
2dust
db25fdeee1 Revert "Remove using System.Reflection"
This reverts commit 732c3eee8b.
2025-02-01 13:51:28 +08:00
2dust
732c3eee8b Remove using System.Reflection 2025-02-01 10:34:05 +08:00
2dust
2b0d805b2f Update Directory.Build.props 2025-02-01 10:33:11 +08:00
2dust
4faa94b2a3 Cache when reading embedded resources 2025-01-31 16:02:29 +08:00
2dust
331d11aee2 Update .editorconfig 2025-01-31 15:59:44 +08:00
2dust
4d95d7d8c0 Update MainWindow.axaml.cs
if (Utils.IsOSX() || _config.UiItem.Hide2TrayWhenClose)
2025-01-31 15:59:35 +08:00
dependabot[bot]
fd82623c74 Bump actions/setup-dotnet from 4.2.0 to 4.3.0 (#6604)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-31 09:17:42 +08:00
2dust
4a32b2c814 Fixed the issue of update the Windows self contained version 2025-01-30 18:06:13 +08:00
2dust
45264005a4 Code clean 2025-01-30 17:10:05 +08:00
2dust
253219dd16 Update .editorconfig 2025-01-30 16:35:48 +08:00
ShiinaRinne
b2feaf3ba9 [macOS] feat: auto run. https://github.com/2dust/v2rayN/issues/6383 (#6597) 2025-01-30 13:24:01 +08:00
2dust
6c5011ad68 Try to remove the tun device when restarting the service in Windows
https://github.com/2dust/v2rayN/pull/6561
2025-01-30 10:46:04 +08:00
ShiinaRinne
c0f8b6b84c [macOS] amend: Child window visibility handling (#6598)
https://github.com/2dust/v2rayN/pull/6596

When the main window is hidden, open child windows are mistakenly hidden as well.
This prevents reopening child windows after the main window is shown again.
2025-01-30 10:12:59 +08:00
ShiinaRinne
f71125d8f3 macOS 一些优化 (#6596)
* [macOS] hide icon in Dock

* [macOS] fix: close button not work

* [macOS] fix: cant directly show window when click `Show or hide the main window` on minimized
2025-01-29 19:26:40 +08:00
2dust
e674a025d8 Adjust Directory.Build.props
https://learn.microsoft.com/zh-cn/dotnet/core/deploying/trimming/trimming-options

https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/compiler-options/code-generation
2025-01-29 15:17:30 +08:00
2dust
dadc24876f Change Directory 2025-01-29 14:22:30 +08:00
Anatoliy
98d4801b7e Use Directory.Build.props for centralized configuration management (#6591)
* use Directory.Buld.props

* remove some properties
2025-01-28 20:01:59 +08:00
Anatoliy
0e50bfb6eb create editorconfig (#6592) 2025-01-28 12:16:04 +08:00
2dust
a5aa286535 Add Name to App.axaml 2025-01-25 20:45:56 +08:00
2dust
3caf025c3c Update README.md 2025-01-25 20:19:02 +08:00
2dust
c943c6c60a Update README.md 2025-01-25 20:15:16 +08:00
2dust
8c55c629cd Fix PackageReference 2025-01-24 20:28:04 +08:00
2dust
0c38ebb63f Added Avalonia UI Windows version compilation script 2025-01-24 20:01:36 +08:00
2dust
75df85f598 Release script adjust prerelease = true 2025-01-24 19:48:34 +08:00
2dust
59e99b2316 Downgrade ZXing.Net.Bindings.SkiaSharp to version 0.16.14
In linux report
Unhandled exception. System.TypeInitializationException: The type initializer for 'SkiaSharp.SKFontManager' threw an exception.
 ---> System.TypeInitializationException: The type initializer for 'SkiaSharp.SKObject' threw an exception.
 ---> System.InvalidOperationException: The version of the native libSkiaSharp library (88.1) is incompatible with this version of SkiaSharp. Supported versions of the native libSkiaSharp library are in the range [116.0, 117.0).
   at SkiaSharp.SkiaSharpVersion.CheckNativeLibraryCompatible(Version minSupported, Version current, Boolean throwIfIncompatible)
   at SkiaSharp.SkiaSharpVersion.CheckNativeLibraryCompatible(Boolean throwIfIncompatible)
   at SkiaSharp.SKObject..cctor()
2025-01-24 19:46:28 +08:00
Anatoliy
02f4dcbaf7 use Directory.Packages.props (#6581) 2025-01-24 19:00:21 +08:00
2dust
470dec5588 up PackageReference 2025-01-24 17:24:07 +08:00
2dust
be1e37ddd0 Hide the upgrade core in Windows 7 2025-01-24 14:26:51 +08:00
2dust
fdc32601a9 Update ResUI.Designer.cs 2025-01-24 14:25:56 +08:00
2dust
d3b86858e1 Revert "Use Directory.Packages.props (#6575)"
This reverts commit 9c20d9cb1f.
2025-01-24 14:25:13 +08:00
Anatoliy
9c20d9cb1f Use Directory.Packages.props (#6575) 2025-01-24 09:57:38 +08:00
alphax-hue3682
d2cda16378 Update Persian Translate (#6559) 2025-01-22 10:00:33 +08:00
aucub
2ab00d5b6f Add Package AppImage script (#6556) 2025-01-21 09:27:38 +08:00
OnceUponATimeInAmerica
1b8a58cd07 Update English translation (#6555) 2025-01-21 09:15:48 +08:00
Anatoliy
0c3293045f Remove extra .gitignore file and move .gitattributes to the root folder for consistent storage of .git-related files. (#6545) 2025-01-20 09:28:30 +08:00
2dust
4c4ce7e8d1 Revert "增加macos dmg软件签名 (#6541)"
This reverts commit a0d4a3f2e8.
2025-01-19 18:58:32 +08:00
zjt003
a0d4a3f2e8 增加macos dmg软件签名 (#6541)
* Update package-osx.sh

* Update build-osx.yml
2025-01-19 18:34:39 +08:00
2dust
26abe7e7d7 up 7.7.0 2025-01-19 09:36:43 +08:00
2dust
b9c86ed3a1 Modify the file name in winget release package 2025-01-19 09:35:19 +08:00
2dust
b7cff66a80 Remove the core name in the Windows release version 2025-01-19 09:34:37 +08:00
2dust
61a40d2860 up PackageReference 2025-01-19 09:27:20 +08:00
2dust
80eb569366 The bin folder is added to the package
54fe669d89
2025-01-18 21:10:41 +08:00
2dust
54fe669d89 Copy the bin folder to the storage location (for init) 2025-01-18 16:58:44 +08:00
2dust
0953237e9e Improvements 2025-01-18 16:01:52 +08:00
2dust
cf1a8599eb When updating the bin folder, skip the file if it already exists.
https://github.com/2dust/v2rayN/issues/6515
2025-01-17 10:27:29 +08:00
alphax-hue3682
171132be12 Update Persian Translate (#6513) 2025-01-16 15:56:05 +08:00
208 changed files with 5933 additions and 4178 deletions

170
.editorconfig Normal file
View File

@@ -0,0 +1,170 @@
root = true
[*]
charset = utf-8
indent_style = space
tab_width = 4
indent_size = 4
end_of_line = crlf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.cs]
dotnet_hide_advanced_members = true
dotnet_member_insertion_location = with_other_members_of_the_same_kind
dotnet_property_generation_behavior = prefer_throwing_properties
dotnet_search_reference_assemblies = true
dotnet_separate_import_directive_groups = false:warning
dotnet_sort_system_directives_first = true:warning
file_header_template = unset
dotnet_style_qualification_for_event = false:warning
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_operators = always_for_clarity:warning
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
dotnet_style_require_accessibility_modifiers = always:warning
dotnet_prefer_system_hash_code = true:warning
dotnet_style_coalesce_expression = true:warning
dotnet_style_collection_initializer = false:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true:warning
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_collection_expression = false:warning
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = false:warning
dotnet_style_prefer_conditional_expression_over_return = false:warning
dotnet_style_prefer_foreach_explicit_cast_in_source = always:warning
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_prefer_simplified_interpolation = true:warning
dotnet_style_readonly_field = true:warning
dotnet_code_quality_unused_parameters = all:warning
dotnet_remove_unnecessary_suppression_exclusions = none
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = true:warning
csharp_style_var_elsewhere = true:warning
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_expression_bodied_accessors = when_on_single_line:warning
csharp_style_expression_bodied_constructors = false:warning
csharp_style_expression_bodied_indexers = when_on_single_line:warning
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
csharp_style_expression_bodied_local_functions = false:warning
csharp_style_expression_bodied_methods = false:warning
csharp_style_expression_bodied_operators = false:warning
csharp_style_expression_bodied_properties = when_on_single_line:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_prefer_extended_property_pattern = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_switch_expression = false:warning
csharp_style_conditional_delegate_call = true:warning
csharp_prefer_static_anonymous_function = true:warning
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,internal,private,protected,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:warning
csharp_style_prefer_readonly_struct = true:warning
csharp_style_prefer_readonly_struct_member = true:warning
csharp_prefer_braces = true:warning
csharp_prefer_simple_using_statement = true:warning
csharp_prefer_system_threading_lock = true:warning
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_prefer_method_group_conversion = true:warning
csharp_style_prefer_primary_constructors = true:warning
csharp_style_prefer_top_level_statements = false:warning
csharp_prefer_simple_default_expression = true:warning
csharp_style_deconstructed_variable_declaration = true:warning
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = true:warning
csharp_style_prefer_index_operator = false:warning
csharp_style_prefer_local_over_anonymous_function = true:warning
csharp_style_prefer_null_check_over_type_check = true:warning
csharp_style_prefer_range_operator = false:warning
csharp_style_prefer_tuple_swap = true:warning
csharp_style_prefer_utf8_string_literals = true:warning
csharp_style_throw_expression = true:warning
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
csharp_using_directive_placement = outside_namespace:warning
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:warning
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:warning
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false:warning
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:warning
csharp_style_allow_embedded_statements_on_same_line_experimental = false:warning
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = no_change
csharp_indent_switch_labels = true
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false:warning
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
dotnet_naming_rule.interface_should_be_pascal.severity = warning
dotnet_naming_rule.interface_should_be_pascal.symbols = interface
dotnet_naming_rule.interface_should_be_pascal.style = pascal
dotnet_naming_rule.types_should_be_pascal.severity = warning
dotnet_naming_rule.types_should_be_pascal.symbols = types
dotnet_naming_rule.types_should_be_pascal.style = pascal
dotnet_naming_rule.non_field_members_should_be_pascal.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal.style = pascal
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = *
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = *
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = *
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_style.pascal.required_prefix =
dotnet_naming_style.pascal.required_suffix =
dotnet_naming_style.pascal.word_separator =
dotnet_naming_style.pascal.capitalization = pascal_case

View File

@@ -3,6 +3,13 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
title: "[Bug]: "
labels: ["bug"]
body:
- type: input
id: "os-version"
attributes:
label: "操作系统和版本"
description: "操作系统和版本"
validations:
required: true
- type: input
id: "expectation"
attributes:

View File

@@ -1,10 +1,11 @@
# Set update schedule for GitHub Actions
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every daily
interval: "daily"
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"

69
.github/workflows/build-all.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: release all platforms
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Trigger build windows
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build linux
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-linux.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build osx
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-osx.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build windows desktop
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows-desktop.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"

View File

@@ -22,34 +22,42 @@ jobs:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v4.6.1
with:
name: v2rayN-linux
path: |
${{ github.workspace }}/v2rayN/Release/linux*
# release debian package
- name: Package debian
- name: Package debian
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-debian.sh
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload deb to release
uses: svenstaro/upload-release-action@v2
@@ -58,11 +66,27 @@ jobs:
file: ${{ github.workspace }}/v2rayN*.deb
tag: ${{ github.event.inputs.release_tag }}
file_glob: 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
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64

View File

@@ -22,34 +22,42 @@ jobs:
matrix:
configuration: [Release]
runs-on: macos-latest
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v4.6.1
with:
name: v2rayN-macos
path: |
${{ github.workspace }}/v2rayN/Release/macos*
# release osx package
- name: Package osx
- name: Package osx
if: github.event.inputs.release_tag != ''
run: |
brew install create-dmg
chmod 755 package-osx.sh
./package-osx.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-osx.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-osx.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload dmg to release
@@ -59,11 +67,12 @@ jobs:
file: ${{ github.workspace }}/v2rayN*.dmg
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64

View File

@@ -0,0 +1,71 @@
name: release Windows desktop (Avalonia UI)
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
push:
branches:
- master
env:
OutputArch: "windows-64"
OutputArchArm: "windows-arm64"
OutputPath64: "${{ github.workspace }}/v2rayN/Release/windows-64"
OutputPathArm64: "${{ github.workspace }}/v2rayN/Release/windows-arm64"
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
with:
name: v2rayN-windows-desktop
path: |
${{ github.workspace }}/v2rayN/Release/windows*
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
mv "v2rayN-${OutputArch}.zip" "v2rayN-${OutputArch}-desktop.zip"
./package-release-zip.sh $OutputArchArm $OutputPathArm64
mv "v2rayN-${OutputArchArm}.zip" "v2rayN-${OutputArchArm}-desktop.zip"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

View File

@@ -23,45 +23,43 @@ jobs:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v4.2.2
- name: Setup
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v4.3.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v4.6.1
with:
name: v2rayN-windows
path: |
${{ github.workspace }}/v2rayN/Release/windows*
${{ github.workspace }}/v2rayN/Release/windows*
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh "windows-64-With-Core" $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
./package-release-zip.sh "windows-64-SelfContained" $OutputPath64Sc
./package-release-zip.sh "windows-64-SelfContained-With-Core" $OutputPath64Sc
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2

View File

@@ -22,7 +22,7 @@ jobs:
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64\.zip*' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name

415
.gitignore vendored
View File

@@ -1,19 +1,400 @@
################################################################################
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
################################################################################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
/v2rayN/.vs/
/v2rayN/v2rayN/bin/Debug/app.publish
/v2rayN/v2rayN/bin/Debug
/v2rayN/v2rayN/bin/Release
/v2rayN/v2rayN/obj/
/v2rayN/.vs/v2rayN/DesignTimeBuild
/v2rayN/packages
.vs/ProjectSettings.json
.vs/slnx.sqlite
.vs/VSWorkspaceState.json
/v2rayN/v2rayUpgrade/bin/Debug
/v2rayN/v2rayUpgrade/bin/Release
/v2rayN/v2rayUpgrade/obj/
# User-specific files
*.rsuser
*.suo
*.user
/.vs/v2rayN
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "v2rayN/GlobalHotKeys"]
path = v2rayN/GlobalHotKeys
url = https://github.com/2dust/GlobalHotKeys

View File

@@ -1,5 +1,5 @@
# v2rayN
A GUI client for Windows, Linux and macOS, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core) and [sing-box](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master)
@@ -9,26 +9,8 @@ A GUI client for Windows, Linux and macOS, support [Xray core](https://github.co
## How to use
Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) and select the version you need to download
### Windows
- Run `v2rayN.exe`
### Linux
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
```
Debian 9+
Ubuntu 16.04+
Fedora 30+
```
### macOS
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
```
macOS 11+
```
## Requirements
- [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details.
## Telegram Channel
[github_2dust](https://t.me/github_2dust)

14
package-appimage.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
sudo apt update -y
sudo apt install -y libfuse2
wget -O pkg2appimage https://github.com/AppImageCommunity/pkg2appimage/releases/download/continuous/pkg2appimage-1eceb30-x86_64.AppImage
chmod a+x pkg2appimage
export AppImageOutputArch=$OutputArch
export OutputPath=$OutputPath64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage
export AppImageOutputArch=$OutputArchArm
export OutputPath=$OutputPathArm64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage

View File

@@ -4,6 +4,11 @@ Arch="$1"
OutputPath="$2"
Version="$3"
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
7z x $FileName
cp -rf v2rayN-${Arch}/* $OutputPath
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "${PackagePath}/DEBIAN"
mkdir -p "${PackagePath}/opt"

View File

@@ -4,6 +4,11 @@ Arch="$1"
OutputPath="$2"
Version="$3"
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
7z x $FileName
cp -rf v2rayN-${Arch}/* $OutputPath
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "$PackagePath/v2rayN.app/Contents/Resources"
cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS"

37
pkg2appimage.yml Normal file
View File

@@ -0,0 +1,37 @@
app: v2rayN
binpatch: true
ingredients:
script:
- export FileName="v2rayN-${AppImageOutputArch}.zip"
- wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/${FileName}"
- 7z x $FileName -aoa
- cp -rf v2rayN-${AppImageOutputArch}/* $OutputPath
script:
- mkdir -p usr/bin usr/lib
- cp -rf $OutputPath usr/lib/v2rayN
- echo "When this file exists, app will not store configs under this folder" > usr/lib/v2rayN/NotStoreConfigHere.txt
- ln -sf usr/lib/v2rayN/v2rayN usr/bin/v2rayN
- chmod a+x usr/lib/v2rayN/v2rayN
- find usr -type f -exec sh -c 'file "{}" | grep -qi "executable" && chmod +x "{}"' \;
- install -Dm644 usr/lib/v2rayN/v2rayN.png v2rayN.png
- install -Dm644 usr/lib/v2rayN/v2rayN.png usr/share/pixmaps/v2rayN.png
- cat > 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 v2rayN.desktop usr/share/applications/v2rayN.desktop
- cat > AppRun <<\EOF
- #!/bin/sh
- HERE="$(dirname "$(readlink -f "${0}")")"
- cd ${HERE}/usr/lib/v2rayN
- exec ${HERE}/usr/lib/v2rayN/v2rayN $@
- EOF
- chmod a+x AppRun

363
v2rayN/.gitignore vendored
View File

@@ -1,363 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

View File

@@ -1,27 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Copyright>Copyright © 2017-2025 (GPLv3)</Copyright>
<FileVersion>1.3.1</FileVersion>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resx\Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Resx\Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@@ -1,10 +1,7 @@
namespace AmazTool
namespace AmazTool
{
internal static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
private static void Main(string[] args)
{
@@ -26,4 +23,4 @@
UpgradeApp.Upgrade(argData);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.IO.Compression;
using System.Text;
@@ -10,7 +10,7 @@ namespace AmazTool
{
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
Waiting(5);
Utils.Waiting(5);
if (!File.Exists(fileName))
{
@@ -42,12 +42,12 @@ namespace AmazTool
StringBuilder sb = new();
try
{
string thisAppOldFile = $"{Utils.GetExePath()}.tmp";
var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
File.Delete(thisAppOldFile);
string splitKey = "/";
var splitKey = "/";
using ZipArchive archive = ZipFile.OpenRead(fileName);
foreach (ZipArchiveEntry entry in archive.Entries)
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{
try
{
@@ -59,17 +59,35 @@ namespace AmazTool
Console.WriteLine(entry.FullName);
var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1) continue;
string fullName = string.Join(splitKey, lst[1..lst.Length]);
if (lst.Length == 1)
{
continue;
}
var fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(Utils.GetExePath(), thisAppOldFile);
}
string entryOutputPath = Utils.GetPath(fullName);
var entryOutputPath = Utils.GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
entry.ExtractToFile(entryOutputPath, true);
//In the bin folder, if the file already exists, it will be skipped
if (fullName.StartsWith("bin") && File.Exists(entryOutputPath))
{
continue;
}
try
{
entry.ExtractToFile(entryOutputPath, true);
}
catch
{
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath);
}
@@ -91,18 +109,9 @@ namespace AmazTool
}
Console.WriteLine(Resx.Resource.Restartv2rayN);
Waiting(2);
Utils.Waiting(2);
Utils.StartV2RayN();
}
public static void Waiting(int second)
{
for (var i = second; i > 0; i--)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
namespace AmazTool
{
@@ -16,7 +16,7 @@ namespace AmazTool
public static string GetPath(string fileName)
{
string startupPath = StartupPath();
var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
@@ -39,5 +39,14 @@ namespace AmazTool
};
process.Start();
}
public static void Waiting(int second)
{
for (var i = second; i > 0; i--)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
<Project>
<PropertyGroup>
<Version>7.10.4</Version>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416</NoWarn>
<Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors>
<PackageLicenseExpression>GPL-3.0</PackageLicenseExpression>
<Copyright>Copyright © 2017-$([System.DateTime]::UtcNow.Year) $(Authors)</Copyright>
<InvariantGlobalization>false</InvariantGlobalization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>embedded</DebugType>
<EventSourceSupport>false</EventSourceSupport>
<StackTraceSupport>false</StackTraceSupport>
<MetricsSupport>false</MetricsSupport>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,30 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.5" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" />
<PackageVersion Include="CliWrap" Version="3.8.1" />
<PackageVersion Include="Downloader" Version="3.3.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.6.0" />
<PackageVersion Include="ReactiveUI" Version="20.1.63" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.1.63" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.5" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.0" />
<PackageVersion Include="WebDav.Client" Version="2.8.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>
</Project>

1
v2rayN/GlobalHotKeys Submodule

Submodule v2rayN/GlobalHotKeys added at ef73fa22c4

View File

@@ -1,5 +1,5 @@
using Downloader;
using System.Net;
using Downloader;
namespace ServiceLib.Common
{
@@ -10,7 +10,7 @@ namespace ServiceLib.Common
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
@@ -18,7 +18,7 @@ namespace ServiceLib.Common
Uri uri = new(url);
//Authorization Header
var headers = new WebHeaderCollection();
if (Utils.IsNotEmpty(uri.UserInfo))
if (uri.UserInfo.IsNotEmpty())
{
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
}
@@ -56,7 +56,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
@@ -86,7 +86,7 @@ namespace ServiceLib.Common
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
var ts = (DateTime.Now - totalDatetime);
var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond)
{
hasValue = true;
@@ -119,11 +119,11 @@ namespace ServiceLib.Common
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(fileName));
}
@@ -146,10 +146,7 @@ namespace ServiceLib.Common
var progressPercentage = 0;
var hasValue = false;
await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadStarted += (sender, value) =>
{
progress?.Report(0);
};
downloader.DownloadStarted += (sender, value) => progress?.Report(0);
downloader.DownloadProgressChanged += (sender, value) =>
{
hasValue = true;
@@ -181,4 +178,4 @@ namespace ServiceLib.Common
downloadOpt = null;
}
}
}
}

View File

@@ -0,0 +1,61 @@
using System.Collections.Concurrent;
using System.Reflection;
namespace ServiceLib.Common;
public static class EmbedUtils
{
private static readonly string _tag = "EmbedUtils";
private static readonly ConcurrentDictionary<string, string> _dicEmbedCache = new();
/// <summary>
/// Get embedded text resources
/// </summary>
/// <param name="res"></param>
/// <returns></returns>
public static string GetEmbedText(string res)
{
if (_dicEmbedCache.TryGetValue(res, out var value))
{
return value;
}
var result = string.Empty;
try
{
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream(res);
ArgumentNullException.ThrowIfNull(stream);
using StreamReader reader = new(stream);
result = reader.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
_dicEmbedCache.TryAdd(res, result);
return result;
}
/// <summary>
/// Get local storage resources
/// </summary>
/// <returns></returns>
public static string? LoadResource(string? res)
{
try
{
if (File.Exists(res))
{
return File.ReadAllText(res);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
using System.Formats.Tar;
using System.Formats.Tar;
using System.IO.Compression;
using System.Text;
@@ -99,7 +99,7 @@ namespace ServiceLib.Common
}
try
{
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{
continue;
}
@@ -156,25 +156,27 @@ namespace ServiceLib.Common
return true;
}
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string? ignoredName)
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, bool overwrite, string? ignoredName = null)
{
// Get information about the source directory
var dir = new DirectoryInfo(sourceDir);
// Check if the source directory exists
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
}
// Cache directories before we start copying
var dirs = dir.GetDirectories();
// Create the destination directory
Directory.CreateDirectory(destinationDir);
_ = Directory.CreateDirectory(destinationDir);
// Get the files in the source directory and copy to the destination directory
foreach (var file in dir.GetFiles())
{
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
{
continue;
}
@@ -183,7 +185,11 @@ namespace ServiceLib.Common
continue;
}
var targetFilePath = Path.Combine(destinationDir, file.Name);
file.CopyTo(targetFilePath, true);
if (!overwrite && File.Exists(targetFilePath))
{
continue;
}
_ = file.CopyTo(targetFilePath, overwrite);
}
// If recursive and copying subdirectories, recursively call this method
@@ -192,7 +198,7 @@ namespace ServiceLib.Common
foreach (var subDir in dirs)
{
var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
CopyDirectory(subDir.FullName, newDestinationDir, true, ignoredName);
CopyDirectory(subDir.FullName, newDestinationDir, true, overwrite, ignoredName);
}
}
}
@@ -218,4 +224,4 @@ namespace ServiceLib.Common
}
}
}
}
}

View File

@@ -18,12 +18,17 @@ namespace ServiceLib.Common
public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
private HttpClientHelper(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<string?> TryGetAsync(string url)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
try
{
@@ -38,13 +43,19 @@ namespace ServiceLib.Common
public async Task<string?> GetAsync(string url)
{
if (Utils.IsNullOrEmpty(url)) return null;
if (url.IsNullOrEmpty())
{
return null;
}
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (Utils.IsNullOrEmpty(url)) return null;
if (url.IsNullOrEmpty())
{
return null;
}
return await client.GetStringAsync(url, token);
}
@@ -53,13 +64,13 @@ namespace ServiceLib.Common
var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content);
await httpClient.PutAsync(url, content);
}
public async Task PatchAsync(string url, Dictionary<string, string> headers)
{
var myContent = JsonUtils.Serialize(headers);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var buffer = Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
@@ -75,11 +86,17 @@ namespace ServiceLib.Common
{
ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName)) File.Delete(fileName);
if (File.Exists(fileName))
{
File.Delete(fileName);
}
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString());
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
@@ -97,7 +114,10 @@ namespace ServiceLib.Common
var read = await stream.ReadAsync(buffer, token);
totalRead += read;
if (read == 0) break;
if (read == 0)
{
break;
}
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
@@ -118,7 +138,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
@@ -168,7 +188,7 @@ namespace ServiceLib.Common
totalRead += read;
var ts = (DateTime.Now - totalDatetime);
var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
@@ -183,4 +203,4 @@ namespace ServiceLib.Common
} while (isMoreToRead);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ServiceLib.Common
@@ -15,27 +15,29 @@ namespace ServiceLib.Common
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
IntPtr extendedInfoPtr = IntPtr.Zero;
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length))
{
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
}
finally
{
@@ -48,7 +50,7 @@ namespace ServiceLib.Common
public bool AddProcess(IntPtr processHandle)
{
bool succ = AssignProcessToJobObject(handle, processHandle);
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
@@ -75,7 +77,10 @@ namespace ServiceLib.Common
private void Dispose(bool disposing)
{
if (disposed) return;
if (disposed)
{
return;
}
disposed = true;
if (disposing)
@@ -103,7 +108,7 @@ namespace ServiceLib.Common
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
@@ -120,34 +125,34 @@ namespace ServiceLib.Common
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public uint LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public uint nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
@@ -173,4 +178,4 @@ namespace ServiceLib.Common
}
#endregion Helper classes
}
}

View File

@@ -1,4 +1,4 @@
using NLog;
using NLog;
using NLog.Config;
using NLog.Targets;
@@ -27,14 +27,20 @@ namespace ServiceLib.Common
public static void SaveLog(string strContent)
{
if (!LogManager.IsLoggingEnabled()) return;
if (!LogManager.IsLoggingEnabled())
{
return;
}
LogManager.GetLogger("Log1").Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled()) return;
if (!LogManager.IsLoggingEnabled())
{
return;
}
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
@@ -45,4 +51,4 @@ namespace ServiceLib.Common
}
}
}
}
}

View File

@@ -8,7 +8,7 @@ public static class ProcUtils
public static void ProcessStart(string? fileName, string arguments = "")
{
ProcessStart(fileName, arguments, null);
_ = ProcessStart(fileName, arguments, null);
}
public static int? ProcessStart(string? fileName, string arguments, string? dir)
@@ -19,21 +19,27 @@ public static class ProcUtils
}
try
{
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
if (arguments.Contains(' ')) arguments = arguments.AppendQuotes();
if (fileName.Contains(' '))
{
fileName = fileName.AppendQuotes();
}
if (arguments.Contains(' '))
{
arguments = arguments.AppendQuotes();
}
Process process = new()
Process proc = new()
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = fileName,
Arguments = arguments,
WorkingDirectory = dir
WorkingDirectory = dir ?? string.Empty
}
};
process.Start();
return process.Id;
_ = proc.Start();
return dir is null ? null : proc.Id;
}
catch (Exception ex)
{
@@ -54,7 +60,7 @@ public static class ProcUtils
FileName = Utils.GetExePath().AppendQuotes(),
Verb = blAdmin ? "runas" : null,
};
Process.Start(startInfo);
_ = Process.Start(startInfo);
}
catch (Exception ex)
{
@@ -83,10 +89,44 @@ public static class ProcUtils
GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
try { proc?.Kill(true); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Kill(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Close(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try { proc?.Dispose(); } catch (Exception ex) { Logging.SaveLog(_tag, ex); }
try
{
if (Utils.IsNonWindows())
{
proc?.Kill(true);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try
{
proc?.Kill();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try
{
proc?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try
{
proc?.Dispose();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.Delay(300);
await ProcessKillByKeyInfo(review, procId, fileName, processName);
@@ -97,7 +137,10 @@ public static class ProcUtils
procId = null;
fileName = null;
processName = null;
if (!review) return;
if (!review)
{
return;
}
try
{
procId = proc?.Id;
@@ -136,4 +179,4 @@ public static class ProcUtils
}
}
}
}
}

View File

@@ -60,7 +60,7 @@ namespace ServiceLib.Common
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);
if (result != null && Utils.IsNotEmpty(result.Text))
if (result != null && result.Text.IsNotEmpty())
{
return result.Text;
}

View File

@@ -1,29 +1,29 @@
namespace ServiceLib.Common
namespace ServiceLib.Common
{
public class SemanticVersion
{
private int major;
private int minor;
private int patch;
private string version;
private readonly int major;
private readonly int minor;
private readonly int patch;
private readonly string version;
public SemanticVersion(int major, int minor, int patch)
{
this.major = major;
this.minor = minor;
this.patch = patch;
this.version = $"{major}.{minor}.{patch}";
version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string? version)
{
try
{
if (version.IsNullOrEmpty())
if (string.IsNullOrEmpty(version))
{
this.major = 0;
this.minor = 0;
this.patch = 0;
major = 0;
minor = 0;
patch = 0;
return;
}
this.version = version.RemovePrefix('v');
@@ -31,15 +31,15 @@
var parts = this.version.Split('.');
if (parts.Length == 2)
{
this.major = int.Parse(parts.First());
this.minor = int.Parse(parts.Last());
this.patch = 0;
major = int.Parse(parts.First());
minor = int.Parse(parts.Last());
patch = 0;
}
else if (parts.Length is 3 or 4)
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]);
major = int.Parse(parts[0]);
minor = int.Parse(parts[1]);
patch = int.Parse(parts[2]);
}
else
{
@@ -48,9 +48,9 @@
}
catch
{
this.major = 0;
this.minor = 0;
this.patch = 0;
major = 0;
minor = 0;
patch = 0;
}
}
@@ -58,7 +58,7 @@
{
if (obj is SemanticVersion other)
{
return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
return major == other.major && minor == other.minor && patch == other.patch;
}
else
{
@@ -68,7 +68,7 @@
public override int GetHashCode()
{
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
}
/// <summary>
@@ -77,18 +77,18 @@
/// <returns>major.minor.patch</returns>
public override string ToString()
{
return this.version;
return version;
}
public string ToVersionString(string? prefix = null)
{
if (prefix == null)
{
return this.version;
return version;
}
else
{
return $"{prefix}{this.version}";
return $"{prefix}{version}";
}
}
@@ -108,31 +108,31 @@
private bool GreaterEquals(SemanticVersion other)
{
if (this.major < other.major)
if (major < other.major)
{
return false;
}
else if (this.major > other.major)
else if (major > other.major)
{
return true;
}
else
{
if (this.minor < other.minor)
if (minor < other.minor)
{
return false;
}
else if (this.minor > other.minor)
else if (minor > other.minor)
{
return true;
}
else
{
if (this.patch < other.patch)
if (patch < other.patch)
{
return false;
}
else if (this.patch > other.patch)
else if (patch > other.patch)
{
return true;
}
@@ -146,31 +146,31 @@
private bool LessEquals(SemanticVersion other)
{
if (this.major < other.major)
if (major < other.major)
{
return true;
}
else if (this.major > other.major)
else if (major > other.major)
{
return false;
}
else
{
if (this.minor < other.minor)
if (minor < other.minor)
{
return true;
}
else if (this.minor > other.minor)
else if (minor > other.minor)
{
return false;
}
else
{
if (this.patch < other.patch)
if (patch < other.patch)
{
return true;
}
else if (this.patch > other.patch)
else if (patch > other.patch)
{
return false;
}
@@ -184,4 +184,4 @@
#endregion Private
}
}
}

View File

@@ -1,5 +1,5 @@
using SQLite;
using System.Collections;
using SQLite;
namespace ServiceLib.Common
{
@@ -7,7 +7,7 @@ namespace ServiceLib.Common
{
private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
public static SQLiteHelper Instance => _instance.Value;
private string _connstr;
private readonly string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
@@ -88,4 +88,4 @@ namespace ServiceLib.Common
});
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common
{
@@ -6,7 +6,7 @@ namespace ServiceLib.Common
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{
return string.IsNullOrEmpty(value);
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
@@ -21,7 +21,10 @@ namespace ServiceLib.Common
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty()) return false;
if (s.IsNullOrEmpty())
{
return false;
}
return chars.Contains(s.First());
}
@@ -34,7 +37,10 @@ namespace ServiceLib.Common
{
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace()) continue;
if (line.IsWhiteSpace())
{
continue;
}
yield return line;
}
}
@@ -68,5 +74,10 @@ namespace ServiceLib.Common
{
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
}
public static int ToInt(this string? value, int defaultValue = 0)
{
return int.TryParse(value, out var result) ? result : defaultValue;
}
}
}
}

View File

@@ -1,5 +1,3 @@
using CliWrap;
using CliWrap.Buffered;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
@@ -10,6 +8,8 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using CliWrap;
using CliWrap.Buffered;
namespace ServiceLib.Common
{
@@ -17,138 +17,72 @@ namespace ServiceLib.Common
{
private static readonly string _tag = "Utils";
#region
/// <summary>
/// 获取嵌入文本资源
/// </summary>
/// <param name="res"></param>
/// <returns></returns>
public static string GetEmbedText(string res)
{
var result = string.Empty;
try
{
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream(res);
ArgumentNullException.ThrowIfNull(stream);
using StreamReader reader = new(stream);
result = reader.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return result;
}
/// <summary>
/// 取得存储资源
/// </summary>
/// <returns></returns>
public static string? LoadResource(string? res)
{
try
{
if (File.Exists(res))
{
return File.ReadAllText(res);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
}
#endregion
#region
/// <summary>
/// 转逗号分隔的字符串
/// Convert to comma-separated string
/// </summary>
/// <param name="lst"></param>
/// <param name="wrap"></param>
/// <returns></returns>
public static string List2String(List<string>? lst, bool wrap = false)
{
if (lst == null || lst.Count == 0)
{
return string.Empty;
}
var separator = wrap ? "," + Environment.NewLine : ",";
try
{
if (lst == null)
{
return string.Empty;
}
if (wrap)
{
return string.Join("," + Environment.NewLine, lst);
}
else
{
return string.Join(",", lst);
}
return string.Join(separator, lst);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
return string.Empty;
}
/// <summary>
/// 逗号分隔的字符串
/// Comma-separated string
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static List<string>? String2List(string? str)
{
if (string.IsNullOrWhiteSpace(str))
{
return null;
}
try
{
if (str == null)
{
return null;
}
str = str.Replace(Environment.NewLine, "");
str = str.Replace(Environment.NewLine, string.Empty);
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
return null;
}
/// <summary>
/// 逗号分隔的字符串,先排序后转List
/// Comma-separated string, sorted and then converted to List
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static List<string>? String2ListSorted(string str)
{
try
{
str = str.Replace(Environment.NewLine, "");
List<string> list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
list.Sort();
return list;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
var lst = String2List(str);
lst?.Sort();
return lst;
}
/// <summary>
/// Base64编码
/// Base64 Encode
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
@@ -168,7 +102,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// Base64解码
/// Base64 Decode
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
@@ -176,7 +110,11 @@ namespace ServiceLib.Common
{
try
{
if (plainText.IsNullOrEmpty()) return "";
if (plainText.IsNullOrEmpty())
{
return "";
}
plainText = plainText.Trim()
.Replace(Environment.NewLine, "")
.Replace("\n", "")
@@ -201,18 +139,6 @@ namespace ServiceLib.Common
return string.Empty;
}
public static int ToInt(object? obj)
{
try
{
return Convert.ToInt32(obj ?? string.Empty);
}
catch
{
return 0;
}
}
public static bool ToBool(object obj)
{
try
@@ -237,55 +163,25 @@ namespace ServiceLib.Common
}
}
private static void ToHumanReadable(long amount, out double result, out string unit)
{
var factor = 1024u;
//long KBs = amount / factor;
var KBs = amount;
if (KBs > 0)
{
// multi KB
var MBs = KBs / factor;
if (MBs > 0)
{
// multi MB
var GBs = MBs / factor;
if (GBs > 0)
{
// multi GB
var TBs = GBs / factor;
if (TBs > 0)
{
result = TBs + ((GBs % factor) / (factor + 0.0));
unit = "TB";
return;
}
result = GBs + ((MBs % factor) / (factor + 0.0));
unit = "GB";
return;
}
result = MBs + ((KBs % factor) / (factor + 0.0));
unit = "MB";
return;
}
result = KBs + ((amount % factor) / (factor + 0.0));
unit = "KB";
return;
}
else
{
result = amount;
unit = "B";
}
}
public static string HumanFy(long amount)
{
ToHumanReadable(amount, out var result, out var unit);
return $"{result:f1} {unit}";
if (amount <= 0)
{
return $"{amount:f1} B";
}
string[] units = ["KB", "MB", "GB", "TB", "PB"];
var unitIndex = 0;
double size = amount;
// Loop and divide by 1024 until a suitable unit is found
while (size >= 1024 && unitIndex < units.Length - 1)
{
size /= 1024;
unitIndex++;
}
return $"{size:f1} {units[unitIndex]}";
}
public static string UrlEncode(string url)
@@ -301,7 +197,7 @@ namespace ServiceLib.Common
public static NameValueCollection ParseQueryString(string query)
{
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
if (IsNullOrEmpty(query))
if (query.IsNullOrEmpty())
{
return result;
}
@@ -347,7 +243,7 @@ namespace ServiceLib.Common
/// <returns></returns>
public static string GetPunycode(string url)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return url;
}
@@ -372,14 +268,15 @@ namespace ServiceLib.Common
public static bool IsBase64String(string? plainText)
{
if (plainText.IsNullOrEmpty()) return false;
if (plainText.IsNullOrEmpty())
return false;
var buffer = new Span<byte>(new byte[plainText.Length]);
return Convert.TryFromBase64String(plainText, buffer, out var _);
}
public static string Convert2Comma(string text)
{
if (IsNullOrEmpty(text))
if (text.IsNullOrEmpty())
{
return text;
}
@@ -392,7 +289,7 @@ namespace ServiceLib.Common
#region
/// <summary>
/// 判断输入的是否是数字
/// Determine if the input is a number
/// </summary>
/// <param name="oText"></param>
/// <returns></returns>
@@ -401,28 +298,13 @@ namespace ServiceLib.Common
return oText.All(char.IsNumber);
}
public static bool IsNullOrEmpty(string? text)
{
if (string.IsNullOrWhiteSpace(text))
{
return true;
}
return text == "null";
}
public static bool IsNotEmpty(string? text)
{
return !string.IsNullOrEmpty(text);
}
/// <summary>
/// 验证Domain地址是否合法
/// Validate if the domain address is valid
/// </summary>
/// <param name="domain"></param>
public static bool IsDomain(string? domain)
{
if (IsNullOrEmpty(domain))
if (domain.IsNullOrEmpty())
{
return false;
}
@@ -462,9 +344,12 @@ namespace ServiceLib.Common
if (IPAddress.TryParse(ip, out var address))
{
var ipBytes = address.GetAddressBytes();
if (ipBytes[0] == 10) return true;
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
if (ipBytes[0] == 10)
return true;
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168)
return true;
}
return false;
@@ -478,10 +363,22 @@ namespace ServiceLib.Common
{
try
{
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
var ipEndPoints = ipProperties.GetActiveTcpListeners();
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
return ipEndPoints.Any(endPoint => endPoint.Port == port);
List<IPEndPoint> lstIpEndPoints = new();
List<TcpConnectionInformation> lstTcpConns = new();
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
if (lstIpEndPoints?.FindIndex(it => it.Port == port) >= 0)
{
return true;
}
if (lstTcpConns?.FindIndex(it => it.LocalEndPoint.Port == port) >= 0)
{
return true;
}
}
catch (Exception ex)
{
@@ -519,12 +416,12 @@ namespace ServiceLib.Common
public static bool UpgradeAppExists(out string upgradeFileName)
{
upgradeFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GetExeName("AmazTool"));
upgradeFileName = Path.Combine(GetBaseDirectory(), GetExeName("AmazTool"));
return File.Exists(upgradeFileName);
}
/// <summary>
/// 取得版本
/// Get version
/// </summary>
/// <returns></returns>
public static string GetVersion(bool blFull = true)
@@ -562,7 +459,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// 取得GUID
/// GUID
/// </summary>
/// <returns></returns>
public static string GetGuid(bool full = true)
@@ -604,9 +501,11 @@ namespace ServiceLib.Common
foreach (var host in hostsList)
{
if (host.StartsWith("#")) continue;
if (host.StartsWith("#"))
continue;
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (hostItem.Length != 2) continue;
if (hostItem.Length != 2)
continue;
systemHosts.Add(hostItem.Last(), hostItem.First());
}
}
@@ -666,12 +565,12 @@ namespace ServiceLib.Common
try
{
//When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NotStoreConfigHere.txt")))
if (File.Exists(Path.Combine(GetBaseDirectory(), "NotStoreConfigHere.txt")))
{
return false;
}
var tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "guiTemps");
var tempPath = Path.Combine(GetBaseDirectory(), "guiTemps");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
@@ -691,7 +590,7 @@ namespace ServiceLib.Common
public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
return startupPath;
}
@@ -699,6 +598,11 @@ namespace ServiceLib.Common
return Path.Combine(startupPath, fileName);
}
public static string GetBaseDirectory(string fileName = "")
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
}
public static string GetExePath()
{
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
@@ -706,12 +610,12 @@ namespace ServiceLib.Common
public static string StartupPath()
{
if (Utils.IsNonWindows() && Environment.GetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA") == "1")
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "v2rayN");
}
return AppDomain.CurrentDomain.BaseDirectory;
return GetBaseDirectory();
}
public static string GetTempPath(string filename = "")
@@ -722,7 +626,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -751,7 +655,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -778,7 +682,7 @@ namespace ServiceLib.Common
}
}
if (IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -796,7 +700,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -814,7 +718,25 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetBinConfigPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "binConfigs");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -865,23 +787,25 @@ namespace ServiceLib.Common
private static async Task<string?> GetLinuxUserId()
{
var arg = new List<string>() { "-c", "id -u" };
return await GetCliWrapOutput("/bin/bash", arg);
return await GetCliWrapOutput(Global.LinuxBash, arg);
}
public static async Task<string?> SetLinuxChmod(string? fileName)
{
if (fileName.IsNullOrEmpty()) return null;
if (fileName.Contains(' ')) fileName = fileName.AppendQuotes();
if (fileName.IsNullOrEmpty())
return null;
if (fileName.Contains(' '))
fileName = fileName.AppendQuotes();
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput("/bin/bash", arg);
return await GetCliWrapOutput(Global.LinuxBash, arg);
}
public static async Task<string?> GetLinuxFontFamily(string lang)
{
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
var arg = new List<string>() { "-c", $"fc-list : family" };
return await GetCliWrapOutput("/bin/bash", arg);
return await GetCliWrapOutput(Global.LinuxBash, arg);
}
public static string? GetHomePath()
@@ -891,12 +815,6 @@ namespace ServiceLib.Common
: Environment.GetEnvironmentVariable("HOME");
}
public static async Task<string?> GetListNetworkServices()
{
var arg = new List<string>() { "-c", $"networksetup -listallnetworkservices" };
return await GetCliWrapOutput("/bin/bash", arg);
}
#endregion Platform
}
}
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Win32;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
namespace ServiceLib.Common
{
@@ -13,7 +15,7 @@ namespace ServiceLib.Common
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return Utils.IsNullOrEmpty(value) ? def : value;
return value.IsNullOrEmpty() ? def : value;
}
catch (Exception ex)
{
@@ -32,7 +34,7 @@ namespace ServiceLib.Common
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString()))
if (value.ToString().IsNullOrEmpty())
{
regKey?.DeleteValue(name, false);
}
@@ -50,5 +52,23 @@ namespace ServiceLib.Common
regKey?.Close();
}
}
public static async Task RemoveTunDevice()
{
try
{
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
var guid = new Guid(sum);
var pnpUtilPath = @"C:\Windows\System32\pnputil.exe";
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device
_ = await Utils.GetCliWrapOutput(pnpUtilPath, arg);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using YamlDotNet.Core;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -62,9 +62,6 @@ namespace ServiceLib.Common
public static string? PreprocessYaml(string str)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
@@ -80,4 +77,4 @@ namespace ServiceLib.Common
#endregion YAML
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Enums
namespace ServiceLib.Enums
{
public enum ECoreType
{
@@ -12,6 +12,8 @@
sing_box = 24,
juicity = 25,
hysteria2 = 26,
brook = 27,
overtls = 28,
v2rayN = 99
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Enums
namespace ServiceLib.Enums
{
public enum EMsgCommand
{
@@ -6,7 +6,6 @@
SendMsgView,
SendSnackMsg,
RefreshProfiles,
StopSpeedtest,
AppExit
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib
namespace ServiceLib
{
public class Global
{
@@ -7,18 +7,8 @@
public const string AppName = "v2rayN";
public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos";
public const string V2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
public const string XrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
public const string MihomoCoreUrl = "https://github.com/MetaCubeX/mihomo/releases";
public const string HysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
public const string NaiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
public const string TuicCoreUrl = "https://github.com/EAimTY/tuic/releases";
public const string SingboxCoreUrl = "https://github.com/SagerNet/sing-box/releases";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://api.ip.sb/geoip";
@@ -26,7 +16,7 @@
public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml";
@@ -48,6 +38,8 @@
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac";
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp";
@@ -68,83 +60,93 @@
public const string UserEMail = "t@t.tt";
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun";
public const string CustomIconName = "v2rayN.ico";
public const string SystemProxyExceptionsWindows = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string SystemProxyExceptionsLinux = "localhost,127.0.0.0/8,::1";
public const string RoutingRuleComma = "<COMMA>";
public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536;
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 8;
public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/";
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash";
public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}",
"socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
""
};
public static readonly List<string> IEProxyProtocols =
[
"{ip}:{http_port}",
"socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
""
];
public static readonly List<string> SubConvertUrls = new List<string> {
@"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}",
""
};
public static readonly List<string> SubConvertUrls =
[
@"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}",
""
];
public static readonly List<string> SubConvertConfig = new List<string> {
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
};
public static readonly List<string> SubConvertConfig =
[@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"];
public static readonly List<string> SubConvertTargets = new List<string> {
"",
"mixed",
"v2ray",
"clash",
"ss",
};
public static readonly List<string> SubConvertTargets =
[
"",
"mixed",
"v2ray",
"clash",
"ss"
];
public static readonly List<string> SpeedTestUrls = new() {
@"https://speed.cloudflare.com/__down?bytes=100000000",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=10000000",
public static readonly List<string> SpeedTestUrls =
[
@"https://cachefly.cachefly.net/50mb.test",
};
@"https://speed.cloudflare.com/__down?bytes=10000000",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=100000000",
];
public static readonly List<string> SpeedPingTestUrls = new() {
public static readonly List<string> SpeedPingTestUrls =
[
@"https://www.google.com/generate_204",
@"https://www.gstatic.com/generate_204",
@"https://www.apple.com/library/test/success.html",
@"http://www.msftconnecttest.com/connecttest.txt",
};
@"http://www.msftconnecttest.com/connecttest.txt"
];
public static readonly List<string> GeoFilesSources = new() {
public static readonly List<string> GeoFilesSources =
[
"",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat",
};
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat"
];
public static readonly List<string> SingboxRulesetSources = new() {
public static readonly List<string> SingboxRulesetSources =
[
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs",
};
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs"
];
public static readonly List<string> RoutingRulesSources = new() {
public static readonly List<string> RoutingRulesSources =
[
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json",
};
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json"
];
public static readonly List<string> DNSTemplateSources = new() {
public static readonly List<string> DNSTemplateSources =
[
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/",
};
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/"
];
public static readonly Dictionary<string, string> UserAgentTexts = new()
{
@@ -159,66 +161,363 @@
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
{
{EConfigType.VMess,"vmess://"},
{EConfigType.Shadowsocks,"ss://"},
{EConfigType.SOCKS,"socks://"},
{EConfigType.VLESS,"vless://"},
{EConfigType.Trojan,"trojan://"},
{EConfigType.Hysteria2,"hysteria2://"},
{EConfigType.TUIC,"tuic://"},
{EConfigType.WireGuard,"wireguard://"}
{ EConfigType.VMess, "vmess://" },
{ EConfigType.Shadowsocks, "ss://" },
{ EConfigType.SOCKS, "socks://" },
{ EConfigType.VLESS, "vless://" },
{ EConfigType.Trojan, "trojan://" },
{ EConfigType.Hysteria2, "hysteria2://" },
{ EConfigType.TUIC, "tuic://" },
{ EConfigType.WireGuard, "wireguard://" }
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
{
{EConfigType.VMess,"vmess"},
{EConfigType.Shadowsocks,"shadowsocks"},
{EConfigType.SOCKS,"socks"},
{EConfigType.HTTP,"http"},
{EConfigType.VLESS,"vless"},
{EConfigType.Trojan,"trojan"},
{EConfigType.Hysteria2,"hysteria2"},
{EConfigType.TUIC,"tuic"},
{EConfigType.WireGuard,"wireguard"}
{ EConfigType.VMess, "vmess" },
{ EConfigType.Shadowsocks, "shadowsocks" },
{ EConfigType.SOCKS, "socks" },
{ EConfigType.HTTP, "http" },
{ EConfigType.VLESS, "vless" },
{ EConfigType.Trojan, "trojan" },
{ EConfigType.Hysteria2, "hysteria2" },
{ EConfigType.TUIC, "tuic" },
{ EConfigType.WireGuard, "wireguard" }
};
public static readonly List<string> VmessSecurities = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
public static readonly List<string> SsSecurities = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
public static readonly List<string> VmessSecurities =
[
"aes-128-gcm",
"chacha20-poly1305",
"auto",
"none",
"zero"
];
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru", "hu" };
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "socks2", "socks3" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
public static readonly List<string> TunStacks = new() { "gvisor", "system", "mixed" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
public static readonly List<string> SsSecurities =
[
"aes-256-gcm",
"aes-128-gcm",
"chacha20-poly1305",
"chacha20-ietf-poly1305",
"none",
"plain"
];
public static readonly List<string> allowSelectType = new() { "selector", "urltest", "loadbalance", "fallback" };
public static readonly List<string> notAllowTestType = new() { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
public static readonly List<string> proxyVehicleType = new() { "file", "http" };
public static readonly List<string> SsSecuritiesInXray =
[
"aes-256-gcm",
"aes-128-gcm",
"chacha20-poly1305",
"chacha20-ietf-poly1305",
"xchacha20-poly1305",
"xchacha20-ietf-poly1305",
"none",
"plain",
"2022-blake3-aes-128-gcm",
"2022-blake3-aes-256-gcm",
"2022-blake3-chacha20-poly1305"
];
public static readonly List<string> SsSecuritiesInSingbox =
[
"aes-256-gcm",
"aes-192-gcm",
"aes-128-gcm",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305",
"none",
"2022-blake3-aes-128-gcm",
"2022-blake3-aes-256-gcm",
"2022-blake3-chacha20-poly1305",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"rc4-md5",
"chacha20-ietf",
"xchacha20"
];
public static readonly List<string> Flows =
[
"",
"xtls-rprx-vision",
"xtls-rprx-vision-udp443"
];
public static readonly List<string> Networks =
[
"tcp",
"kcp",
"ws",
"httpupgrade",
"xhttp",
"h2",
"quic",
"grpc"
];
public static readonly List<string> KcpHeaderTypes =
[
"srtp",
"utp",
"wechat-video",
"dtls",
"wireguard",
"dns"
];
public static readonly List<string> CoreTypes =
[
"Xray",
"sing_box"
];
public static readonly List<string> DomainStrategies =
[
"AsIs",
"IPIfNonMatch",
"IPOnDemand"
];
public static readonly List<string> DomainStrategies4Singbox =
[
"ipv4_only",
"ipv6_only",
"prefer_ipv4",
"prefer_ipv6",
""
];
public static readonly List<string> DomainMatchers =
[
"linear",
"mph",
""
];
public static readonly List<string> Fingerprints =
[
"chrome",
"firefox",
"safari",
"ios",
"android",
"edge",
"360",
"qq",
"random",
"randomized",
""
];
public static readonly List<string> UserAgent =
[
"chrome",
"firefox",
"safari",
"edge",
"none"
];
public static readonly List<string> XhttpMode =
[
"auto",
"packet-up",
"stream-up",
"stream-one"
];
public static readonly List<string> AllowInsecure =
[
"true",
"false",
""
];
public static readonly List<string> DomainStrategy4Freedoms =
[
"AsIs",
"UseIP",
"UseIPv4",
"UseIPv6",
""
];
public static readonly List<string> SingboxDomainStrategy4Out =
[
"ipv4_only",
"prefer_ipv4",
"prefer_ipv6",
"ipv6_only",
""
];
public static readonly List<string> DomainDNSAddress =
[
"223.5.5.5",
"223.6.6.6",
"localhost"
];
public static readonly List<string> SingboxDomainDNSAddress =
[
"223.5.5.5",
"223.6.6.6",
"dhcp://auto"
];
public static readonly List<string> Languages =
[
"zh-Hans",
"zh-Hant",
"en",
"fa-Ir",
"ru",
"hu"
];
public static readonly List<string> Alpns =
[
"h3",
"h2",
"http/1.1",
"h3,h2",
"h2,http/1.1",
"h3,h2,http/1.1",
""
];
public static readonly List<string> LogLevels =
[
"debug",
"info",
"warning",
"error",
"none"
];
public static readonly List<string> InboundTags =
[
"socks",
"socks2",
"socks3"
];
public static readonly List<string> RuleProtocols =
[
"http",
"tls",
"bittorrent"
];
public static readonly List<string> RuleNetworks =
[
"",
"tcp",
"udp",
"tcp,udp"
];
public static readonly List<string> destOverrideProtocols =
[
"http",
"tls",
"quic",
"fakedns",
"fakedns+others"
];
public static readonly List<int> TunMtus =
[
1280,
1408,
1500,
9000
];
public static readonly List<string> TunStacks =
[
"gvisor",
"system",
"mixed"
];
public static readonly List<string> PresetMsgFilters =
[
"proxy",
"direct",
"block",
""
];
public static readonly List<string> SingboxMuxs =
[
"h2mux",
"smux",
"yamux",
""
];
public static readonly List<string> TuicCongestionControls =
[
"cubic",
"new_reno",
"bbr"
];
public static readonly List<string> allowSelectType =
[
"selector",
"urltest",
"loadbalance",
"fallback"
];
public static readonly List<string> notAllowTestType =
[
"selector",
"urltest",
"direct",
"reject",
"compatible",
"pass",
"loadbalance",
"fallback"
];
public static readonly List<string> proxyVehicleType =
[
"file",
"http"
];
public static readonly Dictionary<ECoreType, string> CoreUrls = new()
{
{ ECoreType.v2fly, "v2fly/v2ray-core" },
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" },
{ ECoreType.Xray, "XTLS/Xray-core" },
{ ECoreType.sing_box, "SagerNet/sing-box" },
{ ECoreType.mihomo, "MetaCubeX/mihomo" },
{ ECoreType.hysteria, "apernet/hysteria" },
{ ECoreType.hysteria2, "apernet/hysteria" },
{ ECoreType.naiveproxy, "klzgrad/naiveproxy" },
{ ECoreType.tuic, "EAimTY/tuic" },
{ ECoreType.juicity, "juicity/juicity" },
{ ECoreType.brook, "txthinking/brook" },
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
{ ECoreType.v2rayN, "2dust/v2rayN" },
};
public static readonly List<string> OtherGeoUrls =
[
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
];
#endregion const
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler
namespace ServiceLib.Handler
{
public sealed class AppHandler
{
@@ -46,9 +46,9 @@
public bool InitApp()
{
if (Utils.IsNonWindows() && Utils.HasWritePermission() == false)
if (Utils.HasWritePermission() == false)
{
Environment.SetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA", "1", EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.LocalAppData, "1", EnvironmentVariableTarget.Process);
}
Logging.Setup();
@@ -80,8 +80,6 @@
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
ClearExpiredFiles();
return true;
}
@@ -92,15 +90,6 @@
return true;
}
private void ClearExpiredFiles()
{
Task.Run(() =>
{
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
});
}
#endregion Init
#region Config
@@ -142,7 +131,7 @@
public async Task<List<ProfileItem>?> ProfileItems(string subid)
{
if (Utils.IsNullOrEmpty(subid))
if (subid.IsNullOrEmpty())
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
}
@@ -164,11 +153,11 @@
from ProfileItem a
left join SubItem b on a.subid = b.id
where 1=1 ";
if (Utils.IsNotEmpty(subid))
if (subid.IsNotEmpty())
{
sql += $" and a.subid = '{subid}'";
}
if (Utils.IsNotEmpty(filter))
if (filter.IsNotEmpty())
{
if (filter.Contains('\''))
{
@@ -182,7 +171,7 @@
public async Task<ProfileItem?> GetProfileItem(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
if (indexId.IsNullOrEmpty())
{
return null;
}
@@ -191,7 +180,7 @@
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (Utils.IsNullOrEmpty(remarks))
if (remarks.IsNullOrEmpty())
{
return null;
}
@@ -252,4 +241,4 @@
#endregion Core Type
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Security.Principal;
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler
@@ -29,7 +29,12 @@ namespace ServiceLib.Handler
}
else if (Utils.IsOSX())
{
//TODO
await ClearTaskOSX();
if (config.GuiItem.AutoRun)
{
await SetTaskOSX();
}
}
return true;
@@ -80,7 +85,7 @@ namespace ServiceLib.Handler
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (Utils.IsNullOrEmpty(taskName))
if (taskName.IsNullOrEmpty())
{
return;
}
@@ -88,7 +93,7 @@ namespace ServiceLib.Handler
var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
foreach (var t in tasks)
{
@@ -104,7 +109,7 @@ namespace ServiceLib.Handler
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
@@ -137,7 +142,7 @@ namespace ServiceLib.Handler
{
try
{
var linuxConfig = Utils.GetEmbedText(Global.LinuxAutostartConfig);
var linuxConfig = EmbedUtils.GetEmbedText(Global.LinuxAutostartConfig);
if (linuxConfig.IsNotEmpty())
{
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath());
@@ -161,5 +166,77 @@ namespace ServiceLib.Handler
}
#endregion Linux
#region macOS
private static async Task ClearTaskOSX()
{
try
{
var launchAgentPath = GetLaunchAgentPathMacOS();
if (File.Exists(launchAgentPath))
{
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args);
File.Delete(launchAgentPath);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
private static async Task SetTaskOSX()
{
try
{
var plistContent = GenerateLaunchAgentPlist();
var launchAgentPath = GetLaunchAgentPathMacOS();
await File.WriteAllTextAsync(launchAgentPath, plistContent);
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
private static string GetLaunchAgentPathMacOS()
{
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var launchAgentPath = Path.Combine(homePath, "Library", "LaunchAgents", $"{Global.AppName}-LaunchAgent.plist");
Directory.CreateDirectory(Path.GetDirectoryName(launchAgentPath));
return launchAgentPath;
}
private static string GenerateLaunchAgentPlist()
{
var exePath = Utils.GetExePath();
var appName = Path.GetFileNameWithoutExtension(exePath);
return $@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>Label</key>
<string>{Global.AppName}-LaunchAgent</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>if ! pgrep -x ""{appName}"" > /dev/null; then ""{exePath}""; fi</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<false/>
</dict>
</plist>";
}
#endregion macOS
}
}
}

View File

@@ -1,4 +1,4 @@
using static ServiceLib.Models.ClashProxies;
using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler
{
@@ -7,9 +7,9 @@ namespace ServiceLib.Handler
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value;
private static readonly string _tag = "ClashApiHandler";
private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
private static readonly string _tag = "ClashApiHandler";
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
{
@@ -37,64 +37,63 @@ namespace ServiceLib.Handler
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{
Task.Run(() =>
{
if (blAll)
Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
if (blAll)
{
if (_proxies != null)
for (var i = 0; i < 5; i++)
{
break;
if (_proxies != null)
{
break;
}
await Task.Delay(5000);
}
if (_proxies == null)
{
return;
}
lstProxy = new List<ClashProxyModel>();
foreach (var kv in _proxies)
{
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
{
Name = kv.Value.name,
Type = kv.Value.type.ToLower(),
});
}
Task.Delay(5000).Wait();
}
if (_proxies == null)
if (lstProxy == null)
{
return;
}
lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
var tasks = new List<Task>();
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{
Name = kv.Value.name,
Type = kv.Value.type.ToLower(),
});
var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result);
}));
}
}
if (lstProxy == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
List<Task> tasks = new List<Task>();
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{
continue;
}
var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{
var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result);
}));
}
Task.WaitAll(tasks.ToArray());
Task.Delay(1000).Wait();
updateFunc?.Invoke(null, "");
});
await Task.WhenAll(tasks);
await Task.Delay(1000);
updateFunc?.Invoke(null, "");
});
}
public List<ProxiesItem>? GetClashProxyGroups()
@@ -120,7 +119,7 @@ namespace ServiceLib.Handler
try
{
var url = $"{GetApiUrl()}/proxies/{name}";
Dictionary<string, string> headers = new Dictionary<string, string>();
var headers = new Dictionary<string, string>();
headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
@@ -148,7 +147,7 @@ namespace ServiceLib.Handler
try
{
var url = $"{GetApiUrl()}/configs?force=true";
Dictionary<string, string> headers = new Dictionary<string, string>();
var headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
@@ -194,4 +193,4 @@ namespace ServiceLib.Handler
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
}
}
}
}

View File

@@ -21,8 +21,8 @@ namespace ServiceLib.Handler
public static Config? LoadConfig()
{
Config? config = null;
var result = Utils.LoadResource(Utils.GetConfigPath(_configRes));
if (Utils.IsNotEmpty(result))
var result = EmbedUtils.LoadResource(Utils.GetConfigPath(_configRes));
if (result.IsNotEmpty())
{
config = JsonUtils.Deserialize<Config>(result);
}
@@ -67,7 +67,7 @@ namespace ServiceLib.Handler
}
config.RoutingBasicItem ??= new();
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
if (config.RoutingBasicItem.DomainStrategy.IsNullOrEmpty())
{
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();
}
@@ -103,16 +103,11 @@ namespace ServiceLib.Handler
};
config.UiItem.MainColumnItem ??= new();
if (Utils.IsNullOrEmpty(config.UiItem.CurrentLanguage))
if (config.UiItem.CurrentLanguage.IsNullOrEmpty())
{
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
{
config.UiItem.CurrentLanguage = Global.Languages.First();
}
else
{
config.UiItem.CurrentLanguage = Global.Languages[2];
}
config.UiItem.CurrentLanguage = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.Equals("zh", StringComparison.CurrentCultureIgnoreCase)
? Global.Languages.First()
: Global.Languages[2];
}
config.ConstItem ??= new ConstItem();
@@ -122,14 +117,18 @@ namespace ServiceLib.Handler
{
config.SpeedTestItem.SpeedTestTimeout = 10;
}
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
if (config.SpeedTestItem.SpeedTestUrl.IsNullOrEmpty())
{
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
}
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty())
{
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl;
}
if (config.SpeedTestItem.MixedConcurrencyCount < 1)
{
config.SpeedTestItem.MixedConcurrencyCount = 5;
}
config.Mux4RayItem ??= new()
{
@@ -159,6 +158,7 @@ namespace ServiceLib.Handler
Length = "100-200",
Interval = "10-20"
};
config.GlobalHotkeys ??= new();
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
{
@@ -217,6 +217,7 @@ namespace ServiceLib.Handler
item.Remarks = profileItem.Remarks;
item.Address = profileItem.Address;
item.Port = profileItem.Port;
item.Ports = profileItem.Ports;
item.Id = profileItem.Id;
item.AlterId = profileItem.AlterId;
@@ -295,7 +296,7 @@ namespace ServiceLib.Handler
/// <param name="config"></param>
/// <param name="indexes"></param>
/// <returns></returns>
public static async Task<int> RemoveServer(Config config, List<ProfileItem> indexes)
public static async Task<int> RemoveServers(Config config, List<ProfileItem> indexes)
{
var subid = "TempRemoveSubId";
foreach (var item in indexes)
@@ -304,7 +305,7 @@ namespace ServiceLib.Handler
}
await SQLiteHelper.Instance.UpdateAllAsync(indexes);
await RemoveServerViaSubid(config, subid, false);
await RemoveServersViaSubid(config, subid, false);
return 0;
}
@@ -353,7 +354,7 @@ namespace ServiceLib.Handler
/// <returns></returns>
public static async Task<int> SetDefaultServerIndex(Config config, string? indexId)
{
if (Utils.IsNullOrEmpty(indexId))
if (indexId.IsNullOrEmpty())
{
return -1;
}
@@ -505,7 +506,7 @@ namespace ServiceLib.Handler
profileItem.Address = newFileName;
profileItem.ConfigType = EConfigType.Custom;
if (Utils.IsNullOrEmpty(profileItem.Remarks))
if (profileItem.Remarks.IsNullOrEmpty())
{
profileItem.Remarks = $"import custom@{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}";
}
@@ -623,7 +624,7 @@ namespace ServiceLib.Handler
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Id = profileItem.Id.TrimEx();
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity))
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
@@ -653,7 +654,7 @@ namespace ServiceLib.Handler
profileItem.Path = profileItem.Path.TrimEx();
profileItem.Network = string.Empty;
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity))
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
@@ -688,11 +689,11 @@ namespace ServiceLib.Handler
profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!;
}
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity))
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
if (Utils.IsNullOrEmpty(profileItem.Alpn))
if (profileItem.Alpn.IsNullOrEmpty())
{
profileItem.Alpn = "h3";
}
@@ -725,7 +726,7 @@ namespace ServiceLib.Handler
profileItem.Network = string.Empty;
if (profileItem.ShortId.IsNullOrEmpty())
{
profileItem.ShortId = Global.TunMtus.FirstOrDefault();
profileItem.ShortId = Global.TunMtus.First().ToString();
}
if (profileItem.Id.IsNullOrEmpty())
@@ -759,9 +760,9 @@ namespace ServiceLib.Handler
Security = t.Security,
Network = t.Network,
StreamSecurity = t.StreamSecurity,
Delay = t33 == null ? 0 : t33.Delay,
Speed = t33 == null ? 0 : t33.Speed,
Sort = t33 == null ? 0 : t33.Sort
Delay = t33?.Delay ?? 0,
Speed = t33?.Speed ?? 0,
Sort = t33?.Sort ?? 0
}).ToList();
Enum.TryParse(colName, true, out EServerColName name);
@@ -857,7 +858,7 @@ namespace ServiceLib.Handler
{
return -1;
}
if (Utils.IsNotEmpty(profileItem.Security) && profileItem.Security != Global.None)
if (profileItem.Security.IsNotEmpty() && profileItem.Security != Global.None)
{
profileItem.Security = Global.None;
}
@@ -873,7 +874,8 @@ namespace ServiceLib.Handler
List<ProfileItem> lstKeep = new();
List<ProfileItem> lstRemove = new();
if (!config.GuiItem.KeepOlderDedupl) lstProfile.Reverse();
if (!config.GuiItem.KeepOlderDedupl)
lstProfile.Reverse();
foreach (ProfileItem item in lstProfile)
{
@@ -886,7 +888,7 @@ namespace ServiceLib.Handler
lstRemove.Add(item);
}
}
await RemoveServer(config, lstRemove);
await RemoveServers(config, lstRemove);
return new Tuple<int, int>(lstProfile.Count, lstKeep.Count);
}
@@ -895,7 +897,7 @@ namespace ServiceLib.Handler
{
profileItem.ConfigVersion = 2;
if (Utils.IsNotEmpty(profileItem.StreamSecurity))
if (profileItem.StreamSecurity.IsNotEmpty())
{
if (profileItem.StreamSecurity != Global.StreamSecurity
&& profileItem.StreamSecurity != Global.StreamSecurityReality)
@@ -904,24 +906,24 @@ namespace ServiceLib.Handler
}
else
{
if (Utils.IsNullOrEmpty(profileItem.AllowInsecure))
if (profileItem.AllowInsecure.IsNullOrEmpty())
{
profileItem.AllowInsecure = config.CoreBasicItem.DefAllowInsecure.ToString().ToLower();
}
if (Utils.IsNullOrEmpty(profileItem.Fingerprint) && profileItem.StreamSecurity == Global.StreamSecurityReality)
if (profileItem.Fingerprint.IsNullOrEmpty() && profileItem.StreamSecurity == Global.StreamSecurityReality)
{
profileItem.Fingerprint = config.CoreBasicItem.DefFingerprint;
}
}
}
if (Utils.IsNotEmpty(profileItem.Network) && !Global.Networks.Contains(profileItem.Network))
if (profileItem.Network.IsNotEmpty() && !Global.Networks.Contains(profileItem.Network))
{
profileItem.Network = Global.DefaultNetwork;
}
var maxSort = -1;
if (Utils.IsNullOrEmpty(profileItem.IndexId))
if (profileItem.IndexId.IsNullOrEmpty())
{
profileItem.IndexId = Utils.GetGuid(false);
maxSort = ProfileExHandler.Instance.GetMaxSort();
@@ -1051,6 +1053,24 @@ namespace ServiceLib.Handler
return itemSocks;
}
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
{
var lstModel = await AppHandler.Instance.ProfileItems(subid, "");
if (lstModel is { Count: <= 0 })
{
return -1;
}
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel
join t2 in lstProfileExs on t.IndexId equals t2.IndexId
where t2.Delay == -1
select t).ToList();
await RemoveServers(config, JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstProfile)));
return lstProfile.Count;
}
#endregion Server
#region Batch add servers
@@ -1064,16 +1084,16 @@ namespace ServiceLib.Handler
/// <returns>成功导入的数量</returns>
private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub)
{
if (Utils.IsNullOrEmpty(strData))
if (strData.IsNullOrEmpty())
{
return -1;
}
var subFilter = string.Empty;
//remove sub items
if (isSub && Utils.IsNotEmpty(subid))
if (isSub && subid.IsNotEmpty())
{
await RemoveServerViaSubid(config, subid, isSub);
await RemoveServersViaSubid(config, subid, isSub);
subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? "";
}
@@ -1102,7 +1122,7 @@ namespace ServiceLib.Handler
}
//exist sub items //filter
if (isSub && Utils.IsNotEmpty(subid) && Utils.IsNotEmpty(subFilter))
if (isSub && subid.IsNotEmpty() && subFilter.IsNotEmpty())
{
if (!Regex.IsMatch(profileItem.Remarks, subFilter))
{
@@ -1143,7 +1163,7 @@ namespace ServiceLib.Handler
private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub)
{
if (Utils.IsNullOrEmpty(strData))
if (strData.IsNullOrEmpty())
{
return -1;
}
@@ -1165,9 +1185,9 @@ namespace ServiceLib.Handler
}
if (lstProfiles != null && lstProfiles.Count > 0)
{
if (isSub && Utils.IsNotEmpty(subid))
if (isSub && subid.IsNotEmpty())
{
await RemoveServerViaSubid(config, subid, isSub);
await RemoveServersViaSubid(config, subid, isSub);
}
int count = 0;
foreach (var it in lstProfiles)
@@ -1216,14 +1236,14 @@ namespace ServiceLib.Handler
{
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
}
if (profileItem is null || Utils.IsNullOrEmpty(profileItem.Address))
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
{
return -1;
}
if (isSub && Utils.IsNotEmpty(subid))
if (isSub && subid.IsNotEmpty())
{
await RemoveServerViaSubid(config, subid, isSub);
await RemoveServersViaSubid(config, subid, isSub);
}
profileItem.Subid = subid;
@@ -1241,14 +1261,14 @@ namespace ServiceLib.Handler
private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub)
{
if (Utils.IsNullOrEmpty(strData))
if (strData.IsNullOrEmpty())
{
return -1;
}
if (isSub && Utils.IsNotEmpty(subid))
if (isSub && subid.IsNotEmpty())
{
await RemoveServerViaSubid(config, subid, isSub);
await RemoveServersViaSubid(config, subid, isSub);
}
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
@@ -1273,13 +1293,13 @@ namespace ServiceLib.Handler
public static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub)
{
if (Utils.IsNullOrEmpty(strData))
if (strData.IsNullOrEmpty())
{
return -1;
}
List<ProfileItem>? lstOriSub = null;
ProfileItem? activeProfile = null;
if (isSub && Utils.IsNotEmpty(subid))
if (isSub && subid.IsNotEmpty())
{
lstOriSub = await AppHandler.Instance.ProfileItems(subid);
activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId);
@@ -1363,7 +1383,8 @@ namespace ServiceLib.Handler
};
var uri = Utils.TryUri(url);
if (uri == null) return -1;
if (uri == null)
return -1;
//Do not allow http protocol
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
{
@@ -1403,7 +1424,7 @@ namespace ServiceLib.Handler
item.Memo = subItem.Memo;
}
if (Utils.IsNullOrEmpty(item.Id))
if (item.Id.IsNullOrEmpty())
{
item.Id = Utils.GetGuid(false);
@@ -1434,9 +1455,9 @@ namespace ServiceLib.Handler
/// <param name="config"></param>
/// <param name="subid"></param>
/// <returns></returns>
public static async Task<int> RemoveServerViaSubid(Config config, string subid, bool isSub)
public static async Task<int> RemoveServersViaSubid(Config config, string subid, bool isSub)
{
if (Utils.IsNullOrEmpty(subid))
if (subid.IsNullOrEmpty())
{
return -1;
}
@@ -1465,7 +1486,7 @@ namespace ServiceLib.Handler
return 0;
}
await SQLiteHelper.Instance.DeleteAsync(item);
await RemoveServerViaSubid(config, id, false);
await RemoveServersViaSubid(config, id, false);
return 0;
}
@@ -1487,7 +1508,7 @@ namespace ServiceLib.Handler
public static async Task<int> SaveRoutingItem(Config config, RoutingItem item)
{
if (Utils.IsNullOrEmpty(item.Id))
if (item.Id.IsNullOrEmpty())
{
item.Id = Utils.GetGuid(false);
}
@@ -1510,7 +1531,7 @@ namespace ServiceLib.Handler
/// <returns></returns>
public static async Task<int> AddBatchRoutingRules(RoutingItem routingItem, string strData)
{
if (Utils.IsNullOrEmpty(strData))
if (strData.IsNullOrEmpty())
{
return -1;
}
@@ -1528,7 +1549,7 @@ namespace ServiceLib.Handler
routingItem.RuleNum = lstRules.Count;
routingItem.RuleSet = JsonUtils.Serialize(lstRules, false);
if (Utils.IsNullOrEmpty(routingItem.Id))
if (routingItem.Id.IsNullOrEmpty())
{
routingItem.Id = Utils.GetGuid(false);
}
@@ -1647,7 +1668,7 @@ namespace ServiceLib.Handler
public static async Task<int> InitRouting(Config config, bool blImportAdvancedRules = false)
{
if (string.IsNullOrEmpty(config.ConstItem.RouteRulesTemplateSourceUrl))
if (config.ConstItem.RouteRulesTemplateSourceUrl.IsNullOrEmpty())
{
await InitBuiltinRouting(config, blImportAdvancedRules);
}
@@ -1663,7 +1684,7 @@ namespace ServiceLib.Handler
{
var downloadHandle = new DownloadService();
var templateContent = await downloadHandle.TryDownloadString(config.ConstItem.RouteRulesTemplateSourceUrl, true, "");
if (string.IsNullOrEmpty(templateContent))
if (templateContent.IsNullOrEmpty())
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
var template = JsonUtils.Deserialize<RoutingTemplate>(templateContent);
@@ -1680,14 +1701,14 @@ namespace ServiceLib.Handler
{
var item = template.RoutingItems[i];
if (string.IsNullOrEmpty(item.Url) && string.IsNullOrEmpty(item.RuleSet))
if (item.Url.IsNullOrEmpty() && item.RuleSet.IsNullOrEmpty())
continue;
var ruleSetsString = !string.IsNullOrEmpty(item.RuleSet)
var ruleSetsString = !item.RuleSet.IsNullOrEmpty()
? item.RuleSet
: await downloadHandle.TryDownloadString(item.Url, true, "");
if (string.IsNullOrEmpty(ruleSetsString))
if (ruleSetsString.IsNullOrEmpty())
continue;
item.Remarks = $"{template.Version}-{item.Remarks}";
@@ -1733,7 +1754,7 @@ namespace ServiceLib.Handler
Url = string.Empty,
Sort = maxSort + 1,
};
await AddBatchRoutingRules(item2, Utils.GetEmbedText(Global.CustomRoutingFileName + "white"));
await AddBatchRoutingRules(item2, EmbedUtils.GetEmbedText(Global.CustomRoutingFileName + "white"));
//Blacklist
var item3 = new RoutingItem()
@@ -1742,7 +1763,7 @@ namespace ServiceLib.Handler
Url = string.Empty,
Sort = maxSort + 2,
};
await AddBatchRoutingRules(item3, Utils.GetEmbedText(Global.CustomRoutingFileName + "black"));
await AddBatchRoutingRules(item3, EmbedUtils.GetEmbedText(Global.CustomRoutingFileName + "black"));
//Global
var item1 = new RoutingItem()
@@ -1751,7 +1772,7 @@ namespace ServiceLib.Handler
Url = string.Empty,
Sort = maxSort + 3,
};
await AddBatchRoutingRules(item1, Utils.GetEmbedText(Global.CustomRoutingFileName + "global"));
await AddBatchRoutingRules(item1, EmbedUtils.GetEmbedText(Global.CustomRoutingFileName + "global"));
if (!blImportAdvancedRules)
{
@@ -1799,7 +1820,7 @@ namespace ServiceLib.Handler
return -1;
}
if (Utils.IsNullOrEmpty(item.Id))
if (item.Id.IsNullOrEmpty())
{
item.Id = Utils.GetGuid(false);
}
@@ -1820,17 +1841,17 @@ namespace ServiceLib.Handler
var downloadHandle = new DownloadService();
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
if (string.IsNullOrEmpty(templateContent))
if (templateContent.IsNullOrEmpty())
return currentItem;
var template = JsonUtils.Deserialize<DNSItem>(templateContent);
if (template == null)
return currentItem;
if (!string.IsNullOrEmpty(template.NormalDNS))
if (!template.NormalDNS.IsNullOrEmpty())
template.NormalDNS = await downloadHandle.TryDownloadString(template.NormalDNS, true, "");
if (!string.IsNullOrEmpty(template.TunDNS))
if (!template.TunDNS.IsNullOrEmpty())
template.TunDNS = await downloadHandle.TryDownloadString(template.TunDNS, true, "");
template.Id = currentItem.Id;
@@ -1885,4 +1906,4 @@ namespace ServiceLib.Handler
#endregion Regional Presets
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler
namespace ServiceLib.Handler
{
/// <summary>
/// Core configuration file processing class
@@ -33,7 +33,7 @@
{
return result;
}
if (Utils.IsNotEmpty(fileName) && result.Data != null)
if (fileName.IsNotEmpty() && result.Data != null)
{
await File.WriteAllTextAsync(fileName, result.Data.ToString());
}
@@ -109,6 +109,30 @@
return result;
}
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
{
var result = new RetResult();
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
testItem.Port = port;
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
@@ -129,4 +153,4 @@
return result;
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Text;
namespace ServiceLib.Handler
@@ -22,8 +22,19 @@ namespace ServiceLib.Handler
_config = config;
_updateFunc = updateFunc;
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
{
var fromPath = Utils.GetBaseDirectory("bin");
var toPath = Utils.GetBinPath("");
if (fromPath != toPath)
{
FileManager.CopyDirectory(fromPath, toPath, true, false);
}
}
if (Utils.IsNonWindows())
{
@@ -59,7 +70,7 @@ namespace ServiceLib.Handler
return;
}
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true)
{
@@ -72,6 +83,13 @@ namespace ServiceLib.Handler
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await CoreStop();
await Task.Delay(100);
if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
{
await Task.Delay(100);
await WindowsUtils.RemoveTunDevice();
}
await CoreStart(node);
await CoreStartPreService(node);
if (_process != null)
@@ -83,7 +101,8 @@ namespace ServiceLib.Handler
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg);
if (result.Success != true)
@@ -95,7 +114,34 @@ namespace ServiceLib.Handler
UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
{
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
if (node is null)
{
return -1;
}
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
return -1;
}
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
@@ -157,7 +203,7 @@ namespace ServiceLib.Handler
if (itemSocks != null)
{
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success)
{
@@ -194,7 +240,7 @@ namespace ServiceLib.Handler
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
UpdateFunc(false, msg);
return null;
@@ -207,8 +253,8 @@ namespace ServiceLib.Handler
StartInfo = new()
{
FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, configPath),
WorkingDirectory = Utils.GetConfigPath(),
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
@@ -228,12 +274,14 @@ namespace ServiceLib.Handler
{
proc.OutputDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data)) return;
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
proc.ErrorDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data)) return;
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
}
@@ -247,7 +295,8 @@ namespace ServiceLib.Handler
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
if (isNeedSudo) _linuxSudoPid = proc.Id;
if (isNeedSudo)
_linuxSudoPid = proc.Id;
if (displayLog)
{
@@ -266,7 +315,7 @@ namespace ServiceLib.Handler
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(true, ex.Message);
UpdateFunc(mayNeedSudo, ex.Message);
return null;
}
}
@@ -277,7 +326,7 @@ namespace ServiceLib.Handler
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
@@ -331,7 +380,7 @@ namespace ServiceLib.Handler
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
@@ -357,4 +406,4 @@ namespace ServiceLib.Handler
#endregion Linux
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler
namespace ServiceLib.Handler
{
public sealed class CoreInfoHandler
{
@@ -44,7 +44,7 @@
}
if (fileName.IsNullOrEmpty())
{
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo?.CoreType.ToString()), coreInfo?.CoreExes?.LastOrDefault(), coreInfo?.Url);
Logging.SaveLog(msg);
}
return fileName;
@@ -52,107 +52,102 @@
private void InitCoreInfo()
{
var urlN = GetCoreUrl(ECoreType.v2rayN);
var urlXray = GetCoreUrl(ECoreType.Xray);
var urlMihomo = GetCoreUrl(ECoreType.mihomo);
var urlSingbox = GetCoreUrl(ECoreType.sing_box);
_coreInfo =
[
new CoreInfo
{
CoreType = ECoreType.v2rayN,
Url = Global.NUrl,
ReleaseApiUrl = Global.NUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.NUrl + "/download/{0}/v2rayN-windows-64.zip",
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
Url = GetCoreUrl(ECoreType.v2rayN),
ReleaseApiUrl = urlN.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = urlN + "/download/{0}/v2rayN-windows-64.zip",
DownloadUrlWinArm64 = urlN + "/download/{0}/v2rayN-windows-arm64.zip",
DownloadUrlLinux64 = urlN + "/download/{0}/v2rayN-linux-64.zip",
DownloadUrlLinuxArm64 = urlN + "/download/{0}/v2rayN-linux-arm64.zip",
DownloadUrlOSX64 = urlN + "/download/{0}/v2rayN-macos-64.zip",
DownloadUrlOSXArm64 = urlN + "/download/{0}/v2rayN-macos-arm64.zip",
},
new CoreInfo
{
CoreType = ECoreType.v2fly,
CoreExes = ["wv2ray", "v2ray"],
Arguments = "",
Url = Global.V2flyCoreUrl,
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
CoreExes = ["v2ray"],
Arguments = "{0}",
Url = GetCoreUrl(ECoreType.v2fly),
Match = "V2Ray",
VersionArg = "-version",
RedirectInfo = true,
},
new CoreInfo
{
CoreType = ECoreType.v2fly_v5,
CoreExes = ["v2ray"],
Arguments = "run -c config.json -format jsonv5",
Url = Global.V2flyCoreUrl,
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
Arguments = "run -c {0} -format jsonv5",
Url = GetCoreUrl(ECoreType.v2fly_v5),
Match = "V2Ray",
VersionArg = "version",
RedirectInfo = true,
},
new CoreInfo
{
CoreType = ECoreType.Xray,
CoreExes = ["xray", "wxray"],
CoreExes = ["xray"],
Arguments = "run -c {0}",
Url = Global.XrayCoreUrl,
ReleaseApiUrl = Global.XrayCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-64.zip",
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
Url = GetCoreUrl(ECoreType.Xray),
ReleaseApiUrl = urlXray.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = urlXray + "/download/{0}/Xray-windows-64.zip",
DownloadUrlWinArm64 = urlXray + "/download/{0}/Xray-windows-arm64-v8a.zip",
DownloadUrlLinux64 = urlXray + "/download/{0}/Xray-linux-64.zip",
DownloadUrlLinuxArm64 = urlXray + "/download/{0}/Xray-linux-arm64-v8a.zip",
DownloadUrlOSX64 = urlXray + "/download/{0}/Xray-macos-64.zip",
DownloadUrlOSXArm64 = urlXray + "/download/{0}/Xray-macos-arm64-v8a.zip",
Match = "Xray",
VersionArg = "-version",
RedirectInfo = true,
},
new CoreInfo
{
CoreType = ECoreType.mihomo,
CoreExes = ["mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "mihomo", "clash"],
Arguments = "-f config.json" + PortableMode(),
Url = Global.MihomoCoreUrl,
ReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
CoreExes = ["mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "clash", "mihomo"],
Arguments = "-f {0}" + PortableMode(),
Url = GetCoreUrl(ECoreType.mihomo),
ReleaseApiUrl = urlMihomo.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = urlMihomo + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
DownloadUrlWinArm64 = urlMihomo + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = urlMihomo + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
DownloadUrlLinuxArm64 = urlMihomo + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = urlMihomo + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
DownloadUrlOSXArm64 = urlMihomo + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
Match = "Mihomo",
VersionArg = "-v",
RedirectInfo = true,
},
new CoreInfo
{
CoreType = ECoreType.hysteria,
CoreExes = ["hysteria-windows-amd64", "hysteria"],
CoreExes = ["hysteria"],
Arguments = "",
Url = Global.HysteriaCoreUrl,
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
RedirectInfo = true,
Url = GetCoreUrl(ECoreType.hysteria),
},
new CoreInfo
{
CoreType = ECoreType.naiveproxy,
CoreExes = ["naiveproxy", "naive"],
Arguments = "config.json",
Url = Global.NaiveproxyCoreUrl,
RedirectInfo = false,
CoreExes = [ "naive", "naiveproxy"],
Arguments = "{0}",
Url = GetCoreUrl(ECoreType.naiveproxy),
},
new CoreInfo
{
CoreType = ECoreType.tuic,
CoreExes = ["tuic-client", "tuic"],
Arguments = "-c config.json",
Url = Global.TuicCoreUrl,
RedirectInfo = true,
Arguments = "-c {0}",
Url = GetCoreUrl(ECoreType.tuic),
},
new CoreInfo
@@ -160,15 +155,15 @@
CoreType = ECoreType.sing_box,
CoreExes = ["sing-box-client", "sing-box"],
Arguments = "run -c {0} --disable-color",
Url = Global.SingboxCoreUrl,
RedirectInfo = true,
ReleaseApiUrl = Global.SingboxCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
Url = GetCoreUrl(ECoreType.sing_box),
ReleaseApiUrl = urlSingbox.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = urlSingbox + "/download/{0}/sing-box-{1}-windows-amd64.zip",
DownloadUrlWinArm64 = urlSingbox + "/download/{0}/sing-box-{1}-windows-arm64.zip",
DownloadUrlLinux64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
DownloadUrlLinuxArm64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
DownloadUrlOSX64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
Match = "sing-box",
VersionArg = "version",
},
@@ -177,8 +172,8 @@
{
CoreType = ECoreType.juicity,
CoreExes = ["juicity-client", "juicity"],
Arguments = "run -c config.json",
Url = Global.JuicityCoreUrl
Arguments = "run -c {0}",
Url = GetCoreUrl(ECoreType.juicity)
},
new CoreInfo
@@ -186,17 +181,38 @@
CoreType = ECoreType.hysteria2,
CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"],
Arguments = "",
Url = Global.HysteriaCoreUrl,
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
RedirectInfo = true,
Url = GetCoreUrl(ECoreType.hysteria2),
},
new CoreInfo
{
CoreType = ECoreType.brook,
CoreExes = ["brook_windows_amd64", "brook_linux_amd64", "brook"],
Arguments = " {0}",
Url = GetCoreUrl(ECoreType.brook),
AbsolutePath = true,
},
new CoreInfo
{
CoreType = ECoreType.overtls,
CoreExes = [ "overtls-bin", "overtls"],
Arguments = "-r client -c {0}",
Url = GetCoreUrl(ECoreType.overtls),
AbsolutePath = false,
}
];
}
private string PortableMode()
private static string PortableMode()
{
return $" -d {Utils.GetBinPath("").AppendQuotes()}";
}
private static string GetCoreUrl(ECoreType eCoreType)
{
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Specialized;
using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt
{
@@ -16,12 +16,12 @@ namespace ServiceLib.Handler.Fmt
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (Utils.IsNotEmpty(item.Flow))
if (item.Flow.IsNotEmpty())
{
dicQuery.Add("flow", item.Flow);
}
if (Utils.IsNotEmpty(item.StreamSecurity))
if (item.StreamSecurity.IsNotEmpty())
{
dicQuery.Add("security", item.StreamSecurity);
}
@@ -32,27 +32,27 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("security", securityDef);
}
}
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Fingerprint))
if (item.Fingerprint.IsNotEmpty())
{
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
}
if (Utils.IsNotEmpty(item.PublicKey))
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.ShortId))
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
}
if (Utils.IsNotEmpty(item.SpiderX))
if (item.SpiderX.IsNotEmpty())
{
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
}
@@ -61,21 +61,21 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("allowInsecure", "1");
}
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp));
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
switch (item.Network)
{
case nameof(ETransport.tcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.RequestHost))
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
break;
case nameof(ETransport.kcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.Path))
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.Path.IsNotEmpty())
{
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
}
@@ -83,30 +83,30 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.HeaderType) && Global.XhttpMode.Contains(item.HeaderType))
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
if (Utils.IsNotEmpty(item.Extra))
if (item.Extra.IsNotEmpty())
{
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
}
@@ -115,24 +115,24 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.quic):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("key", Utils.UrlEncode(item.Path));
break;
case nameof(ETransport.grpc):
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
@@ -214,7 +214,10 @@ namespace ServiceLib.Handler.Fmt
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
@@ -236,4 +239,4 @@ namespace ServiceLib.Handler.Fmt
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
}
}
}

View File

@@ -37,7 +37,7 @@
try
{
string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str))
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;
return null;

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class Hysteria2Fmt : BaseFmt
{
@@ -11,7 +11,8 @@
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
return null;
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -23,34 +24,41 @@
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
if (item == null)
return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
}
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
if (item.Ports.IsNotEmpty())
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
}
@@ -91,4 +99,4 @@
return null;
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt
{
@@ -26,11 +26,12 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
@@ -52,12 +53,14 @@ namespace ServiceLib.Handler.Fmt
{
var match = UrlFinder.Match(result);
if (!match.Success)
{
return null;
}
ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (Utils.IsNotEmpty(tag))
if (tag.IsNotEmpty())
{
item.Remarks = Utils.UrlDecode(tag);
}
@@ -71,18 +74,23 @@ namespace ServiceLib.Handler.Fmt
return null;
}
if (!details.Success)
{
return null;
}
item.Security = details.Groups["method"].Value;
item.Id = details.Groups["password"].Value;
item.Address = details.Groups["hostname"].Value;
item.Port = Utils.ToInt(details.Groups["port"].Value);
item.Port = details.Groups["port"].Value.ToInt();
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) return null;
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
@@ -94,7 +102,7 @@ namespace ServiceLib.Handler.Fmt
//2022-blake3
if (rawUserInfo.Contains(':'))
{
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
@@ -105,8 +113,8 @@ namespace ServiceLib.Handler.Fmt
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
@@ -120,7 +128,7 @@ namespace ServiceLib.Handler.Fmt
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
@@ -160,7 +168,7 @@ namespace ServiceLib.Handler.Fmt
Security = it.method,
Id = it.password,
Address = it.server,
Port = Utils.ToInt(it.server_port)
Port = it.server_port.ToInt()
};
lst.Add(ssItem);
}
@@ -169,4 +177,4 @@ namespace ServiceLib.Handler.Fmt
return null;
}
}
}
}

View File

@@ -1,10 +1,10 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class SingboxFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
@@ -52,4 +52,4 @@
return null;
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class SocksFmt : BaseFmt
{
@@ -23,11 +23,12 @@
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
var url = string.Empty;
if (item == null)
{
return null;
}
var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
@@ -76,7 +77,7 @@
return null;
}
item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.Port = arr1[1][(indexPort + 1)..].ToInt();
item.Security = arr21.First();
item.Id = arr21[1];
@@ -86,7 +87,10 @@
private static ProfileItem? ResolveSocksNew(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) return null;
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
@@ -108,4 +112,4 @@
return item;
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class TrojanFmt : BaseFmt
{
@@ -12,7 +12,10 @@
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -20,25 +23,26 @@
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
_ = ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery);
_ = GetStdTransport(item, null, ref dicQuery);
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class TuicFmt : BaseFmt
{
@@ -12,7 +12,10 @@
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -34,20 +37,22 @@
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
if (item == null)
{
return null;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
@@ -56,4 +61,4 @@
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
}
}
}
}

View File

@@ -1,10 +1,10 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class V2rayFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
@@ -53,4 +53,4 @@
return null;
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class VLESSFmt : BaseFmt
{
@@ -13,7 +13,10 @@
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -23,23 +26,25 @@
var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? "";
ResolveStdTransport(query, ref item);
_ = ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
if (item == null)
{
return null;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Security))
if (item.Security.IsNotEmpty())
{
dicQuery.Add("encryption", item.Security);
}
@@ -47,9 +52,9 @@
{
dicQuery.Add("encryption", Global.None);
}
GetStdTransport(item, Global.None, ref dicQuery);
_ = GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class VmessFmt : BaseFmt
{
@@ -19,10 +19,11 @@
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
VmessQRCode vmessQRCode = new()
if (item == null)
{
return null;
}
var vmessQRCode = new VmessQRCode
{
v = item.ConfigVersion,
ps = item.Remarks.TrimEx(),
@@ -41,7 +42,7 @@
fp = item.Fingerprint
};
url = JsonUtils.Serialize(vmessQRCode);
var url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
@@ -59,7 +60,7 @@
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
@@ -77,12 +78,12 @@
item.AlterId = vmessQRCode.aid;
item.Security = Utils.ToString(vmessQRCode.scy);
item.Security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
if (Utils.IsNotEmpty(vmessQRCode.net))
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
if (vmessQRCode.net.IsNotEmpty())
{
item.Network = vmessQRCode.net;
}
if (Utils.IsNotEmpty(vmessQRCode.type))
if (vmessQRCode.type.IsNotEmpty())
{
item.HeaderType = vmessQRCode.type;
}
@@ -99,14 +100,17 @@
public static ProfileItem? ResolveStdVmess(string str)
{
ProfileItem item = new()
var item = new ProfileItem
{
ConfigType = EConfigType.VMess,
Security = "auto"
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -119,4 +123,4 @@
return item;
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler.Fmt
namespace ServiceLib.Handler.Fmt
{
public class WireguardFmt : BaseFmt
{
@@ -12,7 +12,10 @@
};
var url = Utils.TryUri(str);
if (url == null) return null;
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -31,33 +34,35 @@
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
if (item == null)
{
return null;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.PublicKey))
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.ShortId))
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
}
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Net.Sockets;
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Handler
@@ -15,7 +15,7 @@ namespace ServiceLib.Handler
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
_configPath = configPath;
_httpPort = httpPort;
@@ -35,7 +35,7 @@ namespace ServiceLib.Handler
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
var pac = Utils.GetEmbedText(Global.PacFileName);
var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
@@ -70,7 +70,7 @@ namespace ServiceLib.Handler
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
await Task.Run(() => WriteContent(client));
}
catch
{
@@ -89,7 +89,10 @@ namespace ServiceLib.Handler
public static void Stop()
{
if (_tcpListener == null) return;
if (_tcpListener == null)
{
return;
}
try
{
_isRunning = false;
@@ -102,4 +105,4 @@ namespace ServiceLib.Handler
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Concurrent;
//using System.Reactive.Linq;
@@ -8,7 +8,7 @@ namespace ServiceLib.Handler
{
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new();
private readonly Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler";
@@ -20,14 +20,6 @@ namespace ServiceLib.Handler
public async Task Init()
{
await InitData();
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 600);
await SaveQueueIndexIds();
}
});
}
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
@@ -44,7 +36,7 @@ namespace ServiceLib.Handler
private void IndexIdEnqueue(string indexId)
{
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
if (indexId.IsNotEmpty() && !_queIndexIds.Contains(indexId))
{
_queIndexIds.Enqueue(indexId);
}
@@ -59,7 +51,7 @@ namespace ServiceLib.Handler
List<ProfileExItem> lstInserts = [];
List<ProfileExItem> lstUpdates = [];
for (int i = 0; i < cnt; i++)
for (var i = 0; i < cnt; i++)
{
var id = _queIndexIds.Dequeue();
var item = lstExists.FirstOrDefault(t => t.IndexId == id);
@@ -78,13 +70,18 @@ namespace ServiceLib.Handler
lstInserts.Add(itemNew);
}
}
try
{
if (lstInserts.Count() > 0)
if (lstInserts.Count > 0)
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count() > 0)
if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
}
catch (Exception ex)
{
@@ -93,17 +90,24 @@ namespace ServiceLib.Handler
}
}
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx)
private ProfileExItem AddProfileEx(string indexId)
{
profileEx = new()
var profileEx = new ProfileExItem()
{
IndexId = indexId,
Delay = 0,
Speed = 0,
Sort = 0
Sort = 0,
Message = string.Empty
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
return profileEx;
}
private ProfileExItem GetProfileExItem(string? indexId)
{
return _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId) ?? AddProfileEx(indexId);
}
public async Task ClearAll()
@@ -124,39 +128,34 @@ namespace ServiceLib.Handler
}
}
public void SetTestDelay(string indexId, string delayVal)
public void SetTestDelay(string indexId, int delay)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
var profileEx = GetProfileExItem(indexId);
int.TryParse(delayVal, out int delay);
profileEx.Delay = delay;
IndexIdEnqueue(indexId);
}
public void SetTestSpeed(string indexId, string speedVal)
public void SetTestSpeed(string indexId, decimal speed)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
var profileEx = GetProfileExItem(indexId);
decimal.TryParse(speedVal, out decimal speed);
profileEx.Speed = speed;
IndexIdEnqueue(indexId);
}
public void SetTestMessage(string indexId, string message)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Message = message;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
var profileEx = GetProfileExItem(indexId);
profileEx.Sort = sort;
IndexIdEnqueue(indexId);
}
@@ -180,4 +179,4 @@ namespace ServiceLib.Handler
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
}
}
}
}

View File

@@ -1,202 +1,33 @@
namespace ServiceLib.Handler.SysProxy
namespace ServiceLib.Handler.SysProxy
{
public class ProxySettingLinux
{
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions)
{
var lstCmd = GetSetCmds(host, port, exceptions);
await ExecCmd(lstCmd);
List<string> args = ["manual", host, port.ToString(), exceptions];
await ExecCmd(args);
}
public static async Task UnsetProxy()
{
var lstCmd = GetUnsetCmds();
await ExecCmd(lstCmd);
List<string> args = ["none"];
await ExecCmd(args);
}
private static async Task ExecCmd(List<CmdItem> lstCmd)
private static async Task ExecCmd(List<string> args)
{
foreach (var cmd in lstCmd)
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
{
continue;
}
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
await File.AppendAllTextAsync(fileName, contents);
private static List<CmdItem> GetSetCmds(string host, int port, string exceptions)
{
var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = [];
//GNOME
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Gnome("exceptions", exceptions, 0));
await Utils.SetLinuxChmod(fileName);
}
if (isKde)
{
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Kde("exceptions", exceptions, 0, configDir));
}
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds()
{
var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = [];
//GNOME
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
if (isKde)
{
lstCmd.Add(new CmdItem()
{
Cmd = GetKdeVersion(),
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
});
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{
List<CmdItem> lstCmd = [];
var cmd = GetKdeVersion();
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "NoProxyFor", host]
});
}
else
{
var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
{
List<CmdItem> lstCmd = [];
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy", "ignore-hosts", JsonUtils.Serialize(host.Split(','), false)]
});
}
else
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
});
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
});
}
return lstCmd;
}
private static bool IsKde(out string configDir)
{
configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var desktop2 = Environment.GetEnvironmentVariable("XDG_SESSION_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop, "plasma", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "plasma", StringComparison.OrdinalIgnoreCase);
if (isKde)
{
var homeDir = Environment.GetEnvironmentVariable("HOME");
if (homeDir != null)
{
configDir = Path.Combine(homeDir, ".config");
}
}
return isKde;
}
private static string GetKdeVersion()
{
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
return ver switch
{
"6" => "kwriteconfig6",
_ => "kwriteconfig5"
};
await Utils.GetCliWrapOutput(fileName, args);
}
}
}
}

View File

@@ -1,101 +1,38 @@
namespace ServiceLib.Handler.SysProxy
namespace ServiceLib.Handler.SysProxy
{
public class ProxySettingOSX
{
/// <summary>
/// 应用接口类型
/// </summary>
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge", "USB 10/100/1000 LAN"];
/// <summary>
/// 代理类型,对应 http,https,socks
/// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions)
{
var lstInterface = await GetListNetworkServices();
var lstCmd = GetSetCmds(lstInterface, host, port, exceptions);
await ExecCmd(lstCmd);
List<string> args = ["set", host, port.ToString()];
if (exceptions.IsNotEmpty())
{
args.AddRange(exceptions.Split(','));
}
await ExecCmd(args);
}
public static async Task UnsetProxy()
{
var lstInterface = await GetListNetworkServices();
var lstCmd = GetUnsetCmds(lstInterface);
await ExecCmd(lstCmd);
List<string> args = ["clear"];
await ExecCmd(args);
}
private static async Task ExecCmd(List<CmdItem> lstCmd)
private static async Task ExecCmd(List<string> args)
{
foreach (var cmd in lstCmd)
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
{
continue;
}
var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
await File.AppendAllTextAsync(fileName, contents);
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(List<string> lstInterface, string host, int port, string exceptions)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, port.ToString()]
});
}
if (exceptions.IsNotEmpty())
{
List<string> args = [$"-setproxybypassdomains", interf];
args.AddRange(exceptions.Split(','));
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = args
});
}
await Utils.SetLinuxChmod(fileName);
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds(List<string> lstInterface)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}state", interf, "off"]
});
}
}
return lstCmd;
}
public static async Task<List<string>> GetListNetworkServices()
{
var services = await Utils.GetListNetworkServices();
if (services.IsNullOrEmpty())
{
return LstInterface;
}
var lst = services.Split(Environment.NewLine).Where(t => t.Length > 0 && t.Contains('*') == false);
return lst.ToList();
await Utils.GetCliWrapOutput(fileName, args);
}
}
}
}

View File

@@ -60,7 +60,7 @@ namespace ServiceLib.Handler.SysProxy
try
{
// set proxy for LAN
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
var result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
@@ -69,29 +69,29 @@ namespace ServiceLib.Handler.SysProxy
}
return result;
}
catch (Exception ex)
catch
{
SetProxyFallback(strProxy, exceptions, type);
_ = SetProxyFallback(strProxy, exceptions, type);
return false;
}
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{
InternetPerConnOptionList list = new();
var list = new InternetPerConnOptionList();
int optionCount = 1;
var optionCount = 1;
if (type == 1) // No proxy
{
optionCount = 1;
}
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
optionCount = exceptions.IsNullOrEmpty() ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
var m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
var m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
@@ -103,11 +103,9 @@ namespace ServiceLib.Handler.SysProxy
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
//int optionCount = Utile.IsNullOrEmpty(strProxy) ? 1 : (Utile.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
var options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int;
// use THIS proxy server
if (optionCount > 1)
@@ -135,20 +133,20 @@ namespace ServiceLib.Handler.SysProxy
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
nint optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
for (var i = 0; i < options.Length; ++i)
{
if (Environment.Is64BitOperatingSystem)
{
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
var opt = new nint(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
@@ -156,14 +154,14 @@ namespace ServiceLib.Handler.SysProxy
list.options = optionsPtr;
// and then make a pointer out of the whole list
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
var isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize);
int returnvalue = 0; // ERROR_SUCCESS
var returnvalue = 0; // ERROR_SUCCESS
if (!isSuccess)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError();
@@ -171,12 +169,15 @@ namespace ServiceLib.Handler.SysProxy
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
}
// FREE the data ASAP
if (list.szConnection != nint.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
if (list.szConnection != nint.Zero)
{
Marshal.FreeHGlobal(list.szConnection); // release mem 3
}
if (optionCount > 1)
{
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
@@ -203,18 +204,18 @@ namespace ServiceLib.Handler.SysProxy
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries()
{
int entries = 0;
var entries = 0;
// attempt to query with 1 entry buffer
RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
var rasEntryNames = new RASENTRYNAME[1];
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
uint result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
// increase buffer if the buffer is not large enough
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
{
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
for (int i = 0; i < rasEntryNames.Length; i++)
for (var i = 0; i < rasEntryNames.Length; i++)
{
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
}
@@ -224,7 +225,7 @@ namespace ServiceLib.Handler.SysProxy
if (result == 0)
{
var entryNames = new List<string>();
for (int i = 0; i < entries; i++)
for (var i = 0; i < entries; i++)
{
entryNames.Add(rasEntryNames[i].szEntryName);
}
@@ -356,4 +357,4 @@ namespace ServiceLib.Handler.SysProxy
);
}
}
}
}

View File

@@ -75,7 +75,7 @@
}
strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol))
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{
strProxy = $"{Global.Loopback}:{port}";
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Handler
namespace ServiceLib.Handler
{
public class TaskHandler
{
@@ -7,66 +7,92 @@
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc));
Task.Run(() => UpdateTaskRunGeo(config, updateFunc));
Task.Run(() => ScheduledTasks(config, updateFunc));
}
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc)
{
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{
//1 minute
await Task.Delay(1000 * 60);
//Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc);
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{
//Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config);
await ProfileExHandler.Instance.SaveTo();
}
//Execute once 1 hour
if (numOfExecuted % 60 == 0)
{
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc);
}
numOfExecuted++;
}
}
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
{
await Task.Delay(60000);
Logging.SaveLog("UpdateTaskRunSubscription");
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems())?
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
var updateHandle = new UpdateService();
while (true)
if (lstSubs is not { Count: > 0 })
{
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems())
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
return;
}
foreach (var item in lstSubs)
Logging.SaveLog("Execute update subscription");
var updateHandle = new UpdateService();
foreach (var item in lstSubs)
{
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
{
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
{
updateFunc?.Invoke(success, msg);
if (success)
Logging.SaveLog("subscription" + msg);
});
item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item);
await Task.Delay(5000);
}
await Task.Delay(60000);
updateFunc?.Invoke(success, msg);
if (success)
{
Logging.SaveLog($"Update subscription end. {msg}");
}
});
item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item);
await Task.Delay(1000);
}
}
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc)
private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc)
{
var autoUpdateGeoTime = DateTime.Now;
//await Task.Delay(1000 * 120);
Logging.SaveLog("UpdateTaskRunGeo");
var updateHandle = new UpdateService();
while (true)
if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0)
{
await Task.Delay(1000 * 3600);
Logging.SaveLog("Execute update geo files");
var dtNow = DateTime.Now;
if (config.GuiItem.AutoUpdateInterval > 0)
var updateHandle = new UpdateService();
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
{
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
updateFunc?.Invoke(false, msg);
});
autoUpdateGeoTime = dtNow;
}
}
updateFunc?.Invoke(false, msg);
});
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using WebDav;
namespace ServiceLib.Handler
@@ -8,7 +8,7 @@ namespace ServiceLib.Handler
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
public static WebDavHandler Instance => _instance.Value;
private Config? _config;
private readonly Config? _config;
private WebDavClient? _client;
private string? _lastDescription;
private string _webDir = Global.AppName + "_backup";
@@ -61,7 +61,10 @@ namespace ServiceLib.Handler
private async Task<bool> TryCreateDir()
{
if (_client is null) return false;
if (_client is null)
{
return false;
}
try
{
var result2 = await _client.Mkcol(_webDir);
@@ -176,4 +179,4 @@ namespace ServiceLib.Handler
public string GetLastError() => _lastDescription ?? string.Empty;
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models
namespace ServiceLib.Models
{
[Serializable]
public class CoreBasicItem
@@ -156,7 +156,7 @@
public int SpeedTestTimeout { get; set; }
public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; }
public int SpeedTestPageSize { get; set; }
public int MixedConcurrencyCount { get; set; }
}
[Serializable]
@@ -197,6 +197,7 @@
{
public int UpMbps { get; set; }
public int DownMbps { get; set; }
public int HopInterval { get; set; } = 30;
}
[Serializable]
@@ -244,4 +245,4 @@
public string? Length { get; set; }
public string? Interval { get; set; }
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models
namespace ServiceLib.Models
{
[Serializable]
public class CoreInfo
@@ -16,6 +16,6 @@
public string? DownloadUrlOSXArm64 { get; set; }
public string? Match { get; set; }
public string? VersionArg { get; set; }
public bool RedirectInfo { get; set; }
public bool AbsolutePath { get; set; }
}
}
}

View File

@@ -1,4 +1,4 @@
using SQLite;
using SQLite;
namespace ServiceLib.Models
{
@@ -11,5 +11,6 @@ namespace ServiceLib.Models
public int Delay { get; set; }
public decimal Speed { get; set; }
public int Sort { get; set; }
public string? Message { get; set; }
}
}
}

View File

@@ -1,4 +1,4 @@
using SQLite;
using SQLite;
namespace ServiceLib.Models
{
@@ -48,12 +48,12 @@ namespace ServiceLib.Models
public List<string>? GetAlpn()
{
return Utils.IsNullOrEmpty(Alpn) ? null : Utils.String2List(Alpn);
return Alpn.IsNullOrEmpty() ? null : Utils.String2List(Alpn);
}
public string GetNetwork()
{
if (Utils.IsNullOrEmpty(Network) || !Global.Networks.Contains(Network))
if (Network.IsNullOrEmpty() || !Global.Networks.Contains(Network))
{
return Global.DefaultNetwork;
}
@@ -64,11 +64,11 @@ namespace ServiceLib.Models
[PrimaryKey]
public string IndexId { get; set; }
public EConfigType ConfigType { get; set; }
public int ConfigVersion { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public string Ports { get; set; }
public string Id { get; set; }
public int AlterId { get; set; }
public string Security { get; set; }
@@ -93,4 +93,4 @@ namespace ServiceLib.Models
public string SpiderX { get; set; }
public string Extra { get; set; }
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models
namespace ServiceLib.Models
{
[Serializable]
public class ServerTestItem
@@ -8,6 +8,6 @@
public int Port { get; set; }
public EConfigType ConfigType { get; set; }
public bool AllowTest { get; set; }
public int Delay { get; set; }
public int QueueNum { get; set; }
}
}
}

View File

@@ -1,4 +1,4 @@
namespace ServiceLib.Models
namespace ServiceLib.Models
{
public class SingboxConfig
{
@@ -101,21 +101,23 @@
public string tag { get; set; }
public string? server { get; set; }
public int? server_port { get; set; }
public string uuid { get; set; }
public string security { get; set; }
public List<string>? server_ports { get; set; }
public string? uuid { get; set; }
public string? security { get; set; }
public int? alter_id { get; set; }
public string flow { get; set; }
public string? flow { get; set; }
public string? hop_interval { get; set; }
public int? up_mbps { get; set; }
public int? down_mbps { get; set; }
public string auth_str { get; set; }
public string? auth_str { get; set; }
public int? recv_window_conn { get; set; }
public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; }
public string? detour { get; set; }
public string method { get; set; }
public string username { get; set; }
public string password { get; set; }
public string congestion_control { get; set; }
public string? method { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? congestion_control { get; set; }
public string? version { get; set; }
public string? network { get; set; }
public string? packet_encoding { get; set; }
@@ -252,4 +254,4 @@
public string? download_detour { get; set; }
public string? update_interval { get; set; }
}
}
}

View File

@@ -4,23 +4,15 @@ namespace ServiceLib.Models
{
public class V2rayConfig
{
public string? remarks { get; set; }
public Log4Ray log { get; set; }
public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; }
public Stats4Ray? stats { get; set; }
public Metrics4Ray? metrics { get; set; }
public Policy4Ray? policy { get; set; }
public object dns { get; set; }
public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; }
public Routing4Ray routing { get; set; }
public Metrics4Ray? metrics { get; set; }
public Policy4Ray? policy { get; set; }
public Stats4Ray? stats { get; set; }
public string? remarks { get; set; }
}
public class Stats4Ray
@@ -299,6 +291,8 @@ namespace ServiceLib.Models
public object request { get; set; }
public object response { get; set; }
public string? domain { get; set; }
}
public class KcpSettings4Ray
@@ -398,4 +392,4 @@ namespace ServiceLib.Models
public string? length { get; set; }
public string? interval { get; set; }
}
}
}

View File

@@ -61,7 +61,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Do you want to append rules? Choose yes to append, choose otherwise to replace 的本地化字符串。
/// 查找类似 Do you want to append rules? Choose yes to append, choose otherwise to replace. 的本地化字符串。
/// </summary>
public static string AddBatchRoutingRulesYesNo {
get {
@@ -196,7 +196,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Failed to run Core, please see the log 的本地化字符串。
/// 查找类似 Failed to run Core, please check the prompt information 的本地化字符串。
/// </summary>
public static string FailedToRunCore {
get {
@@ -403,7 +403,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Custom Icon 的本地化字符串。
/// 查找类似 Custom icon 的本地化字符串。
/// </summary>
public static string LvCustomIcon {
get {
@@ -610,7 +610,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 URL(Optional) 的本地化字符串。
/// 查找类似 URL (optional) 的本地化字符串。
/// </summary>
public static string LvUrl {
get {
@@ -1006,7 +1006,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 One-click multi test Latency and speed (Ctrl+E) 的本地化字符串。
/// 查找类似 One-click multi-test latency and speed (Ctrl+E) 的本地化字符串。
/// </summary>
public static string menuMixedTestServer {
get {
@@ -1311,6 +1311,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Remove invalid by test results 的本地化字符串。
/// </summary>
public static string menuRemoveInvalidServerResult {
get {
return ResourceManager.GetString("menuRemoveInvalidServerResult", resourceCulture);
}
}
/// <summary>
/// 查找类似 Remove selected servers (Delete) 的本地化字符串。
/// </summary>
@@ -1366,7 +1375,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Set as active rule(Enter) 的本地化字符串。
/// 查找类似 Set as active rule (Enter) 的本地化字符串。
/// </summary>
public static string menuRoutingAdvancedSetDefault {
get {
@@ -1680,6 +1689,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 By test result 的本地化字符串。
/// </summary>
public static string menuTestServerResult {
get {
return ResourceManager.GetString("menuTestServerResult", resourceCulture);
}
}
/// <summary>
/// 查找类似 {0} Website 的本地化字符串。
/// </summary>
@@ -1689,15 +1707,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Clear original subscription content 的本地化字符串。
/// </summary>
public static string MsgClearSubscription {
get {
return ResourceManager.GetString("MsgClearSubscription", resourceCulture);
}
}
/// <summary>
/// 查找类似 Download GeoFile: {0} successfully 的本地化字符串。
/// </summary>
@@ -2004,6 +2013,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Removed {0} invalid test results. 的本地化字符串。
/// </summary>
public static string RemoveInvalidServerResultTip {
get {
return ResourceManager.GetString("RemoveInvalidServerResultTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 Are you sure to remove the rules? 的本地化字符串。
/// </summary>
@@ -2094,6 +2112,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Starting retesting failed parts, {0} remaining. Press ESC to terminate... 的本地化字符串。
/// </summary>
public static string SpeedtestingTestFailedPart {
get {
return ResourceManager.GetString("SpeedtestingTestFailedPart", resourceCulture);
}
}
/// <summary>
/// 查找类似 Waiting for testing (press ESC to terminate)... 的本地化字符串。
/// </summary>
@@ -2195,7 +2222,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 AutoRefresh 的本地化字符串。
/// 查找类似 Auto refresh 的本地化字符串。
/// </summary>
public static string TbAutoRefresh {
get {
@@ -2204,7 +2231,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Auto ScrollToEnd 的本地化字符串。
/// 查找类似 Auto scroll to end 的本地化字符串。
/// </summary>
public static string TbAutoScrollToEnd {
get {
@@ -2294,7 +2321,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Support DnsObject, Click to view the document 的本地化字符串。
/// 查找类似 Supports DnsObject; Click to view documentation 的本地化字符串。
/// </summary>
public static string TbDnsObjectDoc {
get {
@@ -2384,7 +2411,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Set directly by pressing the keyboard, take effect after restart 的本地化字符串。
/// 查找类似 Set directly by pressing the keyboard; Takes effect after restart 的本地化字符串。
/// </summary>
public static string TbGlobalHotkeySettingTip {
get {
@@ -2456,7 +2483,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Address(Ip,Ipv6) 的本地化字符串。
/// 查找类似 Address(Ipv4,Ipv6) 的本地化字符串。
/// </summary>
public static string TbLocalAddress {
get {
@@ -2509,6 +2536,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Server port range 的本地化字符串。
/// </summary>
public static string TbPorts7 {
get {
return ResourceManager.GetString("TbPorts7", resourceCulture);
}
}
/// <summary>
/// 查找类似 Will cover the port, separate with commas (,) 的本地化字符串。
/// </summary>
public static string TbPorts7Tips {
get {
return ResourceManager.GetString("TbPorts7Tips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Socks port 的本地化字符串。
/// </summary>
@@ -2663,7 +2708,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 *Set the rules, separated by commas (,); The comma in the regular is replaced by &lt;COMMA&gt; 的本地化字符串。
/// 查找类似 *Separate rules by commas (,); For a literal comma use &lt;COMMA&gt;; Prefix # to ignore a rule 的本地化字符串。
/// </summary>
public static string TbRoutingTips {
get {
@@ -2771,7 +2816,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Automatic update interval of Geo (hours) 的本地化字符串。
/// 查找类似 Automatic update interval for Geo files (hours) 的本地化字符串。
/// </summary>
public static string TbSettingsAutoUpdateInterval {
get {
@@ -2843,7 +2888,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 FontFamily(Require restart) 的本地化字符串。
/// 查找类似 Font family (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsCurrentFontFamily {
get {
@@ -2852,7 +2897,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Install the font to the system and restart the settings 的本地化字符串。
/// 查找类似 Install the font to the system, select or fill in the font name, restart the settings 的本地化字符串。
/// </summary>
public static string TbSettingsCurrentFontFamilyLinuxTip {
get {
@@ -2861,7 +2906,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Copy the font TTF/TTC file to the directory guiFonts, restart the settings 的本地化字符串。
/// 查找类似 Copy the font TTF/TTC file to the directory guiFonts; Reopen the settings window 的本地化字符串。
/// </summary>
public static string TbSettingsCurrentFontFamilyTip {
get {
@@ -2915,7 +2960,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Display real-time speed 的本地化字符串。
/// 查找类似 Display real-time speed (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsDisplayRealTimeSpeed {
get {
@@ -2951,7 +2996,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Double-click server make active 的本地化字符串。
/// 查找类似 Double-clicking server makes it active 的本地化字符串。
/// </summary>
public static string TbSettingsDoubleClick2Activate {
get {
@@ -2960,7 +3005,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Automatically adjust column width after updating subscription 的本地化字符串。
/// 查找类似 Automatically adjust column width after subscription update 的本地化字符串。
/// </summary>
public static string TbSettingsEnableAutoAdjustMainLvColWidth {
get {
@@ -2987,7 +3032,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Enable Server Drag Drop Sort(Require restart) 的本地化字符串。
/// 查找类似 Enable sorting servers by drag-n-drop (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsEnableDragDropSort {
get {
@@ -3023,7 +3068,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Enable hardware acceleration(Require restart) 的本地化字符串。
/// 查找类似 Enable hardware acceleration (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsEnableHWA {
get {
@@ -3059,7 +3104,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Exception. Do not use proxy server for addresses beginning with,Use semicolon (;) 的本地化字符串。
/// 查找类似 Exclusions: Do not use proxy server for addresses beginning with the following. Use semicolon (;) to separate entries. 的本地化字符串。
/// </summary>
public static string TbSettingsExceptionTip {
get {
@@ -3068,7 +3113,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Exception. Do not use proxy server for addresses,with a comma (,) 的本地化字符串。
/// 查找类似 Exclusions: Do not use proxy server for the following addresses. Use comma (,) to separate entries. 的本地化字符串。
/// </summary>
public static string TbSettingsExceptionTip2 {
get {
@@ -3103,15 +3148,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 HTTP Port 的本地化字符串。
/// </summary>
public static string TbSettingsHttpPort {
get {
return ResourceManager.GetString("TbSettingsHttpPort", resourceCulture);
}
}
/// <summary>
/// 查找类似 Hysteria Max bandwidth (Up/Dw) 的本地化字符串。
/// </summary>
@@ -3122,7 +3158,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Keep older when deduplication 的本地化字符串。
/// 查找类似 Keep the older when de-duplicating 的本地化字符串。
/// </summary>
public static string TbSettingsKeepOlderDedupl {
get {
@@ -3167,7 +3203,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 The password is encrypted and stored only in local files. 的本地化字符串。
/// 查找类似 The password is encrypted and stored only in local files 的本地化字符串。
/// </summary>
public static string TbSettingsLinuxSudoPasswordTip {
get {
@@ -3203,7 +3239,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Main layout orientation(Require restart) 的本地化字符串。
/// 查找类似 Main layout orientation (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsMainGirdOrientation {
get {
@@ -3211,6 +3247,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 The number of concurrent during multi-test 的本地化字符串。
/// </summary>
public static string TbSettingsMixedConcurrencyCount {
get {
return ResourceManager.GetString("TbSettingsMixedConcurrencyCount", resourceCulture);
}
}
/// <summary>
/// 查找类似 sing-box Mux Protocol 的本地化字符串。
/// </summary>
@@ -3346,15 +3391,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
/// </summary>
public static string TbSettingsSpeedTestPageSize {
get {
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
}
}
/// <summary>
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
/// </summary>
@@ -3401,7 +3437,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Enable traffic statistics (Require restart) 的本地化字符串。
/// 查找类似 Enable traffic statistics (requires restart) 的本地化字符串。
/// </summary>
public static string TbSettingsStatistics {
get {
@@ -3868,15 +3904,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it! 的本地化字符串。
/// </summary>
public static string UpdateStandalonePackageTip {
get {
return ResourceManager.GetString("UpdateStandalonePackageTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 UpgradeApp does not exist 的本地化字符串。
/// </summary>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>جابجایی</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>محتوای اشتراک اصلی را پاک کنید</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>دانلود Core با موفقیت</value>
</data>
@@ -344,10 +341,10 @@
<value>لطفاً DNS سفارشی صحیح را پر کنید</value>
</data>
<data name="TransportPathTip1" xml:space="preserve">
<value>*ws path</value>
<value>* مسیر ws</value>
</data>
<data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value>
<value>* مسیر h2</value>
</data>
<data name="TransportPathTip3" xml:space="preserve">
<value>*QUIC key/Kcp seed</value>
@@ -356,13 +353,13 @@
<value>*grpc serviceName</value>
</data>
<data name="TransportRequestHostTip1" xml:space="preserve">
<value>*http host Separated by commas (,)</value>
<value>*هاست http جدا شده با کاما (،)</value>
</data>
<data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws host</value>
<value>*هاست ws</value>
</data>
<data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host Separated by commas (,)</value>
<value>*هاست h2 با کاما (،) جدا شده است</value>
</data>
<data name="TransportRequestHostTip4" xml:space="preserve">
<value>*QUIC securty</value>
@@ -377,7 +374,7 @@
<value>*QUIC camouflage type</value>
</data>
<data name="TransportHeaderTypeTip4" xml:space="preserve">
<value>*grpc mode</value>
<value>*حالت grpc</value>
</data>
<data name="LvTLS" xml:space="preserve">
<value>TLS</value>
@@ -404,7 +401,7 @@
<value>درحال تست کردن...</value>
</data>
<data name="LabLAN" xml:space="preserve">
<value>LAN</value>
<value>شبکه محلی</value>
</data>
<data name="LabLocal" xml:space="preserve">
<value>محلی</value>
@@ -683,7 +680,7 @@
<value>راه اندازی مخفی کردن خودکار</value>
</data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>فاصله به روز رسانی خودکار و Geo (ساعت)</value>
<value>فاصله به روز رسانی خودکار برای فایل های Geo (ساعت)</value>
</data>
<data name="TbSettingsCore" xml:space="preserve">
<value>هسته: تنظیمات اولیه</value>
@@ -704,7 +701,7 @@
<value>Outbound Freedom domainStrategy</value>
</data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>پس از به‌روزرسانی اشتراک، عرض ستون را به صورت خودکار تنظیم شود</value>
<value>پس از به‌روزرسانی اشتراک، عرض ستون به صورت خودکار تنظیم شود</value>
</data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>به روز رسانی های پیش از انتشار را بررسی شود</value>
@@ -713,16 +710,13 @@
<value>استثنا</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>استثنا: از سرور پروکسی برای آدرس هایی که با شروع می شوند استفاده نکنید، از نقطه ویرگول (;) استفاده کنید.</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>پورت Http</value>
<value>استثناها: از سرور پروکسی برای آدرس هایی که با موارد زیر شروع می شوند استفاده نکنید. برای جدا کردن ورودی ها از نقطه ویرگول (;) استفاده کنید.</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value>
<value>نمایش سرعت واقعی (نیاز به راه اندازی مجدد)</value>
</data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>هنگام کپی برداری، نگه داری قدیمی تر ها</value>
<value>هنگام حذف کپی، قدیمی تر را نگه دارید</value>
</data>
<data name="TbSettingsLogEnabled" xml:space="preserve">
<value>ثبت گزارش های محلی</value>
@@ -755,7 +749,7 @@
<value>درهنگام راه ائدازی شروع شود</value>
</data>
<data name="TbSettingsStatistics" xml:space="preserve">
<value>فعال کردن آمار (نیاز به راه اندازی مجدد)</value>
<value>فعال کردن آمار ترافیک (نیاز به راه اندازی مجدد)</value>
</data>
<data name="TbSettingsSubConvert" xml:space="preserve">
<value>آدرس اینترنتی تبدیل اشتراک</value>
@@ -863,7 +857,7 @@
<value>لیست مجموعه قوانین از پیش تعریف شده</value>
</data>
<data name="TbRoutingTips" xml:space="preserve">
<value>*قوانین را تنظیم کنید که با کاما از هم جدا شده اند (,); کاما در حالت عادی با &lt;COMMA&gt;</value>
<value>*قوانین را با کاما (,) جدا کنید. برای کاما به معنای واقعی کلمه از &lt;COMMA&gt;; پیشوند # برای نادیده گرفتن یک قانون</value>
</data>
<data name="menuImportRulesFromClipboard" xml:space="preserve">
<value>وارد کردن قوانین از کلیپ بورد</value>
@@ -899,7 +893,7 @@
<value>مستندات شی قانون</value>
</data>
<data name="TbDnsObjectDoc" xml:space="preserve">
<value>پشتیبانی از DnsObject</value>
<value>پشتیبانی از DnsObject. برای مشاهده مستندات کلیک کنید</value>
</data>
<data name="SubUrlTips" xml:space="preserve">
<value>گروه لطفا اینجا را خالی بگذارید</value>
@@ -950,7 +944,7 @@
<value>این پارامتر فقط برای tcp/http و ws معتبر است</value>
</data>
<data name="TbSettingsEnableHWA" xml:space="preserve">
<value>فعال‌ سازی شتاب‌ دهنده سخت‌افزاری (نیاز به راه‌اندازی مجدد)</value>
<value>فعال‌ سازی شتاب‌ دهنده سخت‌ افزاری (نیاز به راه‌اندازی مجدد)</value>
</data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
@@ -1051,9 +1045,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>نمایش یا پنهان کردن پنجره اصلی</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>شما در حال حاضر در حال اجرای یک بسته مستقل هستید، لطفاً فایل SelfContained.7z را به صورت دستی دانلود کنید تا آن را از حالت فشرده خارج کرده و بازنویسی کنید!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>پیکربندی سفارشی ساکس پورت</value>
</data>
@@ -1208,7 +1199,7 @@
<value>انتقال به گروه</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>فعال کردن مرتب سازی با کشیدن سرور (نیاز به راه اندازی مجدد)</value>
<value>فعال کردن مرتبسازی سرورها با کشیدن و رها کردن (نیاز به راهاندازی مجدد)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>بازخوانی خودکار</value>
@@ -1220,7 +1211,7 @@
<value>ویرایش سرور (Ctrl+D)</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>روی server make active دوبار کلیک کنید</value>
<value>دوبار کلیک کردن سرور باعث فعال شدن آن می شود</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>تست تکمیل شد</value>
@@ -1232,7 +1223,7 @@
<value>FontFamily (نیاز به راه اندازی مجدد)</value>
</data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>فایل TTF/TTC فونت را در دایرکتوری guiFonts کپی کنید، تنظیمات را مجددا راه اندازی کنید</value>
<value>فایل TTF/TTC فونت را در دایرکتوری guiFonts کپی کنید. پنجره تنظیمات را دوباره باز کنید</value>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>پورت Pac = +3; پورت Xray API = +4; پورت mihomo API = +5;</value>
@@ -1328,7 +1319,7 @@
<value>Previous proxy remarks</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>آدرس (IP, IPv6)</value>
<value>آدرس (IPv4, IPv6)</value>
</data>
<data name="TbReserved" xml:space="preserve">
<value>Reserved(2,3,4)</value>
@@ -1372,11 +1363,11 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>هنگام بستن پنجره در سینی پنهان شوید</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>تعداد در هر زمان برای دسته خودکار در طول تست سرعت (حداکثر 1000)</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>The number of concurrent during multi-test</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>استثنا:از سرور پروکسی برای آدرس ها، با کاما (،) استفاده نکنید</value>
<value>موارد استثنا: از سرور پروکسی برای آدرس های زیر استفاده نکنید. برای جدا کردن ورودی ها از کاما (،) استفاده کنید.</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>نوع Sniffing</value>
@@ -1393,4 +1384,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>کپی کردن دستور پروکسی در کلیپ بورد</value>
</data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>شروع آزمایش مجدد قطعات ناموفق، {0} باقی مانده است. برای خاتمه ESC را فشار دهید...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>Server port range</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>Szállítás</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Eredeti előfizetési tartalom törlése</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>A Core letöltése sikerült</value>
</data>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Kivétel. Ne használjon proxy szervert a címek esetében, amelyek pontosan itt kezdődnek, használjon pontosvesszőt (;)</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP Port</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value>
</data>
@@ -1106,7 +1100,7 @@
<value>Fenntartva (2,3,4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Cím (Ip,Ipv6)</value>
<value>Cím (Ipv4,Ipv6)</value>
</data>
<data name="TbPath7" xml:space="preserve">
<value>obfs jelszó</value>
@@ -1255,9 +1249,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Fő ablak megjelenítése vagy elrejtése</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>Jelenleg egy önálló csomagot futtatsz, kérlek, töltsd le manuálisan a SelfContained.7z fájlt, bontsd ki és írd felül!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Testreszabott konfigurációs socks port</value>
</data>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Minimálás tálcára ablak zárásakor</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Szám / időszak az automatikus batch során a sebességvizsgálat során (maximum 1000)</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>The number of concurrent during multi-test</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Kivétel. Ne használj proxy szervert a címeknél, évezz pontosvesszőt (,)</value>
@@ -1393,4 +1384,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>Copy proxy command to clipboard</value>
</data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>Server port range</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>Transport</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Clear original subscription content</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>Download Core successfully</value>
</data>
@@ -320,7 +317,7 @@
<value>Remarks</value>
</data>
<data name="LvUrl" xml:space="preserve">
<value>URL(Optional)</value>
<value>URL (optional)</value>
</data>
<data name="LvCount" xml:space="preserve">
<value>Count</value>
@@ -329,7 +326,7 @@
<value>Please fill in the URL</value>
</data>
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
<value>Do you want to append rules? Choose yes to append, choose otherwise to replace</value>
<value>Do you want to append rules? Choose yes to append, choose otherwise to replace.</value>
</data>
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
<value>Download GeoFile: {0} successfully</value>
@@ -338,7 +335,7 @@
<value>Information</value>
</data>
<data name="LvCustomIcon" xml:space="preserve">
<value>Custom Icon</value>
<value>Custom icon</value>
</data>
<data name="FillCorrectDNSText" xml:space="preserve">
<value>Please fill in the correct custom DNS</value>
@@ -683,7 +680,7 @@
<value>Auto hide startup</value>
</data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>Automatic update interval of Geo (hours)</value>
<value>Automatic update interval for Geo files (hours)</value>
</data>
<data name="TbSettingsCore" xml:space="preserve">
<value>Core: basic settings</value>
@@ -704,7 +701,7 @@
<value>Outbound Freedom domainStrategy</value>
</data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>Automatically adjust column width after updating subscription</value>
<value>Automatically adjust column width after subscription update</value>
</data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>Check for pre-release updates</value>
@@ -713,16 +710,13 @@
<value>Exception</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses beginning with,Use semicolon (;)</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP Port</value>
<value>Exclusions: Do not use proxy server for addresses beginning with the following. Use semicolon (;) to separate entries.</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed (Require restart)</value>
<value>Display real-time speed (requires restart)</value>
</data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>Keep older when deduplication</value>
<value>Keep the older when de-duplicating</value>
</data>
<data name="TbSettingsLogEnabled" xml:space="preserve">
<value>Enable Log</value>
@@ -755,7 +749,7 @@
<value>Start on boot</value>
</data>
<data name="TbSettingsStatistics" xml:space="preserve">
<value>Enable traffic statistics (Require restart)</value>
<value>Enable traffic statistics (requires restart)</value>
</data>
<data name="TbSettingsSubConvert" xml:space="preserve">
<value>Subscription conversion URL</value>
@@ -785,7 +779,7 @@
<value>Global Hotkey Settings</value>
</data>
<data name="TbGlobalHotkeySettingTip" xml:space="preserve">
<value>Set directly by pressing the keyboard, take effect after restart</value>
<value>Set directly by pressing the keyboard; Takes effect after restart</value>
</data>
<data name="TbNotChangeSystemProxy" xml:space="preserve">
<value>Do not change system proxy</value>
@@ -842,7 +836,7 @@
<value>Remove selected (Delete)</value>
</data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Set as active rule(Enter)</value>
<value>Set as active rule (Enter)</value>
</data>
<data name="TbdomainMatcher" xml:space="preserve">
<value>Domain Matcher</value>
@@ -863,7 +857,7 @@
<value>Pre-defined Rule Set List</value>
</data>
<data name="TbRoutingTips" xml:space="preserve">
<value>*Set the rules, separated by commas (,); The comma in the regular is replaced by &lt;COMMA&gt;</value>
<value>*Separate rules by commas (,); For a literal comma use &lt;COMMA&gt;; Prefix # to ignore a rule</value>
</data>
<data name="menuImportRulesFromClipboard" xml:space="preserve">
<value>Import Rules From Clipboard</value>
@@ -899,7 +893,7 @@
<value>Ruleobject Doc</value>
</data>
<data name="TbDnsObjectDoc" xml:space="preserve">
<value>Support DnsObject, Click to view the document</value>
<value>Supports DnsObject; Click to view documentation</value>
</data>
<data name="SubUrlTips" xml:space="preserve">
<value>For group please leave blank here</value>
@@ -917,7 +911,7 @@
<value>Do not use proxy servers for local (intranet) addresses</value>
</data>
<data name="menuMixedTestServer" xml:space="preserve">
<value>One-click multi test Latency and speed (Ctrl+E)</value>
<value>One-click multi-test latency and speed (Ctrl+E)</value>
</data>
<data name="LvTestDelay" xml:space="preserve">
<value>Delay(ms)</value>
@@ -926,7 +920,7 @@
<value>Speed(M/s)</value>
</data>
<data name="FailedToRunCore" xml:space="preserve">
<value>Failed to run Core, please see the log</value>
<value>Failed to run Core, please check the prompt information</value>
</data>
<data name="LvFilter" xml:space="preserve">
<value>Remarks regular filter</value>
@@ -947,10 +941,10 @@
<value>Move to group</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>Enable Server Drag Drop Sort(Require restart)</value>
<value>Enable sorting servers by drag-n-drop (requires restart)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>AutoRefresh</value>
<value>Auto refresh</value>
</data>
<data name="SpeedtestingSkip" xml:space="preserve">
<value>Skip test</value>
@@ -959,7 +953,7 @@
<value>Edit Server (Ctrl+D)</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-click server make active</value>
<value>Double-clicking server makes it active</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>Test completed</value>
@@ -974,10 +968,10 @@
<value>This parameter is valid only for tcp/http and ws</value>
</data>
<data name="TbSettingsCurrentFontFamily" xml:space="preserve">
<value>FontFamily(Require restart)</value>
<value>Font family (requires restart)</value>
</data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
<value>Copy the font TTF/TTC file to the directory guiFonts; Reopen the settings window</value>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac port = +3; Xray API port = +4; mihomo API port = +5;</value>
@@ -1007,7 +1001,7 @@
<value>SpiderX</value>
</data>
<data name="TbSettingsEnableHWA" xml:space="preserve">
<value>Enable hardware acceleration(Require restart)</value>
<value>Enable hardware acceleration (requires restart)</value>
</data>
<data name="SpeedtestingWait" xml:space="preserve">
<value>Waiting for testing (press ESC to terminate)...</value>
@@ -1106,7 +1100,7 @@
<value>Reserved(2,3,4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value>
<value>Address(Ipv4,Ipv6)</value>
</data>
<data name="TbPath7" xml:space="preserve">
<value>obfs password</value>
@@ -1115,7 +1109,7 @@
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value>
</data>
<data name="TbAutoScrollToEnd" xml:space="preserve">
<value>Auto ScrollToEnd</value>
<value>Auto scroll to end</value>
</data>
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
<value>Speed Ping Test URL</value>
@@ -1235,7 +1229,7 @@
<value>Multi-Server lowest latency</value>
</data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Main layout orientation(Require restart)</value>
<value>Main layout orientation (requires restart)</value>
</data>
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
<value>Multi-server load balancing</value>
@@ -1255,9 +1249,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Show or hide the main window</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Custom config socks port</value>
</data>
@@ -1343,7 +1334,7 @@
<value>Please do not use the insecure HTTP protocol subscription address</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value>
<value>Install the font to the system, select or fill in the font name, restart the settings</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>Are you sure to exit?</value>
@@ -1355,7 +1346,7 @@
<value>System sudo password</value>
</data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password is encrypted and stored only in local files.</value>
<value>The password is encrypted and stored only in local files</value>
</data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value>
@@ -1372,11 +1363,11 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>The number of concurrent during multi-test</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
<value>Exclusions: Do not use proxy server for the following addresses. Use comma (,) to separate entries.</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value>
@@ -1393,4 +1384,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>Copy proxy command to clipboard</value>
</data>
</root>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>Server port range</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>Протокол</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>Очистить контент оригинальной подписки</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>Ядро успешно загружено</value>
</data>
@@ -721,9 +718,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>HTTP порт</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>Display real-time speed</value>
</data>
@@ -1058,7 +1052,7 @@
<value>obfs password</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value>
<value>Address(Ipv4,Ipv6)</value>
</data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Default domain strategy for outbound</value>
@@ -1210,9 +1204,6 @@
<data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] server</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value>
</data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>Enable IPv6 Address</value>
</data>
@@ -1372,8 +1363,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>The number of concurrent during multi-test</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
@@ -1393,4 +1384,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>Copy proxy command to clipboard</value>
</data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>By test result</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>Remove invalid by test results</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>Removed {0} invalid test results.</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>Server port range</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -175,10 +175,10 @@
<value>初始化配置</value>
</data>
<data name="IsLatestCore" xml:space="preserve">
<value>{0} {1} 已是最新版本</value>
<value>{0} {1} 已是最新版本</value>
</data>
<data name="IsLatestN" xml:space="preserve">
<value>{0} {1} 已是最新版本</value>
<value>{0} {1} 已是最新版本</value>
</data>
<data name="LvAddress" xml:space="preserve">
<value>地址</value>
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>传输协议</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>清除原订阅内容</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>下载Core成功</value>
</data>
@@ -277,7 +274,7 @@
<value>请先选择服务器</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>服务器去重完成。原数量: {0},现数量: {1}</value>
<value>服务器去重完成。原数量: {0},现数量: {1}</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>是否确定移除服务器?</value>
@@ -715,9 +712,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号(;)分隔。</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>本地http监听端口</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>显示实时速度(需重启)</value>
</data>
@@ -926,7 +920,7 @@
<value>速度(M/s)</value>
</data>
<data name="FailedToRunCore" xml:space="preserve">
<value>运行Core失败请查看日志</value>
<value>运行 Core 失败,请查看提示信息</value>
</data>
<data name="LvFilter" xml:space="preserve">
<value>别名正则过滤</value>
@@ -1103,7 +1097,7 @@
<value>Reserved(2,3,4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value>
<value>Address(Ipv4,Ipv6)</value>
</data>
<data name="TbPath7" xml:space="preserve">
<value>混淆密码(obfs password)</value>
@@ -1252,9 +1246,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>显示或隐藏主界面</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>您当前运行的是独立包,请手动下载 SelfContained.7z文件解压覆盖!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自定义配置的Socks端口</value>
</data>
@@ -1340,7 +1331,7 @@
<value>请不要使用不安全的HTTP协议订阅地址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安装字体到系统中,重启设置</value>
<value>安装字体到系统中,选择或填入字体名称,重启设置</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否确定退出?</value>
@@ -1369,8 +1360,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>关闭窗口时隐藏至托盘</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>测试时自动分批的每批数量最大1000</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>多线程测试时的并发数量</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:对于下列地址不使用代理配置文件。使用逗号(,)分隔。</value>
@@ -1390,4 +1381,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>复制终端代理命令至剪贴板</value>
</data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>开始对失败部分进行重新测试,剩余 {0} 个。可按 ESC 终止...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>按测试结果</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>按测试结果移除无效</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>移除无效测试结果 {0} 个。</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>跳跃端口范围</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>会覆盖端口,多组时用逗号(,)隔开</value>
</data>
</root>

View File

@@ -175,10 +175,10 @@
<value>初始化設定</value>
</data>
<data name="IsLatestCore" xml:space="preserve">
<value>{0} {1} 已是最新版本</value>
<value>{0} {1} 已是最新版本</value>
</data>
<data name="IsLatestN" xml:space="preserve">
<value>{0} {1} 已是最新版本</value>
<value>{0} {1} 已是最新版本</value>
</data>
<data name="LvAddress" xml:space="preserve">
<value>位址</value>
@@ -210,9 +210,6 @@
<data name="LvTransportProtocol" xml:space="preserve">
<value>傳輸協定</value>
</data>
<data name="MsgClearSubscription" xml:space="preserve">
<value>清除原訂閱內容</value>
</data>
<data name="MsgDownloadV2rayCoreSuccessfully" xml:space="preserve">
<value>下載Core成功</value>
</data>
@@ -277,7 +274,7 @@
<value>請先選擇伺服器</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>伺服器去重完成。原數量: {0},現數量: {1}</value>
<value>伺服器去重完成。原數量: {0},現數量: {1}</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>是否確定移除伺服器?</value>
@@ -716,9 +713,6 @@
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號(;)分隔。</value>
</data>
<data name="TbSettingsHttpPort" xml:space="preserve">
<value>本機HTTP偵聽埠</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>顯示即時速度(需重啟)</value>
</data>
@@ -927,7 +921,7 @@
<value>速度(M/s)</value>
</data>
<data name="FailedToRunCore" xml:space="preserve">
<value>執行Core失敗請查閲日誌</value>
<value>執行Core失敗請查看提示訊息</value>
</data>
<data name="LvFilter" xml:space="preserve">
<value>別名正則過濾</value>
@@ -1133,9 +1127,6 @@
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>顯示或隱藏主介面</value>
</data>
<data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>您目前運行的是獨立包,請手動下載 SelfContained.7z檔案解壓縮覆蓋!</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂設定的Socks連接埠</value>
</data>
@@ -1221,7 +1212,7 @@
<value>請不要使用不安全的HTTP協定訂閱位址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字到系統中,重新啟動設定</value>
<value>安裝字到系統中,選擇或填入字體名稱,重新啟動設定</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value>
@@ -1335,7 +1326,7 @@
<value>Reserved(2,3,4)</value>
</data>
<data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value>
<value>Address(Ipv4,Ipv6)</value>
</data>
<data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>使用系統hosts</value>
@@ -1370,8 +1361,8 @@
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>關閉視窗時隱藏至托盤</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>測試時自動分批的每批數量最大1000</value>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>多執行緒測試時的並發數量</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:對於下列位址不使用代理設定檔,使用逗號(,)分隔。</value>
@@ -1391,4 +1382,22 @@
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>複製終端代理指令至剪貼簿</value>
</data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>開始對失敗部分進行重新測試,剩餘 {0} 個。可按 ESC 終止...</value>
</data>
<data name="menuTestServerResult" xml:space="preserve">
<value>按測試結果</value>
</data>
<data name="menuRemoveInvalidServerResult" xml:space="preserve">
<value>按測試結果移除無效</value>
</data>
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
<value>移除無效測試結果 {0} 個。</value>
</data>
<data name="TbPorts7" xml:space="preserve">
<value>跳躍端口範圍</value>
</data>
<data name="TbPorts7Tips" xml:space="preserve">
<value>會覆蓋端口,多組時用逗號(,)隔開</value>
</data>
</root>

View File

@@ -0,0 +1,118 @@
#!/bin/bash
# Function to set proxy for GNOME
set_gnome_proxy() {
local MODE=$1
local PROXY_IP=$2
local PROXY_PORT=$3
local IGNORE_HOSTS=$4
# Set the proxy mode
gsettings set org.gnome.system.proxy mode "$MODE"
if [ "$MODE" == "manual" ]; then
# List of protocols
local PROTOCOLS=("http" "https" "ftp" "socks")
# Loop through protocols to set the proxy
for PROTOCOL in "${PROTOCOLS[@]}"; do
gsettings set org.gnome.system.proxy.$PROTOCOL host "$PROXY_IP"
gsettings set org.gnome.system.proxy.$PROTOCOL port "$PROXY_PORT"
done
# Set ignored hosts
gsettings set org.gnome.system.proxy ignore-hosts "['$IGNORE_HOSTS']"
echo "GNOME: Manual proxy settings applied."
echo "Proxy IP: $PROXY_IP"
echo "Proxy Port: $PROXY_PORT"
echo "Ignored Hosts: $IGNORE_HOSTS"
elif [ "$MODE" == "none" ]; then
echo "GNOME: Proxy disabled."
else
echo "GNOME: Invalid mode. Use 'none' or 'manual'."
exit 1
fi
}
# Function to set proxy for KDE
set_kde_proxy() {
local MODE=$1
local PROXY_IP=$2
local PROXY_PORT=$3
local IGNORE_HOSTS=$4
# Determine the correct kwriteconfig command based on KDE_SESSION_VERSION
if [ "$KDE_SESSION_VERSION" == "6" ]; then
KWRITECONFIG="kwriteconfig6"
else
KWRITECONFIG="kwriteconfig5"
fi
# KDE uses kwriteconfig to modify proxy settings
if [ "$MODE" == "manual" ]; then
# Set proxy for all protocols
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 1
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpsProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ftpProxy "http://$PROXY_IP:$PROXY_PORT"
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key socksProxy "http://$PROXY_IP:$PROXY_PORT"
# Set ignored hosts
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key NoProxyFor "$IGNORE_HOSTS"
echo "KDE: Manual proxy settings applied."
echo "Proxy IP: $PROXY_IP"
echo "Proxy Port: $PROXY_PORT"
echo "Ignored Hosts: $IGNORE_HOSTS"
elif [ "$MODE" == "none" ]; then
# Disable proxy
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 0
echo "KDE: Proxy disabled."
else
echo "KDE: Invalid mode. Use 'none' or 'manual'."
exit 1
fi
# Apply changes by restarting KDE's network settings
dbus-send --type=signal /KIO/Scheduler org.kde.KIO.Scheduler.reparseSlaveConfiguration string:""
}
# Detect the current desktop environment
detect_desktop_environment() {
if [ "$XDG_CURRENT_DESKTOP" == "GNOME" ] || [ "$XDG_CURRENT_DESKTOP" == "ubuntu:GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "GNOME" ] || [ "$XDG_SESSION_DESKTOP" == "ubuntu:GNOME" ]; then
echo "gnome"
elif [ "$XDG_CURRENT_DESKTOP" == "KDE" ] || [ "$XDG_CURRENT_DESKTOP" == "plasma" ] || [ "$XDG_SESSION_DESKTOP" == "KDE" ] || [ "$XDG_SESSION_DESKTOP" == "plasma" ]; then
echo "kde"
else
echo "unsupported"
fi
}
# Main script logic
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <mode> [proxy_ip proxy_port ignore_hosts]"
echo " mode: 'none' or 'manual'"
echo " If mode is 'manual', provide proxy IP, port, and ignore hosts."
exit 1
fi
# Get the mode
MODE=$1
PROXY_IP=$2
PROXY_PORT=$3
IGNORE_HOSTS=$4
# Detect desktop environment
DE=$(detect_desktop_environment)
# Apply settings based on the desktop environment
if [ "$DE" == "gnome" ]; then
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
elif [ "$DE" == "kde" ]; then
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
set_kde_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
else
echo "Unsupported desktop environment: $DE"
exit 1
fi

View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Function to set proxy
set_proxy() {
PROXY_IP=$1
PROXY_PORT=$2
shift 2
BYPASS_DOMAINS=("$@")
# If no bypass domains are provided, set it to empty by default
if [ ${#BYPASS_DOMAINS[@]} -eq 0 ]; then
BYPASS_DOMAINS=("")
fi
# Get all network service names
SERVICES=$(networksetup -listallnetworkservices | grep -v '*')
# Loop through each network service
echo "$SERVICES" | while read -r SERVICE; do
echo "Setting proxy for network service '$SERVICE'..."
# Set HTTP proxy
networksetup -setwebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set HTTPS proxy
networksetup -setsecurewebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set SOCKS proxy
networksetup -setsocksfirewallproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
# Set bypass domains
networksetup -setproxybypassdomains "$SERVICE" "${BYPASS_DOMAINS[@]}"
echo "Proxy for network service '$SERVICE' has been set to $PROXY_IP:$PROXY_PORT"
done
echo "Proxy settings for all network services are complete!"
}
# Function to disable proxy
clear_proxy() {
# Get all network service names
SERVICES=$(networksetup -listallnetworkservices | grep -v '*')
# Loop through each network service
echo "$SERVICES" | while read -r SERVICE; do
echo "Disabling proxy and clearing bypass domains for network service '$SERVICE'..."
# Disable HTTP proxy
networksetup -setwebproxystate "$SERVICE" off
# Disable HTTPS proxy
networksetup -setsecurewebproxystate "$SERVICE" off
# Disable SOCKS proxy
networksetup -setsocksfirewallproxystate "$SERVICE" off
echo "Proxy for network service '$SERVICE' has been disabled"
done
echo "Proxy for all network services has been disabled!"
}
# Main script logic
if [ "$1" == "set" ]; then
# Check if enough parameters are passed for setting proxy
if [ "$#" -lt 3 ]; then
echo "Usage: $0 set <IP Address> <Port> [Bypass Domain 1 Bypass Domain 2 ...]"
exit 1
fi
set_proxy "$2" "$3" "${@:4}"
elif [ "$1" == "clear" ]; then
clear_proxy
else
echo "Usage:"
echo " To set proxy: $0 set <IP Address> <Port> [Bypass Domain 1 Bypass Domain 2 ...]"
echo " To clear proxy: $0 clear"
exit 1
fi

View File

@@ -1,83 +1,79 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>7.6.2</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Downloader" Version="3.3.1" />
<PackageReference Include="ReactiveUI" Version="20.1.63" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="Splat.NLog" Version="15.2.22" />
<PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
<PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.7.0" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Sample\clash_mixin_yaml" />
<EmbeddedResource Include="Sample\clash_tun_yaml" />
<EmbeddedResource Include="Sample\custom_routing_black" />
<EmbeddedResource Include="Sample\custom_routing_global" />
<EmbeddedResource Include="Sample\custom_routing_white" />
<EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\SampleClientConfig" />
<EmbeddedResource Include="Sample\SampleHttpRequest" />
<EmbeddedResource Include="Sample\SampleHttpResponse" />
<EmbeddedResource Include="Sample\SampleInbound" />
<EmbeddedResource Include="Sample\SampleOutbound" />
<EmbeddedResource Include="Sample\SingboxSampleClientConfig" />
<EmbeddedResource Include="Sample\SingboxSampleOutbound" />
<EmbeddedResource Include="Sample\tun_singbox_dns" />
<EmbeddedResource Include="Sample\tun_singbox_inbound" />
<EmbeddedResource Include="Sample\tun_singbox_rules" />
<EmbeddedResource Include="Sample\linux_autostart_config" />
</ItemGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resx\ResUI.Designer.cs">
<DependentUpon>ResUI.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<PackageReference Include="Downloader" />
<PackageReference Include="ReactiveUI">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="ReactiveUI.Fody" />
<PackageReference Include="sqlite-net-pcl" />
<PackageReference Include="Splat.NLog" />
<PackageReference Include="WebDav.Client" />
<PackageReference Include="YamlDotNet" />
<PackageReference Include="QRCoder" />
<PackageReference Include="CliWrap" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" />
<PackageReference Include="TaskScheduler" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.resx">
<SubType>Designer</SubType>
<LastGenOutput>ResUI.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.ru.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Include="Sample\clash_mixin_yaml" />
<EmbeddedResource Include="Sample\clash_tun_yaml" />
<EmbeddedResource Include="Sample\custom_routing_black" />
<EmbeddedResource Include="Sample\custom_routing_global" />
<EmbeddedResource Include="Sample\custom_routing_white" />
<EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\proxy_set_linux_sh" />
<EmbeddedResource Include="Sample\proxy_set_osx_sh" />
<EmbeddedResource Include="Sample\SampleClientConfig" />
<EmbeddedResource Include="Sample\SampleHttpRequest" />
<EmbeddedResource Include="Sample\SampleHttpResponse" />
<EmbeddedResource Include="Sample\SampleInbound" />
<EmbeddedResource Include="Sample\SampleOutbound" />
<EmbeddedResource Include="Sample\SingboxSampleClientConfig" />
<EmbeddedResource Include="Sample\SingboxSampleOutbound" />
<EmbeddedResource Include="Sample\tun_singbox_dns" />
<EmbeddedResource Include="Sample\tun_singbox_inbound" />
<EmbeddedResource Include="Sample\tun_singbox_rules" />
<EmbeddedResource Include="Sample\linux_autostart_config" />
</ItemGroup>
</Project>
<ItemGroup>
<Compile Update="Resx\ResUI.Designer.cs">
<DependentUpon>ResUI.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.resx">
<SubType>Designer</SubType>
<LastGenOutput>ResUI.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.ru.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -44,8 +44,8 @@ namespace ServiceLib.Services.CoreConfig
File.Delete(fileName);
}
string addressFileName = node.Address;
if (Utils.IsNullOrEmpty(addressFileName))
var addressFileName = node.Address;
if (addressFileName.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -60,9 +60,9 @@ namespace ServiceLib.Services.CoreConfig
return ret;
}
string tagYamlStr1 = "!<str>";
string tagYamlStr2 = "__strn__";
string tagYamlStr3 = "!!str";
var tagYamlStr1 = "!<str>";
var tagYamlStr2 = "__strn__";
var tagYamlStr3 = "!!str";
var txtFile = File.ReadAllText(addressFileName);
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
@@ -116,19 +116,21 @@ namespace ServiceLib.Services.CoreConfig
//enable tun mode
if (_config.TunModeItem.EnableTun)
{
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
if (Utils.IsNotEmpty(tun))
var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml);
if (tun.IsNotEmpty())
{
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
if (tunContent != null)
{
fileContent["tun"] = tunContent["tun"];
}
}
}
//Mixin
try
{
MixinContent(fileContent, node);
await MixinContent(fileContent, node);
}
catch (Exception ex)
{
@@ -158,20 +160,21 @@ namespace ServiceLib.Services.CoreConfig
}
}
private void MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
private async Task MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
{
//if (!_config.clashUIItem.enableMixinContent)
//{
// return;
//}
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
if (!File.Exists(path))
if (!_config.ClashUIItem.EnableMixinContent)
{
return;
}
var txtFile = File.ReadAllText(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
if (!File.Exists(path))
{
var mixin = EmbedUtils.GetEmbedText(Global.ClashMixinYaml);
await File.AppendAllTextAsync(path, mixin);
}
var txtFile = await File.ReadAllTextAsync(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
if (mixinContent == null)
@@ -201,8 +204,8 @@ namespace ServiceLib.Services.CoreConfig
private void ModifyContentMerge(Dictionary<string, object> fileContent, string key, object value)
{
bool blPrepend = false;
bool blRemoved = false;
var blPrepend = false;
var blRemoved = false;
if (key.StartsWith("prepend-"))
{
blPrepend = true;
@@ -243,17 +246,11 @@ namespace ServiceLib.Services.CoreConfig
if (blPrepend)
{
lstValue.Reverse();
foreach (var item in lstValue)
{
lstOri.Insert(0, item);
}
lstValue.ForEach(item => lstOri.Insert(0, item));
}
else
{
foreach (var item in lstValue)
{
lstOri.Add(item);
}
lstValue.ForEach(item => lstOri.Add(item));
}
}
@@ -269,4 +266,4 @@ namespace ServiceLib.Services.CoreConfig
}
}
}
}
}

View File

@@ -35,8 +35,8 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.SingboxSampleClient);
if (Utils.IsNullOrEmpty(result))
string result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
if (result.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -91,9 +91,9 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
var result = Utils.GetEmbedText(Global.SingboxSampleClient);
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -138,7 +138,7 @@ namespace ServiceLib.Services.CoreConfig
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (item is null || Utils.IsNullOrEmpty(item.Id) || !Utils.IsGuidByParse(item.Id))
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
@@ -242,6 +242,66 @@ namespace ServiceLib.Services.CoreConfig
}
}
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
{
var ret = new RetResult();
try
{
if (node is not { Port: > 0 })
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
{
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret;
}
ret.Msg = ResUI.InitialConfiguration;
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
if (result.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
if (singboxConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenLog(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds.First());
await GenMoreOutbounds(node, singboxConfig);
await GenDnsDomains(null, singboxConfig, null);
singboxConfig.route.rules.Clear();
singboxConfig.inbounds.Clear();
singboxConfig.inbounds.Add(new()
{
tag = $"{EInboundProtocol.mixed}{port}",
listen = Global.Loopback,
listen_port = port,
type = EInboundProtocol.mixed.ToString(),
});
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(singboxConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
{
var ret = new RetResult();
@@ -255,9 +315,9 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.SingboxSampleClient);
string txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
string result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -294,7 +354,7 @@ namespace ServiceLib.Services.CoreConfig
}
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (Utils.IsNullOrEmpty(item.Id) || !Utils.IsGuidByParse(item.Id))
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
@@ -383,7 +443,7 @@ namespace ServiceLib.Services.CoreConfig
}
string addressFileName = node.Address;
if (Utils.IsNullOrEmpty(addressFileName))
if (addressFileName.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -500,10 +560,10 @@ namespace ServiceLib.Services.CoreConfig
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
inbound.sniff = _config.Inbound.First().SniffingEnabled;
inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
inbound.domain_strategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (Utils.IsNotEmpty(routing.DomainStrategy4Singbox))
if (routing.DomainStrategy4Singbox.IsNotEmpty())
{
inbound.domain_strategy = routing.DomainStrategy4Singbox;
}
@@ -523,7 +583,7 @@ namespace ServiceLib.Services.CoreConfig
singboxConfig.inbounds.Add(inbound3);
//auth
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
{
inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
}
@@ -539,14 +599,14 @@ namespace ServiceLib.Services.CoreConfig
{
if (_config.TunModeItem.Mtu <= 0)
{
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First());
_config.TunModeItem.Mtu = Global.TunMtus.First();
}
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
if (_config.TunModeItem.Stack.IsNullOrEmpty())
{
_config.TunModeItem.Stack = Global.TunStacks.First();
}
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
@@ -614,8 +674,8 @@ namespace ServiceLib.Services.CoreConfig
case EConfigType.SOCKS:
{
outbound.version = "5";
if (Utils.IsNotEmpty(node.Security)
&& Utils.IsNotEmpty(node.Id))
if (node.Security.IsNotEmpty()
&& node.Id.IsNotEmpty())
{
outbound.username = node.Security;
outbound.password = node.Id;
@@ -624,8 +684,8 @@ namespace ServiceLib.Services.CoreConfig
}
case EConfigType.HTTP:
{
if (Utils.IsNotEmpty(node.Security)
&& Utils.IsNotEmpty(node.Id))
if (node.Security.IsNotEmpty()
&& node.Id.IsNotEmpty())
{
outbound.username = node.Security;
outbound.password = node.Id;
@@ -638,7 +698,7 @@ namespace ServiceLib.Services.CoreConfig
outbound.packet_encoding = "xudp";
if (Utils.IsNullOrEmpty(node.Flow))
if (node.Flow.IsNullOrEmpty())
{
await GenOutboundMux(node, outbound);
}
@@ -659,7 +719,7 @@ namespace ServiceLib.Services.CoreConfig
{
outbound.password = node.Id;
if (Utils.IsNotEmpty(node.Path))
if (node.Path.IsNotEmpty())
{
outbound.obfs = new()
{
@@ -670,6 +730,16 @@ namespace ServiceLib.Services.CoreConfig
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
if (node.Ports.IsNotEmpty())
{
outbound.server_port = null;
outbound.server_ports = node.Ports.Split(',')
.Where(p => p.Trim().IsNotEmpty())
.Select(p => p.Replace('-', ':'))
.ToList();
outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null;
}
break;
}
case EConfigType.TUIC:
@@ -685,7 +755,7 @@ namespace ServiceLib.Services.CoreConfig
outbound.peer_public_key = node.PublicKey;
outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList();
outbound.local_address = Utils.String2List(node.RequestHost);
outbound.mtu = Utils.ToInt(node.ShortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.ShortId);
outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
break;
}
}
@@ -705,7 +775,7 @@ namespace ServiceLib.Services.CoreConfig
{
try
{
if (_config.CoreBasicItem.MuxEnabled && Utils.IsNotEmpty(_config.Mux4SboxItem.Protocol))
if (_config.CoreBasicItem.MuxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
{
var mux = new Multiplex4Sbox()
{
@@ -731,11 +801,11 @@ namespace ServiceLib.Services.CoreConfig
if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
{
var server_name = string.Empty;
if (Utils.IsNotEmpty(node.Sni))
if (node.Sni.IsNotEmpty())
{
server_name = node.Sni;
}
else if (Utils.IsNotEmpty(node.RequestHost))
else if (node.RequestHost.IsNotEmpty())
{
server_name = Utils.String2List(node.RequestHost)?.First();
}
@@ -746,7 +816,7 @@ namespace ServiceLib.Services.CoreConfig
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(),
};
if (Utils.IsNotEmpty(node.Fingerprint))
if (node.Fingerprint.IsNotEmpty())
{
tls.utls = new Utls4Sbox()
{
@@ -784,8 +854,8 @@ namespace ServiceLib.Services.CoreConfig
{
case nameof(ETransport.h2):
transport.type = nameof(ETransport.http);
transport.host = Utils.IsNullOrEmpty(node.RequestHost) ? null : Utils.String2List(node.RequestHost);
transport.path = Utils.IsNullOrEmpty(node.Path) ? null : node.Path;
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
break;
case nameof(ETransport.tcp): //http
@@ -799,16 +869,16 @@ namespace ServiceLib.Services.CoreConfig
else
{
transport.type = nameof(ETransport.http);
transport.host = Utils.IsNullOrEmpty(node.RequestHost) ? null : Utils.String2List(node.RequestHost);
transport.path = Utils.IsNullOrEmpty(node.Path) ? null : node.Path;
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
}
}
break;
case nameof(ETransport.ws):
transport.type = nameof(ETransport.ws);
transport.path = Utils.IsNullOrEmpty(node.Path) ? null : node.Path;
if (Utils.IsNotEmpty(node.RequestHost))
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
if (node.RequestHost.IsNotEmpty())
{
transport.headers = new()
{
@@ -819,8 +889,8 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.httpupgrade):
transport.type = nameof(ETransport.httpupgrade);
transport.path = Utils.IsNullOrEmpty(node.Path) ? null : node.Path;
transport.host = Utils.IsNullOrEmpty(node.RequestHost) ? null : node.RequestHost;
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost;
break;
@@ -867,7 +937,7 @@ namespace ServiceLib.Services.CoreConfig
//current proxy
var outbound = singboxConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
//Previous proxy
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
@@ -934,7 +1004,7 @@ namespace ServiceLib.Services.CoreConfig
{
singboxConfig.route.auto_detect_interface = true;
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(Utils.GetEmbedText(Global.TunSingboxRulesFileName));
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
if (tunRules != null)
{
singboxConfig.route.rules.AddRange(tunRules);
@@ -1015,7 +1085,7 @@ namespace ServiceLib.Services.CoreConfig
outbound = item.OutboundTag,
};
if (Utils.IsNotEmpty(item.Port))
if (item.Port.IsNotEmpty())
{
if (item.Port.Contains("-"))
{
@@ -1023,10 +1093,10 @@ namespace ServiceLib.Services.CoreConfig
}
else
{
rule.port = new List<int> { Utils.ToInt(item.Port) };
rule.port = new List<int> { item.Port.ToInt() };
}
}
if (Utils.IsNotEmpty(item.Network))
if (item.Network.IsNotEmpty())
{
rule.network = Utils.String2List(item.Network);
}
@@ -1048,7 +1118,8 @@ namespace ServiceLib.Services.CoreConfig
var countDomain = 0;
foreach (var it in item.Domain)
{
if (ParseV2Domain(it, rule1)) countDomain++;
if (ParseV2Domain(it, rule1))
countDomain++;
}
if (countDomain > 0)
{
@@ -1062,7 +1133,8 @@ namespace ServiceLib.Services.CoreConfig
var countIp = 0;
foreach (var it in item.Ip)
{
if (ParseV2Address(it, rule2)) countIp++;
if (ParseV2Address(it, rule2))
countIp++;
}
if (countIp > 0)
{
@@ -1148,12 +1220,14 @@ namespace ServiceLib.Services.CoreConfig
}
else if (address.StartsWith("geoip:"))
{
if (rule.geoip is null) { rule.geoip = new(); }
if (rule.geoip is null)
{ rule.geoip = new(); }
rule.geoip?.Add(address.Substring(6));
}
else
{
if (rule.ip_cidr is null) { rule.ip_cidr = new(); }
if (rule.ip_cidr is null)
{ rule.ip_cidr = new(); }
rule.ip_cidr?.Add(address);
}
return true;
@@ -1167,11 +1241,11 @@ namespace ServiceLib.Services.CoreConfig
var strDNS = string.Empty;
if (_config.TunModeItem.EnableTun)
{
strDNS = Utils.IsNullOrEmpty(item?.TunDNS) ? Utils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
}
else
{
strDNS = Utils.IsNullOrEmpty(item?.NormalDNS) ? Utils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.NormalDNS;
strDNS = string.IsNullOrEmpty(item?.NormalDNS) ? EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.NormalDNS;
}
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
@@ -1200,9 +1274,9 @@ namespace ServiceLib.Services.CoreConfig
dns4Sbox.servers.Add(new()
{
tag = tag,
address = Utils.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
detour = Global.DirectTag,
strategy = Utils.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom,
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom,
});
dns4Sbox.rules.Insert(0, new()
{
@@ -1216,7 +1290,7 @@ namespace ServiceLib.Services.CoreConfig
});
var lstDomain = singboxConfig.outbounds
.Where(t => Utils.IsNotEmpty(t.server) && Utils.IsDomain(t.server))
.Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server))
.Select(t => t.server)
.Distinct()
.ToList();
@@ -1260,7 +1334,7 @@ namespace ServiceLib.Services.CoreConfig
singboxConfig.experimental.cache_file = new CacheFile4Sbox()
{
enabled = true,
path = Utils.GetConfigPath("cache.db")
path = Utils.GetBinPath("cache.db")
};
}
@@ -1271,7 +1345,8 @@ namespace ServiceLib.Services.CoreConfig
{
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
{
if (rule_set != null) ruleSets.AddRange(rule_set);
if (rule_set != null)
ruleSets.AddRange(rule_set);
}
var geosite = "geosite";
var geoip = "geoip";
@@ -1319,10 +1394,10 @@ namespace ServiceLib.Services.CoreConfig
List<Ruleset4Sbox> customRulesets = [];
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (Utils.IsNotEmpty(routing.CustomRulesetPath4Singbox))
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
{
var result = Utils.LoadResource(routing.CustomRulesetPath4Singbox);
if (Utils.IsNotEmpty(result))
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
if (result.IsNotEmpty())
{
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
.Where(t => t.tag != null)
@@ -1339,7 +1414,8 @@ namespace ServiceLib.Services.CoreConfig
singboxConfig.route.rule_set = [];
foreach (var item in new HashSet<string>(ruleSets))
{
if (Utils.IsNullOrEmpty(item)) { continue; }
if (item.IsNullOrEmpty())
{ continue; }
var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item));
if (customRuleset is null)
{
@@ -1378,4 +1454,4 @@ namespace ServiceLib.Services.CoreConfig
#endregion private gen function
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using System.Net.NetworkInformation;
using System.Text.Json.Nodes;
@@ -36,8 +36,8 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
var result = Utils.GetEmbedText(Global.V2raySampleClient);
if (Utils.IsNullOrEmpty(result))
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (result.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -91,9 +91,9 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -135,7 +135,7 @@ namespace ServiceLib.Services.CoreConfig
}
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (Utils.IsNullOrEmpty(item.Id) || !Utils.IsGuidByParse(item.Id))
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
@@ -214,9 +214,9 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration;
var result = Utils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
@@ -261,7 +261,7 @@ namespace ServiceLib.Services.CoreConfig
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
{
if (item is null || Utils.IsNullOrEmpty(item.Id) || !Utils.IsGuidByParse(item.Id))
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
{
continue;
}
@@ -353,6 +353,64 @@ namespace ServiceLib.Services.CoreConfig
}
}
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
{
var ret = new RetResult();
try
{
if (node is not { Port: > 0 })
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (node.GetNetwork() is nameof(ETransport.quic))
{
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret;
}
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
if (result.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
return ret;
}
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
if (v2rayConfig == null)
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
await GenLog(v2rayConfig);
await GenOutbound(node, v2rayConfig.outbounds.First());
await GenMoreOutbounds(node, v2rayConfig);
v2rayConfig.routing.rules.Clear();
v2rayConfig.inbounds.Clear();
v2rayConfig.inbounds.Add(new()
{
tag = $"{EInboundProtocol.socks}{port}",
listen = Global.Loopback,
port = port,
protocol = EInboundProtocol.socks.ToString(),
});
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
ret.Data = JsonUtils.Serialize(v2rayConfig);
return ret;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
#endregion public gen function
#region private gen function
@@ -407,7 +465,7 @@ namespace ServiceLib.Services.CoreConfig
v2rayConfig.inbounds.Add(inbound3);
//auth
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
{
inbound3.settings.auth = "password";
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
@@ -428,8 +486,8 @@ namespace ServiceLib.Services.CoreConfig
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{
string result = Utils.GetEmbedText(Global.V2raySampleInbound);
if (Utils.IsNullOrEmpty(result))
string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
if (result.IsNullOrEmpty())
{
return new();
}
@@ -457,12 +515,12 @@ namespace ServiceLib.Services.CoreConfig
if (v2rayConfig.routing?.rules != null)
{
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainMatcher) ? null : _config.RoutingBasicItem.DomainMatcher;
v2rayConfig.routing.domainMatcher = _config.RoutingBasicItem.DomainMatcher.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainMatcher;
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null)
{
if (Utils.IsNotEmpty(routing.DomainStrategy))
if (routing.DomainStrategy.IsNotEmpty())
{
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
}
@@ -493,11 +551,11 @@ namespace ServiceLib.Services.CoreConfig
{
return 0;
}
if (Utils.IsNullOrEmpty(rule.port))
if (rule.port.IsNullOrEmpty())
{
rule.port = null;
}
if (Utils.IsNullOrEmpty(rule.network))
if (rule.network.IsNullOrEmpty())
{
rule.network = null;
}
@@ -545,7 +603,7 @@ namespace ServiceLib.Services.CoreConfig
}
if (!hasDomainIp)
{
if (Utils.IsNotEmpty(rule.port)
if (rule.port.IsNotEmpty()
|| rule.protocol?.Count > 0
|| rule.inboundTag?.Count > 0
)
@@ -656,8 +714,8 @@ namespace ServiceLib.Services.CoreConfig
serversItem.method = null;
serversItem.password = null;
if (Utils.IsNotEmpty(node.Security)
&& Utils.IsNotEmpty(node.Id))
if (node.Security.IsNotEmpty()
&& node.Id.IsNotEmpty())
{
SocksUsersItem4Ray socksUsersItem = new()
{
@@ -810,11 +868,11 @@ namespace ServiceLib.Services.CoreConfig
alpn = node.GetAlpn(),
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
};
if (Utils.IsNotEmpty(sni))
if (sni.IsNotEmpty())
{
tlsSettings.serverName = sni;
}
else if (Utils.IsNotEmpty(host))
else if (host.IsNotEmpty())
{
tlsSettings.serverName = Utils.String2List(host)?.First();
}
@@ -857,9 +915,10 @@ namespace ServiceLib.Services.CoreConfig
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
kcpSettings.header = new Header4Ray
{
type = node.HeaderType
type = node.HeaderType,
domain = host.IsNullOrEmpty() ? null : host
};
if (Utils.IsNotEmpty(path))
if (path.IsNotEmpty())
{
kcpSettings.seed = path;
}
@@ -870,16 +929,16 @@ namespace ServiceLib.Services.CoreConfig
WsSettings4Ray wsSettings = new();
wsSettings.headers = new Headers4Ray();
if (Utils.IsNotEmpty(host))
if (host.IsNotEmpty())
{
wsSettings.host = host;
wsSettings.headers.Host = host;
}
if (Utils.IsNotEmpty(path))
if (path.IsNotEmpty())
{
wsSettings.path = path;
}
if (Utils.IsNotEmpty(useragent))
if (useragent.IsNotEmpty())
{
wsSettings.headers.UserAgent = useragent;
}
@@ -890,11 +949,11 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.httpupgrade):
HttpupgradeSettings4Ray httpupgradeSettings = new();
if (Utils.IsNotEmpty(path))
if (path.IsNotEmpty())
{
httpupgradeSettings.path = path;
}
if (Utils.IsNotEmpty(host))
if (host.IsNotEmpty())
{
httpupgradeSettings.host = host;
}
@@ -906,19 +965,19 @@ namespace ServiceLib.Services.CoreConfig
streamSettings.network = ETransport.xhttp.ToString();
XhttpSettings4Ray xhttpSettings = new();
if (Utils.IsNotEmpty(path))
if (path.IsNotEmpty())
{
xhttpSettings.path = path;
}
if (Utils.IsNotEmpty(host))
if (host.IsNotEmpty())
{
xhttpSettings.host = host;
}
if (Utils.IsNotEmpty(node.HeaderType) && Global.XhttpMode.Contains(node.HeaderType))
if (node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(node.HeaderType))
{
xhttpSettings.mode = node.HeaderType;
}
if (Utils.IsNotEmpty(node.Extra))
if (node.Extra.IsNotEmpty())
{
xhttpSettings.extra = JsonUtils.ParseJson(node.Extra);
}
@@ -931,7 +990,7 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.h2):
HttpSettings4Ray httpSettings = new();
if (Utils.IsNotEmpty(host))
if (host.IsNotEmpty())
{
httpSettings.host = Utils.String2List(host);
}
@@ -954,7 +1013,7 @@ namespace ServiceLib.Services.CoreConfig
streamSettings.quicSettings = quicsettings;
if (node.StreamSecurity == Global.StreamSecurity)
{
if (Utils.IsNotEmpty(sni))
if (sni.IsNotEmpty())
{
streamSettings.tlsSettings.serverName = sni;
}
@@ -968,7 +1027,7 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.grpc):
GrpcSettings4Ray grpcSettings = new()
{
authority = Utils.IsNullOrEmpty(host) ? null : host,
authority = host.IsNullOrEmpty() ? null : host,
serviceName = path,
multiMode = node.HeaderType == Global.GrpcMultiMode,
idle_timeout = _config.GrpcItem.IdleTimeout,
@@ -992,14 +1051,14 @@ namespace ServiceLib.Services.CoreConfig
};
//request Host
string request = Utils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
string[] arrHost = host.Split(',');
string host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path
string pathHttp = @"/";
if (Utils.IsNotEmpty(path))
if (path.IsNotEmpty())
{
string[] arrPath = path.Split(',');
pathHttp = string.Join(",".AppendQuotes(), arrPath);
@@ -1026,13 +1085,13 @@ namespace ServiceLib.Services.CoreConfig
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
var normalDNS = item?.NormalDNS;
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
if (Utils.IsNullOrEmpty(normalDNS))
if (normalDNS.IsNullOrEmpty())
{
normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName);
normalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
}
//Outbound Freedom domainStrategy
if (Utils.IsNotEmpty(domainStrategy4Freedom))
if (domainStrategy4Freedom.IsNotEmpty())
{
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
if (outbound != null)
@@ -1097,7 +1156,7 @@ namespace ServiceLib.Services.CoreConfig
{
var dnsServer = new DnsServer4Ray()
{
address = Utils.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
domains = [node.Address]
};
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
@@ -1157,7 +1216,7 @@ namespace ServiceLib.Services.CoreConfig
{
//fragment proxy
if (_config.CoreBasicItem.EnableFragment
&& Utils.IsNotEmpty(v2rayConfig.outbounds.First().streamSettings?.security))
&& v2rayConfig.outbounds.First().streamSettings?.security.IsNullOrEmpty() == false)
{
var fragmentOutbound = new Outbounds4Ray
{
@@ -1196,7 +1255,7 @@ namespace ServiceLib.Services.CoreConfig
//current proxy
var outbound = v2rayConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
//Previous proxy
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
@@ -1247,4 +1306,4 @@ namespace ServiceLib.Services.CoreConfig
#endregion private gen function
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Sockets;
@@ -23,14 +23,7 @@ namespace ServiceLib.Services
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var progress = new Progress<string>();
progress.ProgressChanged += (sender, value) =>
{
if (updateFunc != null)
{
string msg = $"{value}";
updateFunc?.Invoke(false, msg);
}
};
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy,
url,
@@ -56,10 +49,7 @@ namespace ServiceLib.Services
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
var progress = new Progress<double>();
progress.ProgressChanged += (sender, value) =>
{
UpdateCompleted?.Invoke(this, new RetResult(value > 100, $"...{value}%"));
};
progress.ProgressChanged += (sender, value) => UpdateCompleted?.Invoke(this, new RetResult(value > 100, $"...{value}%"));
var webProxy = await GetWebProxy(blProxy);
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
@@ -108,7 +98,7 @@ namespace ServiceLib.Services
try
{
var result1 = await DownloadStringAsync(url, blProxy, userAgent, 15);
if (Utils.IsNotEmpty(result1))
if (result1.IsNotEmpty())
{
return result1;
}
@@ -126,7 +116,7 @@ namespace ServiceLib.Services
try
{
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent, 15);
if (Utils.IsNotEmpty(result2))
if (result2.IsNotEmpty())
{
return result2;
}
@@ -160,7 +150,7 @@ namespace ServiceLib.Services
UseProxy = webProxy != null
});
if (Utils.IsNullOrEmpty(userAgent))
if (userAgent.IsNullOrEmpty())
{
userAgent = Utils.GetVersion(false);
}
@@ -168,7 +158,7 @@ namespace ServiceLib.Services
Uri uri = new(url);
//Authorization Header
if (Utils.IsNotEmpty(uri.UserInfo))
if (uri.UserInfo.IsNotEmpty())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
}
@@ -201,7 +191,7 @@ namespace ServiceLib.Services
var webProxy = await GetWebProxy(blProxy);
if (Utils.IsNullOrEmpty(userAgent))
if (userAgent.IsNullOrEmpty())
{
userAgent = Utils.GetVersion(false);
}
@@ -222,20 +212,20 @@ namespace ServiceLib.Services
public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy)
{
var responseTime = -1;
try
{
webProxy ??= await GetWebProxy(true);
var config = AppHandler.Instance.Config;
try
for (var i = 0; i < 2; i++)
{
var config = AppHandler.Instance.Config;
var responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
return responseTime;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return -1;
responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
if (responseTime > 0)
{
break;
}
await Task.Delay(500);
}
}
catch (Exception ex)
@@ -243,6 +233,7 @@ namespace ServiceLib.Services
Logging.SaveLog(_tag, ex);
return -1;
}
return responseTime;
}
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
@@ -262,7 +253,7 @@ namespace ServiceLib.Services
for (var i = 0; i < 2; i++)
{
var timer = Stopwatch.StartNew();
await client.GetAsync(url, cts.Token);
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
timer.Stop();
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
await Task.Delay(100);
@@ -319,4 +310,4 @@ namespace ServiceLib.Services
ServicePointManager.DefaultConnectionLimit = 256;
}
}
}
}

View File

@@ -1,4 +1,4 @@
using ReactiveUI;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
@@ -7,17 +7,66 @@ namespace ServiceLib.Services
{
public class SpeedtestService
{
private static readonly string _tag = "SpeedtestService";
private Config? _config;
private Action<SpeedTestResult>? _updateFunc;
private static readonly ConcurrentBag<string> _lstExitLoop = new();
private bool _exitLoop = false;
private static readonly string _tag = "SpeedtestService";
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
public SpeedtestService(Config config, Action<SpeedTestResult> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
}
public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
Task.Run(async () =>
{
await RunAsync(actionType, selecteds);
await ProfileExHandler.Instance.SaveTo();
UpdateFunc("", ResUI.SpeedtestingCompleted);
});
}
public void ExitLoop()
{
if (_lstExitLoop.Count > 0)
{
UpdateFunc("", ResUI.SpeedtestingStop);
_lstExitLoop.Clear();
}
}
private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
var exitLoopKey = Utils.GetGuid(false);
_lstExitLoop.Add(exitLoopKey);
var lstSelected = GetClearItem(actionType, selecteds);
switch (actionType)
{
case ESpeedActionType.Tcping:
await RunTcpingAsync(lstSelected);
break;
case ESpeedActionType.Realping:
await RunRealPingBatchAsync(lstSelected, exitLoopKey);
break;
case ESpeedActionType.Speedtest:
await RunMixedTestAsync(lstSelected, 1, true, exitLoopKey);
break;
case ESpeedActionType.Mixedtest:
await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, true, exitLoopKey);
break;
}
}
private List<ServerTestItem> GetClearItem(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
var lstSelected = new List<ServerTestItem>();
foreach (var it in selecteds)
{
@@ -25,16 +74,19 @@ namespace ServiceLib.Services
{
continue;
}
if (it.Port <= 0)
{
continue;
}
lstSelected.Add(new ServerTestItem()
{
IndexId = it.IndexId,
Address = it.Address,
Port = it.Port,
ConfigType = it.ConfigType
ConfigType = it.ConfigType,
QueueNum = selecteds.IndexOf(it)
});
}
@@ -46,124 +98,95 @@ namespace ServiceLib.Services
case ESpeedActionType.Tcping:
case ESpeedActionType.Realping:
UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0");
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
break;
case ESpeedActionType.Speedtest:
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0");
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
break;
case ESpeedActionType.Mixedtest:
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, "0");
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "0");
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
break;
}
}
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
}
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
{
if (actionType == ESpeedActionType.Tcping)
{
await RunTcpingAsync(lstSelected);
return;
}
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
if (pageSize is <= 0 or > 1000)
{
pageSize = 1000;
}
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
foreach (var lst in lstTest)
{
switch (actionType)
{
case ESpeedActionType.Realping:
await RunRealPingAsync(lst);
break;
case ESpeedActionType.Speedtest:
await RunSpeedTestAsync(lst);
break;
case ESpeedActionType.Mixedtest:
await RunMixedTestAsync(lst);
break;
}
await Task.Delay(100);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
}
private void ExitLoop(string x)
{
if (_exitLoop) return;
_exitLoop = true;
UpdateFunc("", ResUI.SpeedtestingStop);
return lstSelected;
}
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
{
try
List<Task> tasks = [];
foreach (var it in selecteds)
{
List<Task> tasks = [];
foreach (var it in selecteds)
if (it.ConfigType == EConfigType.Custom)
{
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
tasks.Add(Task.Run(async () =>
{
try
{
var time = await GetTcpingTime(it.Address, it.Port);
var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}));
continue;
}
Task.WaitAll([.. tasks]);
tasks.Add(Task.Run(async () =>
{
try
{
var responseTime = await GetTcpingTime(it.Address, it.Port);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString());
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}));
}
catch (Exception ex)
await Task.WhenAll(tasks);
}
private async Task RunRealPingBatchAsync(List<ServerTestItem> lstSelected, string exitLoopKey, int pageSize = 0)
{
if (pageSize <= 0)
{
Logging.SaveLog(_tag, ex);
pageSize = lstSelected.Count < Global.SpeedTestPageSize ? lstSelected.Count : Global.SpeedTestPageSize;
}
finally
var lstTest = GetTestBatchItem(lstSelected, pageSize);
List<ServerTestItem> lstFailed = new();
foreach (var lst in lstTest)
{
await ProfileExHandler.Instance.SaveTo();
var ret = await RunRealPingAsync(lst, exitLoopKey);
if (ret == false)
{
lstFailed.AddRange(lst);
}
await Task.Delay(100);
}
//Retest the failed part
var pageSizeNext = pageSize / 2;
if (lstFailed.Count > 0 && pageSizeNext > 0)
{
if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{
UpdateFunc("", ResUI.SpeedtestingSkip);
return;
}
UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count));
if (pageSizeNext > _config.SpeedTestItem.MixedConcurrencyCount)
{
await RunRealPingBatchAsync(lstFailed, exitLoopKey, pageSizeNext);
}
else
{
await RunMixedTestAsync(lstSelected, _config.SpeedTestItem.MixedConcurrencyCount, false, exitLoopKey);
}
}
}
private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds, string exitLoopKey)
{
var pid = -1;
try
@@ -171,8 +194,7 @@ namespace ServiceLib.Services
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
return false;
}
var downloadHandle = new DownloadService();
@@ -190,23 +212,10 @@ namespace ServiceLib.Services
}
tasks.Add(Task.Run(async () =>
{
try
{
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output);
int.TryParse(output, out var delay);
it.Delay = delay;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await DoRealPing(downloadHandle, it);
}));
}
Task.WaitAll(tasks.ToArray());
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
@@ -218,149 +227,98 @@ namespace ServiceLib.Services
{
await ProcUtils.ProcessKill(pid);
}
await ProfileExHandler.Instance.SaveTo();
}
return true;
}
private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds, int concurrencyCount, bool blSpeedTest, string exitLoopKey)
{
var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new();
using var concurrencySemaphore = new SemaphoreSlim(concurrencyCount);
var downloadHandle = new DownloadService();
List<Task> tasks = new();
foreach (var it in selecteds)
{
if (_exitLoop)
if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
if (!it.AllowTest)
{
continue;
}
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
//if (it.delay < 0)
//{
// UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
// continue;
//}
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
await concurrencySemaphore.WaitAsync();
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue;
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
tasks.Add(Task.Run(async () =>
{
decimal.TryParse(msg, out var dec);
if (dec > 0)
var pid = -1;
try
{
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
if (pid > 0)
{
await Task.Delay(500);
var delay = await DoRealPing(downloadHandle, it);
if (blSpeedTest)
{
if (delay > 0)
{
await DoSpeedTest(downloadHandle, it);
}
else
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
}
}
}
else
{
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
}
}
UpdateFunc(it.IndexId, "", msg);
});
}
if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
await ProfileExHandler.Instance.SaveTo();
}
private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
{
var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0)
{
UpdateFunc("", ResUI.FailedToRunCore);
return;
}
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new();
foreach (var it in selecteds)
{
if (_exitLoop)
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
if (!it.AllowTest)
{
continue;
}
if (it.ConfigType == EConfigType.Custom)
{
continue;
}
if (it.Delay < 0)
{
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue;
}
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, "-1");
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue;
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{
decimal.TryParse(msg, out var dec);
if (dec > 0)
catch (Exception ex)
{
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
Logging.SaveLog(_tag, ex);
}
UpdateFunc(it.IndexId, "", msg);
});
await Task.Delay(2000);
finally
{
if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
concurrencySemaphore.Release();
}
}));
}
await Task.Delay((timeout + 2) * 1000);
if (pid > 0)
{
await ProcUtils.ProcessKill(pid);
}
await ProfileExHandler.Instance.SaveTo();
await Task.WhenAll(tasks);
}
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
{
await RunRealPingAsync(selecteds);
await Task.Delay(1000);
await RunSpeedTestMulti(selecteds);
}
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
private async Task<int> DoRealPing(DownloadService downloadHandle, ServerTestItem it)
{
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
return FormatOut(responseTime, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString());
return responseTime;
}
private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it)
{
UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{
decimal.TryParse(msg, out var dec);
if (dec > 0)
{
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, dec);
}
UpdateFunc(it.IndexId, "", msg);
});
}
private async Task<int> GetTcpingTime(string url, int port)
@@ -375,18 +333,19 @@ namespace ServiceLib.Services
ipAddress = ipHostInfo.AddressList.First();
}
var timer = Stopwatch.StartNew();
IPEndPoint endPoint = new(ipAddress, port);
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
var timer = Stopwatch.StartNew();
var result = clientSocket.BeginConnect(endPoint, null, null);
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException("connect timeout (5s): " + url);
clientSocket.EndConnect(result);
}
timer.Stop();
responseTime = (int)timer.Elapsed.TotalMilliseconds;
clientSocket.EndConnect(result);
}
catch (Exception ex)
{
@@ -395,14 +354,31 @@ namespace ServiceLib.Services
return responseTime;
}
private string FormatOut(object time, string unit)
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
{
return $"{time}";
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
return lstTest;
}
private void UpdateFunc(string indexId, string delay, string speed = "")
{
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
if (indexId.IsNotEmpty() && speed.IsNotEmpty())
{
ProfileExHandler.Instance.SetTestMessage(indexId, speed);
}
}
}
}
}

View File

@@ -1,11 +1,11 @@
using System.Net.WebSockets;
using System.Net.WebSockets;
using System.Text;
namespace ServiceLib.Services.Statistics
{
public class StatisticsSingboxService
{
private Config _config;
private readonly Config _config;
private bool _exitFlag;
private ClientWebSocket? webSocket;
private Action<ServerSpeedItem>? _updateFunc;
@@ -18,10 +18,10 @@ namespace ServiceLib.Services.Statistics
_updateFunc = updateFunc;
_exitFlag = false;
Task.Run(Run);
_ = Task.Run(Run);
}
private async void Init()
private async Task Init()
{
await Task.Delay(5000);
@@ -53,9 +53,9 @@ namespace ServiceLib.Services.Statistics
}
}
private async void Run()
private async Task Run()
{
Init();
await Init();
while (!_exitFlag)
{
@@ -68,12 +68,11 @@ namespace ServiceLib.Services.Statistics
}
if (webSocket != null)
{
if (webSocket.State == WebSocketState.Aborted
|| webSocket.State == WebSocketState.Closed)
if (webSocket.State is WebSocketState.Aborted or WebSocketState.Closed)
{
webSocket.Abort();
webSocket = null;
Init();
await Init();
continue;
}
@@ -87,9 +86,9 @@ namespace ServiceLib.Services.Statistics
while (!res.CloseStatus.HasValue)
{
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
if (Utils.IsNotEmpty(result))
if (result.IsNotEmpty())
{
ParseOutput(result, out ulong up, out ulong down);
ParseOutput(result, out var up, out var down);
_updateFunc?.Invoke(new ServerSpeedItem()
{
@@ -109,7 +108,8 @@ namespace ServiceLib.Services.Statistics
private void ParseOutput(string source, out ulong up, out ulong down)
{
up = 0; down = 0;
up = 0;
down = 0;
try
{
var trafficItem = JsonUtils.Deserialize<TrafficItem>(source);
@@ -124,4 +124,4 @@ namespace ServiceLib.Services.Statistics
}
}
}
}
}

View File

@@ -1,10 +1,10 @@
namespace ServiceLib.Services.Statistics
namespace ServiceLib.Services.Statistics
{
public class StatisticsXrayService
{
private const long linkBase = 1024;
private ServerSpeedItem _serverSpeedItem = new();
private Config _config;
private readonly Config _config;
private bool _exitFlag;
private Action<ServerSpeedItem>? _updateFunc;
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
@@ -15,7 +15,7 @@
_updateFunc = updateFunc;
_exitFlag = false;
Task.Run(Run);
_ = Task.Run(Run);
}
public void Close()
@@ -23,7 +23,7 @@
_exitFlag = true;
}
private async void Run()
private async Task Run()
{
while (!_exitFlag)
{
@@ -60,10 +60,13 @@
}
ServerSpeedItem server = new();
foreach (string key in source.stats.outbound.Keys)
foreach (var key in source.stats.outbound.Keys.Cast<string>())
{
var value = source.stats.outbound[key];
if (value == null) continue;
if (value == null)
{
continue;
}
var state = JsonUtils.Deserialize<V2rayMetricsVarsLink>(value.ToString());
if (key.StartsWith(Global.ProxyTag))
@@ -102,4 +105,4 @@
return null;
}
}
}
}

View File

@@ -123,7 +123,7 @@ namespace ServiceLib.Services
var url = item.Url.TrimEx();
var userAgent = item.UserAgent.TrimEx();
var hashCode = $"{item.Remarks}->";
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (Utils.IsNotEmpty(subId) && item.Id != subId))
if (id.IsNullOrEmpty() || url.IsNullOrEmpty() || (subId.IsNotEmpty() && item.Id != subId))
{
//_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
continue;
@@ -149,9 +149,9 @@ namespace ServiceLib.Services
//one url
url = Utils.GetPunycode(url);
//convert
if (Utils.IsNotEmpty(item.ConvertTarget))
if (item.ConvertTarget.IsNotEmpty())
{
var subConvertUrl = Utils.IsNullOrEmpty(config.ConstItem.SubConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.ConstItem.SubConvertUrl;
var subConvertUrl = config.ConstItem.SubConvertUrl.IsNullOrEmpty() ? Global.SubConvertUrls.FirstOrDefault() : config.ConstItem.SubConvertUrl;
url = string.Format(subConvertUrl!, Utils.UrlEncode(url));
if (!url.Contains("target="))
{
@@ -163,15 +163,15 @@ namespace ServiceLib.Services
}
}
var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent);
if (blProxy && Utils.IsNullOrEmpty(result))
if (blProxy && result.IsNullOrEmpty())
{
result = await downloadHandle.TryDownloadString(url, false, userAgent);
}
//more url
if (Utils.IsNullOrEmpty(item.ConvertTarget) && Utils.IsNotEmpty(item.MoreUrl.TrimEx()))
if (item.ConvertTarget.IsNullOrEmpty() && item.MoreUrl.TrimEx().IsNotEmpty())
{
if (Utils.IsNotEmpty(result) && Utils.IsBase64String(result))
if (result.IsNotEmpty() && Utils.IsBase64String(result))
{
result = Utils.Base64Decode(result);
}
@@ -180,17 +180,17 @@ namespace ServiceLib.Services
foreach (var it in lstUrl)
{
var url2 = Utils.GetPunycode(it);
if (Utils.IsNullOrEmpty(url2))
if (url2.IsNullOrEmpty())
{
continue;
}
var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent);
if (blProxy && Utils.IsNullOrEmpty(result2))
if (blProxy && result2.IsNullOrEmpty())
{
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
}
if (Utils.IsNotEmpty(result2))
if (result2.IsNotEmpty())
{
if (Utils.IsBase64String(result2))
{
@@ -204,7 +204,7 @@ namespace ServiceLib.Services
}
}
if (Utils.IsNullOrEmpty(result))
if (result.IsNullOrEmpty())
{
_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
}
@@ -237,13 +237,13 @@ namespace ServiceLib.Services
public async Task UpdateGeoFileAll(Config config, Action<bool, string> updateFunc)
{
await UpdateGeoFile("geosite", config, updateFunc);
await UpdateGeoFile("geoip", config, updateFunc);
await UpdateGeoFiles(config, updateFunc);
await UpdateOtherFiles(config, updateFunc);
await UpdateSrsFileAll(config, updateFunc);
_updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
}
public async Task RunAvailabilityCheck(Action<bool, string> updateFunc)
public async Task<string> RunAvailabilityCheck()
{
var downloadHandle = new DownloadService();
var time = await downloadHandle.RunAvailabilityCheck(null);
@@ -255,7 +255,7 @@ namespace ServiceLib.Services
ip = $"({ipInfo?.country_code}) {ipInfo?.ip}";
}
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time, ip));
return string.Format(ResUI.TestMeOutput, time, ip);
}
#region CheckUpdate private
@@ -287,7 +287,7 @@ namespace ServiceLib.Services
{
var url = coreInfo?.ReleaseApiUrl;
var result = await downloadHandle.TryDownloadString(url, true, Global.AppName);
if (Utils.IsNullOrEmpty(result))
if (result.IsNullOrEmpty())
{
return new RetResult(false, "");
}
@@ -368,6 +368,7 @@ namespace ServiceLib.Services
try
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty;
SemanticVersion curVersion;
string message;
string? url;
@@ -379,28 +380,28 @@ namespace ServiceLib.Services
{
curVersion = await GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"));
url = string.Format(coreUrl, version.ToVersionString("v"));
break;
}
case ECoreType.mihomo:
{
curVersion = await GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion);
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"));
url = string.Format(coreUrl, version.ToVersionString("v"));
break;
}
case ECoreType.sing_box:
{
curVersion = await GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"), version);
url = string.Format(coreUrl, version.ToVersionString("v"), version);
break;
}
case ECoreType.v2rayN:
{
curVersion = new SemanticVersion(Utils.GetVersionInfo());
message = string.Format(ResUI.IsLatestN, type, curVersion);
url = string.Format(GetUrlFromCore(coreInfo), version);
url = string.Format(coreUrl, version);
break;
}
default:
@@ -422,25 +423,36 @@ namespace ServiceLib.Services
}
}
private string? GetUrlFromCore(CoreInfo? coreInfo)
private async Task<string?> GetUrlFromCore(CoreInfo? coreInfo)
{
if (Utils.IsWindows())
{
//Check for standalone windows .Net version
if (coreInfo?.CoreType == ECoreType.v2rayN
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
)
{
return coreInfo?.DownloadUrlWin64?.Replace(".zip", "-SelfContained.zip");
}
return RuntimeInformation.ProcessArchitecture switch
var url = RuntimeInformation.ProcessArchitecture switch
{
Architecture.Arm64 => coreInfo?.DownloadUrlWinArm64,
Architecture.X64 => coreInfo?.DownloadUrlWin64,
_ => null,
};
if (coreInfo?.CoreType != ECoreType.v2rayN)
{
return url;
}
//Check for standalone windows .Net version
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "wpfgfx_cor3.dll"))
&& File.Exists(Path.Combine(Utils.GetBaseDirectory(), "D3DCompiler_47_cor3.dll")))
{
return url?.Replace(".zip", "-SelfContained.zip");
}
//Check for avalonia desktop windows version
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll")))
{
return url?.Replace(".zip", "-desktop.zip");
}
return url;
}
else if (Utils.IsLinux())
{
@@ -460,14 +472,14 @@ namespace ServiceLib.Services
_ => null,
};
}
return null;
return await Task.FromResult("");
}
#endregion CheckUpdate private
#region Geo private
private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> updateFunc)
private async Task UpdateGeoFiles(Config config, Action<bool, string> updateFunc)
{
_updateFunc = updateFunc;
@@ -475,11 +487,28 @@ namespace ServiceLib.Services
? Global.GeoUrl
: config.ConstItem.GeoSourceUrl;
var fileName = $"{geoName}.dat";
var targetPath = Utils.GetBinPath($"{fileName}");
var url = string.Format(geoUrl, geoName);
List<string> files = ["geosite", "geoip"];
foreach (var geoName in files)
{
var fileName = $"{geoName}.dat";
var targetPath = Utils.GetBinPath($"{fileName}");
var url = string.Format(geoUrl, geoName);
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
}
}
private async Task UpdateOtherFiles(Config config, Action<bool, string> updateFunc)
{
_updateFunc = updateFunc;
foreach (var url in Global.OtherGeoUrls)
{
var fileName = Path.GetFileName(url);
var targetPath = Utils.GetBinPath($"{fileName}");
await DownloadGeoFile(url, fileName, targetPath, updateFunc);
}
}
private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc)
@@ -586,4 +615,4 @@ namespace ServiceLib.Services
#endregion Geo private
}
}
}

View File

@@ -1,6 +1,6 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Reactive;
namespace ServiceLib.ViewModels
{
@@ -42,14 +42,14 @@ namespace ServiceLib.ViewModels
private async Task SaveServerAsync()
{
string remarks = SelectedSource.Remarks;
if (Utils.IsNullOrEmpty(remarks))
var remarks = SelectedSource.Remarks;
if (remarks.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.Address))
if (SelectedSource.Address.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
return;
@@ -69,7 +69,7 @@ namespace ServiceLib.ViewModels
public async Task BrowseServer(string fileName)
{
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
return;
}
@@ -80,7 +80,7 @@ namespace ServiceLib.ViewModels
if (await ConfigHandler.AddCustomServer(_config, item, false) == 0)
{
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer);
if (Utils.IsNotEmpty(item.IndexId))
if (item.IndexId.IsNotEmpty())
{
SelectedSource = JsonUtils.DeepCopy(item);
}
@@ -95,7 +95,7 @@ namespace ServiceLib.ViewModels
private async Task EditServer()
{
var address = SelectedSource.Address;
if (Utils.IsNullOrEmpty(address))
if (address.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
return;
@@ -113,4 +113,4 @@ namespace ServiceLib.ViewModels
await Task.CompletedTask;
}
}
}
}

View File

@@ -1,6 +1,6 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels
{
@@ -41,19 +41,19 @@ namespace ServiceLib.ViewModels
private async Task SaveServerAsync()
{
if (Utils.IsNullOrEmpty(SelectedSource.Remarks))
if (SelectedSource.Remarks.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.Address))
if (SelectedSource.Address.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddress);
return;
}
var port = SelectedSource.Port.ToString();
if (Utils.IsNullOrEmpty(port) || !Utils.IsNumeric(port)
if (port.IsNullOrEmpty() || !Utils.IsNumeric(port)
|| SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort)
{
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectServerPort);
@@ -61,21 +61,20 @@ namespace ServiceLib.ViewModels
}
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
{
if (Utils.IsNullOrEmpty(SelectedSource.Id))
if (SelectedSource.Id.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.FillPassword);
return;
}
if (Utils.IsNullOrEmpty(SelectedSource.Security))
if (SelectedSource.Security.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectEncryption);
return;
}
}
if (SelectedSource.ConfigType != EConfigType.SOCKS
&& SelectedSource.ConfigType != EConfigType.HTTP)
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
{
if (Utils.IsNullOrEmpty(SelectedSource.Id))
if (SelectedSource.Id.IsNullOrEmpty())
{
NoticeHandler.Instance.Enqueue(ResUI.FillUUID);
return;
@@ -94,4 +93,4 @@ namespace ServiceLib.ViewModels
}
}
}
}
}

View File

@@ -1,7 +1,7 @@
using ReactiveUI;
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
namespace ServiceLib.ViewModels
{
@@ -50,7 +50,7 @@ namespace ServiceLib.ViewModels
{
DisplayOperationMsg();
_config.WebDavItem = SelectedSource;
await ConfigHandler.SaveConfig(_config);
_ = await ConfigHandler.SaveConfig(_config);
var result = await WebDavHandler.Instance.CheckConnection();
if (result)
@@ -114,7 +114,7 @@ namespace ServiceLib.ViewModels
public async Task LocalRestore(string fileName)
{
DisplayOperationMsg();
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
return;
}
@@ -151,7 +151,7 @@ namespace ServiceLib.ViewModels
{
if (Utils.UpgradeAppExists(out var upgradeFileName))
{
ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
_ = ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
}
}
service?.Shutdown(true);
@@ -164,7 +164,7 @@ namespace ServiceLib.ViewModels
private async Task<bool> CreateZipFileFromDirectory(string fileName)
{
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
return false;
}
@@ -173,10 +173,10 @@ namespace ServiceLib.ViewModels
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db");
FileManager.CopyDirectory(configDir, configDirTemp, false, true, "");
var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
Directory.Delete(configDirZipTemp, true);
return await Task.FromResult(ret);
}
}
}
}

View File

@@ -1,10 +1,10 @@
using DynamicData;
using System.Reactive;
using System.Runtime.InteropServices;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Runtime.InteropServices;
namespace ServiceLib.ViewModels
{
@@ -46,9 +46,13 @@ namespace ServiceLib.ViewModels
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{
_checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
//Not Windows and under Win10
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
{
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
}
}
_checkUpdateModel.Add(GetCheckUpdateModel(_geo));
}
@@ -79,7 +83,8 @@ namespace ServiceLib.ViewModels
for (var k = _checkUpdateModel.Count - 1; k >= 0; k--)
{
var item = _checkUpdateModel[k];
if (item.IsSelected != true) continue;
if (item.IsSelected != true)
continue;
UpdateView(item.CoreType, "...");
if (item.CoreType == _geo)
@@ -248,7 +253,7 @@ namespace ServiceLib.ViewModels
{
foreach (var subDir in dir.GetDirectories())
{
FileManager.CopyDirectory(subDir.FullName, toPath, false, null);
FileManager.CopyDirectory(subDir.FullName, toPath, false, true);
subDir.Delete(true);
}
}
@@ -293,10 +298,11 @@ namespace ServiceLib.ViewModels
public void UpdateViewResult(CheckUpdateModel model)
{
var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType);
if (found == null) return;
if (found == null)
return;
var itemCopy = JsonUtils.DeepCopy(found);
itemCopy.Remarks = model.Remarks;
_checkUpdateModel.Replace(found, itemCopy);
}
}
}
}

View File

@@ -1,9 +1,9 @@
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Reactive;
using System.Reactive.Linq;
namespace ServiceLib.ViewModels
{
@@ -32,7 +32,7 @@ namespace ServiceLib.ViewModels
var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
selectedSource => selectedSource != null && selectedSource.Id.IsNotEmpty());
this.WhenAnyValue(
x => x.AutoRefresh,
@@ -53,27 +53,7 @@ namespace ServiceLib.ViewModels
private async Task Init()
{
var lastTime = DateTime.Now;
Observable.Interval(TimeSpan.FromSeconds(5))
.Subscribe(async x =>
{
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
{
return;
}
var dtNow = DateTime.Now;
if (_config.ClashUIItem.ConnectionsRefreshInterval > 0)
{
if ((dtNow - lastTime).Minutes % _config.ClashUIItem.ConnectionsRefreshInterval == 0)
{
await GetClashConnections();
lastTime = dtNow;
}
Task.Delay(1000).Wait();
}
});
await Task.CompletedTask;
_ = DelayTestTask();
}
private async Task GetClashConnections()
@@ -84,7 +64,7 @@ namespace ServiceLib.ViewModels
return;
}
_updateView?.Invoke(EViewAction.DispatcherRefreshConnections, ret?.connections);
_ = _updateView?.Invoke(EViewAction.DispatcherRefreshConnections, ret?.connections);
}
public void RefreshConnections(List<ConnectionItem>? connections)
@@ -95,27 +75,29 @@ namespace ServiceLib.ViewModels
var lstModel = new List<ClashConnectionModel>();
foreach (var item in connections ?? new())
{
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
var host = $"{(item.metadata.host.IsNullOrEmpty() ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
{
continue;
}
ClashConnectionModel model = new();
model.Id = item.id;
model.Network = item.metadata.network;
model.Type = item.metadata.type;
model.Host = host;
var sp = (dtNow - item.start);
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
model.Elapsed = sp.ToString(@"hh\:mm\:ss");
item.chains?.Reverse();
model.Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}";
var model = new ClashConnectionModel
{
Id = item.id,
Network = item.metadata.network,
Type = item.metadata.type,
Host = host,
Time = (dtNow - item.start).TotalSeconds < 0 ? 1 : (dtNow - item.start).TotalSeconds,
Elapsed = (dtNow - item.start).ToString(@"hh\:mm\:ss"),
Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}"
};
lstModel.Add(model);
}
if (lstModel.Count <= 0) { return; }
if (lstModel.Count <= 0)
{
return;
}
_connectionItems.AddRange(lstModel);
}
@@ -139,5 +121,35 @@ namespace ServiceLib.ViewModels
await ClashApiHandler.Instance.ClashConnectionClose(id);
await GetClashConnections();
}
public async Task DelayTestTask()
{
_ = Task.Run(async () =>
{
var numOfExecuted = 1;
while (true)
{
await Task.Delay(1000 * 5);
numOfExecuted++;
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
{
continue;
}
if (_config.ClashUIItem.ConnectionsRefreshInterval <= 0)
{
continue;
}
if (numOfExecuted % _config.ClashUIItem.ConnectionsRefreshInterval != 0)
{
continue;
}
await GetClashConnections();
}
});
await Task.CompletedTask;
}
}
}
}

View File

@@ -1,9 +1,9 @@
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Reactive;
using System.Reactive.Linq;
using static ServiceLib.Models.ClashProviders;
using static ServiceLib.Models.ClashProxies;
@@ -72,7 +72,7 @@ namespace ServiceLib.ViewModels
this.WhenAnyValue(
x => x.SelectedGroup,
y => y != null && Utils.IsNotEmpty(y.Name))
y => y != null && y.Name.IsNotEmpty())
.Subscribe(c => RefreshProxyDetails(c));
this.WhenAnyValue(
@@ -95,7 +95,6 @@ namespace ServiceLib.ViewModels
private async Task Init()
{
await ProxiesReload();
_ = DelayTestTask();
}
@@ -135,22 +134,12 @@ namespace ServiceLib.ViewModels
RefreshProxyDetails(c);
}
private void UpdateHandler(bool notify, string msg)
{
NoticeHandler.Instance.SendMessageEx(msg);
}
public async Task ProxiesReload()
{
await GetClashProxies(true);
await ProxiesDelayTest();
}
public async Task ProxiesDelayTest()
{
await ProxiesDelayTest(true);
}
#region proxy function
private async Task SetRuleMode(ERuleMode mode)
@@ -193,7 +182,7 @@ namespace ServiceLib.ViewModels
{
foreach (var it in proxyGroups)
{
if (Utils.IsNullOrEmpty(it.name) || !_proxies.ContainsKey(it.name))
if (it.name.IsNullOrEmpty() || !_proxies.ContainsKey(it.name))
{
continue;
}
@@ -218,8 +207,8 @@ namespace ServiceLib.ViewModels
{
continue;
}
var item = _proxyGroups.Where(t => t.Name == kv.Key).FirstOrDefault();
if (item != null && Utils.IsNotEmpty(item.Name))
var item = _proxyGroups.FirstOrDefault(t => t.Name == kv.Key);
if (item != null && item.Name.IsNotEmpty())
{
continue;
}
@@ -256,7 +245,7 @@ namespace ServiceLib.ViewModels
return;
}
var name = SelectedGroup?.Name;
if (Utils.IsNullOrEmpty(name))
if (name.IsNullOrEmpty())
{
return;
}
@@ -341,21 +330,21 @@ namespace ServiceLib.ViewModels
public async Task SetActiveProxy()
{
if (SelectedGroup == null || Utils.IsNullOrEmpty(SelectedGroup.Name))
if (SelectedGroup == null || SelectedGroup.Name.IsNullOrEmpty())
{
return;
}
if (SelectedDetail == null || Utils.IsNullOrEmpty(SelectedDetail.Name))
if (SelectedDetail == null || SelectedDetail.Name.IsNullOrEmpty())
{
return;
}
var name = SelectedGroup.Name;
if (Utils.IsNullOrEmpty(name))
if (name.IsNullOrEmpty())
{
return;
}
var nameNode = SelectedDetail.Name;
if (Utils.IsNullOrEmpty(nameNode))
if (nameNode.IsNullOrEmpty())
{
return;
}
@@ -369,7 +358,7 @@ namespace ServiceLib.ViewModels
await ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
selectedProxy.now = nameNode;
var group = _proxyGroups.Where(it => it.Name == SelectedGroup.Name).FirstOrDefault();
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
if (group != null)
{
group.Now = nameNode;
@@ -381,10 +370,8 @@ namespace ServiceLib.ViewModels
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
}
private async Task ProxiesDelayTest(bool blAll)
private async Task ProxiesDelayTest(bool blAll = true)
{
//UpdateHandler(false, "Clash Proxies Latency Test");
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), async (item, result) =>
{
if (item == null)
@@ -392,7 +379,7 @@ namespace ServiceLib.ViewModels
await GetClashProxies(true);
return;
}
if (Utils.IsNullOrEmpty(result))
if (result.IsNullOrEmpty())
{
return;
}
@@ -405,19 +392,19 @@ namespace ServiceLib.ViewModels
public void ProxiesDelayTestResult(SpeedTestResult result)
{
//UpdateHandler(false, $"{item.name}={result}");
var detail = _proxyDetails.Where(it => it.Name == result.IndexId).FirstOrDefault();
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
if (detail != null)
{
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result.Delay);
if (dicResult != null && dicResult.ContainsKey("delay"))
if (dicResult != null && dicResult.TryGetValue("delay", out var value))
{
detail.Delay = Convert.ToInt32(dicResult["delay"].ToString());
detail.Delay = Convert.ToInt32(value.ToString());
detail.DelayName = $"{detail.Delay}ms";
}
else if (dicResult != null && dicResult.ContainsKey("message"))
else if (dicResult != null && dicResult.TryGetValue("message", out var value1))
{
detail.Delay = _delayTimeout;
detail.DelayName = $"{dicResult["message"]}";
detail.DelayName = $"{value1}";
}
else
{
@@ -434,33 +421,31 @@ namespace ServiceLib.ViewModels
public async Task DelayTestTask()
{
var lastTime = DateTime.Now;
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 60);
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
{
continue;
}
if (_config.ClashUIItem.ProxiesAutoDelayTestInterval <= 0)
{
continue;
}
var dtNow = DateTime.Now;
if ((dtNow - lastTime).Minutes % _config.ClashUIItem.ProxiesAutoDelayTestInterval != 0)
{
continue;
}
await ProxiesDelayTest();
lastTime = dtNow;
}
});
{
var numOfExecuted = 1;
while (true)
{
await Task.Delay(1000 * 60);
numOfExecuted++;
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
{
continue;
}
if (_config.ClashUIItem.ProxiesAutoDelayTestInterval <= 0)
{
continue;
}
if (numOfExecuted % _config.ClashUIItem.ProxiesAutoDelayTestInterval != 0)
{
continue;
}
await ProxiesDelayTest();
}
});
await Task.CompletedTask;
}
#endregion task
}
}
}

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