Compare commits

...

41 Commits
6.44 ... 6.50

Author SHA1 Message Date
2dust
5efb784115 up 6.50 2024-07-05 11:18:36 +08:00
2dust
acde5b8384 Bug fix 2024-07-05 11:18:14 +08:00
2dust
373ee6586a Optimize system proxy code 2024-07-04 17:07:05 +08:00
2dust
215308c329 Change srs source
https://github.com/2dust/sing-box-rules
2024-07-04 16:27:44 +08:00
2dust
478cadba5e Bug fix 2024-06-30 20:43:06 +08:00
2dust
f4af4da791 Adjust res 2024-06-30 10:00:28 +08:00
2dust
22d4f435de Add tun and mixin functions to clash 2024-06-28 20:29:44 +08:00
2dust
ccb8cd5c04 up 6.49 2024-06-28 16:14:45 +08:00
2dust
e963f9e349 Add clash display in the main interface 2024-06-28 16:14:19 +08:00
2dust
e3c2a4b8da Bug fix 2024-06-28 10:41:17 +08:00
2dust
324f46cdad Bug fix
https://github.com/2dust/v2rayN/discussions/5268
2024-06-27 17:59:39 +08:00
2dust
691b19b5fd Adding support for the clash proxies interface 2024-06-27 16:36:36 +08:00
2dust
63ed2311dc Bug fix 2024-06-26 16:00:37 +08:00
2dust
d59e59ca40 up 6.48 2024-06-24 17:42:37 +08:00
2dust
c9a278b4a2 Check for updates mihomo 2024-06-24 17:41:53 +08:00
2dust
8ee5304148 Bug fix 2024-06-24 16:42:17 +08:00
2dust
3beaa8145f Bug fix 2024-06-24 16:19:00 +08:00
2dust
e30c55a0af up 6.47 2024-06-24 15:34:41 +08:00
2dust
b583590a64 Update ConfigHandler.cs
https://github.com/2dust/v2rayN/pull/5264
2024-06-24 15:25:00 +08:00
OnceUponATimeInAmerica
a28c63168a Support for setting subs-group remarks (group's name) from the (optional) "remark" query parameter in the pasted subs url (#5264) 2024-06-24 15:20:16 +08:00
2dust
620422350f Improved tun mode
Enabling tun mode.
Only one core will be run when using sing-box core;
When using a non-sing-box, the sing-box will be used to start an additional front Socks service to provide a tun entry, which will then run two cores
2024-06-24 15:19:42 +08:00
2dust
b77cc3c33b Add network attribute to route rule
https://github.com/2dust/v2rayN/discussions/5256
2024-06-22 16:44:15 +08:00
2dust
71bf9b4887 up 6.46 2024-06-21 16:02:08 +08:00
2dust
9039d401da set DOTNET_EnableWriteXorExecute=0 under win10
https://github.com/2dust/v2rayN/issues/5186
2024-06-21 14:09:57 +08:00
2dust
74a0a93201 Improve and refactor the code 2024-06-21 11:15:35 +08:00
2dust
522571f0b3 Add splithttp transport for xray
https://github.com/XTLS/Xray-core/releases/tag/v1.8.15
2024-06-20 16:20:05 +08:00
Lsyx-Good
123c49c22d Adjust singbox DNS server and rule position (#5229) 2024-06-20 16:08:08 +08:00
OnceUponATimeInAmerica
1ff88d29be Extend stats reporting to support custom configs with several load-balanced outbounds in them, with tags starting with "proxy" e.g. proxy1, proxy2 (fully compatible with existing configs) (#5225) 2024-06-20 15:32:10 +08:00
Lsyx-Good
63d5a2a1be Fix bug (#5213) 2024-06-10 15:12:39 +08:00
2dust
1e9a6cb06b Refactor code 2024-06-04 09:48:04 +08:00
2dust
31748aa660 Refactor code 2024-06-03 17:50:18 +08:00
2dust
d0c6ea6a63 remove http://cachefly.cachefly.net/100mb.test 2024-06-03 14:06:59 +08:00
2dust
7ffe286a56 Up PackageReference 2024-06-03 07:37:49 +08:00
2dust
b99b30163b Bug fix 2024-06-03 07:37:26 +08:00
kirilllivanov
83b4f1e660 Fix incorrect filenames (#5122) 2024-05-18 17:11:38 +08:00
2dust
b57ba6a98b up 6.45 2024-05-18 09:53:15 +08:00
2dust
d748e6eff4 Add destOverride 2024-05-14 15:31:19 +08:00
2dust
315d4b75b2 Optimize code 2024-05-14 13:42:42 +08:00
2dust
31267cbc33 Adjust Scan screen qrcode 2024-05-12 17:07:56 +08:00
2dust
c23379b3b6 Adjust sing-box dns 2024-05-12 15:42:28 +08:00
2dust
568144d6a2 TransitionAssist DisableTransitions 2024-05-12 14:59:23 +08:00
110 changed files with 4756 additions and 2245 deletions

View File

@@ -9,9 +9,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.1" /> <PackageReference Include="Google.Protobuf" Version="3.27.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.63.0" />
<PackageReference Include="Grpc.Tools" Version="2.63.0"> <PackageReference Include="Grpc.Tools" Version="2.64.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@@ -1,9 +1,9 @@
<Application <Application
x:Class="v2rayN.App" x:Class="v2rayN.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:v2rayN.Converters" xmlns:conv="clr-namespace:v2rayN.Converters"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
ShutdownMode="OnExplicitShutdown" ShutdownMode="OnExplicitShutdown"
StartupUri="Views/MainWindow.xaml"> StartupUri="Views/MainWindow.xaml">
<Application.Resources> <Application.Resources>
@@ -20,6 +20,7 @@
<system:Double x:Key="StdFontSize1">13</system:Double> <system:Double x:Key="StdFontSize1">13</system:Double>
<system:Double x:Key="StdFontSize2">14</system:Double> <system:Double x:Key="StdFontSize2">14</system:Double>
<system:Double x:Key="StdFontSizeMsg">11</system:Double> <system:Double x:Key="StdFontSizeMsg">11</system:Double>
<system:Double x:Key="StdFontSize-1">11</system:Double>
<conv:InverseBooleanConverter x:Key="InverseBooleanConverter" /> <conv:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<Thickness <Thickness
@@ -68,16 +69,21 @@
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" /> <Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" />
</Style> </Style>
<Style x:Key="lvItemSelected" TargetType="{x:Type ListViewItem}"> <Style x:Key="lvItemSelected" TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="20" /> <Setter Property="Margin" Value="2" />
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" /> <Setter Property="Template">
<Style.Triggers> <Setter.Value>
<Trigger Property="IsSelected" Value="true"> <ControlTemplate TargetType="ListViewItem">
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" /> <materialDesign:Card Name="_Card" SnapsToDevicePixels="true">
</Trigger> <ContentPresenter />
<Trigger Property="IsMouseOver" Value="true"> </materialDesign:Card>
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary}" /> <ControlTemplate.Triggers>
</Trigger> <Trigger Property="IsSelected" Value="true">
</Style.Triggers> <Setter TargetName="_Card" Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style> </Style>
<Style x:Key="GridViewColumnHeaderStyle" TargetType="{x:Type GridViewColumnHeader}"> <Style x:Key="GridViewColumnHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
@@ -156,6 +162,27 @@
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" /> <Setter Property="FontSize" Value="{DynamicResource StdFontSize}" />
<Setter Property="Padding" Value="{StaticResource OutlinedTextBoxDefaultPadding}" /> <Setter Property="Padding" Value="{StaticResource OutlinedTextBoxDefaultPadding}" />
</Style> </Style>
<Style x:Key="ListItemChip" TargetType="{x:Type materialDesign:Chip}">
<Setter Property="FontSize" Value="{DynamicResource StdFontSize1}" />
</Style>
<Style
x:Key="ListItemTitle"
BasedOn="{StaticResource MaterialDesignTextBlock}"
TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="{DynamicResource StdFontSize1}" />
</Style>
<Style
x:Key="ListItemSubTitle"
BasedOn="{StaticResource MaterialDesignTextBlock}"
TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="{DynamicResource StdFontSize}" />
</Style>
<Style
x:Key="ListItemSubTitle2"
BasedOn="{StaticResource MaterialDesignTextBlock}"
TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="{DynamicResource StdFontSize-1}" />
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@@ -43,6 +43,7 @@ namespace v2rayN
Init(); Init();
Logging.LoggingEnabled(_config.guiItem.enableLog); Logging.LoggingEnabled(_config.guiItem.enableLog);
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}"); Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.ClearLogs(); Logging.ClearLogs();
Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage); Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage);
@@ -58,10 +59,12 @@ namespace v2rayN
Environment.Exit(0); Environment.Exit(0);
return; return;
} }
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
//{ //Under Win10
// _config.guiItem.enableStatistics = false; if (Environment.OSVersion.Version.Major < 10)
//} {
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
}
} }
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)

View File

@@ -1,5 +1,6 @@
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
@@ -21,6 +22,22 @@ namespace v2rayN
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient; private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
public async Task<string?> TryGetAsync(string url)
{
if (string.IsNullOrEmpty(url))
return null;
try
{
HttpResponseMessage response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
catch
{
return null;
}
}
public async Task<string?> GetAsync(string url) public async Task<string?> GetAsync(string url)
{ {
if (Utils.IsNullOrEmpty(url)) return null; if (Utils.IsNullOrEmpty(url)) return null;
@@ -41,6 +58,21 @@ namespace v2rayN
var result = await httpClient.PutAsync(url, content); var result = 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 byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PatchAsync(url, byteContent);
}
public async Task DeleteAsync(string url)
{
await httpClient.DeleteAsync(url);
}
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default) public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
{ {
ArgumentNullException.ThrowIfNull(url); ArgumentNullException.ThrowIfNull(url);

View File

@@ -105,9 +105,12 @@ namespace v2rayN
/// <param name="filePath"></param> /// <param name="filePath"></param>
/// <param name="nullValue"></param> /// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static int ToFile(object? obj, string filePath, bool nullValue = true) public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{ {
int result; if (filePath is null)
{
return -1;
}
try try
{ {
using FileStream file = File.Create(filePath); using FileStream file = File.Create(filePath);
@@ -119,14 +122,13 @@ namespace v2rayN
}; };
JsonSerializer.Serialize(file, obj, options); JsonSerializer.Serialize(file, obj, options);
result = 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
result = -1; return -1;
} }
return result;
} }
} }
} }

View File

@@ -1,6 +1,13 @@
using QRCoder; using QRCoder;
using QRCoder.Xaml; using QRCoder.Xaml;
using System.Drawing;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using ZXing.Windows.Compatibility;
namespace v2rayN namespace v2rayN
{ {
@@ -28,5 +35,70 @@ namespace v2rayN
return null; return null;
} }
} }
public static string ScanScreen(float dpiX, float dpiY)
{
try
{
var left = (int)(SystemParameters.WorkArea.Left);
var top = (int)(SystemParameters.WorkArea.Top);
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
using Bitmap fullImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new(width, height);
double imageScale = (double)width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
BitmapLuminanceSource source = new(target);
QRCodeReader reader = new();
BinaryBitmap bitmap = new(new HybridBinarizer(source));
var result = reader.decode(bitmap);
if (result != null)
{
return result.Text;
}
else
{
BinaryBitmap bitmap2 = new(new HybridBinarizer(source.invert()));
var result2 = reader.decode(bitmap2);
if (result2 != null)
{
return result2.Text;
}
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
public static Tuple<float, float> GetDpiXY(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
Graphics g = Graphics.FromHwnd(hWnd);
return new(96 / g.DpiX, 96 / g.DpiY);
}
} }
} }

View File

@@ -18,10 +18,6 @@ using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using ZXing.Windows.Compatibility;
namespace v2rayN namespace v2rayN
{ {
@@ -83,7 +79,7 @@ namespace v2rayN
/// </summary> /// </summary>
/// <param name="lst"></param> /// <param name="lst"></param>
/// <returns></returns> /// <returns></returns>
public static string List2String(List<string> lst, bool wrap = false) public static string List2String(List<string>? lst, bool wrap = false)
{ {
try try
{ {
@@ -481,7 +477,7 @@ namespace v2rayN
/// 验证Domain地址是否合法 /// 验证Domain地址是否合法
/// </summary> /// </summary>
/// <param name="domain"></param> /// <param name="domain"></param>
public static bool IsDomain(string domain) public static bool IsDomain(string? domain)
{ {
//如果为空 //如果为空
if (IsNullOrEmpty(domain)) if (IsNullOrEmpty(domain))
@@ -950,66 +946,6 @@ namespace v2rayN
#endregion TempPath #endregion TempPath
#region scan screen
public static string ScanScreen(float dpiX, float dpiY)
{
try
{
var left = (int)(SystemParameters.WorkArea.Left);
var top = (int)(SystemParameters.WorkArea.Top);
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
using Bitmap fullImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new(width, height);
double imageScale = (double)width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
BitmapLuminanceSource source = new(target);
BinaryBitmap bitmap = new(new HybridBinarizer(source));
QRCodeReader reader = new();
Result result = reader.decode(bitmap);
if (result != null)
{
string ret = result.Text;
return ret;
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
public static Tuple<float, float> GetDpiXY(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
Graphics g = Graphics.FromHwnd(hWnd);
return new(96 / g.DpiX, 96 / g.DpiY);
}
#endregion scan screen
#region #region
/// <summary> /// <summary>
@@ -1148,8 +1084,10 @@ namespace v2rayN
{ {
try try
{ {
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
var guid = new Guid(sum);
string pnputilPath = @"C:\Windows\System32\pnputil.exe"; string pnputilPath = @"C:\Windows\System32\pnputil.exe";
string arg = $" /remove-device /deviceid \"wintun\""; string arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device // Try to remove the device
Process proc = new() Process proc = new()

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace v2rayN.Common
{
internal class YamlUtils
{
#region YAML
/// <summary>
/// 反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T FromYaml<T>(string str)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
T obj = deserializer.Deserialize<T>(str);
return obj;
}
catch (Exception ex)
{
Logging.SaveLog("FromYaml", ex);
return deserializer.Deserialize<T>("");
}
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToYaml(Object obj)
{
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
string result = string.Empty;
try
{
result = serializer.Serialize(obj);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return result;
}
#endregion YAML
}
}

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EConfigType public enum EConfigType
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum ECoreType public enum ECoreType
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EGlobalHotkey public enum EGlobalHotkey
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EInboundProtocol public enum EInboundProtocol
{ {
@@ -8,6 +8,7 @@
http2, http2,
pac, pac,
api, api,
api2,
speedtest = 21 speedtest = 21
} }
} }

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EMove public enum EMove
{ {

View File

@@ -0,0 +1,10 @@
namespace v2rayN.Enums
{
public enum ERuleMode
{
Rule = 0,
Global = 1,
Direct = 2,
Unchanged = 3
}
}

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EServerColName public enum EServerColName
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum ESpeedActionType public enum ESpeedActionType
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum ESysProxyType public enum ESysProxyType
{ {

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum ETransport public enum ETransport
{ {
@@ -6,6 +6,7 @@
kcp, kcp,
ws, ws,
httpupgrade, httpupgrade,
splithttp,
h2, h2,
http, http,
quic, quic,

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models namespace v2rayN.Enums
{ {
public enum EViewAction public enum EViewAction
{ {

View File

@@ -1,4 +1,4 @@
using v2rayN.Models; using v2rayN.Enums;
namespace v2rayN namespace v2rayN
{ {
@@ -23,13 +23,14 @@ namespace v2rayN
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases"; public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/SagerNet/sing-{0}/rule-set/{1}.srs"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json"; public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json"; public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json"; public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
public const string ClashMixinConfigFileName = "Mixin.yaml";
public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig"; public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig"; public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
public const string V2raySampleHttpRequestFileName = "v2rayN.Sample.SampleHttpRequest"; public const string V2raySampleHttpRequestFileName = "v2rayN.Sample.SampleHttpRequest";
@@ -43,6 +44,8 @@ namespace v2rayN
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules"; public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal"; public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal";
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal"; public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
public const string ClashMixinYaml = "v2rayN.Sample.clash_mixin_yaml";
public const string ClashTunYaml = "v2rayN.Sample.clash_tun_yaml";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -106,7 +109,6 @@ namespace v2rayN
@"https://speed.cloudflare.com/__down?bytes=100000000", @"https://speed.cloudflare.com/__down?bytes=100000000",
@"https://speed.cloudflare.com/__down?bytes=10000000", @"https://speed.cloudflare.com/__down?bytes=10000000",
@"http://cachefly.cachefly.net/50mb.test", @"http://cachefly.cachefly.net/50mb.test",
@"http://cachefly.cachefly.net/100mb.test",
@"http://cachefly.cachefly.net/10mb.test" @"http://cachefly.cachefly.net/10mb.test"
}; };
@@ -158,7 +160,7 @@ namespace v2rayN
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> 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> 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> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "h2", "quic", "grpc" }; public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "splithttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "v2fly", "SagerNet", "Xray", "sing_box" }; public static readonly List<string> CoreTypes = new() { "v2fly", "SagerNet", "Xray", "sing_box" };
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" }; public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
@@ -175,12 +177,18 @@ namespace v2rayN
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" }; public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" }; public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" }; 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> TunMtus = new() { "1280", "1408", "1500", "9000" };
public static readonly List<string> TunStacks = new() { "gvisor", "system" }; public static readonly List<string> TunStacks = new() { "gvisor", "system" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" }; 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> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" }; public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
public static readonly List<string> allowSelectType = new List<string> { "selector", "urltest", "loadbalance", "fallback" };
public static readonly List<string> notAllowTestType = new List<string> { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
public static readonly List<string> proxyVehicleType = new List<string> { "file", "http" };
#endregion const #endregion const
} }
} }

View File

@@ -0,0 +1,218 @@
using v2rayN.Models;
using static v2rayN.Models.ClashProxies;
namespace v2rayN.Handler
{
public sealed class ClashApiHandler
{
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value;
private Dictionary<String, ProxiesItem> _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
public void SetProxies(Dictionary<String, ProxiesItem> proxies)
{
_proxies = proxies;
}
public Dictionary<String, ProxiesItem> GetProxies()
{
return _proxies;
}
public void GetClashProxies(Config config, Action<ClashProxies, ClashProviders> update)
{
Task.Run(() => GetClashProxiesAsync(config, update));
}
private async Task GetClashProxiesAsync(Config config, Action<ClashProxies, ClashProviders> update)
{
for (var i = 0; i < 5; i++)
{
var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
var url2 = $"{GetApiUrl()}/providers/proxies";
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
if (clashProxies != null || clashProviders != null)
{
update(clashProxies, clashProviders);
return;
}
Thread.Sleep(5000);
}
update(null, null);
}
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> update)
{
Task.Run(() =>
{
if (blAll)
{
for (int i = 0; i < 5; i++)
{
if (GetProxies() != null)
{
break;
}
Thread.Sleep(5000);
}
var proxies = GetProxies();
if (proxies == null)
{
return;
}
lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> 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(),
});
}
}
if (lstProxy == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + LazyConfig.Instance.GetConfig().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);
update(it, result);
}));
}
Task.WaitAll(tasks.ToArray());
Thread.Sleep(1000);
update(null, "");
});
}
public List<ProxiesItem>? GetClashProxyGroups()
{
try
{
var fileContent = ProfileContent;
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
{
return null;
}
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
}
catch (Exception ex)
{
Logging.SaveLog("GetClashProxyGroups", ex);
return null;
}
}
public async void ClashSetActiveProxy(string name, string nameNode)
{
try
{
var url = $"{GetApiUrl()}/proxies/{name}";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public void ClashConfigUpdate(Dictionary<string, string> headers)
{
Task.Run(async () =>
{
var proxies = GetProxies();
if (proxies == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/configs";
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
});
}
public async void ClashConfigReload(string filePath)
{
ClashConnectionClose("");
try
{
var url = $"{GetApiUrl()}/configs?force=true";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public void GetClashConnections(Config config, Action<ClashConnections> update)
{
Task.Run(() => GetClashConnectionsAsync(config, update));
}
private async Task GetClashConnectionsAsync(Config config, Action<ClashConnections> update)
{
try
{
var url = $"{GetApiUrl()}/connections";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
update(clashConnections);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async void ClashConnectionClose(string id)
{
try
{
var url = $"{GetApiUrl()}/connections/{id}";
await HttpClientHelper.Instance.DeleteAsync(url);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private string GetApiUrl()
{
return $"{Global.HttpProtocol}{Global.Loopback}:{LazyConfig.Instance.StatePort2}";
}
}
}

View File

@@ -1,6 +1,9 @@
using System.Data; using System.Data;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web;
using v2rayN.Enums;
using v2rayN.Handler.Fmt;
using v2rayN.Models; using v2rayN.Models;
namespace v2rayN.Handler namespace v2rayN.Handler
@@ -191,6 +194,7 @@ namespace v2rayN.Handler
down_mbps = 100 down_mbps = 100
}; };
} }
config.clashUIItem ??= new();
LazyConfig.Instance.SetConfig(config); LazyConfig.Instance.SetConfig(config);
return 0; return 0;
@@ -1069,12 +1073,12 @@ namespace v2rayN.Handler
/// 批量添加服务器 /// 批量添加服务器
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="clipboardData"></param> /// <param name="strData"></param>
/// <param name="subid"></param> /// <param name="subid"></param>
/// <returns>成功导入的数量</returns> /// <returns>成功导入的数量</returns>
private static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static int AddBatchServers(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{ {
if (Utils.IsNullOrEmpty(clipboardData)) if (Utils.IsNullOrEmpty(strData))
{ {
return -1; return -1;
} }
@@ -1091,7 +1095,7 @@ namespace v2rayN.Handler
//Check for duplicate indexId //Check for duplicate indexId
List<string>? lstDbIndexId = null; List<string>? lstDbIndexId = null;
List<ProfileItem> lstAdd = new(); List<ProfileItem> lstAdd = new();
var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
if (isSub) if (isSub)
{ {
arrData = arrData.Distinct(); arrData = arrData.Distinct();
@@ -1107,7 +1111,7 @@ namespace v2rayN.Handler
} }
continue; continue;
} }
var profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg); var profileItem = FmtHandler.ResolveConfig(str, out string msg);
if (profileItem is null) if (profileItem is null)
{ {
continue; continue;
@@ -1176,55 +1180,39 @@ namespace v2rayN.Handler
return countServers; return countServers;
} }
private static int AddBatchServers4Custom(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static int AddBatchServers4Custom(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{ {
if (Utils.IsNullOrEmpty(clipboardData)) if (Utils.IsNullOrEmpty(strData))
{ {
return -1; return -1;
} }
var subRemarks = LazyConfig.Instance.GetSubItem(subid)?.remarks;
//判断str是否包含s的任意一个字符串 List<ProfileItem>? lstProfiles = null;
static bool Contains(string str, params string[] s) //Is sing-box array configuration
if (lstProfiles is null || lstProfiles.Count <= 0)
{ {
foreach (var item in s) lstProfiles = SingboxFmt.ResolveFullArray(strData, subRemarks);
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
} }
//Is v2ray array configuration //Is v2ray array configuration
var configObjects = JsonUtils.Deserialize<Object[]>(clipboardData); if (lstProfiles is null || lstProfiles.Count <= 0)
if (configObjects != null && configObjects.Length > 0) {
lstProfiles = V2rayFmt.ResolveFullArray(strData, subRemarks);
}
if (lstProfiles != null && lstProfiles.Count > 0)
{ {
if (isSub && !Utils.IsNullOrEmpty(subid)) if (isSub && !Utils.IsNullOrEmpty(subid))
{ {
RemoveServerViaSubid(config, subid, isSub); RemoveServerViaSubid(config, subid, isSub);
} }
int count = 0; int count = 0;
foreach (var configObject in configObjects) foreach (var it in lstProfiles)
{ {
var objectString = JsonUtils.Serialize(configObject); it.subid = subid;
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString); it.isSub = isSub;
if (v2rayCon?.inbounds?.Count > 0 && v2rayCon.outbounds?.Count > 0) if (AddCustomServer(config, it, true) == 0)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); count++;
File.WriteAllText(fileName, objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayCon.remarks ?? "v2ray_custom",
subid = subid,
isSub = isSub
};
if (AddCustomServer(config, profileIt, true) == 0)
{
count++;
}
} }
} }
if (count > 0) if (count > 0)
@@ -1233,58 +1221,39 @@ namespace v2rayN.Handler
} }
} }
ProfileItem profileItem = new(); ProfileItem? profileItem = null;
//Is v2ray configuration //Is sing-box configuration
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(clipboardData); if (profileItem is null)
if (v2rayConfig?.inbounds?.Count > 0
&& v2rayConfig.outbounds?.Count > 0)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); profileItem = SingboxFmt.ResolveFull(strData, subRemarks);
File.WriteAllText(fileName, clipboardData); }
//Is v2ray configuration
profileItem.coreType = ECoreType.Xray; if (profileItem is null)
profileItem.address = fileName; {
profileItem.remarks = v2rayConfig.remarks ?? "v2ray_custom"; profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
} }
//Is Clash configuration //Is Clash configuration
else if (Contains(clipboardData, "port", "socks-port", "proxies")) if (profileItem is null)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml"); profileItem = ClashFmt.ResolveFull(strData, subRemarks);
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.mihomo;
profileItem.address = fileName;
profileItem.remarks = "clash_custom";
} }
//Is hysteria configuration //Is hysteria configuration
else if (Contains(clipboardData, "server", "up", "down", "listen", "<html>", "<body>")) if (profileItem is null)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
File.WriteAllText(fileName, clipboardData); }
if (profileItem is null)
profileItem.coreType = ECoreType.hysteria; {
profileItem.address = fileName; profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
profileItem.remarks = "hysteria_custom";
} }
//Is naiveproxy configuration //Is naiveproxy configuration
else if (Contains(clipboardData, "listen", "proxy", "<html>", "<body>")) if (profileItem is null)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.naiveproxy;
profileItem.address = fileName;
profileItem.remarks = "naiveproxy_custom";
} }
//Is Other configuration if (profileItem is null || Utils.IsNullOrEmpty(profileItem.address))
else
{ {
return -1; return -1;
//var fileName = Utile.GetTempPath($"{Utile.GetGUID(false)}.txt");
//File.WriteAllText(fileName, clipboardData);
//profileItem.address = fileName;
//profileItem.remarks = "other_custom";
} }
if (isSub && !Utils.IsNullOrEmpty(subid)) if (isSub && !Utils.IsNullOrEmpty(subid))
@@ -1297,12 +1266,6 @@ namespace v2rayN.Handler
} }
profileItem.subid = subid; profileItem.subid = subid;
profileItem.isSub = isSub; profileItem.isSub = isSub;
if (Utils.IsNullOrEmpty(profileItem.address))
{
return -1;
}
if (AddCustomServer(config, profileItem, true) == 0) if (AddCustomServer(config, profileItem, true) == 0)
{ {
return 1; return 1;
@@ -1313,9 +1276,9 @@ namespace v2rayN.Handler
} }
} }
private static int AddBatchServers4SsSIP008(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static int AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{ {
if (Utils.IsNullOrEmpty(clipboardData)) if (Utils.IsNullOrEmpty(strData))
{ {
return -1; return -1;
} }
@@ -1325,31 +1288,12 @@ namespace v2rayN.Handler
RemoveServerViaSubid(config, subid, isSub); RemoveServerViaSubid(config, subid, isSub);
} }
//SsSIP008 var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(clipboardData);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(clipboardData);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0) if (lstSsServer?.Count > 0)
{ {
int counter = 0; int counter = 0;
foreach (var it in lstSsServer) foreach (var ssItem in lstSsServer)
{ {
var ssItem = new ProfileItem()
{
subid = subid,
remarks = it.remarks,
security = it.method,
id = it.password,
address = it.server,
port = Utils.ToInt(it.server_port)
};
ssItem.subid = subid; ssItem.subid = subid;
ssItem.isSub = isSub; ssItem.isSub = isSub;
if (AddShadowsocksServer(config, ssItem) == 0) if (AddShadowsocksServer(config, ssItem) == 0)
@@ -1364,7 +1308,7 @@ namespace v2rayN.Handler
return -1; return -1;
} }
public static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub) public static int AddBatchServers(Config config, string strData, string subid, bool isSub)
{ {
List<ProfileItem>? lstOriSub = null; List<ProfileItem>? lstOriSub = null;
if (isSub && !Utils.IsNullOrEmpty(subid)) if (isSub && !Utils.IsNullOrEmpty(subid))
@@ -1373,28 +1317,28 @@ namespace v2rayN.Handler
} }
var counter = 0; var counter = 0;
if (Utils.IsBase64String(clipboardData)) if (Utils.IsBase64String(strData))
{ {
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = AddBatchServers(config, clipboardData, subid, isSub, lstOriSub); counter = AddBatchServers(config, strData, subid, isSub, lstOriSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = AddBatchServers4SsSIP008(config, clipboardData, subid, isSub, lstOriSub); counter = AddBatchServers4SsSIP008(config, strData, subid, isSub, lstOriSub);
} }
//maybe other sub //maybe other sub
if (counter < 1) if (counter < 1)
{ {
counter = AddBatchServers4Custom(config, clipboardData, subid, isSub, lstOriSub); counter = AddBatchServers4Custom(config, strData, subid, isSub, lstOriSub);
} }
return counter; return counter;
@@ -1413,18 +1357,27 @@ namespace v2rayN.Handler
public static int AddSubItem(Config config, string url) public static int AddSubItem(Config config, string url)
{ {
//already exists //already exists
if (SQLiteHelper.Instance.Table<SubItem>().Where(e => e.url == url).Count() > 0) if (SQLiteHelper.Instance.Table<SubItem>().Any(e => e.url == url))
{ {
return 0; return 0;
} }
SubItem subItem = new() SubItem subItem = new()
{ {
id = string.Empty, id = string.Empty,
remarks = "import_sub",
url = url url = url
}; };
try
{
var uri = new Uri(url);
var queryVars = HttpUtility.ParseQueryString(uri.Query);
subItem.remarks = queryVars["remarks"] ?? "import_sub";
}
catch (UriFormatException)
{
return 0;
}
return AddSubItem(config, subItem); return AddSubItem(config, subItem);
} }
@@ -1532,16 +1485,16 @@ namespace v2rayN.Handler
/// AddBatchRoutingRules /// AddBatchRoutingRules
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="clipboardData"></param> /// <param name="strData"></param>
/// <returns></returns> /// <returns></returns>
public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData) public static int AddBatchRoutingRules(ref RoutingItem routingItem, string strData)
{ {
if (Utils.IsNullOrEmpty(clipboardData)) if (Utils.IsNullOrEmpty(strData))
{ {
return -1; return -1;
} }
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData); var lstRules = JsonUtils.Deserialize<List<RulesItem>>(strData);
if (lstRules == null) if (lstRules == null)
{ {
return -1; return -1;
@@ -1673,7 +1626,7 @@ namespace v2rayN.Handler
public static int InitBuiltinRouting(Config config, bool blImportAdvancedRules = false) public static int InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
{ {
var ver = "V2-"; var ver = "V3-";
var items = LazyConfig.Instance.RoutingItems(); var items = LazyConfig.Instance.RoutingItems();
if (blImportAdvancedRules || items.Where(t => t.remarks.StartsWith(ver)).ToList().Count <= 0) if (blImportAdvancedRules || items.Where(t => t.remarks.StartsWith(ver)).ToList().Count <= 0)
{ {

View File

@@ -0,0 +1,271 @@
using System.IO;
using v2rayN.Common;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.CoreConfig
{
/// <summary>
/// Core configuration file processing class
/// </summary>
internal class CoreConfigClash
{
private Config _config;
public CoreConfigClash(Config config)
{
_config = config;
}
/// <summary>
/// 生成配置文件
/// </summary>
/// <param name="node"></param>
/// <param name="fileName"></param>
/// <param name="msg"></param>
/// <returns></returns>
public int GenerateClientConfig(ProfileItem node, string? fileName, out string msg)
{
if (node == null || fileName is null)
{
msg = ResUI.CheckServerSettings;
return -1;
}
msg = ResUI.InitialConfiguration;
try
{
if (node == null)
{
msg = ResUI.CheckServerSettings;
return -1;
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}
string addressFileName = node.address;
if (string.IsNullOrEmpty(addressFileName))
{
msg = ResUI.FailedGetDefaultConfiguration;
return -1;
}
if (!File.Exists(addressFileName))
{
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
}
if (!File.Exists(addressFileName))
{
msg = ResUI.FailedReadConfiguration + "1";
return -1;
}
string tagYamlStr1 = "!<str>";
string tagYamlStr2 = "__strn__";
string tagYamlStr3 = "!!str";
var txtFile = File.ReadAllText(addressFileName);
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
if (fileContent == null)
{
msg = ResUI.FailedConversionConfiguration;
return -1;
}
//port
fileContent["port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.http);
//socks-port
fileContent["socks-port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks);
//log-level
fileContent["log-level"] = GetLogLevel(_config.coreBasicItem.loglevel);
//external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}";
//allow-lan
if (_config.inbound[0].allowLANConn)
{
fileContent["allow-lan"] = "true";
fileContent["bind-address"] = "*";
}
else
{
fileContent["allow-lan"] = "false";
}
//ipv6
fileContent["ipv6"] = _config.clashUIItem.enableIPv6;
//mode
if (!fileContent.ContainsKey("mode"))
{
fileContent["mode"] = ERuleMode.Rule.ToString().ToLower();
}
else
{
if (_config.clashUIItem.ruleMode != ERuleMode.Unchanged)
{
fileContent["mode"] = _config.clashUIItem.ruleMode.ToString().ToLower();
}
}
//enable tun mode
if (_config.tunModeItem.enableTun)
{
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
if (!string.IsNullOrEmpty(tun))
{
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
if (tunContent != null)
fileContent["tun"] = tunContent["tun"];
}
}
//Mixin
try
{
MixinContent(fileContent, node);
}
catch (Exception ex)
{
Logging.SaveLog("GenerateClientConfigClash-Mixin", ex);
}
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
File.WriteAllText(fileName, txtFileNew);
//check again
if (!File.Exists(fileName))
{
msg = ResUI.FailedReadConfiguration + "2";
return -1;
}
ClashApiHandler.Instance.ProfileContent = fileContent;
msg = string.Format(ResUI.SuccessfulConfiguration, $"{node.GetSummary()}");
}
catch (Exception ex)
{
Logging.SaveLog("GenerateClientConfigClash", ex);
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
return 0;
}
private void MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
{
//if (!_config.clashUIItem.enableMixinContent)
//{
// return;
//}
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
if (!File.Exists(path))
{
return;
}
var txtFile = File.ReadAllText(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
if (mixinContent == null)
{
return;
}
foreach (var item in mixinContent)
{
if (!_config.tunModeItem.enableTun && item.Key == "tun")
{
continue;
}
if (item.Key.StartsWith("prepend-")
|| item.Key.StartsWith("append-")
|| item.Key.StartsWith("removed-"))
{
ModifyContentMerge(fileContent, item.Key, item.Value);
}
else
{
fileContent[item.Key] = item.Value;
}
}
return;
}
private void ModifyContentMerge(Dictionary<string, object> fileContent, string key, object value)
{
bool blPrepend = false;
bool blRemoved = false;
if (key.StartsWith("prepend-"))
{
blPrepend = true;
key = key.Replace("prepend-", "");
}
else if (key.StartsWith("append-"))
{
blPrepend = false;
key = key.Replace("append-", "");
}
else if (key.StartsWith("removed-"))
{
blRemoved = true;
key = key.Replace("removed-", "");
}
else
{
return;
}
if (!blRemoved && !fileContent.ContainsKey(key))
{
fileContent.Add(key, value);
return;
}
var lstOri = (List<object>)fileContent[key];
var lstValue = (List<object>)value;
if (blRemoved)
{
foreach (var item in lstValue)
{
lstOri.RemoveAll(t => t.ToString().StartsWith(item.ToString()));
}
return;
}
if (blPrepend)
{
lstValue.Reverse();
foreach (var item in lstValue)
{
lstOri.Insert(0, item);
}
}
else
{
foreach (var item in lstValue)
{
lstOri.Add(item);
}
}
}
private string GetLogLevel(string level)
{
if (level == "none")
{
return "silent";
}
else
{
return level;
}
}
}
}

View File

@@ -1,8 +1,9 @@
using System.IO; using System.IO;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler.CoreConfig
{ {
/// <summary> /// <summary>
/// Core configuration file processing class /// Core configuration file processing class
@@ -24,9 +25,17 @@ namespace v2rayN.Handler
msg = ResUI.InitialConfiguration; msg = ResUI.InitialConfiguration;
if (node.configType == EConfigType.Custom) if (node.configType == EConfigType.Custom)
{ {
return GenerateClientCustomConfig(node, fileName, out msg); if (node.coreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo)
{
var configGenClash = new CoreConfigClash(config);
return configGenClash.GenerateClientConfig(node, fileName, out msg);
}
else
{
return GenerateClientCustomConfig(node, fileName, out msg);
}
} }
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box) else if (LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
{ {
var configGenSingbox = new CoreConfigSingbox(config); var configGenSingbox = new CoreConfigSingbox(config);
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0) if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
@@ -154,7 +163,7 @@ namespace v2rayN.Handler
{ {
if (coreType == ECoreType.sing_box) if (coreType == ECoreType.sing_box)
{ {
if ((new CoreConfigSingbox(config)).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0) if (new CoreConfigSingbox(config).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
{ {
return -1; return -1;
} }
@@ -162,7 +171,7 @@ namespace v2rayN.Handler
} }
else else
{ {
if ((new CoreConfigV2ray(config)).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0) if (new CoreConfigV2ray(config).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
{ {
return -1; return -1;
} }

View File

@@ -1,9 +1,11 @@
using System.Net; using System.Data;
using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler.CoreConfig
{ {
internal class CoreConfigSingbox internal class CoreConfigSingbox
{ {
@@ -25,6 +27,11 @@ namespace v2rayN.Handler
msg = ResUI.CheckServerSettings; msg = ResUI.CheckServerSettings;
return -1; return -1;
} }
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp))
{
msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return -1;
}
msg = ResUI.InitialConfiguration; msg = ResUI.InitialConfiguration;
@@ -111,9 +118,11 @@ namespace v2rayN.Handler
{ {
try try
{ {
singboxConfig.inbounds.Clear(); var listen = "::";
singboxConfig.inbounds = [];
if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound)) if (!_config.tunModeItem.enableTun
|| (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound && _config.runningCoreType == ECoreType.sing_box))
{ {
var inbound = new Inbound4Sbox() var inbound = new Inbound4Sbox()
{ {
@@ -146,11 +155,11 @@ namespace v2rayN.Handler
if (_config.inbound[0].newPort4LAN) if (_config.inbound[0].newPort4LAN)
{ {
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true); var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
inbound3.listen = "::"; inbound3.listen = listen;
singboxConfig.inbounds.Add(inbound3); singboxConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false); var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
inbound4.listen = "::"; inbound4.listen = listen;
singboxConfig.inbounds.Add(inbound4); singboxConfig.inbounds.Add(inbound4);
//auth //auth
@@ -162,8 +171,8 @@ namespace v2rayN.Handler
} }
else else
{ {
inbound.listen = "::"; inbound.listen = listen;
inbound2.listen = "::"; inbound2.listen = listen;
} }
} }
} }
@@ -184,7 +193,7 @@ namespace v2rayN.Handler
tunInbound.strict_route = _config.tunModeItem.strictRoute; tunInbound.strict_route = _config.tunModeItem.strictRoute;
tunInbound.stack = _config.tunModeItem.stack; tunInbound.stack = _config.tunModeItem.stack;
tunInbound.sniff = _config.inbound[0].sniffingEnabled; tunInbound.sniff = _config.inbound[0].sniffingEnabled;
tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled; //tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
if (_config.tunModeItem.enableIPv6Address == false) if (_config.tunModeItem.enableIPv6Address == false)
{ {
tunInbound.inet6_address = null; tunInbound.inet6_address = null;
@@ -545,7 +554,7 @@ namespace v2rayN.Handler
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
port = [53], port = [53],
network = "udp", network = ["udp"],
outbound = dnsOutbound outbound = dnsOutbound
}); });
} }
@@ -661,6 +670,10 @@ namespace v2rayN.Handler
rule.port = new List<int> { Utils.ToInt(item.port) }; rule.port = new List<int> { Utils.ToInt(item.port) };
} }
} }
if (!Utils.IsNullOrEmpty(item.network))
{
rule.network = Utils.String2List(item.network);
}
if (item.protocol?.Count > 0) if (item.protocol?.Count > 0)
{ {
rule.protocol = item.protocol; rule.protocol = item.protocol;
@@ -810,24 +823,9 @@ namespace v2rayN.Handler
{ {
return 0; return 0;
} }
//Add the dns of the remote server domain
if (dns4Sbox.rules is null)
{
dns4Sbox.rules = new();
}
dns4Sbox.servers.Add(new()
{
tag = "local_local",
address = "223.5.5.5",
detour = Global.DirectTag,
});
dns4Sbox.rules.Add(new()
{
server = "local_local",
outbound = "any"
});
singboxConfig.dns = dns4Sbox; singboxConfig.dns = dns4Sbox;
GenDnsDomains(node, singboxConfig);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -836,6 +834,48 @@ namespace v2rayN.Handler
return 0; return 0;
} }
private int GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig)
{
var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= [];
dns4Sbox.rules ??= [];
var tag = "local_local";
dns4Sbox.servers.Add(new()
{
tag = tag,
address = "223.5.5.5",
detour = Global.DirectTag,
//strategy = strategy
});
var lstDomain = singboxConfig.outbounds
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
.Select(t => t.server)
.ToList();
if (lstDomain != null && lstDomain.Count > 0)
{
dns4Sbox.rules.Insert(0, new()
{
server = tag,
domain = lstDomain
});
}
//Tun2SocksAddress
if (_config.tunModeItem.enableTun && node?.configType == EConfigType.Socks && Utils.IsDomain(node?.sni))
{
dns4Sbox.rules.Insert(0, new()
{
server = tag,
domain = [node?.sni]
});
}
singboxConfig.dns = dns4Sbox;
return 0;
}
private int GenExperimental(SingboxConfig singboxConfig) private int GenExperimental(SingboxConfig singboxConfig)
{ {
if (_config.guiItem.enableStatistics) if (_config.guiItem.enableStatistics)
@@ -843,7 +883,7 @@ namespace v2rayN.Handler
singboxConfig.experimental ??= new Experimental4Sbox(); singboxConfig.experimental ??= new Experimental4Sbox();
singboxConfig.experimental.clash_api = new Clash_Api4Sbox() singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
{ {
external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort}", external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}",
}; };
} }
@@ -861,6 +901,10 @@ namespace v2rayN.Handler
private int ConvertGeo2Ruleset(SingboxConfig singboxConfig) private int ConvertGeo2Ruleset(SingboxConfig singboxConfig)
{ {
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
{
if (rule_set != null) ruleSets.AddRange(rule_set);
}
var geosite = "geosite"; var geosite = "geosite";
var geoip = "geoip"; var geoip = "geoip";
var ruleSets = new List<string>(); var ruleSets = new List<string>();
@@ -870,13 +914,13 @@ namespace v2rayN.Handler
{ {
rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList(); rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList();
rule.geosite = null; rule.geosite = null;
ruleSets.AddRange(rule.rule_set); AddRuleSets(ruleSets, rule.rule_set);
} }
foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
{ {
rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList(); rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList();
rule.geoip = null; rule.geoip = null;
ruleSets.AddRange(rule.rule_set); AddRuleSets(ruleSets, rule.rule_set);
} }
//convert dns geosite & geoip to ruleset //convert dns geosite & geoip to ruleset
@@ -892,7 +936,15 @@ namespace v2rayN.Handler
} }
foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? []) foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
{ {
ruleSets.AddRange(dnsRule.rule_set); AddRuleSets(ruleSets, dnsRule.rule_set);
}
//rules in rules
foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
{
foreach (var item2 in item ?? [])
{
AddRuleSets(ruleSets, item2.rule_set);
}
} }
//load custom ruleset file //load custom ruleset file
@@ -918,6 +970,7 @@ namespace v2rayN.Handler
singboxConfig.route.rule_set = []; singboxConfig.route.rule_set = [];
foreach (var item in new HashSet<string>(ruleSets)) foreach (var item in new HashSet<string>(ruleSets))
{ {
if (Utils.IsNullOrEmpty(item)) { continue; }
var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item)); var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item));
if (customRuleset != null) if (customRuleset != null)
{ {
@@ -930,7 +983,7 @@ namespace v2rayN.Handler
type = "remote", type = "remote",
format = "binary", format = "binary",
tag = item, tag = item,
url = string.Format(Global.SingboxRulesetUrl, item.StartsWith(geosite) ? geosite : geoip, item), url = string.Format(Global.SingboxRulesetUrl, item.StartsWith(geosite) ? geosite : geoip, item),
download_detour = Global.ProxyTag download_detour = Global.ProxyTag
}); });
} }
@@ -1076,18 +1129,18 @@ namespace v2rayN.Handler
singboxConfig.route.rules.Add(rule); singboxConfig.route.rules.Add(rule);
} }
GenDns(new(), singboxConfig); GenDnsDomains(null, singboxConfig);
var dnsServer = singboxConfig.dns?.servers.FirstOrDefault(); //var dnsServer = singboxConfig.dns?.servers.FirstOrDefault();
if (dnsServer != null) //if (dnsServer != null)
{ //{
dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound; // dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound;
} //}
var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault(); //var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault();
if (dnsRule != null) //if (dnsRule != null)
{ //{
singboxConfig.dns.rules = []; // singboxConfig.dns.rules = [];
singboxConfig.dns.rules.Add(dnsRule); // singboxConfig.dns.rules.Add(dnsRule);
} //}
//msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); //msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
return 0; return 0;

View File

@@ -1,9 +1,11 @@
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Text.Json.Nodes;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler.CoreConfig
{ {
internal class CoreConfigV2ray internal class CoreConfigV2ray
{ {
@@ -52,7 +54,7 @@ namespace v2rayN.Handler
GenMoreOutbounds(node, v2rayConfig); GenMoreOutbounds(node, v2rayConfig);
GenDns(v2rayConfig); GenDns(node, v2rayConfig);
GenStatistic(v2rayConfig); GenStatistic(v2rayConfig);
@@ -98,7 +100,8 @@ namespace v2rayN.Handler
{ {
try try
{ {
v2rayConfig.inbounds = new List<Inbounds4Ray>(); var listen = "0.0.0.0";
v2rayConfig.inbounds = [];
Inbounds4Ray? inbound = GetInbound(_config.inbound[0], EInboundProtocol.socks, true); Inbounds4Ray? inbound = GetInbound(_config.inbound[0], EInboundProtocol.socks, true);
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
@@ -112,11 +115,11 @@ namespace v2rayN.Handler
if (_config.inbound[0].newPort4LAN) if (_config.inbound[0].newPort4LAN)
{ {
var inbound3 = GetInbound(_config.inbound[0], EInboundProtocol.socks2, true); var inbound3 = GetInbound(_config.inbound[0], EInboundProtocol.socks2, true);
inbound3.listen = "0.0.0.0"; inbound3.listen = listen;
v2rayConfig.inbounds.Add(inbound3); v2rayConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(_config.inbound[0], EInboundProtocol.http2, false); var inbound4 = GetInbound(_config.inbound[0], EInboundProtocol.http2, false);
inbound4.listen = "0.0.0.0"; inbound4.listen = listen;
v2rayConfig.inbounds.Add(inbound4); v2rayConfig.inbounds.Add(inbound4);
//auth //auth
@@ -131,8 +134,8 @@ namespace v2rayN.Handler
} }
else else
{ {
inbound.listen = "0.0.0.0"; inbound.listen = listen;
inbound2.listen = "0.0.0.0"; inbound2.listen = listen;
} }
} }
} }
@@ -143,24 +146,25 @@ namespace v2rayN.Handler
return 0; return 0;
} }
private Inbounds4Ray? GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks) private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{ {
string result = Utils.GetEmbedText(Global.V2raySampleInbound); string result = Utils.GetEmbedText(Global.V2raySampleInbound);
if (Utils.IsNullOrEmpty(result)) if (Utils.IsNullOrEmpty(result))
{ {
return null; return new();
} }
var inbound = JsonUtils.Deserialize<Inbounds4Ray>(result); var inbound = JsonUtils.Deserialize<Inbounds4Ray>(result);
if (inbound == null) if (inbound == null)
{ {
return null; return new();
} }
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.port = inItem.localPort + (int)protocol; inbound.port = inItem.localPort + (int)protocol;
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString(); inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
inbound.settings.udp = inItem.udpEnabled; inbound.settings.udp = inItem.udpEnabled;
inbound.sniffing.enabled = inItem.sniffingEnabled; inbound.sniffing.enabled = inItem.sniffingEnabled;
inbound.sniffing.destOverride = inItem.destOverride;
inbound.sniffing.routeOnly = inItem.routeOnly; inbound.sniffing.routeOnly = inItem.routeOnly;
return inbound; return inbound;
@@ -217,39 +221,43 @@ namespace v2rayN.Handler
return 0; return 0;
} }
private int GenRoutingUserRule(RulesItem4Ray? rules, V2rayConfig v2rayConfig) private int GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
{ {
try try
{ {
if (rules == null) if (rule == null)
{ {
return 0; return 0;
} }
if (Utils.IsNullOrEmpty(rules.port)) if (Utils.IsNullOrEmpty(rule.port))
{ {
rules.port = null; rule.port = null;
} }
if (rules.domain?.Count == 0) if (Utils.IsNullOrEmpty(rule.network))
{ {
rules.domain = null; rule.network = null;
} }
if (rules.ip?.Count == 0) if (rule.domain?.Count == 0)
{ {
rules.ip = null; rule.domain = null;
} }
if (rules.protocol?.Count == 0) if (rule.ip?.Count == 0)
{ {
rules.protocol = null; rule.ip = null;
} }
if (rules.inboundTag?.Count == 0) if (rule.protocol?.Count == 0)
{ {
rules.inboundTag = null; rule.protocol = null;
}
if (rule.inboundTag?.Count == 0)
{
rule.inboundTag = null;
} }
var hasDomainIp = false; var hasDomainIp = false;
if (rules.domain?.Count > 0) if (rule.domain?.Count > 0)
{ {
var it = JsonUtils.DeepCopy(rules); var it = JsonUtils.DeepCopy(rule);
it.ip = null; it.ip = null;
it.type = "field"; it.type = "field";
for (int k = it.domain.Count - 1; k >= 0; k--) for (int k = it.domain.Count - 1; k >= 0; k--)
@@ -263,9 +271,9 @@ namespace v2rayN.Handler
v2rayConfig.routing.rules.Add(it); v2rayConfig.routing.rules.Add(it);
hasDomainIp = true; hasDomainIp = true;
} }
if (rules.ip?.Count > 0) if (rule.ip?.Count > 0)
{ {
var it = JsonUtils.DeepCopy(rules); var it = JsonUtils.DeepCopy(rule);
it.domain = null; it.domain = null;
it.type = "field"; it.type = "field";
v2rayConfig.routing.rules.Add(it); v2rayConfig.routing.rules.Add(it);
@@ -273,12 +281,12 @@ namespace v2rayN.Handler
} }
if (!hasDomainIp) if (!hasDomainIp)
{ {
if (!Utils.IsNullOrEmpty(rules.port) if (!Utils.IsNullOrEmpty(rule.port)
|| (rules.protocol?.Count > 0) || rule.protocol?.Count > 0
|| (rules.inboundTag?.Count > 0) || rule.inboundTag?.Count > 0
) )
{ {
var it = JsonUtils.DeepCopy(rules); var it = JsonUtils.DeepCopy(rule);
it.type = "field"; it.type = "field";
v2rayConfig.routing.rules.Add(it); v2rayConfig.routing.rules.Add(it);
} }
@@ -562,6 +570,7 @@ namespace v2rayN.Handler
publicKey = node.publicKey, publicKey = node.publicKey,
shortId = node.shortId, shortId = node.shortId,
spiderX = node.spiderX, spiderX = node.spiderX,
show = false,
}; };
streamSettings.realitySettings = realitySettings; streamSettings.realitySettings = realitySettings;
@@ -627,6 +636,25 @@ namespace v2rayN.Handler
} }
streamSettings.httpupgradeSettings = httpupgradeSettings; streamSettings.httpupgradeSettings = httpupgradeSettings;
break;
//splithttp
case nameof(ETransport.splithttp):
SplithttpSettings4Ray splithttpSettings = new()
{
maxUploadSize = 1000000,
maxConcurrentUploads = 10
};
if (!Utils.IsNullOrEmpty(node.path))
{
splithttpSettings.path = node.path;
}
if (!Utils.IsNullOrEmpty(host))
{
splithttpSettings.host = host;
}
streamSettings.splithttpSettings = splithttpSettings;
break; break;
//h2 //h2
case nameof(ETransport.h2): case nameof(ETransport.h2):
@@ -671,7 +699,7 @@ namespace v2rayN.Handler
{ {
authority = Utils.IsNullOrEmpty(host) ? null : host, authority = Utils.IsNullOrEmpty(host) ? null : host,
serviceName = node.path, serviceName = node.path,
multiMode = (node.headerType == Global.GrpcMultiMode), multiMode = node.headerType == Global.GrpcMultiMode,
idle_timeout = _config.grpcItem.idle_timeout, idle_timeout = _config.grpcItem.idle_timeout,
health_check_timeout = _config.grpcItem.health_check_timeout, health_check_timeout = _config.grpcItem.health_check_timeout,
permit_without_stream = _config.grpcItem.permit_without_stream, permit_without_stream = _config.grpcItem.permit_without_stream,
@@ -721,7 +749,7 @@ namespace v2rayN.Handler
return 0; return 0;
} }
private int GenDns(V2rayConfig v2rayConfig) private int GenDns(ProfileItem node, V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -773,6 +801,8 @@ namespace v2rayN.Handler
} }
} }
GenDnsDomains(node, obj);
v2rayConfig.dns = obj; v2rayConfig.dns = obj;
} }
catch (Exception ex) catch (Exception ex)
@@ -782,6 +812,24 @@ namespace v2rayN.Handler
return 0; return 0;
} }
private int GenDnsDomains(ProfileItem node, JsonNode dns)
{
var servers = dns["servers"];
if (servers != null)
{
if (Utils.IsDomain(node.address))
{
var dnsServer = new DnsServer4Ray()
{
address = "223.5.5.5",
domains = [node.address]
};
servers.AsArray().Insert(0, JsonUtils.SerializeToNode(dnsServer));
}
}
return 0;
}
private int GenStatistic(V2rayConfig v2rayConfig) private int GenStatistic(V2rayConfig v2rayConfig)
{ {
if (_config.guiItem.enableStatistics) if (_config.guiItem.enableStatistics)

View File

@@ -1,7 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reactive.Linq;
using System.Text; using System.Text;
using v2rayN.Enums;
using v2rayN.Handler.CoreConfig;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
@@ -39,6 +40,7 @@ namespace v2rayN.Handler
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0) if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
{ {
ShowMsg(false, msg); ShowMsg(false, msg);
return;
} }
else else
{ {
@@ -54,22 +56,22 @@ namespace v2rayN.Handler
CoreStart(node); CoreStart(node);
//In tun mode, do a delay check and restart the core //In tun mode, do a delay check and restart the core
if (_config.tunModeItem.enableTun) //if (_config.tunModeItem.enableTun)
{ //{
Observable.Range(1, 1) // Observable.Range(1, 1)
.Delay(TimeSpan.FromSeconds(15)) // .Delay(TimeSpan.FromSeconds(15))
.Subscribe(x => // .Subscribe(x =>
{ // {
{ // {
if (_process == null || _process.HasExited) // if (_process == null || _process.HasExited)
{ // {
CoreStart(node); // CoreStart(node);
ShowMsg(false, "Tun mode restart the core once"); // ShowMsg(false, "Tun mode restart the core once");
Logging.SaveLog("Tun mode restart the core once"); // Logging.SaveLog("Tun mode restart the core once");
} // }
} // }
}); // });
} //}
} }
} }
@@ -183,15 +185,16 @@ namespace v2rayN.Handler
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ECoreType coreType; //ECoreType coreType;
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun) //if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
{ //{
coreType = ECoreType.sing_box; // coreType = ECoreType.sing_box;
} //}
else //else
{ //{
coreType = LazyConfig.Instance.GetCoreType(node, node.configType); // coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
} //}
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
_config.runningCoreType = coreType; _config.runningCoreType = coreType;
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType); var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
@@ -203,13 +206,26 @@ namespace v2rayN.Handler
} }
_process = proc; _process = proc;
//start a socks service //start a pre service
if (_process != null && !_process.HasExited) if (_process != null && !_process.HasExited)
{ {
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0)) ProfileItem? itemSocks = null;
var preCoreType = ECoreType.sing_box;
if (node.configType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.tunModeItem.enableTun)
{ {
var preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray; itemSocks = new ProfileItem()
var itemSocks = new ProfileItem() {
coreType = preCoreType,
configType = EConfigType.Socks,
address = Global.Loopback,
sni = node.address, //Tun2SocksAddress
port = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
{
preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{ {
coreType = preCoreType, coreType = preCoreType,
configType = EConfigType.Socks, configType = EConfigType.Socks,
@@ -217,6 +233,9 @@ namespace v2rayN.Handler
port = node.preSocksPort port = node.preSocksPort
}; };
_config.runningCoreType = preCoreType; _config.runningCoreType = preCoreType;
}
if (itemSocks != null)
{
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName); string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0) if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
{ {

View File

@@ -4,7 +4,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Sockets; using System.Net.Sockets;
using v2rayN.Models; using v2rayN.Enums;
using v2rayN.Resx; using v2rayN.Resx;
namespace v2rayN.Handler namespace v2rayN.Handler

View File

@@ -0,0 +1,203 @@
using System.Collections.Specialized;
using System.IO;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class BaseFmt
{
protected static string GetIpv6(string address)
{
if (Utils.IsIpv6(address))
{
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
}
return address; // 如果不是IPv6地址直接返回原地址
}
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (!Utils.IsNullOrEmpty(item.flow))
{
dicQuery.Add("flow", item.flow);
}
if (!Utils.IsNullOrEmpty(item.streamSecurity))
{
dicQuery.Add("security", item.streamSecurity);
}
else
{
if (securityDef != null)
{
dicQuery.Add("security", securityDef);
}
}
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
if (!Utils.IsNullOrEmpty(item.fingerprint))
{
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
}
if (!Utils.IsNullOrEmpty(item.publicKey))
{
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
}
if (!Utils.IsNullOrEmpty(item.shortId))
{
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
}
if (!Utils.IsNullOrEmpty(item.spiderX))
{
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
}
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
switch (item.network)
{
case nameof(ETransport.tcp):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
break;
case nameof(ETransport.kcp):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("seed", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("path", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("path", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.quic):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? 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.IsNullOrEmpty(item.path))
{
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode)
{
dicQuery.Add("mode", Utils.UrlEncode(item.headerType));
}
}
break;
}
return 0;
}
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
item.flow = query["flow"] ?? "";
item.streamSecurity = query["security"] ?? "";
item.sni = query["sni"] ?? "";
item.alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.publicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.shortId = Utils.UrlDecode(query["sid"] ?? "");
item.spiderX = Utils.UrlDecode(query["spx"] ?? "");
item.network = query["type"] ?? nameof(ETransport.tcp);
switch (item.network)
{
case nameof(ETransport.tcp):
item.headerType = query["headerType"] ?? Global.None;
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
break;
case nameof(ETransport.kcp):
item.headerType = query["headerType"] ?? Global.None;
item.path = Utils.UrlDecode(query["seed"] ?? "");
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
item.path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.network = nameof(ETransport.h2);
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
item.path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.quic):
item.headerType = query["headerType"] ?? Global.None;
item.requestHost = query["quicSecurity"] ?? Global.None;
item.path = Utils.UrlDecode(query["key"] ?? "");
break;
case nameof(ETransport.grpc):
item.requestHost = Utils.UrlDecode(query["authority"] ?? "");
item.path = Utils.UrlDecode(query["serviceName"] ?? "");
item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
break;
default:
break;
}
return 0;
}
protected static bool Contains(string str, params string[] s)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
protected static string WriteAllText(string strData, string ext = "json")
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.{ext}");
File.WriteAllText(fileName, strData);
return fileName;
}
}
}

View File

@@ -0,0 +1,26 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class ClashFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "port", "socks-port", "proxies"))
{
var fileName = WriteAllText(strData, "yaml");
var profileItem = new ProfileItem
{
coreType = ECoreType.mihomo,
address = fileName,
remarks = subRemarks ?? "clash_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,94 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class FmtHandler
{
public static string? GetShareUri(ProfileItem item)
{
try
{
var url = item.configType switch
{
EConfigType.VMess => VmessFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.Socks => SocksFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.Tuic => TuicFmt.ToUri(item),
EConfigType.Wireguard => WireguardFmt.ToUri(item),
_ => null,
};
return url;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return "";
}
}
public static ProfileItem? ResolveConfig(string config, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
try
{
string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str))
{
msg = ResUI.FailedReadConfiguration;
return null;
}
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{
return VmessFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
{
return ShadowsocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Socks]))
{
return SocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
{
return TrojanFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
{
return VLESSFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
{
return Hysteria2Fmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Tuic]))
{
return TuicFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Wireguard]))
{
return WireguardFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
return null;
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
msg = ResUI.Incorrectconfiguration;
return null;
}
}
}
}

View File

@@ -0,0 +1,104 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class Hysteria2Fmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Hysteria2
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
}
dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0");
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}";
return url;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.hysteria,
address = fileName,
remarks = subRemarks ?? "hysteria_custom"
};
return profileItem;
}
return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.hysteria2,
address = fileName,
remarks = subRemarks ?? "hysteria2_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,26 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class NaiveproxyFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.naiveproxy,
address = fileName,
remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,183 @@
using System.Text.RegularExpressions;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class ShadowsocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
if (item == null)
{
return null;
}
if (item.address.Length == 0 || item.port == 0 || item.security.Length == 0 || item.id.Length == 0)
{
return null;
}
item.configType = EConfigType.Shadowsocks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}";
return url;
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static ProfileItem? ResolveSSLegacy(string result)
{
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.IsNullOrEmpty(tag))
{
item.remarks = Utils.UrlDecode(tag);
}
Match details;
try
{
details = DetailsParser.Match(Utils.Base64Decode(base64));
}
catch (FormatException)
{
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);
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(result);
}
catch (UriFormatException)
{
return null;
}
ProfileItem item = new()
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
address = parsedUrl.IdnHost,
port = parsedUrl.Port,
};
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped);
//2022-blake3
if (rawUserInfo.Contains(':'))
{
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.security = userInfoParts[0];
item.id = Utils.UrlDecode(userInfoParts[1]);
}
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.security = userInfoParts[0];
item.id = userInfoParts[1];
}
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.network = Global.DefaultNetwork;
item.headerType = Global.TcpHeaderHttp;
item.requestHost = obfsHost ?? "";
}
else
{
return null;
}
}
return item;
}
public static List<ProfileItem>? ResolveSip008(string result)
{
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0)
{
List<ProfileItem> lst = [];
foreach (var it in lstSsServer)
{
var ssItem = new ProfileItem()
{
remarks = it.remarks,
security = it.method,
id = it.password,
address = it.server,
port = Utils.ToInt(it.server_port)
};
lst.Add(ssItem);
}
return lst;
}
return null;
}
}
}

View File

@@ -0,0 +1,58 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class SingboxFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
if (singboxCon?.inbounds?.Count > 0
&& singboxCon.outbounds?.Count > 0
&& singboxCon.route != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.sing_box,
address = fileName,
remarks = subRemarks ?? "singbox_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
if (singboxConfig?.inbounds?.Count > 0
&& singboxConfig.outbounds?.Count > 0
&& singboxConfig.route != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.sing_box,
address = fileName,
remarks = subRemarks ?? "singbox_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,131 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class SocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null)
{
return null;
}
if (item.address.Length == 0 || item.port == 0)
{
return null;
}
item.configType = EConfigType.Socks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}";
return url;
}
private static ProfileItem? ResolveSocks(string result)
{
ProfileItem item = new()
{
configType = EConfigType.Socks
};
result = result[Global.ProtocolShares[EConfigType.Socks].Length..];
//remark
int indexRemark = result.IndexOf("#");
if (indexRemark > 0)
{
try
{
item.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
}
catch { }
result = result[..indexRemark];
}
//part decode
int indexS = result.IndexOf("@");
if (indexS > 0)
{
}
else
{
result = Utils.Base64Decode(result);
}
string[] arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
string[] arr21 = arr1[0].Split(':');
//string[] arr22 = arr1[1].Split(':');
int indexPort = arr1[1].LastIndexOf(":");
if (arr21.Length != 2 || indexPort < 0)
{
return null;
}
item.address = arr1[1][..indexPort];
item.port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.security = arr21[0];
item.id = arr21[1];
return item;
}
private static ProfileItem? ResolveSocksNew(string result)
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(result);
}
catch (UriFormatException)
{
return null;
}
ProfileItem item = new()
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
address = parsedUrl.IdnHost,
port = parsedUrl.Port,
};
// parse base64 UserInfo
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.security = userInfoParts[0];
item.id = userInfoParts[1];
}
return item;
}
}
}

View File

@@ -0,0 +1,53 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class TrojanFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Trojan
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
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.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,68 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class TuicFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Tuic
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.id = userInfoParts[0];
item.security = userInfoParts[1];
}
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.headerType = query["congestion_control"] ?? "";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
dicQuery.Add("congestion_control", item.headerType);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
$"{item.id}:{item.security}",
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,59 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class V2rayFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
if (v2rayCon?.inbounds?.Count > 0
&& v2rayCon.outbounds?.Count > 0
&& v2rayCon.routing != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
if (v2rayConfig?.inbounds?.Count > 0
&& v2rayConfig.outbounds?.Count > 0
&& v2rayConfig.routing != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,64 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class VLESSFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.VLESS,
security = Global.None
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.security = query["encryption"] ?? Global.None;
item.streamSecurity = query["security"] ?? "";
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.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.security))
{
dicQuery.Add("encryption", item.security);
}
else
{
dicQuery.Add("encryption", Global.None);
}
GetStdTransport(item, Global.None, ref dicQuery);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,229 @@
using System.Text.RegularExpressions;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class VmessFmt : BaseFmt
{
private static readonly Regex StdVmessUserInfo = new(
@"^(?<network>[a-z]+)(\+(?<streamSecurity>[a-z]+))?:(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled);
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
int indexSplit = str.IndexOf("?");
if (indexSplit > 0)
{
item = ResolveStdVmess(str) ?? ResolveVmess4Kitsunebi(str);
}
else
{
item = ResolveVmess(str, out msg);
}
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
VmessQRCode vmessQRCode = new()
{
v = item.configVersion,
ps = item.remarks.TrimEx(),
add = item.address,
port = item.port,
id = item.id,
aid = item.alterId,
scy = item.security,
net = item.network,
type = item.headerType,
host = item.requestHost,
path = item.path,
tls = item.streamSecurity,
sni = item.sni,
alpn = item.alpn,
fp = item.fingerprint
};
url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
return url;
}
private static ProfileItem? ResolveVmess(string result, out string msg)
{
msg = string.Empty;
var item = new ProfileItem
{
configType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
//转成Json
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
return null;
}
item.network = Global.DefaultNetwork;
item.headerType = Global.None;
item.configVersion = Utils.ToInt(vmessQRCode.v);
item.remarks = Utils.ToString(vmessQRCode.ps);
item.address = Utils.ToString(vmessQRCode.add);
item.port = Utils.ToInt(vmessQRCode.port);
item.id = Utils.ToString(vmessQRCode.id);
item.alterId = Utils.ToInt(vmessQRCode.aid);
item.security = Utils.ToString(vmessQRCode.scy);
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
{
item.network = vmessQRCode.net;
}
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
{
item.headerType = vmessQRCode.type;
}
item.requestHost = Utils.ToString(vmessQRCode.host);
item.path = Utils.ToString(vmessQRCode.path);
item.streamSecurity = Utils.ToString(vmessQRCode.tls);
item.sni = Utils.ToString(vmessQRCode.sni);
item.alpn = Utils.ToString(vmessQRCode.alpn);
item.fingerprint = Utils.ToString(vmessQRCode.fp);
return item;
}
private static ProfileItem? ResolveStdVmess(string result)
{
ProfileItem item = new()
{
configType = EConfigType.VMess,
security = "auto"
};
Uri u = new(result);
item.address = u.IdnHost;
item.port = u.Port;
item.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var query = Utils.ParseQueryString(u.Query);
var m = StdVmessUserInfo.Match(u.UserInfo);
if (!m.Success) return null;
item.id = m.Groups["id"].Value;
if (m.Groups["streamSecurity"].Success)
{
item.streamSecurity = m.Groups["streamSecurity"].Value;
}
switch (item.streamSecurity)
{
case Global.StreamSecurity:
break;
default:
if (!Utils.IsNullOrEmpty(item.streamSecurity))
return null;
break;
}
item.network = m.Groups["network"].Value;
switch (item.network)
{
case nameof(ETransport.tcp):
string t1 = query["type"] ?? Global.None;
item.headerType = t1;
break;
case nameof(ETransport.kcp):
item.headerType = query["type"] ?? Global.None;
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
string p1 = query["path"] ?? "/";
string h1 = query["host"] ?? "";
item.requestHost = Utils.UrlDecode(h1);
item.path = p1;
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.network = nameof(ETransport.h2);
string p2 = query["path"] ?? "/";
string h2 = query["host"] ?? "";
item.requestHost = Utils.UrlDecode(h2);
item.path = p2;
break;
case nameof(ETransport.quic):
string s = query["security"] ?? Global.None;
string k = query["key"] ?? "";
string t3 = query["type"] ?? Global.None;
item.headerType = t3;
item.requestHost = Utils.UrlDecode(s);
item.path = k;
break;
default:
return null;
}
return item;
}
private static ProfileItem? ResolveVmess4Kitsunebi(string result)
{
ProfileItem item = new()
{
configType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
int indexSplit = result.IndexOf("?");
if (indexSplit > 0)
{
result = result[..indexSplit];
}
result = Utils.Base64Decode(result);
string[] arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
string[] arr21 = arr1[0].Split(':');
string[] arr22 = arr1[1].Split(':');
if (arr21.Length != 2 || arr22.Length != 2)
{
return null;
}
item.address = arr22[0];
item.port = Utils.ToInt(arr22[1]);
item.security = arr21[0];
item.id = arr21[1];
item.network = Global.DefaultNetwork;
item.headerType = Global.None;
item.remarks = "Alien";
return item;
}
}
}

View File

@@ -0,0 +1,73 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class WireguardFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Wireguard
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.publicKey = Utils.UrlDecode(query["publickey"] ?? "");
item.path = Utils.UrlDecode(query["reserved"] ?? "");
item.requestHost = Utils.UrlDecode(query["address"] ?? "");
item.shortId = Utils.UrlDecode(query["mtu"] ?? "");
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.publicKey))
{
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
}
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.shortId))
{
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
}
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
Utils.UrlEncode(item.id),
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}";
return url;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Interop;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -1,4 +1,5 @@
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
namespace v2rayN.Handler namespace v2rayN.Handler
@@ -12,20 +13,26 @@ namespace v2rayN.Handler
public static LazyConfig Instance => _instance.Value; public static LazyConfig Instance => _instance.Value;
private int? _statePort; private int? _statePort;
private int? _statePort2;
public int StatePort public int StatePort
{ {
get get
{ {
if (_statePort is null) _statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
{
_statePort = Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
}
return _statePort.Value; return _statePort.Value;
} }
} }
public int StatePort2
{
get
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value;
}
}
private Job _processJob = new(); private Job _processJob = new();
public LazyConfig() public LazyConfig()
@@ -329,7 +336,11 @@ namespace v2rayN.Handler
arguments = "-f config.json", arguments = "-f config.json",
coreUrl = Global.MihomoCoreUrl, coreUrl = Global.MihomoCoreUrl,
coreReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl), coreReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
coreDownloadUrl32 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-386-{0}.zip",
coreDownloadUrl64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
coreDownloadUrlArm64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
match = "Mihomo", match = "Mihomo",
versionArg = "-v",
redirectInfo = true, redirectInfo = true,
}); });

View File

@@ -3,6 +3,8 @@ using Splat;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using v2rayN.Enums;
using v2rayN.Handler.CoreConfig;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -29,15 +29,23 @@ namespace v2rayN.Handler
/// <returns>true: one of connection is successfully updated proxy settings</returns> /// <returns>true: one of connection is successfully updated proxy settings</returns>
public static bool SetProxy(string? strProxy, string? exceptions, int type) public static bool SetProxy(string? strProxy, string? exceptions, int type)
{ {
// set proxy for LAN try
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
{ {
result |= SetConnectionProxy(connection, strProxy, exceptions, type); // set proxy for LAN
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
{
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
}
return result;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return false;
} }
return result;
} }
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type) private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -1,5 +1,6 @@
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
namespace v2rayN.Handler namespace v2rayN.Handler
@@ -27,7 +28,7 @@ namespace v2rayN.Handler
try try
{ {
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort}/traffic"; url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort2}/traffic";
if (webSocket == null) if (webSocket == null)
{ {

View File

@@ -1,6 +1,7 @@
using Grpc.Core; using Grpc.Core;
using Grpc.Net.Client; using Grpc.Net.Client;
using ProtosLib.Statistics; using ProtosLib.Statistics;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
namespace v2rayN.Handler namespace v2rayN.Handler
@@ -86,6 +87,8 @@ namespace v2rayN.Handler
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server) private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
{ {
server = new(); server = new();
long aggregateProxyUp = 0;
long aggregateProxyDown = 0;
try try
{ {
foreach (Stat stat in source) foreach (Stat stat in source)
@@ -100,15 +103,15 @@ namespace v2rayN.Handler
name = nStr[1]; name = nStr[1];
type = nStr[3]; type = nStr[3];
if (name == Global.ProxyTag) if (name.StartsWith(Global.ProxyTag))
{ {
if (type == "uplink") if (type == "uplink")
{ {
server.proxyUp = value; aggregateProxyUp += value;
} }
else if (type == "downlink") else if (type == "downlink")
{ {
server.proxyDown = value; aggregateProxyDown += value;
} }
} }
else if (name == Global.DirectTag) else if (name == Global.DirectTag)
@@ -123,6 +126,8 @@ namespace v2rayN.Handler
} }
} }
} }
server.proxyUp = aggregateProxyUp;
server.proxyDown = aggregateProxyDown;
} }
catch catch
{ {

View File

@@ -1,34 +1,12 @@
using PacLib; using PacLib;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
namespace v2rayN.Handler namespace v2rayN.Handler
{ {
public static class SysProxyHandle public static class SysProxyHandle
{ {
//private const string _userWininetConfigFile = "user-wininet.json"; private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
//private static string _queryStr;
// In general, this won't change
// format:
// <flags><CR-LF>
// <proxy-server><CR-LF>
// <bypass-list><CR-LF>
// <pac-url>
private enum RET_ERRORS : int
{
RET_NO_ERROR = 0,
INVALID_FORMAT = 1,
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4,
INVAILD_OPTION_COUNT = 5,
};
static SysProxyHandle()
{
}
public static bool UpdateSysProxy(Config config, bool forceDisable) public static bool UpdateSysProxy(Config config, bool forceDisable)
{ {
@@ -64,11 +42,17 @@ namespace v2rayN.Handler
.Replace("{http_port}", port.ToString()) .Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString()); .Replace("{socks_port}", portSocks.ToString());
} }
ProxySetting.SetProxy(strProxy, strExceptions, 2); // set a named proxy if (!ProxySetting.SetProxy(strProxy, strExceptions, 2))
{
SetProxy(strProxy, strExceptions, 2);
}
} }
else if (type == ESysProxyType.ForcedClear) else if (type == ESysProxyType.ForcedClear)
{ {
ProxySetting.UnsetProxy(); // set to no proxy if (!ProxySetting.UnsetProxy())
{
UnsetProxy();
}
} }
else if (type == ESysProxyType.Unchanged) else if (type == ESysProxyType.Unchanged)
{ {
@@ -77,7 +61,10 @@ namespace v2rayN.Handler
{ {
PacHandler.Start(Utils.GetConfigPath(), port, portPac); PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySetting.SetProxy(strProxy, "", 4); // use pac script url for auto-config proxy if (!ProxySetting.SetProxy(strProxy, "", 4))
{
SetProxy(strProxy, "", 4);
}
} }
if (type != ESysProxyType.Pac) if (type != ESysProxyType.Pac)
@@ -94,14 +81,38 @@ namespace v2rayN.Handler
public static void ResetIEProxy4WindowsShutDown() public static void ResetIEProxy4WindowsShutDown()
{ {
try SetProxy(null, null, 1);
}
private static void UnsetProxy()
{
SetProxy(null, null, 1);
}
private static bool SetProxy(string? strProxy, string? exceptions, int type)
{
if (type == 1)
{ {
//TODO To be verified Utils.RegWriteValue(_regPath, "ProxyEnable", 0);
Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0); Utils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
Utils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
Utils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
} }
catch if (type == 2)
{ {
Utils.RegWriteValue(_regPath, "ProxyEnable", 1);
Utils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
Utils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
Utils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
} }
else if (type == 4)
{
Utils.RegWriteValue(_regPath, "ProxyEnable", 0);
Utils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
Utils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
Utils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
}
return true;
} }
} }
} }

View File

@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
@@ -299,9 +300,6 @@ namespace v2rayN.Handler
{ {
await UpdateGeoFile("geosite", _config, update); await UpdateGeoFile("geosite", _config, update);
await UpdateGeoFile("geoip", _config, update); await UpdateGeoFile("geoip", _config, update);
//await UpdateGeoFile4Singbox("geosite", _config, false, update);
//await UpdateGeoFile4Singbox("geoip", _config, true, update);
}); });
} }
@@ -327,7 +325,7 @@ namespace v2rayN.Handler
var result = await (new DownloadHandle()).DownloadStringAsync(url, true, ""); var result = await (new DownloadHandle()).DownloadStringAsync(url, true, "");
if (!Utils.IsNullOrEmpty(result)) if (!Utils.IsNullOrEmpty(result))
{ {
responseHandler(type, result, preRelease); ResponseHandler(type, result, preRelease);
} }
else else
{ {
@@ -345,7 +343,7 @@ namespace v2rayN.Handler
/// <summary> /// <summary>
/// 获取V2RayCore版本 /// 获取V2RayCore版本
/// </summary> /// </summary>
private SemanticVersion getCoreVersion(ECoreType type) private SemanticVersion GetCoreVersion(ECoreType type)
{ {
try try
{ {
@@ -410,7 +408,7 @@ namespace v2rayN.Handler
} }
} }
private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease) private void ResponseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
{ {
try try
{ {
@@ -431,7 +429,7 @@ namespace v2rayN.Handler
case ECoreType.Xray: case ECoreType.Xray:
case ECoreType.v2fly_v5: case ECoreType.v2fly_v5:
{ {
curVersion = getCoreVersion(type); curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v")); message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
string osBit = "64"; string osBit = "64";
switch (RuntimeInformation.ProcessArchitecture) switch (RuntimeInformation.ProcessArchitecture)
@@ -456,7 +454,7 @@ namespace v2rayN.Handler
case ECoreType.clash_meta: case ECoreType.clash_meta:
case ECoreType.mihomo: case ECoreType.mihomo:
{ {
curVersion = getCoreVersion(type); curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion); message = string.Format(ResUI.IsLatestCore, type, curVersion);
switch (RuntimeInformation.ProcessArchitecture) switch (RuntimeInformation.ProcessArchitecture)
{ {
@@ -477,7 +475,7 @@ namespace v2rayN.Handler
} }
case ECoreType.sing_box: case ECoreType.sing_box:
{ {
curVersion = getCoreVersion(type); curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v")); message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
switch (RuntimeInformation.ProcessArchitecture) switch (RuntimeInformation.ProcessArchitecture)
{ {

View File

@@ -0,0 +1,17 @@
namespace v2rayN.Models
{
public class ClashConnectionModel
{
public string id { get; set; }
public string network { get; set; }
public string type { get; set; }
public string host { get; set; }
public ulong upload { get; set; }
public ulong download { get; set; }
public string uploadTraffic { get; set; }
public string downloadTraffic { get; set; }
public double time { get; set; }
public string elapsed { get; set; }
public string chain { get; set; }
}
}

View File

@@ -0,0 +1,37 @@
namespace v2rayN.Models
{
public class ClashConnections
{
public ulong downloadTotal { get; set; }
public ulong uploadTotal { get; set; }
public List<ConnectionItem>? connections { get; set; }
}
public class ConnectionItem
{
public string id { get; set; } = string.Empty;
public MetadataItem metadata { get; set; }
public ulong upload { get; set; }
public ulong download { get; set; }
public DateTime start { get; set; }
public List<string>? chains { get; set; }
public string rule { get; set; }
public string rulePayload { get; set; }
}
public class MetadataItem
{
public string network { get; set; }
public string type { get; set; }
public string sourceIP { get; set; }
public string destinationIP { get; set; }
public string sourcePort { get; set; }
public string destinationPort { get; set; }
public string host { get; set; }
public string nsMode { get; set; }
public object uid { get; set; }
public string process { get; set; }
public string processPath { get; set; }
public string remoteDestination { get; set; }
}
}

View File

@@ -0,0 +1,19 @@

using static v2rayN.Models.ClashProxies;
namespace v2rayN.Models
{
public class ClashProviders
{
public Dictionary<String, ProvidersItem> providers { get; set; }
public class ProvidersItem
{
public string name { get; set; }
public ProxiesItem[] proxies { get; set; }
public string type { get; set; }
public string vehicleType { get; set; }
}
}
}

View File

@@ -0,0 +1,24 @@
namespace v2rayN.Models
{
public class ClashProxies
{
public Dictionary<String, ProxiesItem> proxies { get; set; }
public class ProxiesItem
{
public string[] all { get; set; }
public List<HistoryItem> history { get; set; }
public string name { get; set; }
public string type { get; set; }
public bool udp { get; set; }
public string now { get; set; }
public int delay { get; set; }
}
public class HistoryItem
{
public string time { get; set; }
public int delay { get; set; }
}
}
}

View File

@@ -0,0 +1,26 @@
using ReactiveUI.Fody.Helpers;
namespace v2rayN.Models
{
[Serializable]
public class ClashProxyModel
{
[Reactive]
public string name { get; set; }
[Reactive]
public string type { get; set; }
[Reactive]
public string now { get; set; }
[Reactive]
public int delay { get; set; }
[Reactive]
public string delayName { get; set; }
[Reactive]
public bool isActive { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models using v2rayN.Enums;
namespace v2rayN.Models
{ {
/// <summary> /// <summary>
/// 本软件配置文件实体类 /// 本软件配置文件实体类
@@ -31,6 +33,7 @@
public SpeedTestItem speedTestItem { get; set; } public SpeedTestItem speedTestItem { get; set; }
public Mux4SboxItem mux4SboxItem { get; set; } public Mux4SboxItem mux4SboxItem { get; set; }
public HysteriaItem hysteriaItem { get; set; } public HysteriaItem hysteriaItem { get; set; }
public ClashUIItem clashUIItem { get; set; }
public List<InItem> inbound { get; set; } public List<InItem> inbound { get; set; }
public List<KeyEventItem> globalHotkeys { get; set; } public List<KeyEventItem> globalHotkeys { get; set; }
public List<CoreTypeItem> coreTypeItem { get; set; } public List<CoreTypeItem> coreTypeItem { get; set; }

View File

@@ -1,4 +1,5 @@
using System.Windows.Input; using System.Windows.Input;
using v2rayN.Enums;
namespace v2rayN.Models namespace v2rayN.Models
{ {
@@ -47,8 +48,8 @@ namespace v2rayN.Models
public bool udpEnabled { get; set; } public bool udpEnabled { get; set; }
public bool sniffingEnabled { get; set; } = true; public bool sniffingEnabled { get; set; } = true;
public List<string>? destOverride { get; set; } = ["http", "tls"];
public bool routeOnly { get; set; } public bool routeOnly { get; set; }
public bool allowLANConn { get; set; } public bool allowLANConn { get; set; }
public bool newPort4LAN { get; set; } public bool newPort4LAN { get; set; }
@@ -186,7 +187,7 @@ namespace v2rayN.Models
public string domainStrategy4Singbox { get; set; } public string domainStrategy4Singbox { get; set; }
public string domainMatcher { get; set; } public string domainMatcher { get; set; }
public string routingIndexId { get; set; } public string routingIndexId { get; set; }
public bool enableRoutingAdvanced { get; set; } public bool enableRoutingAdvanced { get; set; }
} }
[Serializable] [Serializable]
@@ -210,4 +211,19 @@ namespace v2rayN.Models
public int up_mbps { get; set; } public int up_mbps { get; set; }
public int down_mbps { get; set; } public int down_mbps { get; set; }
} }
[Serializable]
public class ClashUIItem
{
public ERuleMode ruleMode { get; set; }
public bool showInTaskbar { get; set; }
public bool enableIPv6 { get; set; }
public bool enableMixinContent { get; set; }
public int proxiesSorting { get; set; }
public bool proxiesAutoRefresh { get; set; }
public int proxiesAutoDelayTestInterval { get; set; } = 10;
public int connectionsSorting { get; set; }
public bool connectionsAutoRefresh { get; set; }
public int connectionsRefreshInterval { get; set; } = 2;
}
} }

View File

@@ -1,440 +0,0 @@
namespace v2rayN.Models
{
[Serializable]
public class ConfigOld
{
#region property
/// <summary>
/// 允许日志
/// </summary>
public bool logEnabled
{
get; set;
}
/// <summary>
/// 日志等级
/// </summary>
public string loglevel
{
get; set;
}
public string indexId
{
get; set;
}
/// <summary>
/// 允许Mux多路复用
/// </summary>
public bool muxEnabled
{
get; set;
}
/// <summary>
///
/// </summary>
public ESysProxyType sysProxyType
{
get; set;
}
/// <summary>
/// 启用实时网速和流量统计
/// </summary>
public bool enableStatistics
{
get; set;
}
/// <summary>
/// 去重时优先保留较旧(顶部)节点
/// </summary>
public bool keepOlderDedupl
{
get; set;
}
/// <summary>
/// 视图刷新率
/// </summary>
public int statisticsFreshRate
{
get; set;
}
/// <summary>
/// 自定义远程DNS
/// </summary>
public string remoteDNS
{
get; set;
}
/// <summary>
/// Outbound Freedom domainStrategy
/// </summary>
public string domainStrategy4Freedom
{
get; set;
}
/// <summary>
/// 是否允许不安全连接
/// </summary>
public bool defAllowInsecure
{
get; set;
}
/// <summary>
/// 域名解析策略
/// </summary>
public string domainStrategy
{
get; set;
}
public string domainMatcher
{
get; set;
}
public int routingIndex
{
get; set;
}
public bool enableRoutingAdvanced
{
get; set;
}
public bool ignoreGeoUpdateCore
{
get; set;
}
/// <summary>
/// systemProxyExceptions
/// </summary>
public string systemProxyExceptions
{
get; set;
}
public string systemProxyAdvancedProtocol { get; set; }
public int autoUpdateInterval { get; set; } = 0;
public int autoUpdateSubInterval { get; set; } = 0;
public bool checkPreReleaseUpdate { get; set; } = false;
public bool enableSecurityProtocolTls13
{
get; set;
}
public int trayMenuServersLimit { get; set; }
#endregion property
#region other entities
/// <summary>
/// 本地监听
/// </summary>
public List<InItem> inbound
{
get; set;
}
/// <summary>
/// vmess服务器信息
/// </summary>
public List<VmessItem> vmess
{
get; set;
}
/// <summary>
/// KcpItem
/// </summary>
public KcpItem kcpItem
{
get; set;
}
/// <summary>
/// 订阅
/// </summary>
public List<SubItem> subItem
{
get; set;
}
/// <summary>
/// UI
/// </summary>
public UIItem uiItem
{
get; set;
}
public List<RoutingItemOld> routings
{
get; set;
}
public ConstItem constItem
{
get; set;
}
public List<KeyEventItem> globalHotkeys
{
get; set;
}
public List<CoreTypeItem> coreTypeItem
{
get; set;
}
#endregion other entities
}
[Serializable]
public class VmessItem
{
public VmessItem()
{
indexId = string.Empty;
configType = EConfigType.VMess;
configVersion = 2;
sort = 0;
address = string.Empty;
port = 0;
id = string.Empty;
alterId = 0;
security = string.Empty;
network = string.Empty;
remarks = string.Empty;
headerType = string.Empty;
requestHost = string.Empty;
path = string.Empty;
streamSecurity = string.Empty;
allowInsecure = string.Empty;
testResult = string.Empty;
subid = string.Empty;
flow = string.Empty;
groupId = string.Empty;
}
public string indexId
{
get; set;
}
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType
{
get; set;
}
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion
{
get; set;
}
public int sort
{
get; set;
}
/// <summary>
/// 远程服务器地址
/// </summary>
public string address
{
get; set;
}
/// <summary>
/// 远程服务器端口
/// </summary>
public int port
{
get; set;
}
/// <summary>
/// 远程服务器ID
/// </summary>
public string id
{
get; set;
}
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId
{
get; set;
}
/// <summary>
/// 本地安全策略
/// </summary>
public string security
{
get; set;
}
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network
{
get; set;
}
/// <summary>
///
/// </summary>
public string remarks
{
get; set;
}
/// <summary>
/// 伪装类型
/// </summary>
public string headerType
{
get; set;
}
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost
{
get; set;
}
/// <summary>
/// ws h2 path
/// </summary>
public string path
{
get; set;
}
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity
{
get; set;
}
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure
{
get; set;
}
/// <summary>
///
/// </summary>
public string testResult
{
get; set;
}
/// <summary>
/// SubItem id
/// </summary>
public string subid
{
get; set;
}
/// <summary>
/// VLESS flow
/// </summary>
public string flow
{
get; set;
}
/// <summary>
/// tls sni
/// </summary>
public string sni
{
get; set;
}
public string groupId
{
get; set;
} = string.Empty;
public ECoreType? coreType
{
get; set;
}
public int preSocksPort
{
get; set;
}
public string fingerprint { get; set; }
}
[Serializable]
public class RoutingItemOld
{
public string remarks
{
get; set;
}
public string url
{
get; set;
}
public List<RulesItem> rules
{
get; set;
}
public bool enabled { get; set; } = true;
public bool locked
{
get; set;
}
public string customIcon
{
get; set;
}
}
}

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models using v2rayN.Enums;
namespace v2rayN.Models
{ {
[Serializable] [Serializable]
public class CoreInfo public class CoreInfo

View File

@@ -1,4 +1,5 @@
using SQLite; using SQLite;
using v2rayN.Enums;
namespace v2rayN.Models namespace v2rayN.Models
{ {

View File

@@ -1,4 +1,5 @@
using SQLite; using SQLite;
using v2rayN.Enums;
namespace v2rayN.Models namespace v2rayN.Models
{ {

View File

@@ -4,21 +4,22 @@
public class RulesItem public class RulesItem
{ {
public string id { get; set; } public string id { get; set; }
public string type { get; set; } public string? type { get; set; }
public string port { get; set; } public string? port { get; set; }
public string? network { get; set; }
public List<string> inboundTag { get; set; } public List<string>? inboundTag { get; set; }
public string outboundTag { get; set; } public string? outboundTag { get; set; }
public List<string> ip { get; set; } public List<string>? ip { get; set; }
public List<string> domain { get; set; } public List<string>? domain { get; set; }
public List<string> protocol { get; set; } public List<string>? protocol { get; set; }
public List<string> process { get; set; } public List<string>? process { get; set; }
public bool enabled { get; set; } = true; public bool enabled { get; set; } = true;
} }

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models using v2rayN.Enums;
namespace v2rayN.Models
{ {
[Serializable] [Serializable]
internal class ServerTestItem internal class ServerTestItem

View File

@@ -28,6 +28,7 @@
public bool? disable_expire { get; set; } public bool? disable_expire { get; set; }
public bool? independent_cache { get; set; } public bool? independent_cache { get; set; }
public bool? reverse_mapping { get; set; } public bool? reverse_mapping { get; set; }
public string? client_subnet { get; set; }
public Fakeip4Sbox? fakeip { get; set; } public Fakeip4Sbox? fakeip { get; set; }
} }
@@ -44,12 +45,14 @@
public string? outbound { get; set; } public string? outbound { get; set; }
public string? server { get; set; } public string? server { get; set; }
public bool? disable_cache { get; set; } public bool? disable_cache { get; set; }
public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; }
public string? type { get; set; } public string? type { get; set; }
public string? mode { get; set; } public string? mode { get; set; }
public string? network { get; set; }
public bool? ip_is_private { get; set; } public bool? ip_is_private { get; set; }
public string? client_subnet { get; set; }
public bool? invert { get; set; }
public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; }
public List<string>? network { get; set; }
public List<int>? port { get; set; } public List<int>? port { get; set; }
public List<string>? port_range { get; set; } public List<string>? port_range { get; set; }
public List<string>? geosite { get; set; } public List<string>? geosite { get; set; }
@@ -62,6 +65,7 @@
public List<string>? source_ip_cidr { get; set; } public List<string>? source_ip_cidr { get; set; }
public List<string>? process_name { get; set; } public List<string>? process_name { get; set; }
public List<string>? rule_set { get; set; } public List<string>? rule_set { get; set; }
public List<Rule4Sbox>? rules { get; set; }
} }
[Serializable] [Serializable]
@@ -95,7 +99,7 @@
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string server { get; set; } public string? server { get; set; }
public int? server_port { get; set; } public int? server_port { get; set; }
public string uuid { get; set; } public string uuid { get; set; }
public string security { get; set; } public string security { get; set; }
@@ -184,11 +188,13 @@
public class Server4Sbox public class Server4Sbox
{ {
public string tag { get; set; } public string? tag { get; set; }
public string address { get; set; } public string? address { get; set; }
public string address_resolver { get; set; } public string? address_resolver { get; set; }
public string strategy { get; set; } public string? address_strategy { get; set; }
public string? strategy { get; set; }
public string? detour { get; set; } public string? detour { get; set; }
public string? client_subnet { get; set; }
} }
public class Experimental4Sbox public class Experimental4Sbox
@@ -241,6 +247,6 @@
public string? path { get; set; } public string? path { get; set; }
public string? url { get; set; } public string? url { get; set; }
public string? download_detour { get; set; } public string? download_detour { get; set; }
public string? update_interval { get; set; } public string? update_interval { get; set; }
} }
} }

View File

@@ -195,7 +195,7 @@ namespace v2rayN.Models
public class Sniffing4Ray public class Sniffing4Ray
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public List<string> destOverride { get; set; } public List<string>? destOverride { get; set; }
public bool routeOnly { get; set; } public bool routeOnly { get; set; }
} }
@@ -370,6 +370,12 @@ namespace v2rayN.Models
public List<string> servers { get; set; } public List<string> servers { get; set; }
} }
public class DnsServer4Ray
{
public string? address { get; set; }
public List<string>? domains { get; set; }
}
public class Routing4Ray public class Routing4Ray
{ {
/// <summary> /// <summary>
@@ -380,7 +386,7 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string domainMatcher { get; set; } public string? domainMatcher { get; set; }
/// <summary> /// <summary>
/// ///
@@ -394,6 +400,7 @@ namespace v2rayN.Models
public string? type { get; set; } public string? type { get; set; }
public string? port { get; set; } public string? port { get; set; }
public string? network { get; set; }
public List<string>? inboundTag { get; set; } public List<string>? inboundTag { get; set; }
@@ -421,47 +428,52 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public TlsSettings4Ray tlsSettings { get; set; } public TlsSettings4Ray? tlsSettings { get; set; }
/// <summary> /// <summary>
/// Tcp传输额外设置 /// Tcp传输额外设置
/// </summary> /// </summary>
public TcpSettings4Ray tcpSettings { get; set; } public TcpSettings4Ray? tcpSettings { get; set; }
/// <summary> /// <summary>
/// Kcp传输额外设置 /// Kcp传输额外设置
/// </summary> /// </summary>
public KcpSettings4Ray kcpSettings { get; set; } public KcpSettings4Ray? kcpSettings { get; set; }
/// <summary> /// <summary>
/// ws传输额外设置 /// ws传输额外设置
/// </summary> /// </summary>
public WsSettings4Ray wsSettings { get; set; } public WsSettings4Ray? wsSettings { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; } public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
/// <summary>
///
/// </summary>
public SplithttpSettings4Ray? splithttpSettings { get; set; }
/// <summary> /// <summary>
/// h2传输额外设置 /// h2传输额外设置
/// </summary> /// </summary>
public HttpSettings4Ray httpSettings { get; set; } public HttpSettings4Ray? httpSettings { get; set; }
/// <summary> /// <summary>
/// QUIC /// QUIC
/// </summary> /// </summary>
public QuicSettings4Ray quicSettings { get; set; } public QuicSettings4Ray? quicSettings { get; set; }
/// <summary> /// <summary>
/// VLESS only /// VLESS only
/// </summary> /// </summary>
public TlsSettings4Ray realitySettings { get; set; } public TlsSettings4Ray? realitySettings { get; set; }
/// <summary> /// <summary>
/// grpc /// grpc
/// </summary> /// </summary>
public GrpcSettings4Ray grpcSettings { get; set; } public GrpcSettings4Ray? grpcSettings { get; set; }
/// <summary> /// <summary>
/// sockopt /// sockopt
@@ -488,7 +500,7 @@ namespace v2rayN.Models
public string? fingerprint { get; set; } public string? fingerprint { get; set; }
public bool? show { get; set; } = false; public bool? show { get; set; }
public string? publicKey { get; set; } public string? publicKey { get; set; }
public string? shortId { get; set; } public string? shortId { get; set; }
public string? spiderX { get; set; } public string? spiderX { get; set; }
@@ -608,6 +620,17 @@ namespace v2rayN.Models
public string? host { get; set; } public string? host { get; set; }
} }
public class SplithttpSettings4Ray
{
public string? path { get; set; }
public string? host { get; set; }
public int? maxUploadSize { get; set; }
public int? maxConcurrentUploads { get; set; }
}
public class HttpSettings4Ray public class HttpSettings4Ray
{ {
/// <summary> /// <summary>

View File

@@ -753,6 +753,24 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Close Connection 的本地化字符串。
/// </summary>
public static string menuConnectionClose {
get {
return ResourceManager.GetString("menuConnectionClose", resourceCulture);
}
}
/// <summary>
/// 查找类似 Close All Connection 的本地化字符串。
/// </summary>
public static string menuConnectionCloseAll {
get {
return ResourceManager.GetString("menuConnectionCloseAll", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Clone selected server 的本地化字符串。 /// 查找类似 Clone selected server 的本地化字符串。
/// </summary> /// </summary>
@@ -879,6 +897,42 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Direct 的本地化字符串。
/// </summary>
public static string menuModeDirect {
get {
return ResourceManager.GetString("menuModeDirect", resourceCulture);
}
}
/// <summary>
/// 查找类似 Global 的本地化字符串。
/// </summary>
public static string menuModeGlobal {
get {
return ResourceManager.GetString("menuModeGlobal", resourceCulture);
}
}
/// <summary>
/// 查找类似 Do not change 的本地化字符串。
/// </summary>
public static string menuModeNothing {
get {
return ResourceManager.GetString("menuModeNothing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Rule 的本地化字符串。
/// </summary>
public static string menuModeRule {
get {
return ResourceManager.GetString("menuModeRule", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Move to bottom (B) 的本地化字符串。 /// 查找类似 Move to bottom (B) 的本地化字符串。
/// </summary> /// </summary>
@@ -1005,6 +1059,42 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 All Node Latency Test 的本地化字符串。
/// </summary>
public static string menuProxiesDelaytest {
get {
return ResourceManager.GetString("menuProxiesDelaytest", resourceCulture);
}
}
/// <summary>
/// 查找类似 Part Node Latency Test 的本地化字符串。
/// </summary>
public static string menuProxiesDelaytestPart {
get {
return ResourceManager.GetString("menuProxiesDelaytestPart", resourceCulture);
}
}
/// <summary>
/// 查找类似 Refresh Proxies (F5) 的本地化字符串。
/// </summary>
public static string menuProxiesReload {
get {
return ResourceManager.GetString("menuProxiesReload", resourceCulture);
}
}
/// <summary>
/// 查找类似 Select active node (Enter) 的本地化字符串。
/// </summary>
public static string menuProxiesSelectActivity {
get {
return ResourceManager.GetString("menuProxiesSelectActivity", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Test servers real delay (Ctrl+R) 的本地化字符串。 /// 查找类似 Test servers real delay (Ctrl+R) 的本地化字符串。
/// </summary> /// </summary>
@@ -1176,6 +1266,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Rule mode 的本地化字符串。
/// </summary>
public static string menuRulemode {
get {
return ResourceManager.GetString("menuRulemode", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Remove Rule (Delete) 的本地化字符串。 /// 查找类似 Remove Rule (Delete) 的本地化字符串。
/// </summary> /// </summary>
@@ -1996,6 +2095,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Connections 的本地化字符串。
/// </summary>
public static string TbConnections {
get {
return ResourceManager.GetString("TbConnections", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Core Type 的本地化字符串。 /// 查找类似 Core Type 的本地化字符串。
/// </summary> /// </summary>
@@ -2266,6 +2374,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Proxies 的本地化字符串。
/// </summary>
public static string TbProxies {
get {
return ResourceManager.GetString("TbProxies", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 PublicKey 的本地化字符串。 /// 查找类似 PublicKey 的本地化字符串。
/// </summary> /// </summary>
@@ -2915,7 +3032,7 @@ namespace v2rayN.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5; 的本地化字符串。 /// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsSocksPortTip { public static string TbSettingsSocksPortTip {
get { get {
@@ -3139,6 +3256,123 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Sorting 的本地化字符串。
/// </summary>
public static string TbSorting {
get {
return ResourceManager.GetString("TbSorting", resourceCulture);
}
}
/// <summary>
/// 查找类似 Chain 的本地化字符串。
/// </summary>
public static string TbSortingChain {
get {
return ResourceManager.GetString("TbSortingChain", resourceCulture);
}
}
/// <summary>
/// 查找类似 Default 的本地化字符串。
/// </summary>
public static string TbSortingDefault {
get {
return ResourceManager.GetString("TbSortingDefault", resourceCulture);
}
}
/// <summary>
/// 查找类似 Delay 的本地化字符串。
/// </summary>
public static string TbSortingDelay {
get {
return ResourceManager.GetString("TbSortingDelay", resourceCulture);
}
}
/// <summary>
/// 查找类似 Download Speed 的本地化字符串。
/// </summary>
public static string TbSortingDownSpeed {
get {
return ResourceManager.GetString("TbSortingDownSpeed", resourceCulture);
}
}
/// <summary>
/// 查找类似 Download Traffic 的本地化字符串。
/// </summary>
public static string TbSortingDownTraffic {
get {
return ResourceManager.GetString("TbSortingDownTraffic", resourceCulture);
}
}
/// <summary>
/// 查找类似 Host 的本地化字符串。
/// </summary>
public static string TbSortingHost {
get {
return ResourceManager.GetString("TbSortingHost", resourceCulture);
}
}
/// <summary>
/// 查找类似 Name 的本地化字符串。
/// </summary>
public static string TbSortingName {
get {
return ResourceManager.GetString("TbSortingName", resourceCulture);
}
}
/// <summary>
/// 查找类似 Network 的本地化字符串。
/// </summary>
public static string TbSortingNetwork {
get {
return ResourceManager.GetString("TbSortingNetwork", resourceCulture);
}
}
/// <summary>
/// 查找类似 Time 的本地化字符串。
/// </summary>
public static string TbSortingTime {
get {
return ResourceManager.GetString("TbSortingTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 Type 的本地化字符串。
/// </summary>
public static string TbSortingType {
get {
return ResourceManager.GetString("TbSortingType", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upload Speed 的本地化字符串。
/// </summary>
public static string TbSortingUpSpeed {
get {
return ResourceManager.GetString("TbSortingUpSpeed", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upload Traffic 的本地化字符串。
/// </summary>
public static string TbSortingUpTraffic {
get {
return ResourceManager.GetString("TbSortingUpTraffic", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 SpiderX 的本地化字符串。 /// 查找类似 SpiderX 的本地化字符串。
/// </summary> /// </summary>
@@ -3266,7 +3500,7 @@ namespace v2rayN.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 *ws/httpupgrade path 的本地化字符串。 /// 查找类似 *ws/httpupgrade/splithttp path 的本地化字符串。
/// </summary> /// </summary>
public static string TransportPathTip1 { public static string TransportPathTip1 {
get { get {
@@ -3320,7 +3554,7 @@ namespace v2rayN.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 *ws/httpupgrade host 的本地化字符串。 /// 查找类似 *ws/httpupgrade/splithttp host 的本地化字符串。
/// </summary> /// </summary>
public static string TransportRequestHostTip2 { public static string TransportRequestHostTip2 {
get { get {

View File

@@ -1006,4 +1006,70 @@
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve"> <data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value> <value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
</data> </data>
<data name="TbSorting" xml:space="preserve">
<value>مرتب سازی</value>
</data>
<data name="TbSortingDefault" xml:space="preserve">
<value>Default</value>
</data>
<data name="TbSortingDelay" xml:space="preserve">
<value>تاخیر</value>
</data>
<data name="TbSortingDownSpeed" xml:space="preserve">
<value>سرعت دانلود</value>
</data>
<data name="TbSortingDownTraffic" xml:space="preserve">
<value>ترافیک دانلود</value>
</data>
<data name="TbSortingName" xml:space="preserve">
<value>نام</value>
</data>
<data name="TbSortingTime" xml:space="preserve">
<value>زمان</value>
</data>
<data name="TbSortingUpSpeed" xml:space="preserve">
<value>سرعت اپلود</value>
</data>
<data name="TbSortingUpTraffic" xml:space="preserve">
<value>ترافیک آپلود</value>
</data>
<data name="TbConnections" xml:space="preserve">
<value>اتصالات</value>
</data>
<data name="menuConnectionClose" xml:space="preserve">
<value>بستن اتصال</value>
</data>
<data name="menuConnectionCloseAll" xml:space="preserve">
<value>تمام اتصالات را ببندید</value>
</data>
<data name="TbProxies" xml:space="preserve">
<value>پروکسی</value>
</data>
<data name="menuRulemode" xml:space="preserve">
<value>نوع قانون</value>
</data>
<data name="menuModeDirect" xml:space="preserve">
<value>Direct</value>
</data>
<data name="menuModeGlobal" xml:space="preserve">
<value>Global</value>
</data>
<data name="menuModeNothing" xml:space="preserve">
<value>تغییر نده</value>
</data>
<data name="menuModeRule" xml:space="preserve">
<value>قانون</value>
</data>
<data name="menuProxiesDelaytest" xml:space="preserve">
<value>All Node Latency Test</value>
</data>
<data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>Part Node Latency Test</value>
</data>
<data name="menuProxiesReload" xml:space="preserve">
<value>Refresh Proxies (F5)</value>
</data>
<data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Select active node (Enter)</value>
</data>
</root> </root>

View File

@@ -359,7 +359,7 @@
<value>Please fill in the correct custom DNS</value> <value>Please fill in the correct custom DNS</value>
</data> </data>
<data name="TransportPathTip1" xml:space="preserve"> <data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value> <value>*ws/httpupgrade/splithttp path</value>
</data> </data>
<data name="TransportPathTip2" xml:space="preserve"> <data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value> <value>*h2 path</value>
@@ -374,7 +374,7 @@
<value>*http host Separated by commas (,)</value> <value>*http host Separated by commas (,)</value>
</data> </data>
<data name="TransportRequestHostTip2" xml:space="preserve"> <data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value> <value>*ws/httpupgrade/splithttp host</value>
</data> </data>
<data name="TransportRequestHostTip3" xml:space="preserve"> <data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host Separated by commas (,)</value> <value>*h2 host Separated by commas (,)</value>
@@ -1037,7 +1037,7 @@
<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, restart the settings</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5;</value> <value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Set this with admin privileges, get admin privileges after startup</value> <value>Set this with admin privileges, get admin privileges after startup</value>
@@ -1219,4 +1219,82 @@
<data name="menuOpenTheFileLocation" xml:space="preserve"> <data name="menuOpenTheFileLocation" xml:space="preserve">
<value>Open the storage location</value> <value>Open the storage location</value>
</data> </data>
<data name="TbSorting" xml:space="preserve">
<value>Sorting</value>
</data>
<data name="TbSortingChain" xml:space="preserve">
<value>Chain</value>
</data>
<data name="TbSortingDefault" xml:space="preserve">
<value>Default</value>
</data>
<data name="TbSortingDelay" xml:space="preserve">
<value>Delay</value>
</data>
<data name="TbSortingDownSpeed" xml:space="preserve">
<value>Download Speed</value>
</data>
<data name="TbSortingDownTraffic" xml:space="preserve">
<value>Download Traffic</value>
</data>
<data name="TbSortingHost" xml:space="preserve">
<value>Host</value>
</data>
<data name="TbSortingName" xml:space="preserve">
<value>Name</value>
</data>
<data name="TbSortingNetwork" xml:space="preserve">
<value>Network</value>
</data>
<data name="TbSortingTime" xml:space="preserve">
<value>Time</value>
</data>
<data name="TbSortingType" xml:space="preserve">
<value>Type</value>
</data>
<data name="TbSortingUpSpeed" xml:space="preserve">
<value>Upload Speed</value>
</data>
<data name="TbSortingUpTraffic" xml:space="preserve">
<value>Upload Traffic</value>
</data>
<data name="TbConnections" xml:space="preserve">
<value>Connections</value>
</data>
<data name="menuConnectionClose" xml:space="preserve">
<value>Close Connection</value>
</data>
<data name="menuConnectionCloseAll" xml:space="preserve">
<value>Close All Connection</value>
</data>
<data name="TbProxies" xml:space="preserve">
<value>Proxies</value>
</data>
<data name="menuRulemode" xml:space="preserve">
<value>Rule mode</value>
</data>
<data name="menuModeDirect" xml:space="preserve">
<value>Direct</value>
</data>
<data name="menuModeGlobal" xml:space="preserve">
<value>Global</value>
</data>
<data name="menuModeNothing" xml:space="preserve">
<value>Do not change</value>
</data>
<data name="menuModeRule" xml:space="preserve">
<value>Rule</value>
</data>
<data name="menuProxiesDelaytest" xml:space="preserve">
<value>All Node Latency Test</value>
</data>
<data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>Part Node Latency Test</value>
</data>
<data name="menuProxiesReload" xml:space="preserve">
<value>Refresh Proxies (F5)</value>
</data>
<data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Select active node (Enter)</value>
</data>
</root> </root>

View File

@@ -1042,9 +1042,6 @@
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value> <value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>HTTP port=socks port+1</value>
</data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Установите это с правами администратора</value> <value>Установите это с правами администратора</value>
</data> </data>

View File

@@ -359,7 +359,7 @@
<value>请填写正确的自定义DNS</value> <value>请填写正确的自定义DNS</value>
</data> </data>
<data name="TransportPathTip1" xml:space="preserve"> <data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value> <value>*ws/httpupgrade/splithttp path</value>
</data> </data>
<data name="TransportPathTip2" xml:space="preserve"> <data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value> <value>*h2 path</value>
@@ -374,7 +374,7 @@
<value>*http host中间逗号(,)分隔</value> <value>*http host中间逗号(,)分隔</value>
</data> </data>
<data name="TransportRequestHostTip2" xml:space="preserve"> <data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value> <value>*ws/httpupgrade/splithttp host</value>
</data> </data>
<data name="TransportRequestHostTip3" xml:space="preserve"> <data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host中间逗号(,)分隔</value> <value>*h2 host中间逗号(,)分隔</value>
@@ -1037,7 +1037,7 @@
<value>拷贝字体TTF/TTC文件到目录guiFonts重启设置</value> <value>拷贝字体TTF/TTC文件到目录guiFonts重启设置</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http端口=socks端口+1Pac端口=socks端口+4API端口=socks端口+5</value> <value>http端口= +1Pac端口= +4*ray API端口= +5mihomo API端口= +6</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理员权限设置此项,在启动后获得管理员权限</value> <value>以管理员权限设置此项,在启动后获得管理员权限</value>
@@ -1216,4 +1216,82 @@
<data name="menuOpenTheFileLocation" xml:space="preserve"> <data name="menuOpenTheFileLocation" xml:space="preserve">
<value>打开存储所在的位置</value> <value>打开存储所在的位置</value>
</data> </data>
<data name="TbSorting" xml:space="preserve">
<value>排序</value>
</data>
<data name="TbSortingChain" xml:space="preserve">
<value>路由链</value>
</data>
<data name="TbSortingDefault" xml:space="preserve">
<value>默认</value>
</data>
<data name="TbSortingDelay" xml:space="preserve">
<value>延迟</value>
</data>
<data name="TbSortingDownSpeed" xml:space="preserve">
<value>下载速度</value>
</data>
<data name="TbSortingDownTraffic" xml:space="preserve">
<value>下载流量</value>
</data>
<data name="TbSortingHost" xml:space="preserve">
<value>主机</value>
</data>
<data name="TbSortingName" xml:space="preserve">
<value>名称</value>
</data>
<data name="TbSortingNetwork" xml:space="preserve">
<value>网络</value>
</data>
<data name="TbSortingTime" xml:space="preserve">
<value>时间</value>
</data>
<data name="TbSortingType" xml:space="preserve">
<value>类型</value>
</data>
<data name="TbSortingUpSpeed" xml:space="preserve">
<value>上传速度</value>
</data>
<data name="TbSortingUpTraffic" xml:space="preserve">
<value>上传流量</value>
</data>
<data name="TbConnections" xml:space="preserve">
<value>当前连接</value>
</data>
<data name="menuConnectionClose" xml:space="preserve">
<value>关闭连接</value>
</data>
<data name="menuConnectionCloseAll" xml:space="preserve">
<value>关闭所有连接</value>
</data>
<data name="TbProxies" xml:space="preserve">
<value>当前代理</value>
</data>
<data name="menuRulemode" xml:space="preserve">
<value>规则模式</value>
</data>
<data name="menuModeDirect" xml:space="preserve">
<value>直连</value>
</data>
<data name="menuModeGlobal" xml:space="preserve">
<value>全局</value>
</data>
<data name="menuModeNothing" xml:space="preserve">
<value>随原配置</value>
</data>
<data name="menuModeRule" xml:space="preserve">
<value>规则</value>
</data>
<data name="menuProxiesDelaytest" xml:space="preserve">
<value>全部节点延迟测试</value>
</data>
<data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>当前部分节点延迟测试</value>
</data>
<data name="menuProxiesReload" xml:space="preserve">
<value>刷新 (F5)</value>
</data>
<data name="menuProxiesSelectActivity" xml:space="preserve">
<value>设为活动节点 (Enter)</value>
</data>
</root> </root>

View File

@@ -358,7 +358,7 @@
<value>請填寫正確的自訂DNS</value> <value>請填寫正確的自訂DNS</value>
</data> </data>
<data name="TransportPathTip1" xml:space="preserve"> <data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value> <value>*ws/httpupgrade/splithttp path</value>
</data> </data>
<data name="TransportPathTip2" xml:space="preserve"> <data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value> <value>*h2 path</value>
@@ -373,7 +373,7 @@
<value>*http host中間逗號(,)分隔</value> <value>*http host中間逗號(,)分隔</value>
</data> </data>
<data name="TransportRequestHostTip2" xml:space="preserve"> <data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value> <value>*ws/httpupgrade/splithttp host</value>
</data> </data>
<data name="TransportRequestHostTip3" xml:space="preserve"> <data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host中間逗號(,)分隔</value> <value>*h2 host中間逗號(,)分隔</value>
@@ -1037,7 +1037,7 @@
<value>複製字型TTF/TTC文件到目錄guiFonts重啟設定</value> <value>複製字型TTF/TTC文件到目錄guiFonts重啟設定</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>HTTP埠=SOCKS埠+1</value> <value>http端口= +1Pac端口= +4*ray API端口= +5mihomo API端口= +6</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value> <value>以管理員權限設定此項,在啟動後獲得管理員權限</value>

View File

@@ -4,56 +4,7 @@
"error": "Verror.log", "error": "Verror.log",
"loglevel": "warning" "loglevel": "warning"
}, },
"inbounds": [{ "inbounds": [],
"tag": "tag1",
"port": 10808,
"protocol": "socks",
"listen": "127.0.0.1",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "tag2",
"port": 10809,
"protocol": "http",
"listen": "127.0.0.1",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "tag3",
"port": 10809,
"protocol": "http",
"listen": "127.0.0.1",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [{ "outbounds": [{
"tag": "proxy", "tag": "proxy",
"protocol": "vmess", "protocol": "vmess",

View File

@@ -3,14 +3,7 @@
"level": "debug", "level": "debug",
"timestamp": true "timestamp": true
}, },
"inbounds": [ "inbounds": [],
{
"type": "socks",
"tag": "socks",
"listen": "127.0.0.1",
"listen_port": 10000
}
],
"outbounds": [ "outbounds": [
{ {
"type": "vless", "type": "vless",

View File

@@ -0,0 +1,39 @@
#
# 配置文件内容不会被修改,混合行为只会发生在内存中
#
# 注意下面缩进请用支持yaml显示的编辑器打开
#
# 使用clash配置文件关键字则覆盖原配置
#
# removed-rules 循环匹配rules数组每行,符合则移除当前行 (此规则请放最前面)
#
# append-rules 数组合并至原配置rules数组后
# prepend-rules 数组合并至原配置rules数组前
# append-proxies 数组合并至原配置proxies数组后
# prepend-proxies 数组合并至原配置proxies数组前
# append-proxy-groups 数组合并至原配置proxy-groups数组后
# prepend-proxy-groups 数组合并至原配置proxy-groups数组前
# append-rule-providers 数组合并至原配置rule-providers数组后
# prepend-rule-providers 数组合并至原配置rule-providers数组前
#
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- 114.114.114.114
- 223.5.5.5
- 8.8.8.8
fallback: []
fake-ip-filter:
- +.stun.*.*
- +.stun.*.*.*
- +.stun.*.*.*.*
- +.stun.*.*.*.*.*
- "*.n.n.srv.nintendo.net"
- +.stun.playstation.net
- xbox.*.*.microsoft.com
- "*.*.xboxlive.com"
- "*.msftncsi.com"
- "*.msftconnecttest.com"
- WORKGROUP

View File

@@ -0,0 +1,7 @@
tun:
enable: true
stack: gvisor
dns-hijack:
- 0.0.0.0:53
auto-route: true
auto-detect-interface: true

View File

@@ -5,6 +5,11 @@
"bittorrent" "bittorrent"
] ]
}, },
{
"outboundTag": "block",
"port": "443",
"network": "udp"
},
{ {
"outboundTag": "block", "outboundTag": "block",
"domain": [ "domain": [
@@ -15,8 +20,23 @@
"outboundTag": "proxy", "outboundTag": "proxy",
"domain": [ "domain": [
"geosite:geolocation-!cn", "geosite:gfw",
"geosite:greatfire" "geosite:greatfire"
]
},
{
"outboundTag": "proxy",
"ip": [
"1.0.0.1",
"1.1.1.1",
"8.8.8.8",
"8.8.4.4",
"geoip:facebook",
"geoip:fastly",
"geoip:google",
"geoip:netflix",
"geoip:telegram",
"geoip:twitter"
] ]
}, },
{ {

View File

@@ -1,4 +1,9 @@
[ [
{
"outboundTag": "block",
"port": "443",
"network": "udp"
},
{ {
"port": "0-65535", "port": "0-65535",
"outboundTag": "proxy" "outboundTag": "proxy"

View File

@@ -6,6 +6,11 @@
"domain:example-example2.com" "domain:example-example2.com"
] ]
}, },
{
"outboundTag": "block",
"port": "443",
"network": "udp"
},
{ {
"outboundTag": "block", "outboundTag": "block",
"domain": [ "domain": [
@@ -22,6 +27,10 @@
{ {
"outboundTag": "direct", "outboundTag": "direct",
"ip": [ "ip": [
"223.5.5.5/32",
"119.29.29.29/32",
"180.76.76.76/32",
"114.114.114.114/32",
"geoip:private", "geoip:private",
"geoip:cn" "geoip:cn"
] ]

View File

@@ -30,5 +30,6 @@
], ],
"server": "block" "server": "block"
} }
] ],
"final": "local"
} }

View File

@@ -30,5 +30,6 @@
], ],
"server": "block" "server": "block"
} }
] ],
"final": "local"
} }

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
using System.Reactive; using System.Reactive;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -0,0 +1,198 @@
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Reactive;
using System.Reactive.Linq;
using System.Windows;
using v2rayN.Handler;
using v2rayN.Models;
namespace v2rayN.ViewModels
{
public class ClashConnectionsViewModel : ReactiveObject
{
private static Config _config;
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>();
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
[Reactive]
public ClashConnectionModel SelectedSource { get; set; }
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
[Reactive]
public int SortingSelected { get; set; }
[Reactive]
public bool AutoRefresh { get; set; }
public ClashConnectionsViewModel()
{
_config = LazyConfig.Instance.GetConfig();
SortingSelected = _config.clashUIItem.connectionsSorting;
AutoRefresh = _config.clashUIItem.connectionsAutoRefresh;
var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !string.IsNullOrEmpty(selectedSource.id));
this.WhenAnyValue(
x => x.SortingSelected,
y => y >= 0)
.Subscribe(c => DoSortingSelected(c));
this.WhenAnyValue(
x => x.AutoRefresh,
y => y == true)
.Subscribe(c => { _config.clashUIItem.connectionsAutoRefresh = AutoRefresh; });
ConnectionCloseCmd = ReactiveCommand.Create(() =>
{
ClashConnectionClose(false);
}, canEditRemove);
ConnectionCloseAllCmd = ReactiveCommand.Create(() =>
{
ClashConnectionClose(true);
});
Init();
}
private void DoSortingSelected(bool c)
{
if (!c)
{
return;
}
if (SortingSelected != _config.clashUIItem.connectionsSorting)
{
_config.clashUIItem.connectionsSorting = SortingSelected;
}
GetClashConnections();
}
private void Init()
{
var lastTime = DateTime.Now;
Observable.Interval(TimeSpan.FromSeconds(10))
.Subscribe(x =>
{
if (!(AutoRefresh && _config.clashUIItem.showInTaskbar))
{
return;
}
var dtNow = DateTime.Now;
if (_config.clashUIItem.connectionsRefreshInterval > 0)
{
if ((dtNow - lastTime).Minutes % _config.clashUIItem.connectionsRefreshInterval == 0)
{
GetClashConnections();
lastTime = dtNow;
}
Thread.Sleep(1000);
}
});
}
private void GetClashConnections()
{
ClashApiHandler.Instance.GetClashConnections(_config, (it) =>
{
if (it == null)
{
return;
}
Application.Current?.Dispatcher.Invoke((Action)(() =>
{
RefreshConnections(it?.connections);
}));
});
}
private void RefreshConnections(List<ConnectionItem>? connections)
{
_connectionItems.Clear();
var dtNow = DateTime.Now;
var lstModel = new List<ClashConnectionModel>();
foreach (var item in connections ?? [])
{
ClashConnectionModel model = new();
model.id = item.id;
model.network = item.metadata.network;
model.type = item.metadata.type;
model.host = $"{(string.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
var sp = (dtNow - item.start);
model.time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
model.upload = item.upload;
model.download = item.download;
model.uploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
model.downloadTraffic = $"{Utils.HumanFy((long)item.download)}";
model.elapsed = sp.ToString(@"hh\:mm\:ss");
model.chain = item.chains?.Count > 0 ? item.chains[0] : String.Empty;
lstModel.Add(model);
}
if (lstModel.Count <= 0) { return; }
//sort
switch (SortingSelected)
{
case 0:
lstModel = lstModel.OrderBy(t => t.upload / t.time).ToList();
break;
case 1:
lstModel = lstModel.OrderBy(t => t.download / t.time).ToList();
break;
case 2:
lstModel = lstModel.OrderBy(t => t.upload).ToList();
break;
case 3:
lstModel = lstModel.OrderBy(t => t.download).ToList();
break;
case 4:
lstModel = lstModel.OrderBy(t => t.time).ToList();
break;
case 5:
lstModel = lstModel.OrderBy(t => t.host).ToList();
break;
}
_connectionItems.AddRange(lstModel);
}
public void ClashConnectionClose(bool all)
{
var id = string.Empty;
if (!all)
{
var item = SelectedSource;
if (item is null)
{
return;
}
id = item.id;
}
else
{
_connectionItems.Clear();
}
ClashApiHandler.Instance.ClashConnectionClose(id);
GetClashConnections();
}
}
}

View File

@@ -0,0 +1,487 @@
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Reactive.Linq;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;
using static v2rayN.Models.ClashProviders;
using static v2rayN.Models.ClashProxies;
namespace v2rayN.ViewModels
{
public class ClashProxiesViewModel : ReactiveObject
{
private static Config _config;
private NoticeHandler? _noticeHandler;
private Dictionary<String, ProxiesItem>? proxies;
private Dictionary<String, ProvidersItem>? providers;
private int delayTimeout = 99999999;
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
[Reactive]
public ClashProxyModel SelectedGroup { get; set; }
[Reactive]
public ClashProxyModel SelectedDetail { get; set; }
public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesDelaytestCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesDelaytestPartCmd { get; }
public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; }
[Reactive]
public int RuleModeSelected { get; set; }
[Reactive]
public int SortingSelected { get; set; }
[Reactive]
public bool AutoRefresh { get; set; }
public ClashProxiesViewModel()
{
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
_config = LazyConfig.Instance.GetConfig();
SelectedGroup = new();
SelectedDetail = new();
AutoRefresh = _config.clashUIItem.proxiesAutoRefresh;
SortingSelected = _config.clashUIItem.proxiesSorting;
RuleModeSelected = (int)_config.clashUIItem.ruleMode;
this.WhenAnyValue(
x => x.SelectedGroup,
y => y != null && !string.IsNullOrEmpty(y.name))
.Subscribe(c => RefreshProxyDetails(c));
this.WhenAnyValue(
x => x.RuleModeSelected,
y => y >= 0)
.Subscribe(c => DoRulemodeSelected(c));
this.WhenAnyValue(
x => x.SortingSelected,
y => y >= 0)
.Subscribe(c => DoSortingSelected(c));
this.WhenAnyValue(
x => x.AutoRefresh,
y => y == true)
.Subscribe(c => { _config.clashUIItem.proxiesAutoRefresh = AutoRefresh; });
ProxiesReloadCmd = ReactiveCommand.Create(() =>
{
ProxiesReload();
});
ProxiesDelaytestCmd = ReactiveCommand.Create(() =>
{
ProxiesDelayTest(true);
});
ProxiesDelaytestPartCmd = ReactiveCommand.Create(() =>
{
ProxiesDelayTest(false);
});
ProxiesSelectActivityCmd = ReactiveCommand.Create(() =>
{
SetActiveProxy();
});
ProxiesReload();
DelayTestTask();
}
private void DoRulemodeSelected(bool c)
{
if (!c)
{
return;
}
if (_config.clashUIItem.ruleMode == (ERuleMode)RuleModeSelected)
{
return;
}
SetRuleModeCheck((ERuleMode)RuleModeSelected);
}
public void SetRuleModeCheck(ERuleMode mode)
{
if (_config.clashUIItem.ruleMode == mode)
{
return;
}
SetRuleMode(mode);
}
private void DoSortingSelected(bool c)
{
if (!c)
{
return;
}
if (SortingSelected != _config.clashUIItem.proxiesSorting)
{
_config.clashUIItem.proxiesSorting = SortingSelected;
}
RefreshProxyDetails(c);
}
private void UpdateHandler(bool notify, string msg)
{
_noticeHandler?.SendMessage(msg, true);
}
public void ProxiesReload()
{
GetClashProxies(true);
ProxiesDelayTest();
}
public void ProxiesClear()
{
proxies = null;
providers = null;
ClashApiHandler.Instance.SetProxies(proxies);
Application.Current?.Dispatcher.Invoke((Action)(() =>
{
_proxyGroups.Clear();
_proxyDetails.Clear();
}));
}
public void ProxiesDelayTest()
{
ProxiesDelayTest(true);
}
#region proxy function
private void SetRuleMode(ERuleMode mode)
{
_config.clashUIItem.ruleMode = mode;
if (mode != ERuleMode.Unchanged)
{
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("mode", mode.ToString().ToLower());
ClashApiHandler.Instance.ClashConfigUpdate(headers);
}
}
private void GetClashProxies(bool refreshUI)
{
ClashApiHandler.Instance.GetClashProxies(_config, (it, it2) =>
{
UpdateHandler(false, "Refresh Clash Proxies");
proxies = it?.proxies;
providers = it2?.providers;
ClashApiHandler.Instance.SetProxies(proxies);
if (proxies == null)
{
return;
}
if (refreshUI)
{
Application.Current?.Dispatcher.Invoke((Action)(() =>
{
RefreshProxyGroups();
}));
}
});
}
private void RefreshProxyGroups()
{
var selectedName = SelectedGroup?.name;
_proxyGroups.Clear();
var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups();
if (proxyGroups != null && proxyGroups.Count > 0)
{
foreach (var it in proxyGroups)
{
if (string.IsNullOrEmpty(it.name) || !proxies.ContainsKey(it.name))
{
continue;
}
var item = proxies[it.name];
if (!Global.allowSelectType.Contains(item.type.ToLower()))
{
continue;
}
_proxyGroups.Add(new ClashProxyModel()
{
now = item.now,
name = item.name,
type = item.type
});
}
}
//from api
foreach (KeyValuePair<string, ProxiesItem> kv in proxies)
{
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
{
continue;
}
var item = _proxyGroups.Where(t => t.name == kv.Key).FirstOrDefault();
if (item != null && !string.IsNullOrEmpty(item.name))
{
continue;
}
_proxyGroups.Add(new ClashProxyModel()
{
now = kv.Value.now,
name = kv.Key,
type = kv.Value.type
});
}
if (_proxyGroups != null && _proxyGroups.Count > 0)
{
if (selectedName != null && _proxyGroups.Any(t => t.name == selectedName))
{
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.name == selectedName);
}
else
{
SelectedGroup = _proxyGroups[0];
}
}
else
{
SelectedGroup = new();
}
}
private void RefreshProxyDetails(bool c)
{
_proxyDetails.Clear();
if (!c)
{
return;
}
var name = SelectedGroup?.name;
if (string.IsNullOrEmpty(name))
{
return;
}
if (proxies == null)
{
return;
}
proxies.TryGetValue(name, out ProxiesItem proxy);
if (proxy == null || proxy.all == null)
{
return;
}
var lstDetails = new List<ClashProxyModel>();
foreach (var item in proxy.all)
{
var isActive = item == proxy.now;
var proxy2 = TryGetProxy(item);
if (proxy2 == null)
{
continue;
}
int delay = -1;
if (proxy2.history.Count > 0)
{
delay = proxy2.history[proxy2.history.Count - 1].delay;
}
lstDetails.Add(new ClashProxyModel()
{
isActive = isActive,
name = item,
type = proxy2.type,
delay = delay <= 0 ? delayTimeout : delay,
delayName = delay <= 0 ? string.Empty : $"{delay}ms",
});
}
//sort
switch (SortingSelected)
{
case 0:
lstDetails = lstDetails.OrderBy(t => t.delay).ToList();
break;
case 1:
lstDetails = lstDetails.OrderBy(t => t.name).ToList();
break;
default:
break;
}
_proxyDetails.AddRange(lstDetails);
}
private ProxiesItem? TryGetProxy(string name)
{
if(proxies is null)
return null;
proxies.TryGetValue(name, out ProxiesItem proxy2);
if (proxy2 != null)
{
return proxy2;
}
//from providers
if (providers != null)
{
foreach (KeyValuePair<string, ProvidersItem> kv in providers)
{
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
{
var proxy3 = kv.Value.proxies.FirstOrDefault(t => t.name == name);
if (proxy3 != null)
{
return proxy3;
}
}
}
}
return null;
}
public void SetActiveProxy()
{
if (SelectedGroup == null || string.IsNullOrEmpty(SelectedGroup.name))
{
return;
}
if (SelectedDetail == null || string.IsNullOrEmpty(SelectedDetail.name))
{
return;
}
var name = SelectedGroup.name;
if (string.IsNullOrEmpty(name))
{
return;
}
var nameNode = SelectedDetail.name;
if (string.IsNullOrEmpty(nameNode))
{
return;
}
var selectedProxy = TryGetProxy(name);
if (selectedProxy == null || selectedProxy.type != "Selector")
{
_noticeHandler?.Enqueue(ResUI.OperationFailed);
return;
}
ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
selectedProxy.now = nameNode;
var group = _proxyGroups.Where(it => it.name == SelectedGroup.name).FirstOrDefault();
if (group != null)
{
group.now = nameNode;
var group2 = JsonUtils.DeepCopy(group);
_proxyGroups.Replace(group, group2);
SelectedGroup = group2;
//var index = _proxyGroups.IndexOf(group);
//_proxyGroups.Remove(group);
//_proxyGroups.Insert(index, group);
}
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
//RefreshProxyDetails(true);
//GetClashProxies(true);
}
private void ProxiesDelayTest(bool blAll)
{
UpdateHandler(false, "Clash Proxies Latency Test");
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
{
if (item == null)
{
GetClashProxies(true);
return;
}
if (string.IsNullOrEmpty(result))
{
return;
}
Application.Current?.Dispatcher.Invoke((Action)(() =>
{
//UpdateHandler(false, $"{item.name}={result}");
var detail = _proxyDetails.Where(it => it.name == item.name).FirstOrDefault();
if (detail != null)
{
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result);
if (dicResult != null && dicResult.ContainsKey("delay"))
{
detail.delay = Convert.ToInt32(dicResult["delay"].ToString());
detail.delayName = $"{detail.delay}ms";
}
else if (dicResult != null && dicResult.ContainsKey("message"))
{
detail.delay = delayTimeout;
detail.delayName = $"{dicResult["message"]}";
}
else
{
detail.delay = delayTimeout;
detail.delayName = String.Empty;
}
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
}
}));
});
}
#endregion proxy function
#region task
public void DelayTestTask()
{
var lastTime = DateTime.Now;
Observable.Interval(TimeSpan.FromSeconds(60))
.Subscribe(x =>
{
if (!(AutoRefresh && _config.clashUIItem.showInTaskbar))
{
return;
}
var dtNow = DateTime.Now;
if (_config.clashUIItem.proxiesAutoDelayTestInterval > 0)
{
if ((dtNow - lastTime).Minutes % _config.clashUIItem.proxiesAutoDelayTestInterval == 0)
{
ProxiesDelayTest();
lastTime = dtNow;
}
Thread.Sleep(1000);
}
});
}
#endregion task
}
}

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
using System.Reactive; using System.Reactive;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -14,7 +14,9 @@ using System.Reactive.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Handler.Fmt;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
using v2rayN.Views; using v2rayN.Views;
@@ -148,18 +150,10 @@ namespace v2rayN.ViewModels
//CheckUpdate //CheckUpdate
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
//public ReactiveCommand<Unit, Unit> CheckUpdateV2flyCoreCmd { get; }
//public ReactiveCommand<Unit, Unit> CheckUpdateSagerNetCoreCmd { get; }
public ReactiveCommand<Unit, Unit> CheckUpdateXrayCoreCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateXrayCoreCmd { get; }
public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
//public ReactiveCommand<Unit, Unit> CheckUpdateClashCoreCmd { get; }
//public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
public ReactiveCommand<Unit, Unit> CheckUpdateSingBoxCoreCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateSingBoxCoreCmd { get; }
public ReactiveCommand<Unit, Unit> CheckUpdateGeoCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateGeoCmd { get; }
public ReactiveCommand<Unit, Unit> ReloadCmd { get; } public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
[Reactive] [Reactive]
@@ -249,6 +243,9 @@ namespace v2rayN.ViewModels
[Reactive] [Reactive]
public string CurrentLanguage { get; set; } public string CurrentLanguage { get; set; }
[Reactive]
public bool ShowCalshUI { get; set; }
#endregion UI #endregion UI
#region Init #region Init
@@ -267,9 +264,16 @@ namespace v2rayN.ViewModels
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
SelectedRouting = new(); SelectedRouting = new();
SelectedServer = new(); SelectedServer = new();
if (_config.tunModeItem.enableTun && Utils.IsAdministrator()) if (_config.tunModeItem.enableTun)
{ {
EnableTun = true; if (Utils.IsAdministrator())
{
EnableTun = true;
}
else
{
_config.tunModeItem.enableTun = EnableTun = false;
}
} }
_subId = _config.subIndexId; _subId = _config.subIndexId;
@@ -518,29 +522,17 @@ namespace v2rayN.ViewModels
{ {
CheckUpdateN(); CheckUpdateN();
}); });
//CheckUpdateV2flyCoreCmd = ReactiveCommand.Create(() =>
//{
// CheckUpdateCore(ECoreType.v2fly_v5);
//});
//CheckUpdateSagerNetCoreCmd = ReactiveCommand.Create(() =>
//{
// CheckUpdateCore(ECoreType.SagerNet);
//});
CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() => CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() =>
{ {
CheckUpdateCore(ECoreType.Xray); CheckUpdateCore(ECoreType.Xray, null);
});
CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
{
CheckUpdateCore(ECoreType.mihomo, false);
}); });
//CheckUpdateClashCoreCmd = ReactiveCommand.Create(() =>
//{
// CheckUpdateCore(ECoreType.clash);
//});
//CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
//{
// CheckUpdateCore(ECoreType.clash_meta);
//});
CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() => CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() =>
{ {
CheckUpdateCore(ECoreType.sing_box); CheckUpdateCore(ECoreType.sing_box, null);
}); });
CheckUpdateGeoCmd = ReactiveCommand.Create(() => CheckUpdateGeoCmd = ReactiveCommand.Create(() =>
{ {
@@ -580,6 +572,7 @@ namespace v2rayN.ViewModels
AutoHideStartup(); AutoHideStartup();
_showInTaskbar = true; _showInTaskbar = true;
_config.clashUIItem.showInTaskbar = _showInTaskbar;
} }
private void Init() private void Init()
@@ -1026,10 +1019,10 @@ namespace v2rayN.ViewModels
{ {
ShowHideWindow(false); ShowHideWindow(false);
var dpiXY = Utils.GetDpiXY(Application.Current.MainWindow); var dpiXY = QRCodeHelper.GetDpiXY(Application.Current.MainWindow);
string result = await Task.Run(() => string result = await Task.Run(() =>
{ {
return Utils.ScanScreen(dpiXY.Item1, dpiXY.Item2); return QRCodeHelper.ScanScreen(dpiXY.Item1, dpiXY.Item2);
}); });
ShowHideWindow(true); ShowHideWindow(true);
@@ -1152,7 +1145,7 @@ namespace v2rayN.ViewModels
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer); _noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
return; return;
} }
var url = ShareHandler.GetShareUrl(item); var url = FmtHandler.GetShareUri(item);
if (Utils.IsNullOrEmpty(url)) if (Utils.IsNullOrEmpty(url))
{ {
return; return;
@@ -1293,7 +1286,7 @@ namespace v2rayN.ViewModels
StringBuilder sb = new(); StringBuilder sb = new();
foreach (var it in lstSelecteds) foreach (var it in lstSelecteds)
{ {
string url = ShareHandler.GetShareUrl(it); string url = FmtHandler.GetShareUri(it);
if (Utils.IsNullOrEmpty(url)) if (Utils.IsNullOrEmpty(url))
{ {
continue; continue;
@@ -1318,7 +1311,7 @@ namespace v2rayN.ViewModels
StringBuilder sb = new(); StringBuilder sb = new();
foreach (var it in lstSelecteds) foreach (var it in lstSelecteds)
{ {
string? url = ShareHandler.GetShareUrl(it); string? url = FmtHandler.GetShareUri(it);
if (Utils.IsNullOrEmpty(url)) if (Utils.IsNullOrEmpty(url))
{ {
continue; continue;
@@ -1471,7 +1464,7 @@ namespace v2rayN.ViewModels
(new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate); (new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate);
} }
private void CheckUpdateCore(ECoreType type) private void CheckUpdateCore(ECoreType type, bool? preRelease)
{ {
void _updateUI(bool success, string msg) void _updateUI(bool success, string msg)
{ {
@@ -1497,7 +1490,7 @@ namespace v2rayN.ViewModels
} }
} }
} }
(new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, _config.guiItem.checkPreReleaseUpdate); (new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, preRelease ?? _config.guiItem.checkPreReleaseUpdate);
} }
private void CheckUpdateGeo() private void CheckUpdateGeo()
@@ -1520,7 +1513,12 @@ namespace v2rayN.ViewModels
Application.Current?.Dispatcher.Invoke((Action)(() => Application.Current?.Dispatcher.Invoke((Action)(() =>
{ {
BlReloadEnabled = true; BlReloadEnabled = true;
ShowCalshUI = (_config.runningCoreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
if (ShowCalshUI) {
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
}
})); }));
}); });
} }
@@ -1565,7 +1563,7 @@ namespace v2rayN.ViewModels
private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange) private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange)
{ {
SysProxyHandle.UpdateSysProxy(_config, _config.tunModeItem.enableTun ? true : false); SysProxyHandle.UpdateSysProxy(_config, _config.tunModeItem.enableTun ? true : false);
_noticeHandler?.SendMessage(ResUI.TipChangeSystemProxy + _config.sysProxyType.ToString(), true); _noticeHandler?.SendMessage($"{ResUI.TipChangeSystemProxy} - {_config.sysProxyType.ToString()}", true);
Application.Current?.Dispatcher.Invoke((Action)(() => Application.Current?.Dispatcher.Invoke((Action)(() =>
{ {
@@ -1691,6 +1689,7 @@ namespace v2rayN.ViewModels
//Utile.RegWriteValue(Global.MyRegPath, Utile.WindowHwndKey, Convert.ToString((long)windowHandle)); //Utile.RegWriteValue(Global.MyRegPath, Utile.WindowHwndKey, Convert.ToString((long)windowHandle));
} }
_showInTaskbar = bl; _showInTaskbar = bl;
_config.clashUIItem.showInTaskbar = _showInTaskbar;
} }
private void RestoreUI() private void RestoreUI()
@@ -1793,6 +1792,7 @@ namespace v2rayN.ViewModels
Application.Current.Resources["StdFontSize1"] = size + 1; Application.Current.Resources["StdFontSize1"] = size + 1;
Application.Current.Resources["StdFontSize2"] = size + 2; Application.Current.Resources["StdFontSize2"] = size + 2;
Application.Current.Resources["StdFontSizeMsg"] = size - 1; Application.Current.Resources["StdFontSizeMsg"] = size - 1;
Application.Current.Resources["StdFontSize-1"] = size - 1;
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
} }

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
using System.Reactive; using System.Reactive;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
@@ -20,6 +21,7 @@ namespace v2rayN.ViewModels
[Reactive] public int localPort { get; set; } [Reactive] public int localPort { get; set; }
[Reactive] public bool udpEnabled { get; set; } [Reactive] public bool udpEnabled { get; set; }
[Reactive] public bool sniffingEnabled { get; set; } [Reactive] public bool sniffingEnabled { get; set; }
public IList<string> destOverride { get; set; }
[Reactive] public bool routeOnly { get; set; } [Reactive] public bool routeOnly { get; set; }
[Reactive] public bool allowLANConn { get; set; } [Reactive] public bool allowLANConn { get; set; }
[Reactive] public bool newPort4LAN { get; set; } [Reactive] public bool newPort4LAN { get; set; }
@@ -262,7 +264,7 @@ namespace v2rayN.ViewModels
var needReboot = (EnableStatistics != _config.guiItem.enableStatistics var needReboot = (EnableStatistics != _config.guiItem.enableStatistics
|| EnableDragDropSort != _config.uiItem.enableDragDropSort || EnableDragDropSort != _config.uiItem.enableDragDropSort
|| EnableHWA != _config.guiItem.enableHWA || EnableHWA != _config.guiItem.enableHWA
|| CurrentFontFamily != _config.uiItem.currentFontFamily); || CurrentFontFamily != _config.uiItem.currentFontFamily);
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString()) //if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString()) // || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
@@ -279,6 +281,7 @@ namespace v2rayN.ViewModels
_config.inbound[0].localPort = localPort; _config.inbound[0].localPort = localPort;
_config.inbound[0].udpEnabled = udpEnabled; _config.inbound[0].udpEnabled = udpEnabled;
_config.inbound[0].sniffingEnabled = sniffingEnabled; _config.inbound[0].sniffingEnabled = sniffingEnabled;
_config.inbound[0].destOverride = destOverride?.ToList();
_config.inbound[0].routeOnly = routeOnly; _config.inbound[0].routeOnly = routeOnly;
_config.inbound[0].allowLANConn = allowLANConn; _config.inbound[0].allowLANConn = allowLANConn;
_config.inbound[0].newPort4LAN = newPort4LAN; _config.inbound[0].newPort4LAN = newPort4LAN;

View File

@@ -4,6 +4,7 @@ using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
using System.Reactive; using System.Reactive;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
@@ -129,6 +130,7 @@ namespace v2rayN.ViewModels
id = item.id, id = item.id,
outboundTag = item.outboundTag, outboundTag = item.outboundTag,
port = item.port, port = item.port,
network = item.network,
protocols = Utils.List2String(item.protocol), protocols = Utils.List2String(item.protocol),
inboundTags = Utils.List2String(item.inboundTag), inboundTags = Utils.List2String(item.inboundTag),
domains = Utils.List2String(item.domain), domains = Utils.List2String(item.domain),
@@ -289,7 +291,7 @@ namespace v2rayN.ViewModels
private void ImportRulesFromClipboard() private void ImportRulesFromClipboard()
{ {
string clipboardData = Utils.GetClipboardData(); var clipboardData = Utils.GetClipboardData();
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0) if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
{ {
RefreshRulesItems(); RefreshRulesItems();

View File

@@ -1,6 +1,7 @@
using ReactiveUI; using ReactiveUI;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using v2rayN.Enums;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.ViewModels; using v2rayN.ViewModels;

View File

@@ -2,6 +2,7 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;
@@ -332,6 +333,7 @@ namespace v2rayN.Views
case nameof(ETransport.ws): case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2; tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1; tipPath.Text = ResUI.TransportPathTip1;
break; break;

View File

@@ -0,0 +1,112 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.ClashConnectionsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:v2rayN.Views"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:v2rayN.Resx"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
d:DesignHeight="450"
d:DesignWidth="800"
x:TypeArguments="vms:ClashConnectionsViewModel"
mc:Ignorable="d">
<DockPanel>
<ToolBarTray Margin="0,8,0,8" DockPanel.Dock="Top">
<ToolBar ClipToBounds="True" Style="{StaticResource MaterialDesignToolBar}">
<Button Width="1" Visibility="Hidden">
<materialDesign:PackIcon
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="ContentSave" />
</Button>
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSorting}" />
<ComboBox
x:Name="cmbSorting"
Width="100"
Margin="8"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
</ComboBox>
<Separator />
<Button x:Name="btnConnectionCloseAll" ToolTip="{x:Static resx:ResUI.menuConnectionCloseAll}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Close" />
<TextBlock Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.menuConnectionCloseAll}" />
</StackPanel>
</Button>
<Separator />
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleButton
x:Name="togAutoRefresh"
Margin="8"
HorizontalAlignment="Left" />
</ToolBar>
</ToolBarTray>
<DataGrid
x:Name="lstConnections"
AutoGenerateColumns="False"
BorderThickness="1"
CanUserAddRows="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
EnableRowVirtualization="True"
GridLinesVisibility="All"
HeadersVisibility="Column"
IsReadOnly="True"
Style="{StaticResource DefDataGrid}">
<DataGrid.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuConnectionClose" Header="{x:Static resx:ResUI.menuConnectionClose}" />
<MenuItem x:Name="menuConnectionCloseAll" Header="{x:Static resx:ResUI.menuConnectionCloseAll}" />
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn
Width="300"
Binding="{Binding host}"
Header="{x:Static resx:ResUI.TbSortingHost}" />
<DataGridTextColumn
Width="100"
Binding="{Binding network}"
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
<DataGridTextColumn
Width="100"
Binding="{Binding type}"
Header="{x:Static resx:ResUI.TbSortingType}" />
<DataGridTextColumn
Width="200"
Binding="{Binding chain}"
Header="{x:Static resx:ResUI.TbSortingChain}" />
<DataGridTextColumn
Width="100"
Binding="{Binding uploadTraffic}"
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
<DataGridTextColumn
Width="100"
Binding="{Binding downloadTraffic}"
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
<DataGridTextColumn
Width="100"
Binding="{Binding elapsed}"
Header="{x:Static resx:ResUI.TbSortingTime}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</reactiveui:ReactiveUserControl>

View File

@@ -0,0 +1,36 @@
using v2rayN.ViewModels;
using ReactiveUI;
using System.Reactive.Disposables;
namespace v2rayN.Views
{
/// <summary>
/// Interaction logic for ConnectionsView.xaml
/// </summary>
public partial class ClashConnectionsView
{
public ClashConnectionsView()
{
InitializeComponent();
ViewModel = new ClashConnectionsViewModel();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.ConnectionItems, v => v.lstConnections.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstConnections.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
}
private void btnClose_Click(object sender, System.Windows.RoutedEventArgs e)
{
ViewModel?.ClashConnectionClose(false);
}
}
}

View File

@@ -0,0 +1,180 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.ClashProxiesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:v2rayN.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:v2rayN.Views"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:v2rayN.Resx"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
d:DesignHeight="450"
d:DesignWidth="800"
x:TypeArguments="vms:ClashProxiesViewModel"
KeyDown="ProxiesView_KeyDown"
mc:Ignorable="d">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
<converters:DelayColorConverter x:Key="DelayColorConverter" />
</UserControl.Resources>
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar
HorizontalAlignment="Center"
VerticalAlignment="Center"
ClipToBounds="True"
Style="{StaticResource MaterialDesignToolBar}">
<Button Width="1" Visibility="Hidden">
<materialDesign:PackIcon
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="ContentSave" />
</Button>
<Separator />
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.menuRulemode}" />
<ComboBox
x:Name="cmbRulemode"
Width="80"
Margin="8"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeRule}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeGlobal}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeDirect}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeNothing}" />
</ComboBox>
<Separator />
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSorting}" />
<ComboBox
x:Name="cmbSorting"
Width="60"
Margin="8"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDelay}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingName}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDefault}" />
</ComboBox>
<Separator />
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleButton
x:Name="togAutoRefresh"
Margin="8"
HorizontalAlignment="Left" />
</ToolBar>
</ToolBarTray>
<DockPanel>
<ListView
x:Name="lstProxyGroups"
Margin="0,0,5,0"
BorderThickness="0"
DockPanel.Dock="Left"
ItemContainerStyle="{StaticResource lvItemSelected}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Style="{StaticResource MaterialDesignListView}">
<ListView.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuProxiesReload" Header="{x:Static resx:ResUI.menuProxiesReload}" />
<MenuItem x:Name="menuProxiesDelaytest" Header="{x:Static resx:ResUI.menuProxiesDelaytest}" />
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Width="200" Padding="0">
<DockPanel>
<Grid Grid.Column="0" Margin="4">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Style="{StaticResource ListItemTitle}" Text="{Binding name}" />
<TextBlock
Margin="8,0,0,0"
Style="{StaticResource ListItemSubTitle}"
Text="{Binding type}" />
</StackPanel>
<TextBlock
Grid.Row="1"
Style="{StaticResource ListItemSubTitle}"
Text="{Binding now}" />
</Grid>
</DockPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView
x:Name="lstProxyDetails"
BorderThickness="0"
ItemContainerStyle="{StaticResource lvItemSelected}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Style="{StaticResource MaterialDesignListView}">
<ListView.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" />
<MenuItem x:Name="menuProxiesSelectActivity" Header="{x:Static resx:ResUI.menuProxiesSelectActivity}" />
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Width="200" Padding="0">
<DockPanel>
<Border
Width="5"
Height="auto"
Margin="0,1"
Background="{DynamicResource MaterialDesign.Brush.Primary.Light}"
CornerRadius="4"
DockPanel.Dock="Left"
Visibility="{Binding Path=isActive, Converter={StaticResource BoolToVisConverter}}" />
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Style="{StaticResource ListItemSubTitle}"
Text="{Binding name}"
TextWrapping="WrapWithOverflow" />
<DockPanel Grid.Row="1">
<TextBlock
DockPanel.Dock="Right"
Foreground="{Binding Path=delay, Converter={StaticResource DelayColorConverter}}"
Style="{StaticResource ListItemSubTitle2}"
Text="{Binding delayName}" />
<TextBlock Style="{StaticResource ListItemSubTitle2}" Text="{Binding type}" />
</DockPanel>
</Grid>
</DockPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DockPanel>
</DockPanel>
</reactiveui:ReactiveUserControl>

View File

@@ -0,0 +1,60 @@
using v2rayN.ViewModels;
using ReactiveUI;
using Splat;
using System.Reactive.Disposables;
using System.Windows.Input;
namespace v2rayN.Views
{
/// <summary>
/// Interaction logic for ProxiesView.xaml
/// </summary>
public partial class ClashProxiesView
{
public ClashProxiesView()
{
InitializeComponent();
ViewModel = new ClashProxiesViewModel();
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.ProxyGroups, v => v.lstProxyGroups.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedGroup, v => v.lstProxyGroups.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ProxyDetails, v => v.lstProxyDetails.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
}
private void ProxiesView_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.F5:
ViewModel?.ProxiesReload();
break;
case Key.Enter:
ViewModel?.SetActiveProxy();
break;
}
}
private void lstProxyDetails_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ViewModel?.SetActiveProxy();
}
}
}

View File

@@ -51,7 +51,7 @@ namespace v2rayN.Views
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e) private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
{ {
Utils.ProcessStart("https://www.v2fly.org/config/dns.html#dnsobject"); Utils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
} }
private void linkDnsSingboxObjectDoc_Click(object sender, RoutedEventArgs e) private void linkDnsSingboxObjectDoc_Click(object sender, RoutedEventArgs e)

View File

@@ -2,6 +2,7 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using v2rayN.Enums;
using v2rayN.Handler; using v2rayN.Handler;
using v2rayN.Models; using v2rayN.Models;
using v2rayN.Resx; using v2rayN.Resx;

View File

@@ -37,6 +37,7 @@
</Window.Resources> </Window.Resources>
<materialDesign:DialogHost <materialDesign:DialogHost
materialDesign:TransitionAssist.DisableTransitions="True"
Identifier="RootDialog" Identifier="RootDialog"
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}" SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}"
Style="{StaticResource MaterialDesignEmbeddedDialogHost}"> Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
@@ -230,28 +231,14 @@
x:Name="menuCheckUpdateN" x:Name="menuCheckUpdateN"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="V2rayN" /> Header="V2rayN" />
<!--<MenuItem
x:Name="menuCheckUpdateV2flyCore"
Height="{StaticResource MenuItemHeight}"
Header="V2fly v5 Core" />
<MenuItem
x:Name="menuCheckUpdateSagerNetCore"
Height="{StaticResource MenuItemHeight}"
Header="SagerNet Core" />-->
<MenuItem <MenuItem
x:Name="menuCheckUpdateXrayCore" x:Name="menuCheckUpdateXrayCore"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="Xray Core" /> Header="Xray Core" />
<!--<Separator Margin="-40,5" />
<MenuItem <MenuItem
x:Name="menuCheckUpdateClashCore" x:Name="menuCheckUpdateMihomoCore"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="Clash Core" /> Header="Mihomo Core" />
<MenuItem
x:Name="menuCheckUpdateClashMetaCore"
Height="{StaticResource MenuItemHeight}"
Header="Clash.Meta Core" />
<Separator Margin="-40,5" />-->
<MenuItem <MenuItem
x:Name="menuCheckUpdateSingBoxCore" x:Name="menuCheckUpdateSingBoxCore"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -450,6 +437,14 @@
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}"
materialDesign:TextFieldAssist.HasClearButton="True" materialDesign:TextFieldAssist.HasClearButton="True"
Style="{StaticResource DefTextBox}" /> Style="{StaticResource DefTextBox}" />
<Button
x:Name="btnShowCalshUI"
Width="30"
Height="30"
Margin="20,0"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="EyeOutline" />
</Button>
</WrapPanel> </WrapPanel>
<materialDesign:ColorZone <materialDesign:ColorZone
@@ -537,237 +532,240 @@
<RowDefinition Height="10" /> <RowDefinition Height="10" />
<RowDefinition Height="1*" /> <RowDefinition Height="1*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DataGrid <DockPanel Grid.Row="0">
x:Name="lstProfiles" <ContentControl x:Name="tabClashUI" DockPanel.Dock="Right" />
Grid.Row="0" <DataGrid
materialDesign:DataGridAssist.CellPadding="2,2" x:Name="lstProfiles"
AutoGenerateColumns="False" materialDesign:DataGridAssist.CellPadding="2,2"
BorderThickness="1" AutoGenerateColumns="False"
CanUserAddRows="False" BorderThickness="1"
CanUserResizeRows="False" CanUserAddRows="False"
CanUserSortColumns="False" CanUserResizeRows="False"
EnableRowVirtualization="True" CanUserSortColumns="False"
Focusable="True" EnableRowVirtualization="True"
GridLinesVisibility="All" Focusable="True"
HeadersVisibility="All" GridLinesVisibility="All"
IsReadOnly="True" HeadersVisibility="All"
RowHeaderWidth="40" IsReadOnly="True"
Style="{StaticResource DefDataGrid}"> RowHeaderWidth="40"
<DataGrid.InputBindings> Style="{StaticResource DefDataGrid}">
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+C" /> <DataGrid.InputBindings>
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+V" /> <KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+C" />
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Delete" /> <KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+V" />
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Enter" /> <KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Delete" />
</DataGrid.InputBindings> <KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Enter" />
<DataGrid.ContextMenu> </DataGrid.InputBindings>
<ContextMenu Style="{StaticResource DefContextMenu}"> <DataGrid.ContextMenu>
<MenuItem <ContextMenu Style="{StaticResource DefContextMenu}">
x:Name="menuEditServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuEditServer"
Header="{x:Static resx:ResUI.menuEditServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuEditServer}" />
x:Name="menuSetDefaultServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuSetDefaultServer"
Header="{x:Static resx:ResUI.menuSetDefaultServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuSetDefaultServer}" />
x:Name="menuRemoveServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuRemoveServer"
Header="{x:Static resx:ResUI.menuRemoveServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuRemoveServer}" />
x:Name="menuRemoveDuplicateServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuRemoveDuplicateServer"
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
x:Name="menuCopyServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuCopyServer"
Header="{x:Static resx:ResUI.menuCopyServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuCopyServer}" />
x:Name="menuShareServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuShareServer"
Header="{x:Static resx:ResUI.menuShareServer}" /> Height="{StaticResource MenuItemHeight}"
<Separator /> Header="{x:Static resx:ResUI.menuShareServer}" />
<MenuItem <Separator />
x:Name="menuMixedTestServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuMixedTestServer"
Header="{x:Static resx:ResUI.menuMixedTestServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuMixedTestServer}" />
x:Name="menuTcpingServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuTcpingServer"
Header="{x:Static resx:ResUI.menuTcpingServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuTcpingServer}" />
x:Name="menuRealPingServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuRealPingServer"
Header="{x:Static resx:ResUI.menuRealPingServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuRealPingServer}" />
x:Name="menuSpeedServer" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuSpeedServer"
Header="{x:Static resx:ResUI.menuSpeedServer}" /> Height="{StaticResource MenuItemHeight}"
<MenuItem Header="{x:Static resx:ResUI.menuSpeedServer}" />
x:Name="menuSortServerResult" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuSortServerResult"
Header="{x:Static resx:ResUI.menuSortServerResult}" /> Height="{StaticResource MenuItemHeight}"
<Separator /> Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem <Separator />
x:Name="menuMoveToGroup" <MenuItem
Height="{StaticResource MenuItemHeight}" x:Name="menuMoveToGroup"
Header="{x:Static resx:ResUI.menuMoveToGroup}"> Height="{StaticResource MenuItemHeight}"
<MenuItem Height="Auto"> Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem.Header> <MenuItem Height="Auto">
<DockPanel> <MenuItem.Header>
<ComboBox <DockPanel>
x:Name="cmbMoveToGroup" <ComboBox
Width="200" x:Name="cmbMoveToGroup"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSubscription}" Width="200"
DisplayMemberPath="remarks" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSubscription}"
FontSize="{DynamicResource StdFontSize}" DisplayMemberPath="remarks"
Style="{StaticResource MaterialDesignFilledComboBox}" /> FontSize="{DynamicResource StdFontSize}"
</DockPanel> Style="{StaticResource MaterialDesignFilledComboBox}" />
</MenuItem.Header> </DockPanel>
</MenuItem.Header>
</MenuItem>
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
<MenuItem
x:Name="menuMoveTop"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveTop}" />
<MenuItem
x:Name="menuMoveUp"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveUp}" />
<MenuItem
x:Name="menuMoveDown"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveDown}" />
<MenuItem
x:Name="menuMoveBottom"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveBottom}" />
</MenuItem> </MenuItem>
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
<MenuItem <MenuItem
x:Name="menuMoveTop" x:Name="menuSelectAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveTop}" /> Click="menuSelectAll_Click"
Header="{x:Static resx:ResUI.menuSelectAll}" />
<Separator />
<MenuItem <MenuItem
x:Name="menuMoveUp" x:Name="menuExport2ClientConfig"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveUp}" /> Header="{x:Static resx:ResUI.menuExport2ClientConfig}" />
<MenuItem <MenuItem
x:Name="menuMoveDown" x:Name="menuExport2ShareUrl"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuExport2ShareUrl}" />
<MenuItem <MenuItem
x:Name="menuMoveBottom" x:Name="menuExport2SubContent"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveBottom}" /> Header="{x:Static resx:ResUI.menuExport2SubContent}" />
</MenuItem> </ContextMenu>
<MenuItem </DataGrid.ContextMenu>
x:Name="menuSelectAll" <DataGrid.Resources>
Height="{StaticResource MenuItemHeight}" <Style BasedOn="{StaticResource MaterialDesignDataGridRow}" TargetType="DataGridRow">
Click="menuSelectAll_Click" <EventSetter Event="MouseDoubleClick" Handler="LstProfiles_MouseDoubleClick" />
Header="{x:Static resx:ResUI.menuSelectAll}" /> </Style>
<Separator /> <Style BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}" TargetType="DataGridColumnHeader">
<MenuItem <EventSetter Event="Click" Handler="LstProfiles_ColumnHeader_Click" />
x:Name="menuExport2ClientConfig" </Style>
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExport2ClientConfig}" />
<MenuItem
x:Name="menuExport2ShareUrl"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExport2ShareUrl}" />
<MenuItem
x:Name="menuExport2SubContent"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExport2SubContent}" />
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Resources>
<Style BasedOn="{StaticResource MaterialDesignDataGridRow}" TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick" Handler="LstProfiles_MouseDoubleClick" />
</Style>
<Style BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}" TargetType="DataGridColumnHeader">
<EventSetter Event="Click" Handler="LstProfiles_ColumnHeader_Click" />
</Style>
<Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell"> <Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding isActive}" Value="True"> <DataTrigger Binding="{Binding isActive}" Value="True">
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" /> <Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
<Setter Property="Foreground" Value="Black" /> <Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" /> <Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</DataGrid.Resources> </DataGrid.Resources>
<DataGrid.Columns> <DataGrid.Columns>
<base:MyDGTextColumn <base:MyDGTextColumn
Width="80" Width="80"
Binding="{Binding configType}" Binding="{Binding configType}"
ExName="configType" ExName="configType"
Header="{x:Static resx:ResUI.LvServiceType}" /> Header="{x:Static resx:ResUI.LvServiceType}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="150" Width="150"
Binding="{Binding remarks}" Binding="{Binding remarks}"
ExName="remarks" ExName="remarks"
Header="{x:Static resx:ResUI.LvRemarks}" /> Header="{x:Static resx:ResUI.LvRemarks}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="120" Width="120"
Binding="{Binding address}" Binding="{Binding address}"
ExName="address" ExName="address"
Header="{x:Static resx:ResUI.LvAddress}" /> Header="{x:Static resx:ResUI.LvAddress}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="60" Width="60"
Binding="{Binding port}" Binding="{Binding port}"
ExName="port" ExName="port"
Header="{x:Static resx:ResUI.LvPort}" /> Header="{x:Static resx:ResUI.LvPort}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding network}" Binding="{Binding network}"
ExName="network" ExName="network"
Header="{x:Static resx:ResUI.LvTransportProtocol}" /> Header="{x:Static resx:ResUI.LvTransportProtocol}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding streamSecurity}" Binding="{Binding streamSecurity}"
ExName="streamSecurity" ExName="streamSecurity"
Header="{x:Static resx:ResUI.LvTLS}" /> Header="{x:Static resx:ResUI.LvTLS}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding subRemarks}" Binding="{Binding subRemarks}"
ExName="subRemarks" ExName="subRemarks"
Header="{x:Static resx:ResUI.LvSubscription}" /> Header="{x:Static resx:ResUI.LvSubscription}" />
<base:MyDGTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding delayVal}" Binding="{Binding delayVal}"
ExName="delayVal" ExName="delayVal"
Header="{x:Static resx:ResUI.LvTestDelay}"> Header="{x:Static resx:ResUI.LvTestDelay}">
<DataGridTextColumn.ElementStyle> <DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}"> <Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Right" /> <Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Foreground" Value="{Binding delay, Converter={StaticResource DelayColorConverter}}" /> <Setter Property="Foreground" Value="{Binding delay, Converter={StaticResource DelayColorConverter}}" />
</Style> </Style>
</DataGridTextColumn.ElementStyle> </DataGridTextColumn.ElementStyle>
</base:MyDGTextColumn> </base:MyDGTextColumn>
<base:MyDGTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding speedVal}" Binding="{Binding speedVal}"
ExName="speedVal" ExName="speedVal"
Header="{x:Static resx:ResUI.LvTestSpeed}"> Header="{x:Static resx:ResUI.LvTestSpeed}">
<DataGridTextColumn.ElementStyle> <DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}"> <Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Right" /> <Setter Property="HorizontalAlignment" Value="Right" />
</Style> </Style>
</DataGridTextColumn.ElementStyle> </DataGridTextColumn.ElementStyle>
</base:MyDGTextColumn> </base:MyDGTextColumn>
<base:MyDGTextColumn
x:Name="colTodayUp"
Width="100"
Binding="{Binding todayUp}"
ExName="todayUp"
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTodayDown"
Width="100"
Binding="{Binding todayDown}"
ExName="todayDown"
Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTotalUp"
Width="100"
Binding="{Binding totalUp}"
ExName="totalUp"
Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTotalDown"
Width="100"
Binding="{Binding totalDown}"
ExName="totalDown"
Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
<base:MyDGTextColumn
x:Name="colTodayUp"
Width="100"
Binding="{Binding todayUp}"
ExName="todayUp"
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTodayDown"
Width="100"
Binding="{Binding todayDown}"
ExName="todayDown"
Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTotalUp"
Width="100"
Binding="{Binding totalUp}"
ExName="totalUp"
Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}" />
<base:MyDGTextColumn
x:Name="colTotalDown"
Width="100"
Binding="{Binding totalDown}"
ExName="totalDown"
Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}" />
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" />
<local:MsgView Grid.Row="2" /> <local:MsgView Grid.Row="2" />
<materialDesign:Snackbar <materialDesign:Snackbar

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