diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-02-28 23:13:39 +0800 | 
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-02-28 23:13:39 +0800 | 
| commit | a0f2f7e29035553562941f4046db88d707daf199 (patch) | |
| tree | b6b56019f89a2f3b1a6cffccba2849687ee61241 /tools | |
| parent | 346b5256b99e28cd5c67c03dca4be84e1c75f749 (diff) | |
| download | crupest-a0f2f7e29035553562941f4046db88d707daf199.tar.gz crupest-a0f2f7e29035553562941f4046db88d707daf199.tar.bz2 crupest-a0f2f7e29035553562941f4046db88d707daf199.zip  | |
chore(store): move everything to store.
Diffstat (limited to 'tools')
38 files changed, 0 insertions, 2339 deletions
diff --git a/tools/Crupest.SecretTool/.gitignore b/tools/Crupest.SecretTool/.gitignore deleted file mode 100644 index ac4d8a4..0000000 --- a/tools/Crupest.SecretTool/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.vs -bin -obj -*.pubxml.user -*.csproj.user - -publish diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool.sln b/tools/Crupest.SecretTool/Crupest.SecretTool.sln deleted file mode 100644 index fde4347..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool.sln +++ /dev/null @@ -1,30 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34024.191 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F4C2CE80-CDF8-4B08-8912-D1F0F14196AD}" -	ProjectSection(SolutionItems) = preProject -		.gitignore = .gitignore -	EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crupest.SecretTool", "Crupest.SecretTool\Crupest.SecretTool.csproj", "{D6335AE4-FD22-49CD-9624-37371F3B4F82}" -EndProject -Global -	GlobalSection(SolutionConfigurationPlatforms) = preSolution -		Debug|Any CPU = Debug|Any CPU -		Release|Any CPU = Release|Any CPU -	EndGlobalSection -	GlobalSection(ProjectConfigurationPlatforms) = postSolution -		{D6335AE4-FD22-49CD-9624-37371F3B4F82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU -		{D6335AE4-FD22-49CD-9624-37371F3B4F82}.Debug|Any CPU.Build.0 = Debug|Any CPU -		{D6335AE4-FD22-49CD-9624-37371F3B4F82}.Release|Any CPU.ActiveCfg = Release|Any CPU -		{D6335AE4-FD22-49CD-9624-37371F3B4F82}.Release|Any CPU.Build.0 = Release|Any CPU -	EndGlobalSection -	GlobalSection(SolutionProperties) = preSolution -		HideSolutionNode = FALSE -	EndGlobalSection -	GlobalSection(ExtensibilityGlobals) = postSolution -		SolutionGuid = {B1E8FD9C-9157-4F4E-8265-4B37F30EEC5E} -	EndGlobalSection -EndGlobal diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore b/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore deleted file mode 100644 index c936492..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vmess.txt diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs deleted file mode 100644 index ff58551..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Crupest.SecretTool; - -public record ConfigItem(string Value, int LineNumber); - -public class DictionaryConfig(string configString, List<string>? requiredKeys = null) -{ -    private static Dictionary<string, ConfigItem> Parse(string configString, List<string>? requiredKeys = null) -    { -        var config = new Dictionary<string, ConfigItem>(); -        var lines = configString.Split('\n'); -        int lineNumber = 1; - -        foreach (var line in lines) -        { -            var l = line; -            var beginOfComment = l.IndexOf('#'); -            if (beginOfComment >= 0) -            { -                l = line[..beginOfComment]; -            } -            l = l.Trim(); -            if (!string.IsNullOrEmpty(l)) -            { -                var equalIndex = l.IndexOf('='); -                if (equalIndex == -1) -                { -                    throw new FormatException($"No '=' found in line {lineNumber}."); -                } - -                config.Add(l[..equalIndex].Trim(), new ConfigItem(l[(equalIndex + 1)..].Trim(), lineNumber)); -            } - -            lineNumber++; -        } - -        if (requiredKeys is not null) -        { -            foreach (var key in requiredKeys) -            { -                if (!config.ContainsKey(key)) -                { -                    throw new FormatException($"Required key '{key}' not found in config."); -                } -            } -        } - -        return config; -    } - -    public string ConfigString { get; } = configString; -    public List<string>? RequiredKeys { get; } = requiredKeys; -    public Dictionary<string, ConfigItem> Config { get; } = Parse(configString); -    public ConfigItem GetItemCaseInsensitive(string key) -    { -        foreach (var (originalKey, value) in Config) -        { -            if (string.Equals(originalKey, key, StringComparison.OrdinalIgnoreCase)) -            { -                return value; -            } -        } -        throw new KeyNotFoundException($"Key '{key}' not found in config case-insensitively."); -    } -} - -public class ListConfig(string configString) -{ -    private static List<ConfigItem> Parse(string configString) -    { -        var config = new List<ConfigItem>(); -        var lines = configString.Split('\n'); -        int lineNumber = 1; - -        foreach (var line in lines) -        { -            var l = line; -            var beginOfComment = l.IndexOf('#'); -            if (beginOfComment >= 0) -            { -                l = line[..beginOfComment]; -            } -            l = l.Trim(); -            if (!string.IsNullOrEmpty(l)) -            { -                config.Add(new ConfigItem(l, lineNumber)); -            } -            lineNumber++; -        } - -        return config; -    } - -    public string ConfigString { get; } = configString; -    public List<ConfigItem> Config { get; } = Parse(configString); -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs deleted file mode 100644 index 0803b01..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics; - -namespace Crupest.SecretTool; - -public class Controller(string executablePath, string configPath, string? assetPath) -{ -    public const string ToolAssetEnvironmentVariableName = "v2ray.location.asset"; - -    public static string? FindExecutable(string contentDir, out bool isLocal, string? executableName = null) -    { -        isLocal = false; -        executableName ??= "v2ray"; - -        if (OperatingSystem.IsWindows()) -        { -            executableName += ".exe"; -        } - -        var localToolPath = Path.Combine(contentDir, executableName); -        if (File.Exists(localToolPath)) -        { -            isLocal = true; -            return localToolPath; -        } - -        var paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator); -        if (paths is not null) -        { -            foreach (var p in paths) -            { -                var toolPath = Path.Combine(p, executableName); -                if (File.Exists(toolPath)) -                { -                    return toolPath; -                } -            } -        } - -        return null; -    } - -    public string ExecutablePath { get; } = executablePath; -    public string ConfigPath { get; } = configPath; -    public string? AssetPath { get; } = assetPath; -    public Process? CurrentProcess { get; private set; } - -    private Process CreateProcess() -    { -        var process = new Process(); - -        var startInfo = new ProcessStartInfo -        { -            FileName = ExecutablePath, -        }; -        startInfo.ArgumentList.Add("run"); -        startInfo.ArgumentList.Add("-c"); -        startInfo.ArgumentList.Add(ConfigPath); -        if (AssetPath is not null) -        { -            startInfo.EnvironmentVariables[ToolAssetEnvironmentVariableName] = AssetPath; -        } - -        process.StartInfo = startInfo; -        process.OutputDataReceived += (_, args) => -        { -            Console.Out.Write(args.Data); -        }; -        process.ErrorDataReceived += (_, args) => -        { -            Console.Error.WriteLine(args.Data); -        }; - -        return process; -    } - -    public void Stop() -    { -        if (CurrentProcess is not null) -        { -            CurrentProcess.Kill(); -            CurrentProcess.Dispose(); -            CurrentProcess = null; -            Console.WriteLine("V2ray stopped."); -        } -    } - -    public void Start(bool stopOld = false) -    { -        if (stopOld) Stop(); - -        if (CurrentProcess is null) -        { -            CurrentProcess = CreateProcess(); -            CurrentProcess.EnableRaisingEvents = true; -            CurrentProcess.Exited += (_, _) => -            { -                if (CurrentProcess.ExitCode != 0) -                { -                    const string message = "V2ray exited with error."; -                    Console.Error.WriteLine(message); -                    throw new Exception(message); -                } -            }; -            CurrentProcess.Start(); -            Console.WriteLine("V2ray started."); -        } -    } - -    public void Restart() -    { -        Start(true); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj deleted file mode 100644 index 2502e74..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj +++ /dev/null @@ -1,34 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - -  <PropertyGroup> -    <OutputType>Exe</OutputType> -    <TargetFramework>net8.0</TargetFramework> -    <ImplicitUsings>enable</ImplicitUsings> -    <Nullable>enable</Nullable> -  </PropertyGroup> - -  <ItemGroup> -    <None Update="config.json.template"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="proxy.txt"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="vmess.txt"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="hosts.txt"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="sing-config.json.template"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="sing-inbounds-mobile.json"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -    <None Update="sing-inbounds-pc.json"> -      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -    </None> -  </ItemGroup> - -</Project> diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs deleted file mode 100644 index 26e9231..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Crupest.SecretTool; - -public class FileWatcher(string directory, List<string> fileNames) -{ -    public string Directory { get; set; } = directory; -    public List<string> FileNames { get; set; } = fileNames; - -    public delegate void OnChangedHandler(); -    public event OnChangedHandler? OnChanged; - -    public void Run() -    { -        var sourceWatcher = new FileSystemWatcher(Directory); -        foreach (var fileName in FileNames) -        { -            sourceWatcher.Filters.Add(fileName); -        } -        sourceWatcher.NotifyFilter = NotifyFilters.LastWrite; - -        while (true) -        { -            var result = sourceWatcher.WaitForChanged(WatcherChangeTypes.Changed | WatcherChangeTypes.Created); -            OnChanged?.Invoke(); -        } -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs deleted file mode 100644 index 8f4c171..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs +++ /dev/null @@ -1,324 +0,0 @@ -using System.IO.Compression; - -namespace Crupest.SecretTool; - -public interface IGeoSiteEntry -{ -    bool IsInclude { get; } -    string Value { get; } -} - -public record GeoSiteIncludeEntry(string Value, string ContainingSite) : IGeoSiteEntry -{ -    public bool IsInclude => true; -} - -public record GeoSiteRuleEntry(HostMatchKind Kind, string Value, List<string> Attributes, string ContainingSite) : IGeoSiteEntry -{ -    public bool IsInclude => false; - -    public RoutingRuleMatcher GetRoutingRuleMatcher() => new(Kind, Value); -} - -public record GeoSite(string Name, List<IGeoSiteEntry> Entries) -{ -    public static GeoSite Parse(string name, string str) -    { -        List<IGeoSiteEntry> entries = []; -        var listConfig = new ListConfig(str); -        foreach (var item in listConfig.Config) -        { -            var (value, line) = item; - -            if (value.StartsWith("include:")) -            { -                var include = value["include:".Length..].Trim(); -                if (include.Length == 0 || include.Contains(' ')) -                { -                    throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Invalid include value."); -                } -                entries.Add(new GeoSiteIncludeEntry(include, name)); -                continue; -            } - -            var segments = value.Split(':', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); -            if (segments.Length > 2) -            { -                throw new FormatException($"Invalid geo site rule '{name}' in line {line}. More than one ':'."); -            } - -            HostMatchKind kind; -            if (segments.Length == 2) -            { -                kind = segments[0] switch -                { -                    "domain" => kind = HostMatchKind.DomainSuffix, -                    "full" => kind = HostMatchKind.DomainFull, -                    "keyword" => kind = HostMatchKind.DomainKeyword, -                    "regexp" => kind = HostMatchKind.DomainRegex, -                    _ => throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Unknown matcher.") -                }; -            } -            else -            { -                kind = HostMatchKind.DomainSuffix; -            } - -            var domainSegments = segments[^1].Split('@', StringSplitOptions.TrimEntries); -            var domain = domainSegments[0]; -            if (kind != HostMatchKind.DomainRegex && Uri.CheckHostName(domain) != UriHostNameType.Dns) -            { -                throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Invalid domain."); -            } - -            List<string> attributes = []; -            foreach (var s in domainSegments[1..]) -            { -                if (s.Length == 0) -                { -                    throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Empty attribute value."); -                } -                attributes.Add(s); -            } - -            entries.Add(new GeoSiteRuleEntry(kind, domain, attributes, name)); -        } -        return new GeoSite(name, entries); -    } -} - -public class GeoSiteData(string directory) -{ -    private static List<GeoSite> Parse(string directory) -    { -        var sites = new List<GeoSite>(); -        foreach (var path in Directory.GetFileSystemEntries(directory)) -        { -            var content = File.ReadAllText(path); -            sites.Add(GeoSite.Parse(Path.GetFileName(path), content)); -        } -        return sites; -    } - -    public string DataDirectory { get; } = directory; - -    public List<GeoSite> Sites { get; } = Parse(directory); - -    public GeoSite? GetSite(string name) -    { -        return Sites.Where(s => s.Name == name).FirstOrDefault(); -    } - -    public List<GeoSiteRuleEntry> GetEntriesRecursive(List<string> sites, -        List<HostMatchKind>? onlyMatcherKinds = null, List<string>? onlyAttributes = null) -    { -        List<GeoSiteRuleEntry> entries = []; -        HashSet<string> visited = []; -        HashSet<HostMatchKind>? kinds = onlyMatcherKinds?.ToHashSet(); - -        void Visit(string site) -        { -            if (visited.Contains(site)) -            { -                return; -            } - -            visited.Add(site); -            var siteData = GetSite(site); -            if (siteData == null) -            { -                return; -            } -            foreach (var entry in siteData.Entries) -            { -                if (entry is GeoSiteIncludeEntry includeEntry) -                { -                    Visit(includeEntry.Value); -                } -                else if (entry is GeoSiteRuleEntry geoSiteRuleEntry) -                { -                    if (kinds != null && !kinds.Contains(geoSiteRuleEntry.Kind)) -                    { -                        continue; -                    } - -                    if (onlyAttributes != null && !geoSiteRuleEntry.Attributes.Intersect(onlyAttributes).Any()) -                    { -                        continue; -                    } - -                    entries.Add(geoSiteRuleEntry); -                } -            } -        } - -        foreach (var s in sites) -        { -            Visit(s); -        } - -        return entries; -    } -} - -public class GeoDataManager -{ -    public const string GeoSiteFileName = "geosite.dat"; -    public const string GeoIpFileName = "geoip.dat"; -    public const string GeoIpCnFileName = "geoip-only-cn-private.dat"; - -    public static class ToolGithub -    { -        public const string Organization = "v2fly"; -        public const string GeoSiteRepository = "domain-list-community"; -        public const string GeoIpRepository = "geoip"; -        public const string GeoSiteReleaseFilename = "dlc.dat"; -        public const string GeoIpReleaseFilename = "geoip.dat"; -        public const string GeoIpCnReleaseFilename = "geoip-only-cn-private.dat"; -    } - -    public static GeoDataManager Instance { get; } = new GeoDataManager(); - -    public record GeoDataAsset(string Name, string FileName, string GithubUser, string GithubRepo, string GithubReleaseFileName); - -    public GeoDataManager() -    { -        Assets = -        [ -            new("geosite", GeoSiteFileName, ToolGithub.Organization, ToolGithub.GeoSiteRepository, ToolGithub.GeoSiteReleaseFilename), -            new("geoip", GeoIpFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpReleaseFilename), -            new("geoip-cn", GeoIpCnFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpCnReleaseFilename), -        ]; -    } - -    public List<GeoDataAsset> Assets { get; set; } - -    public GeoSiteData? GeoSiteData { get; set; } - -    public GeoSiteData GetOrCreateGeoSiteData(bool clean, bool silent) -    { -        if (GeoSiteData is not null) { return GeoSiteData; } -        GeoSiteData = DownloadAndGenerateGeoSiteData(clean, silent); -        return GeoSiteData; -    } - -    private static string GetReleaseFileUrl(string user, string repo, string fileName) -    { -        return $"https://github.com/{user}/{repo}/releases/latest/download/{fileName}"; -    } - -    private static void GithubDownloadRelease(HttpClient httpClient, string user, string repo, string fileName, string outputPath, bool silent) -    { -        var url = GetReleaseFileUrl(user, repo, fileName); -        if (!silent) Console.WriteLine($"Downloading {url} to {outputPath}"); -        using var responseStream = httpClient.GetStreamAsync(url).Result; -        using var outputFileStream = File.OpenWrite(outputPath); -        responseStream.CopyTo(outputFileStream); -    } - -    public bool HasAllAssets(string directory, out List<string> missing) -    { -        missing = []; -        foreach (var asset in Assets) -        { -            var assetPath = Path.Combine(directory, asset.FileName); -            if (!File.Exists(assetPath)) -            { -                missing.Add(asset.Name); -            } -        } -        return missing.Count == 0; -    } - -    public void Download(string outputDir, bool silent) -    { -        using var httpClient = new HttpClient(); - -        foreach (var asset in Assets) -        { -            if (!silent) -            { -                Console.WriteLine($"Downloading {asset.Name}..."); -            } -            GithubDownloadRelease(httpClient, asset.GithubUser, asset.GithubRepo, asset.GithubReleaseFileName, Path.Combine(outputDir, asset.FileName), silent); -            if (!silent) -            { -                Console.WriteLine($"Downloaded {asset.Name}!"); -            } -        } - -        if (!File.Exists(Program.RestartLabelFilePath)) -        { -            File.Create(Program.RestartLabelFilePath); -        } -        else -        { -            File.SetLastWriteTime(Program.RestartLabelFilePath, DateTime.Now); -        } -    } - -    private static string GetGithubRepositoryArchiveUrl(string user, string repo) -    { -        return $"https://github.com/{user}/{repo}/archive/refs/heads/master.zip"; -    } - -    private static void GithubDownloadRepository(HttpClient httpClient, string user, string repo, string outputPath, bool silent) -    { -        var url = GetGithubRepositoryArchiveUrl(user, repo); -        if (!silent) { Console.WriteLine($"Begin to download data from {url} to {outputPath}."); } -        using var responseStream = httpClient.GetStreamAsync(url).Result; -        using var outputFileStream = File.OpenWrite(outputPath); -        responseStream.CopyTo(outputFileStream); -        if (!silent) { Console.WriteLine("Succeeded to download."); } -    } - -    private static void Unzip(string zipPath, string outputPath) -    { -        using var zip = ZipFile.OpenRead(zipPath) ?? throw new Exception($"Failed to open zip file {zipPath}"); -        zip.ExtractToDirectory(outputPath); -    } - -    private static string DownloadAndExtractGeoDataRepository(bool cleanTempDirIfFailed, bool silent, out string tempDirectoryPath) -    { -        tempDirectoryPath = ""; -        const string zipFileName = "v2ray-geosite-master.zip"; -        using var httpClient = new HttpClient(); -        var tempDirectory = Directory.CreateTempSubdirectory(Program.Name); -        tempDirectoryPath = tempDirectory.FullName; -        try -        { -            var archivePath = Path.Combine(tempDirectoryPath, zipFileName); -            var extractPath = Path.Combine(tempDirectoryPath, "repo"); -            GithubDownloadRepository(httpClient, ToolGithub.Organization, ToolGithub.GeoSiteRepository, archivePath, silent); -            if (!silent) { Console.WriteLine($"Extract geo data to {extractPath}."); } -            Directory.CreateDirectory(extractPath); -            Unzip(archivePath, extractPath); -            if (!silent) { Console.WriteLine($"Extraction done."); } -            return Path.Join(extractPath, "domain-list-community-master"); -        } -        catch (Exception) -        { -            if (cleanTempDirIfFailed) -            { -                Directory.Delete(tempDirectoryPath, true); -            } -            throw; -        } -    } - -    private static GeoSiteData DownloadAndGenerateGeoSiteData(bool clean, bool silent) -    { -        var repoDirectory = DownloadAndExtractGeoDataRepository(clean, silent, out var tempDirectoryPath); -        try -        { -            return new GeoSiteData(Path.Join(repoDirectory, "data")); -        } -        finally -        { -            if (clean) -            { -                Directory.Delete(tempDirectoryPath, true); -            } -        } -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs deleted file mode 100644 index 858333d..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs +++ /dev/null @@ -1,123 +0,0 @@ -namespace Crupest.SecretTool; - -public enum HostMatchKind -{ -    DomainFull, -    DomainSuffix, -    DomainKeyword, -    DomainRegex, -    Ip, -    GeoSite, -    GeoIp, -} - -public static class HostMatchKindExtensions -{ -    public static bool IsDomain(this HostMatchKind kind) -    { -        return kind.IsNonRegexDomain() || kind == HostMatchKind.DomainRegex; -    } - -    public static bool IsNonRegexDomain(this HostMatchKind kind) -    { -        return kind is HostMatchKind.DomainFull or HostMatchKind.DomainSuffix or HostMatchKind.DomainKeyword; -    } - - -    public static List<HostMatchKind> DomainMatchKinds { get; } = [HostMatchKind.DomainFull, HostMatchKind.DomainSuffix, HostMatchKind.DomainKeyword, HostMatchKind.DomainRegex]; - -    public static List<HostMatchKind> NonRegexDomainMatchKinds { get; } = [HostMatchKind.DomainFull, HostMatchKind.DomainSuffix, HostMatchKind.DomainKeyword]; - -    public static List<HostMatchKind> SupportedInSingRouteMatchKinds { get; } = [..DomainMatchKinds, HostMatchKind.Ip]; - -    public static bool IsSupportedInSingRoute(this HostMatchKind kind) => SupportedInSingRouteMatchKinds.Contains(kind); -} - -public record HostMatchConfigItem(HostMatchKind Kind, string MatchString, List<string> Values); - -public class HostMatchConfig(string configString, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) -{ -    private static List<HostMatchConfigItem> Parse(string configString, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) -    { -        var items = new ListConfig(configString).Config; -        var result = new List<HostMatchConfigItem>(); - -        foreach (var item in items) -        { -            var lineNumber = item.LineNumber; -            var line = item.Value; -            var hasExplicitMatchKind = false; -            var segments = line.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); - -            foreach (var matchKind in Enum.GetValues<HostMatchKind>()) -            { -                var matchKindName = Enum.GetName(matchKind) ?? throw new Exception("No such match kind."); -                if (segments[0] == matchKindName) -                { -                    hasExplicitMatchKind = true; - -                    if (segments.Count < 2) -                    { -                        throw new FormatException($"Explicit match item needs a value in line {lineNumber}."); -                    } -                    if (allowedMatchKinds.Contains(matchKind)) -                    { -                        if (matchKind.IsNonRegexDomain() && Uri.CheckHostName(matchKindName) != UriHostNameType.Dns) -                        { -                            throw new FormatException($"Invalid domain format in line {lineNumber}."); -                        } - -                        var components = segments[2..].ToList(); -                        if (minComponentCount > 0 && components.Count < minComponentCount) -                        { -                            throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); -                        } -                        if (maxComponentCount >= 0 && components.Count > maxComponentCount) -                        { -                            throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); -                        } -                        result.Add(new HostMatchConfigItem(matchKind, segments[1], components)); -                    } -                    else -                    { -                        throw new FormatException($"Match kind {matchKindName} is not allowed at line {lineNumber}."); -                    } -                } -            } - -            if (!hasExplicitMatchKind) -            { -                if (minComponentCount > 0 && segments.Count - 1 < minComponentCount) -                { -                    throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); -                } -                if (maxComponentCount >= 0 && segments.Count - 1 > maxComponentCount) -                { -                    throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); -                } -                result.Add(new HostMatchConfigItem(HostMatchKind.DomainSuffix, segments[0], segments.Count == 1 ? [] : segments[1..])); -            } -        } -        return result; -    } - -    public string ConfigString { get; } = configString; -    public List<HostMatchKind> AllowedMatchKinds { get; } = allowedMatchKinds; -    public int MinComponentCount { get; } = minComponentCount; -    public int MaxComponentCount { get; } = maxComponentCount; -    public List<HostMatchConfigItem> Items { get; } = Parse(configString, allowedMatchKinds, minComponentCount, maxComponentCount); -} - -public class HostMatchConfigFile -{ -    public HostMatchConfigFile(string path, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) -    { -        Path = path; -        FileContent = File.ReadAllText(path); -        Config = new HostMatchConfig(FileContent, allowedMatchKinds, minComponentCount, maxComponentCount); ; -    } - -    public string Path { get; } -    public string FileContent { get; } -    public HostMatchConfig Config { get; } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs deleted file mode 100644 index 18b1ac0..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Reflection; - -namespace Crupest.SecretTool; - -public static class Program -{ -    public static string Name { get; } = typeof(Program).Namespace ?? throw new Exception("Can't get the name of Crupest.SecretTool."); - -    public static string CrupestSecretToolDirectory { get; } = -        Environment.GetEnvironmentVariable("CRUPEST_V2RAY_DIR") ?? -        Path.GetFullPath(Path.GetDirectoryName( -            Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Can't get the path of Crupest.SecretTool.")); - -    private const string ConfigOutputFileName = "config.json"; -    private const string SurgeRuleSetChinaOutputFileName = "ChinaRuleSet.txt"; -    private const string SurgeRuleSetGlobalOutputFileName = "GlobalRuleSet.txt"; - -    public const string RestartLabelFileName = "restart.label"; -    public static string RestartLabelFilePath { get; } = Path.Combine(CrupestSecretToolDirectory, RestartLabelFileName); - -    public static void RunToolAndWatchConfigChange() -    { -        var executablePath = Controller.FindExecutable(CrupestSecretToolDirectory, out var isLocal) ?? -            throw new Exception("Can't find v2ray executable either in Crupest.SecretTool directory or in PATH."); - -        string? assetsPath; -        if (isLocal) -        { -            assetsPath = CrupestSecretToolDirectory; -            var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); -            if (!assetsComplete) -            { -                throw new Exception($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is local. So only use assets in Crupest.SecretTool directory."); -            } -        } -        else -        { -            assetsPath = CrupestSecretToolDirectory; -            var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); -            if (!assetsComplete) -            { -                Console.WriteLine($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is global. So fallback to its own assets."); -                assetsPath = null; -            } -        } - -        var controller = new Controller(executablePath, Path.Combine(CrupestSecretToolDirectory, ConfigOutputFileName), assetsPath); -        var configFileWatcher = new FileWatcher(CrupestSecretToolDirectory, -            [.. ToolConfig.ConfigFileNames, RestartLabelFileName]); - -        ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); -        controller.Start(); - -        configFileWatcher.OnChanged += () => -        { -            ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); -            controller.Restart(); -        }; - -        configFileWatcher.Run(); -    } - -    public static void Main(string[] args) -    { -        if (args.Length != 0) -        { -            var verb = args[0].ToLower(); -            if (verb == "download-geodata" || verb == "dg") -            { -                if (args.Length != 1) -                { -                    throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); -                } -                GeoDataManager.Instance.Download(CrupestSecretToolDirectory, false); -                return; -            } -            else if (verb == "generate-surge-rule-set" || verb == "gsr") -            { -                                if (args.Length != 1) -                { -                    throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); -                } -                SurgeConfigGenerator.GenerateTo( -                    CrupestSecretToolDirectory, -                    Path.Join(CrupestSecretToolDirectory, SurgeRuleSetChinaOutputFileName), -                    Path.Join(CrupestSecretToolDirectory, SurgeRuleSetGlobalOutputFileName), -                    true, true -                ); -                return; -            } -            else if (verb == "generate-sing-config" || verb == "gs") -            { -                if (args.Length != 2 || args[1].ToLower() is not ("pc" or "mobile")) -                { -                    throw new Exception("Invalid command line arguments. generate-sing-config requires 1 argument. The argument must be either 'pc' or 'mobile'."); -                } -                 -                var config = SingToolConfig.FromDirectory(CrupestSecretToolDirectory, args[1].ToLower() == "mobile", true, true); -                Console.Out.WriteLine(config.ToSingConfigString()); -                return; -            } -            else if (verb == "generate" || verb == "g") -            { -                var config = ToolConfig.FromDirectory(CrupestSecretToolDirectory); -                Console.Out.WriteLine(config.ToJsonStringV4()); -                return; -            } -            throw new Exception("Invalid command line arguments."); -        } - -        RunToolAndWatchConfigChange(); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml b/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 5fca454..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -https://go.microsoft.com/fwlink/?LinkID=208121. ---> -<Project> -  <PropertyGroup> -    <Configuration>Release</Configuration> -    <Platform>Any CPU</Platform> -    <PublishDir>bin\Release\net8.0\publish\</PublishDir> -    <PublishProtocol>FileSystem</PublishProtocol> -    <_TargetId>Folder</_TargetId> -  </PropertyGroup> -</Project>
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs deleted file mode 100644 index d2703ba..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Crupest.SecretTool; - -public abstract class Proxy(string tag) : IV4ConfigObject, ISingConfigObject -{ -    public string Tag { get; set; } = tag; - -    public abstract V4ConfigJsonObjects.Outbound ToJsonObjectV4(); -    public abstract SingConfigJsonObjects.OutboundBase ToJsonObjectSing(); - -    object IV4ConfigObject.ToJsonObjectV4() -    { -        return ToJsonObjectV4(); -    } - -    object ISingConfigObject.ToJsonObjectSing() -    { -        return ToJsonObjectSing(); -    } -} - -public class HttpProxy(string host, int port, string tag) : Proxy(tag) -{ -    public string Host { get; set; } = host; -    public int Port { get; set; } = port; - -    public override SingConfigJsonObjects.OutboundBase ToJsonObjectSing() -    { -        throw new NotImplementedException("Http proxy is not supported in sing now."); -    } - -    public override V4ConfigJsonObjects.Outbound ToJsonObjectV4() -    { -        return new V4ConfigJsonObjects.Outbound(Tag, "http", -            new V4ConfigJsonObjects.HttpOutboundSettings([new V4ConfigJsonObjects.HttpOutboundServer(Host, Port, [])]), -            null -        ); -    } -} - - -public class VmessProxy(string host, int port, string userId, string path, string tag) : Proxy(tag) -{ -    public string Host { get; set; } = host; -    public int Port { get; set; } = port; -    public string Path { get; set; } = path; -    public string UserId { get; set; } = userId; - -    public override SingConfigJsonObjects.OutboundBase ToJsonObjectSing() -    { -        return new SingConfigJsonObjects.VmessOutbound(Tag, Host, Port, UserId, -            Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path, new Dictionary<string, string> { { "Host", Host } }), -            Tls: new SingConfigJsonObjects.OutboundTls(true)); -    } - -    public override V4ConfigJsonObjects.Outbound ToJsonObjectV4() -    { -        return new V4ConfigJsonObjects.Outbound(Tag, "vmess", -            new V4ConfigJsonObjects.VmessOutboundSettings( -                [new V4ConfigJsonObjects.VnextServer(Host, Port, [new V4ConfigJsonObjects.VnextServerUser(UserId, 0, "auto", 0)])]), -            new V4ConfigJsonObjects.WsStreamSettings("ws", "tls", new V4ConfigJsonObjects.WsSettings(Path, new() { ["Host"] = Host })) -        ); -    } - -    public static VmessProxy CreateFromConfigString(string configString, string tag) -    { -        var config = new DictionaryConfig(configString, ["host", "port", "userid", "path"]); -        var portString = config.GetItemCaseInsensitive("port").Value; -        if (!int.TryParse(portString, out var port) || port <= 0) -        { -            throw new FormatException($"Invalid port number: {portString}: not an integer or is a invalid number."); -        } -        return new VmessProxy(config.GetItemCaseInsensitive("host").Value, port, -            config.GetItemCaseInsensitive("userid").Value, config.GetItemCaseInsensitive("path").Value, tag -        ); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs deleted file mode 100644 index 81698a3..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Crupest.SecretTool;
 -
 -public class ProxyFile : HostMatchConfigFile
 -{
 -    public ProxyFile(string path) : base(path, [.. Enum.GetValues<HostMatchKind>()], maxComponentCount: 0)
 -    {
 -        RoutingRuleMatchers = Config.Items.Select(i => new RoutingRuleMatcher(i.Kind, i.MatchString)).ToList();
 -    }
 -
 -    public List<RoutingRuleMatcher> RoutingRuleMatchers { get; }
 -
 -    public List<RoutingRuleMatcher> GetChinaRulesByGeoSite(GeoSiteData geoSiteData)
 -    {
 -        var geoSites = RoutingRuleMatchers.Where(m => m.MatchKind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList();
 -        return geoSiteData.GetEntriesRecursive(geoSites, HostMatchKindExtensions.DomainMatchKinds, ["cn"]).Select(e => e.GetRoutingRuleMatcher()).ToList();
 -    }
 -
 -    public List<RoutingRuleMatcher> GetRulesFlattenGeoSite(GeoSiteData geoSiteData, bool noCn = false)
 -    {
 -        var geoSites = RoutingRuleMatchers.Where(m => m.MatchKind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList();
 -        var flattenGeoSiteRules = geoSiteData.GetEntriesRecursive(geoSites, HostMatchKindExtensions.DomainMatchKinds)
 -            .Where(e => !noCn || !e.Attributes.Contains("cn"))
 -            .Select(e => e.GetRoutingRuleMatcher())
 -            .ToList();
 -        var otherRules = RoutingRuleMatchers.Where(m => m.MatchKind != HostMatchKind.GeoSite).ToList();
 -        return [
 -            ..flattenGeoSiteRules,
 -            ..otherRules
 -        ];
 -    }
 -}
 diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs deleted file mode 100644 index fdf1b93..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs +++ /dev/null @@ -1,155 +0,0 @@ -namespace Crupest.SecretTool; - -public record RoutingRuleMatcher(HostMatchKind MatchKind, string MatchString) -{ -    public RoutingRule ToRoutingRule(string OutboundTag) => new(MatchKind, MatchString, OutboundTag); -} - -public record RoutingRule(HostMatchKind MatchKind, string MatchString, string OutboundTag) : IV4ConfigObject -{ -    public string ToolConfigString => MatchKind switch -    { -        HostMatchKind.DomainFull => $"full:{MatchString}", -        HostMatchKind.DomainSuffix => $"domain:{MatchString}", -        HostMatchKind.DomainKeyword => MatchString, -        HostMatchKind.DomainRegex => $"regexp:{MatchString}", -        HostMatchKind.Ip => MatchString, -        HostMatchKind.GeoSite => $"geosite:{MatchString}", -        HostMatchKind.GeoIp => $"geoip:{MatchString}", -        _ => throw new ArgumentException("Invalid matcher kind.") -    }; - -    public string ToolConfigStringSing => MatchKind.IsSupportedInSingRoute() ? MatchString : throw new ArgumentException("Unsupported matcher kind for sing."); - -    public static Dictionary<string, List<RoutingRule>> GroupByOutboundTag(List<RoutingRule> rules) -        => rules.GroupBy(r => r.OutboundTag).Select(g => (g.Key, g.ToList())).ToDictionary(); - -    public static Dictionary<HostMatchKind, List<RoutingRule>> GroupByMatchKind(List<RoutingRule> rules) -        => rules.GroupBy(r => r.MatchKind).Select(g => (g.Key, g.ToList())).ToDictionary(); - -    public static List<List<RoutingRule>> GroupByOutboundTagAndMatcherKind(List<RoutingRule> rules) -        => GroupByOutboundTag(rules).Values.SelectMany((groupByTag) => GroupByMatchKind(groupByTag).Values).ToList(); - -    public static SingConfigJsonObjects.RouteRule ListToJsonObjectSing(List<RoutingRule> rules) -    { -        if (rules.Count == 0) -        { -            throw new ArgumentException("Rule list is empty."); -        } - -        var outboundTag = rules[0].OutboundTag; - -        if (rules.Any(r => !r.MatchKind.IsSupportedInSingRoute())) -        { -            throw new ArgumentException("Rules must have matcher kinds supported in sing."); -        } - -        if (rules.Any(r => r.OutboundTag != outboundTag)) -        { -            throw new ArgumentException("Rules must have the same outbound tag."); -        } - -        return new SingConfigJsonObjects.RouteRule(Outbound: outboundTag, -            Domain: rules.Where(r => r.MatchKind == HostMatchKind.DomainFull).Select(r => r.ToolConfigStringSing).ToList(), -            DomainSuffix: rules.Where(r => r.MatchKind == HostMatchKind.DomainSuffix).Select(r => r.ToolConfigStringSing).ToList(), -            DomainKeyword: rules.Where(r => r.MatchKind == HostMatchKind.DomainKeyword).Select(r => r.ToolConfigStringSing).ToList(), -            DomainRegex: rules.Where(r => r.MatchKind == HostMatchKind.DomainRegex).Select(r => r.ToolConfigStringSing).ToList(), -            IpCidr: rules.Where(r => r.MatchKind == HostMatchKind.Ip).Select(r => r.ToolConfigStringSing).ToList() -        ); -    } - -    public static V4ConfigJsonObjects.RoutingRule ListToJsonObject(List<RoutingRule> rules) -    { -        if (rules.Count == 0) -        { -            throw new ArgumentException("Rule list is empty."); -        } - -        var matchKind = rules[0].MatchKind; -        var outboundTag = rules[0].OutboundTag; - -        if (rules.Any(r => r.OutboundTag != outboundTag) || rules.Any(r => r.MatchKind != matchKind)) -        { -            throw new ArgumentException("Rules must have the same matcher kind and outbound tag."); -        } - -        List<string> toolConfigList = rules.Select(r => r.ToolConfigString).ToList(); - -        return new V4ConfigJsonObjects.RoutingRule(OutboundTag: outboundTag, -            Ip: (matchKind is HostMatchKind.Ip or HostMatchKind.GeoIp) ? toolConfigList : null, -            Domains: (matchKind.IsDomain() || matchKind == HostMatchKind.GeoSite) ? toolConfigList : null -        ); -    } - -    public RoutingRule CloneGeositeWithCnAttribute(string outboundTag) -    { -        if (MatchKind is not HostMatchKind.GeoSite) -        { -            throw new ArgumentException("Matcher kind must be GeoSite."); -        } - -        return new RoutingRule(HostMatchKind.GeoSite, $"{MatchString}@cn", outboundTag); -    } - -    public RoutingRuleMatcher GetMatcher() => new(MatchKind, MatchString); - -    public V4ConfigJsonObjects.RoutingRule ToJsonObjectV4() => ListToJsonObject([this]); - -    object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); -} - -public record Routing(List<RoutingRule> Rules) : IV4ConfigObject, ISingConfigObject -{ -    public List<RoutingRule> CreateGeositeCnDirectRules() -    { -        return Rules.Where(r => r.MatchKind is HostMatchKind.GeoSite) -            .Select(r => r.CloneGeositeWithCnAttribute("direct")).ToList(); -    } - -    public SingConfigJsonObjects.Route ToJsonObjectSing() -    { -        List<SingConfigJsonObjects.RouteRule> ruleJsonObjects = [ new SingConfigJsonObjects.RouteRule(Outbound: "dns-out", Protocol: "dns")]; -        ruleJsonObjects.AddRange(RoutingRule.GroupByOutboundTag(Rules).Values.Select(RoutingRule.ListToJsonObjectSing)); -        return new SingConfigJsonObjects.Route(ruleJsonObjects); -    } - -    public V4ConfigJsonObjects.Routing ToJsonObjectV4(string domainStrategy = "IpOnDemand", bool directGeositeCn = true) -    { -        List<V4ConfigJsonObjects.RoutingRule> ruleJsonObjects = []; - -        if (directGeositeCn) -        { -            ruleJsonObjects.Add(RoutingRule.ListToJsonObject(CreateGeositeCnDirectRules())); -        } - -        ruleJsonObjects.AddRange(RoutingRule.GroupByOutboundTagAndMatcherKind(Rules).Select(RoutingRule.ListToJsonObject)); - -        return new V4ConfigJsonObjects.Routing(ruleJsonObjects, domainStrategy); -    } - -    object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); - -    object ISingConfigObject.ToJsonObjectSing() => ToJsonObjectSing(); - -    public static Routing FromProxyFile(ProxyFile proxyFile, string outboundTag) -    { -        return new Routing( -            proxyFile.RoutingRuleMatchers.Select(m => m.ToRoutingRule(outboundTag)).ToList()); -    } - -    public static Routing FromProxyFileForSing(ProxyFile proxyFile, GeoSiteData geoSiteData, string outboundTag, string? directCnOutboundTag = null) -    { -        List<RoutingRule> rules = []; - -        if (directCnOutboundTag is not null) -        { -            rules.AddRange(proxyFile.GetChinaRulesByGeoSite(geoSiteData).Select(m => m.ToRoutingRule(directCnOutboundTag)).ToList()); -        } - -        rules.AddRange(proxyFile.GetRulesFlattenGeoSite(geoSiteData).Where(m => m.MatchKind.IsSupportedInSingRoute()).Select(m => m.ToRoutingRule(outboundTag)).ToList()); - -        return new Routing( -            rules -        ); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs deleted file mode 100644 index 56b5563..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Crupest.SecretTool;
 -
 -public static class SingConfigJsonObjects
 -{
 -    public interface IObject;
 -
 -    public record OutboundTls(bool Enabled);
 -    public record V2rayTransportBase(string Type);
 -    public record V2rayWebsocketTransport(string Path, Dictionary<string, string>? Headers = null) : V2rayTransportBase("ws");
 -    public record OutboundBase(string Tag, string Type) : IObject;
 -    public record VmessOutbound(string Tag, string Server, int ServerPort, string Uuid, string Security = "auto",
 -        V2rayTransportBase? Transport = null, OutboundTls? Tls = null): OutboundBase(Tag, "vmess");
 -
 -    public record RouteRule(List<string>? Domain = null, List<string>? DomainSuffix = null, List<string>? DomainKeyword = null,
 -        List<string>? DomainRegex = null, List<string>? IpCidr = null, List<string>? SourceIpCidr = null, string? Protocol = null,
 -         List<int>? Port = null, List<int>? SourcePort = null, List<string>? PortRange = null, List<string>? SourcePortRange = null,
 -         string? Network = null, List<string>? Inbound = null, string? Outbound = null) : IObject;
 -
 -    public record Route(List<RouteRule> Rules) : IObject;
 -}
 diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs deleted file mode 100644 index b112e1c..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Crupest.SecretTool; - -public record StaticHostRule(HostMatchKind MatchKind, string MatchString, List<string> ResolveResult) -{ -    public string AddressString() -    { -        return MatchKind switch -        { -            HostMatchKind.DomainFull => MatchString, -            HostMatchKind.DomainSuffix => $"domain:{MatchString}", -            HostMatchKind.DomainKeyword => $"keyword:{MatchString}", -            HostMatchKind.DomainRegex => $"regexp:{MatchString}", -            _ => throw new ArgumentOutOfRangeException($"Match kind {MatchKind} is not allowed in static host rule."), -        }; -    } - -    public object ResolveResultToJsonObject() -    { -        return ResolveResult.Count == 1 ? ResolveResult[0] : ResolveResult; -    } -} - -public class StaticHosts(List<StaticHostRule> rules) : IV4ConfigObject -{ -    public List<StaticHostRule> Rules { get; } = rules; - -    public Dictionary<string, object> ToJsonObjectV4() => -        Rules.ToDictionary(rule => rule.AddressString(), rule => rule.ResolveResultToJsonObject()); - -    object IV4ConfigObject.ToJsonObjectV4() -    { -        return ToJsonObjectV4(); -    } - -    public static StaticHosts CreateFromHostMatchConfigString(string configString) -    { -        var config = new HostMatchConfig(configString, HostMatchKindExtensions.DomainMatchKinds, minComponentCount: 1); -        return new StaticHosts(config.Items.Select(i => new StaticHostRule(i.Kind, i.MatchString, [.. i.Values])).ToList()); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs deleted file mode 100644 index 8a57c9f..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Crupest.SecretTool; - -public class SurgeConfigGenerator(ProxyFile proxyFile, GeoSiteData geoData) -{ -    public ProxyFile ProxyFile => proxyFile; -    public GeoSiteData GeoData => geoData; - -    private static string ToSurgeRuleString(HostMatchKind kind, string value) -    { -        var ruleType = kind switch -        { -            HostMatchKind.DomainFull => "DOMAIN", -            HostMatchKind.DomainSuffix => "DOMAIN-SUFFIX", -            HostMatchKind.DomainKeyword => "DOMAIN-KEYWORD", -            HostMatchKind.DomainRegex => "URL-REGEX", -            _ => throw new Exception("Unacceptable matcher kind for Surge rule.") -        }; - -        return $"{ruleType},{value}"; -    } - -    public static string GenerateSurgeRuleSetString(List<RoutingRuleMatcher> rules) -    { -        return string.Join('\n', rules.Select(r => ToSurgeRuleString(r.MatchKind, r.MatchString))); -    } - -    public string GenerateChinaRuleSet() -    { -        return GenerateSurgeRuleSetString(proxyFile.GetChinaRulesByGeoSite(GeoData)); -    } - -    public string GenerateGlobalRuleSet() -    { -        return GenerateSurgeRuleSetString(proxyFile.GetRulesFlattenGeoSite(geoData, true)); -    } - -    public static void GenerateTo(ProxyFile proxyFile, GeoSiteData geoSiteData, string cnPath, string globalPath, bool silent) -    { -        var generator = new SurgeConfigGenerator(proxyFile, geoSiteData); -        File.WriteAllText(cnPath, generator.GenerateChinaRuleSet()); -        if (!silent) Console.WriteLine($"China rule set written to {cnPath}."); -        File.WriteAllText(globalPath, generator.GenerateGlobalRuleSet()); -        if (!silent) Console.WriteLine($"Global rule set written to {globalPath}."); -    } - -    public static void GenerateTo(string directory, string cnPath, string globalPath, bool clean, bool silent) -    { -        var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); -        var proxyFile = new ProxyFile(Path.Combine(directory, ToolConfig.ProxyConfigFileName)); -        var generator = new SurgeConfigGenerator(proxyFile, geoSiteData); -        File.WriteAllText(cnPath, generator.GenerateChinaRuleSet()); -        if (!silent) Console.WriteLine($"China rule set written to {cnPath}."); -        File.WriteAllText(globalPath, generator.GenerateGlobalRuleSet()); -        if (!silent) Console.WriteLine($"Global rule set written to {globalPath}."); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs deleted file mode 100644 index 1fe91b1..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Crupest.SecretTool; - -public class Template -{ -    private enum ParseState -    { -        Text, -        Dollar, -        LeftBracket, -        VariableName, -        VariableNameFinish, -    } - -    private interface ITemplateNode -    { -        string Render(Dictionary<string, string> values); -    } - -    private class TextNode(string text) : ITemplateNode -    { - -        public string Text { get; } = text; - -        public string Render(Dictionary<string, string> values) -        { -            return Text; -        } -    } - -    private class VariableNode(string variableName) : ITemplateNode -    { -        public string VariableName { get; } = variableName; - -        public string Render(Dictionary<string, string> values) -        { -            return values.GetValueOrDefault(VariableName) ?? ""; -        } -    } - -    public Template(string templateString) -    { -        TemplateString = templateString; -        Nodes = Parse(templateString); -        VariableNames = Nodes.OfType<VariableNode>().Select(node => node.VariableName).ToList(); -    } - -    private static List<ITemplateNode> Parse(string templateString) -    { -        int lineNumber = 1; -        int columnNumber = 0; -        List<ITemplateNode> nodes = []; -        ParseState state = ParseState.Text; -        StringBuilder stringBuilder = new(); - -        string GetPosition() => $"line {lineNumber} column{columnNumber}"; - -        [DoesNotReturn] -        void ReportInvalidState(string message) -        { -            throw new Exception($"Invalid state at {GetPosition()}: {message}"); -        } - -        [DoesNotReturn] -        void ReportInvalidCharacter(char c) -        { -            throw new FormatException($"Unexpected '{c}' at {GetPosition()}."); -        } - -        void FinishText() -        { -            if (state != ParseState.Text) -            { -                ReportInvalidState($"Can't call FinishText here."); -            } - -            if (stringBuilder.Length > 0) -            { -                nodes.Add(new TextNode(stringBuilder.ToString())); -                stringBuilder.Clear(); -            } -        } - -        foreach (var c in templateString) -        { -            if (c == '\n') -            { -                lineNumber++; -                columnNumber = 0; -            } - -            columnNumber++; - -            switch (c) -            { -                case '$': -                    if (state == ParseState.Text) -                    { -                        FinishText(); -                        state = ParseState.Dollar; -                    } -                    else if (state == ParseState.Dollar) -                    { -                        if (stringBuilder.Length > 0) -                        { -                            throw new Exception($"Invalid state at {GetPosition()}: when we meet the second '$', text builder should be empty."); -                        } -                        stringBuilder.Append(c); -                        state = ParseState.Text; -                    } -                    else -                    { -                        throw new FormatException($"Unexpected '$' at {GetPosition()}."); -                    } -                    break; -                case '{': -                    if (state == ParseState.Text) -                    { -                        stringBuilder.Append(c); -                    } -                    else if (state == ParseState.Dollar) -                    { -                        state = ParseState.LeftBracket; -                    } -                    else -                    { -                        throw new Exception($"Unexpected '{{' at {GetPosition()}."); -                    } -                    break; -                case '}': -                    if (state == ParseState.Text) -                    { -                        stringBuilder.Append(c); -                        state = ParseState.Text; -                    } -                    else if (state == ParseState.VariableName || state == ParseState.VariableNameFinish) -                    { -                        nodes.Add(new VariableNode(stringBuilder.ToString())); -                        stringBuilder.Clear(); -                        state = ParseState.Text; -                    } -                    else -                    { -                        ReportInvalidCharacter(c); -                    } -                    break; -                default: -                    if (state == ParseState.Dollar) -                    { -                        ReportInvalidCharacter(c); -                    } - -                    if (char.IsWhiteSpace(c)) -                    { -                        if (state == ParseState.LeftBracket || state == ParseState.VariableNameFinish) -                        { -                            continue; -                        } -                        else if (state == ParseState.Text) -                        { -                            stringBuilder.Append(c); -                        } -                        else if (state == ParseState.VariableName) -                        { -                            state = ParseState.VariableNameFinish; -                        } -                        else -                        { -                            ReportInvalidCharacter(c); -                        } -                    } -                    else -                    { -                        if (state == ParseState.Text) -                        { -                            stringBuilder.Append(c); -                        } -                        else if (state == ParseState.LeftBracket || state == ParseState.VariableName) -                        { -                            stringBuilder.Append(c); -                            state = ParseState.VariableName; -                        } -                        else -                        { -                            ReportInvalidCharacter(c); -                        } -                    } -                    break; -            } -        } - -        if (state == ParseState.Text) -        { -            FinishText(); -        } -        else -        { -            throw new FormatException("Unexpected end of template string."); -        } - -        return nodes; -    } - -    public string TemplateString { get; } -    private List<ITemplateNode> Nodes { get; set; } -    public List<string> VariableNames { get; } - -    public string Generate(Dictionary<string, string> values, bool allowMissingVariable = false) -    { -        StringBuilder stringBuilder = new(); -        foreach (var node in Nodes) -        { -            if (node is TextNode textNode) -            { -                stringBuilder.Append(textNode.Text); -            } -            else if (node is VariableNode variableNode) -            { -                var hasValue = values.TryGetValue(variableNode.VariableName, out var value); -                if (!hasValue && !allowMissingVariable) -                { -                    throw new Exception($"Variable '{variableNode.VariableName}' is not set."); -                } -                stringBuilder.Append(hasValue ? value : string.Empty); -            } -        } -        return stringBuilder.ToString(); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs deleted file mode 100644 index 809fba1..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Crupest.SecretTool; - -public interface IV4ConfigObject -{ -    object ToJsonObjectV4(); -} - -public interface ISingConfigObject -{ -    object ToJsonObjectSing(); -} - -public class ToolConfigBase(Template template, List<Proxy> proxies, Routing router) -{ -    protected class JsonInterfaceConverter<Interface> : JsonConverter<Interface> -    { -        public override Interface Read( -            ref Utf8JsonReader reader, -            Type typeToConvert, -            JsonSerializerOptions options) -        { -            throw new NotImplementedException(); -        } - -        public override void Write( -            Utf8JsonWriter writer, -            Interface value, -            JsonSerializerOptions options) -        { -            JsonSerializer.Serialize(writer, value, typeof(object), options); -        } -    } - -    public const string VmessConfigFileName = "vmess.txt"; -    public const string ProxyConfigFileName = "proxy.txt"; - -    public Template Template { get; set; } = template; -    public List<Proxy> Proxies { get; set; } = proxies; -    public Routing Routing { get; set; } = router; -} - -public class ToolConfig(Template template, List<Proxy> proxies, Routing router, StaticHosts? hosts) : ToolConfigBase(template, proxies, router) -{ -    public const string ConfigTemplateFileName = "config.json.template"; -    public const string HostsConfigFileName = "hosts.txt"; - -    public static List<string> RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName]; -    public static List<string> ConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, HostsConfigFileName]; - -    private const string ProxyAnchor = "PROXY_ANCHOR"; -    private const string RoutingAnchor = "ROUTING_ANCHOR"; -    private const string HostsAnchor = "HOSTS_ANCHOR"; - -    public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V2RAY_GEOSITE_USE_CN"; - -    private static bool UseCnGeoSite => Environment.GetEnvironmentVariable(AddCnAttributeToGeositeEnvironmentVariable) switch -    { -        "0" or "false" or "off" or "disable" => false, -        _ => true -    }; - -    public StaticHosts Hosts { get; set; } = hosts is null ? new StaticHosts([]) : hosts; - -    public string ToJsonStringV4(string domainStrategy = "IpOnDemand", bool directGeositeCn = true, bool pretty = true) -    { -        var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions -        { -            PropertyNamingPolicy = JsonNamingPolicy.CamelCase, -            DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, -            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, -        }); -        // TODO: Make interface converter generic. -        jsonOptions.Converters.Add(new JsonInterfaceConverter<V4ConfigJsonObjects.IOutboundSettings>()); -        jsonOptions.Converters.Add(new JsonInterfaceConverter<V4ConfigJsonObjects.IOutboundStreamSettings>()); - -        var templateValues = new Dictionary<string, string> -        { -            [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectV4(), jsonOptions))), -            [RoutingAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectV4(domainStrategy, directGeositeCn), jsonOptions), -            [HostsAnchor] = JsonSerializer.Serialize(Hosts.ToJsonObjectV4(), jsonOptions), -        }; - -        var configString = Template.Generate(templateValues); - -        if (pretty) -        { -            var jsonOptionsPretty = new JsonSerializerOptions(jsonOptions) -            { -                WriteIndented = true, -            }; -            return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(configString, jsonOptionsPretty), jsonOptionsPretty); -        } -        else -        { -            return configString; -        } -    } - -    public static ToolConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string? hostsPath) -    { -        foreach (var path in new List<string>([templatePath, vmessPath, proxyPath])) -        { -            if (!File.Exists(path)) -            { -                throw new FileNotFoundException($"Required config file not found: {path}."); -            } -        } - -        ProxyFile proxyFile = new(proxyPath); -        string templateString, vmessString; -        string? hostsString; - -        string file = ""; -        try -        { -            file = templatePath; -            templateString = File.ReadAllText(templatePath); -            file = vmessPath; -            vmessString = File.ReadAllText(vmessPath); -            hostsString = hostsPath is not null ? File.ReadAllText(hostsPath) : null; -        } -        catch (Exception e) -        { -            throw new Exception($"Error reading config file {file}.", e); -        } - -        try -        { -            file = templatePath; -            var template = new Template(templateString); -            file = vmessPath; -            var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy"); -            file = proxyPath; -            var routing = Routing.FromProxyFile(proxyFile, "proxy"); -            file = hostsPath ?? ""; -            var hosts = hostsString is not null ? StaticHosts.CreateFromHostMatchConfigString(hostsString) : null; -            return new ToolConfig(template, [vmess], routing, hosts); -        } -        catch (Exception e) -        { -            throw new Exception($"Error parsing config file {file}.", e); -        } -    } - -    public static ToolConfig FromDirectory(string directory) -    { -        return FromFiles( -            Path.Join(directory, ConfigTemplateFileName), -            Path.Join(directory, VmessConfigFileName), -            Path.Join(directory, ProxyConfigFileName), -            Path.Join(directory, HostsConfigFileName) -        ); -    } - -    public static void FromDirectoryAndWriteToFile(string directory, string outputPath) -    { -        var config = FromDirectory(directory); -        File.WriteAllText(outputPath, config.ToJsonStringV4()); -    } -} - -public class SingToolConfig(Template template, List<Proxy> proxies, Routing router, string inboundsString) : ToolConfigBase(template, proxies, router) -{ - -    public const string ConfigTemplateFileName = "sing-config.json.template"; -    public const string ConfigInboundsPcFileName = "sing-inbounds-pc.json"; -    public const string ConfigInboundsMobileFileName = "sing-inbounds-mobile.json"; - -    public static List<string> RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, ConfigInboundsMobileFileName, ConfigInboundsPcFileName]; - -    private const string ProxyAnchor = "PROXY_ANCHOR"; -    private const string RouteAnchor = "ROUTE_ANCHOR"; -    private const string InboundsAnchor = "INBOUNDS_ANCHOR"; - -    public string InboundsString { get; } = inboundsString; - -    public string ToSingConfigString(bool pretty = true) -    { -        var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions -        { -            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, -            DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower, -            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, -        }); -        // TODO: Make interface converter generic. -        jsonOptions.Converters.Add(new JsonInterfaceConverter<SingConfigJsonObjects.OutboundBase>()); -        jsonOptions.Converters.Add(new JsonInterfaceConverter<SingConfigJsonObjects.V2rayTransportBase>()); - -        var templateValues = new Dictionary<string, string> -        { -            [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectSing(), jsonOptions))), -            [RouteAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectSing(), jsonOptions), -            [InboundsAnchor] = InboundsString -        }; - -        var configString = Template.Generate(templateValues); - -        if (pretty) -        { -            var jsonOptionsPretty = new JsonSerializerOptions(jsonOptions) -            { -                WriteIndented = true, -            }; -            return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(configString, jsonOptionsPretty), jsonOptionsPretty); -        } -        else -        { -            return configString; -        } -    } - -    public static SingToolConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string inboundsPath, bool clean, bool silent) -    { -        foreach (var path in new List<string>([templatePath, vmessPath, proxyPath, inboundsPath])) -        { -            if (!File.Exists(path)) -            { -                throw new FileNotFoundException($"Required config file not found: {path}."); -            } -        } - -        var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); - -        ProxyFile proxyFile = new(proxyPath); -        string templateString, vmessString, inboundsString; - -        string file = ""; -        try -        { -            file = templatePath; -            templateString = File.ReadAllText(templatePath); -            file = vmessPath; -            vmessString = File.ReadAllText(vmessPath); -            file = inboundsPath; -            inboundsString = File.ReadAllText(inboundsPath); -        } -        catch (Exception e) -        { -            throw new Exception($"Error reading config file {file}.", e); -        } - -        try -        { -            file = templatePath; -            var template = new Template(templateString); -            file = vmessPath; -            var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy-out"); -            file = proxyPath; -            var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy-out", "direct-out"); -            return new SingToolConfig(template, [vmess], routing, inboundsString); -        } -        catch (Exception e) -        { -            throw new Exception($"Error parsing config file {file}.", e); -        } -    } - -    public static SingToolConfig FromDirectory(string directory, bool isMobile, bool clean, bool silent) -    { -        return FromFiles( -            Path.Join(directory, ConfigTemplateFileName), -            Path.Join(directory, VmessConfigFileName), -            Path.Join(directory, ProxyConfigFileName), -            isMobile ? Path.Join(directory, ConfigInboundsMobileFileName) : Path.Join(directory, ConfigInboundsPcFileName), -            clean, silent -        ); -    } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs deleted file mode 100644 index 3e81dbb..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Crupest.SecretTool; - -public static class V4ConfigJsonObjects -{ -    public interface IObject; -    public interface IOutboundSettings : IObject; -    public interface IOutboundStreamSettings : IObject; - -    public record WsSettings(string Path, Dictionary<string, string> Headers) : IObject; -    public record WsStreamSettings(string Network, string Security, WsSettings WsSettings) : IOutboundStreamSettings; -    public record VnextServerUser(string Id, int AlterId, string Security, int Level) : IObject; -    public record VnextServer(string Address, int Port, List<VnextServerUser> Users) : IObject; -    public record VmessOutboundSettings(List<VnextServer> Vnext) : IOutboundSettings; -    public record HttpOutboundUser(string User, string Pass) : IObject; -    public record HttpOutboundServer(string Address, int Port, List<HttpOutboundUser> Users) : IObject; -    public record HttpOutboundSettings(List<HttpOutboundServer> Servers) : IOutboundSettings; -    public record Outbound(string Tag, string Protocol, IOutboundSettings Settings, -        IOutboundStreamSettings? StreamSettings) : IObject; - -    public record RoutingRule(string DomainMatcher = "mph", string Type = "field", List<string>? Domains = null, List<string>? Ip = null, -        string? Port = null, string? SourcePort = null, string? Network = null, List<string>? Source = null, -        List<string>? User = null, List<string>? InboundTag = null, List<string>? Protocol = null, string? Attrs = null, -        string? OutboundTag = null, string? BalancerTag = null) : IObject; -    public record Routing(List<RoutingRule> Rules, string DomainStrategy = "IpOnDemand", string DomainMatcher = "mph") : IObject; -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs deleted file mode 100644 index a50e9be..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Crupest.SecretTool; - -public static class V5ConfigJsonObjects -{ -    public record OutboundObject(string Protocol, object Settings, string Tag, object? StreamSettings) -    { -        public static OutboundObject VmessViaWs(string tag, string address, int port, string uuid, string path) -        { -            return new OutboundObject("vmess", new VmessSettings(address, port, uuid), tag, StreamSettingsObject.Ws(path)); -        } - -        public static OutboundObject Http(string tag, string address, int port) -        { -            return new OutboundObject("http", new HttpSettingsObject(address, port), tag, null); -        } -    } - -    public record WsSettingsObject(string Path, Dictionary<string, string> Headers); - -    public record StreamSettingsObject(string Transport, object TransportSettings, string Security, object SecuritySettings) -    { -        public static StreamSettingsObject Ws(string path) -        { -            return new StreamSettingsObject("ws", new WsSettingsObject(path, new()), "tls", new()); -        } -    } - -    public record VmessSettings(string Address, int Port, string Uuid); - -    public record HttpSettingsObject(string Address, int Port); -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template deleted file mode 100644 index 424e996..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template +++ /dev/null @@ -1,63 +0,0 @@ -{ -  "log": { -    "loglevel": "warning" -  }, -  "inbounds": [ -    { -      "port": 2081, -      "listen": "127.0.0.1", -      "tag": "socks-inbound", -      "protocol": "socks", -      "settings": { -        "auth": "noauth" -      } -    }, -    { -      "port": 2080, -      "listen": "127.0.0.1", -      "tag": "http-inbound", -      "protocol": "http", -      "settings": { -        "auth": "noauth" -      } -    } -  ], -  "outbounds": [ -    { -      "protocol": "freedom", -      "settings": {}, -      "tag": "direct" -    }, -    { -      "protocol": "blackhole", -      "settings": {}, -      "tag": "blocked" -    }, -    ${PROXY_ANCHOR} -  ], -  "routing": ${ROUTING_ANCHOR}, -  "dns": { -    "hosts": ${HOSTS_ANCHOR}, -    "servers": [ -      "https://doh.pub/dns-query", -      "1.1.1.1", -      "8.8.8.8", -      "localhost" -    ] -  }, -  "policy": { -    "levels": { -      "0": { -        "uplinkOnly": 0, -        "downlinkOnly": 0 -      } -    }, -    "system": { -      "statsInboundUplink": false, -      "statsInboundDownlink": false, -      "statsOutboundUplink": false, -      "statsOutboundDownlink": false -    } -  }, -  "other": {} -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template deleted file mode 100644 index 01ccf7a..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template +++ /dev/null @@ -1,55 +0,0 @@ -{ -    "log": { -        "access": { -            "type": "Console", -            "level": "Info" -        } -    }, -    "dns": { -        "nameServer": [{ -            "address": "https://doh.pub/dns-query" -        }, { -            "address": "1.1.1.1" -        }, { -            "address": "8.8.8.8" -        }, { -            "address": "localhost" -        }], -        "staticHosts": ${HOSTS_ANCHOR} -    }, -    "inbounds": [{ -        { -          "protocol": "socks", -          "port": 2081, -          "listen": "127.0.0.1", -          "tag": "socks-inbound", -          "settings": { -            "auth": "noauth" -          } -        }, -        { -          "protocol": "http", -          "port": 2080, -          "listen": "127.0.0.1", -          "tag": "http-inbound", -          "settings": { -            "auth": "noauth" -          } -        } -    }], -    "outbounds": [ -        { -          "protocol": "freedom", -          "settings": {}, -          "tag": "direct" -        }, -        { -          "protocol": "blackhole", -          "settings": {}, -          "tag": "blocked" -        }, -        ${PROXY_ANCHOR} -    ], -    "router": ${ROUTER_ANCHOR} -} - diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt deleted file mode 100644 index 88d5015..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt +++ /dev/null @@ -1,2 +0,0 @@ -cdn.jsdelivr.net cdn.jsdelivr.net.cdn.cloudflare.net - diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt deleted file mode 100644 index 39800f9..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt +++ /dev/null @@ -1,50 +0,0 @@ -GeoSite microsoft -GeoSite google -GeoSite youtube -GeoSite x -GeoSite facebook -GeoSite discord -GeoSite reddit -GeoSite twitch -GeoSite quora -GeoSite telegram -GeoSite imgur -GeoSite stackexchange -GeoSite medium - -GeoSite duckduckgo -GeoSite wikimedia -GeoSite gitbook -GeoSite github -GeoSite gitlab -GeoSite sourceforge -GeoSite creativecommons -GeoSite archive -GeoSite matrix -GeoSite tor - -GeoSite python -GeoSite ruby -GeoSite rust -GeoSite nodejs -GeoSite npmjs -GeoSite qt -GeoSite docker -GeoSite v2ray -GeoSite homebrew - -GeoSite azure -GeoSite akamai -GeoSite aws -GeoSite jsdelivr -GeoSite fastly -GeoSite heroku -GeoSite bootstrap -GeoSite vercel - -GeoSite ieee -GeoSite sci-hub -GeoSite libgen -GeoSite z-library - -sagernet.org diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template deleted file mode 100644 index d7e55a0..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template +++ /dev/null @@ -1,45 +0,0 @@ -{
 -  "log": {
 -    "disabled": false,
 -    "level": "info",
 -    "timestamp": true
 -  },
 -  "dns": {
 -    "servers": [
 -      {
 -        "tag": "ali-doh",
 -        "address": "https://dns.alidns.com/dns-query",
 -        "address_resolver": "ali"
 -      },
 -      {
 -        "tag": "ali",
 -        "address": "223.5.5.5"
 -      },
 -      {
 -        "tag": "cloudflare",
 -        "address": "1.1.1.1"
 -      },
 -      {
 -        "tag": "google",
 -        "address": "8.8.8.8"
 -      }
 -    ]
 -  },
 -  "inbounds": ${INBOUNDS_ANCHOR},
 -  "outbounds": [
 -    {
 -      "type": "direct",
 -      "tag": "direct-out"
 -    },
 -    {
 -      "type": "block",
 -      "tag": "block-out"
 -    },
 -    {
 -      "tag": "dns-out",
 -      "type": "dns"
 -    },
 -    ${PROXY_ANCHOR}
 -  ],
 -  "route": ${ROUTE_ANCHOR}
 -}
 diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json deleted file mode 100644 index 5038c40..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json +++ /dev/null @@ -1,11 +0,0 @@ -[
 -  {
 -    "tag": "tun-in",
 -    "type": "tun",
 -    "auto_route": true,
 -    "strict_route": true,
 -    "address": [ "172.23.0.1/30", "fdfe:acbd:9876::1/126"],
 -    "sniff": true,
 -    "sniff_override_destination": true
 -  }
 -]
 diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json deleted file mode 100644 index 956d751..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json +++ /dev/null @@ -1,14 +0,0 @@ -[
 -  {
 -    "tag": "http-in",
 -    "type": "http",
 -    "listen": "127.0.0.1",
 -    "listen_port": 3080
 -  },
 -  {
 -    "tag": "socks-in",
 -    "type": "socks",
 -    "listen": "127.0.0.1",
 -    "listen_port": 3081
 -  }
 -]
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/build-secret.bash b/tools/Crupest.SecretTool/build-secret.bash deleted file mode 100755 index 8878049..0000000 --- a/tools/Crupest.SecretTool/build-secret.bash +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env bash - -set -e - -function print_argument_error_message_and_exit() { -    argument_error_message="You must specify exactly one argument, the build target (win-x64 | linux-x64 | osx-x64)." -    echo "$argument_error_message" -    exit 1 -} - - - -if [[ $# != 1 ]]; then -    print_argument_error_message_and_exit -fi - -case "$1" in -    win-x64 | linux-x64 | osx-x64) -        echo "Build target: $1" -    ;; -    *) -        print_argument_error_message_and_exit -    ;; -esac - -secret_dir=$(realpath "$(dirname "$0")") - -echo "Secret dir: ${secret_dir}" - -echo "Check dotnet..." -dotnet --version - -echo "Enter \"secret\" dir..." -pushd "$secret_dir" - -echo "Begin to build..." -dotnet publish Crupest.SecretTool -c Release -o "$secret_dir/publish" --sc -r "$1" - -popd - -echo "Finish!" diff --git a/tools/Crupest.SecretTool/build-secret.ps1 b/tools/Crupest.SecretTool/build-secret.ps1 deleted file mode 100644 index 8aa7987..0000000 --- a/tools/Crupest.SecretTool/build-secret.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -if ($args.Count -ne 1 || $args[0] -notmatch "^win-x64|linux-x64|osx-x64$") -{ -    Write-Error "You must specify exactly one argument, the build target (win-x64 | linux-x64 | osx-x64)." -    exit 1 -} - -Write-Output "Secret dir: $PSScriptRoot" - -Write-Output "Check dotnet..." -dotnet --version -if ($LASTEXITCODE -ne 0) -{ -    Write-Error "dotnet not found." -    exit 2 -} - -Write-Output "Enter `"secret`" dir..." -Push-Location $PSScriptRoot - -Write-Output "Begin to build..." -dotnet publish Crupest.SecretTool -c Release -o "$secret_dir/publish" --sc -r $args[0] - -Pop-Location - -Write-Host "Finish!" -ForegroundColor Green diff --git a/tools/Crupest.SecretTool/tools/cru-proxy-edit b/tools/Crupest.SecretTool/tools/cru-proxy-edit deleted file mode 100755 index 51a33e1..0000000 --- a/tools/Crupest.SecretTool/tools/cru-proxy-edit +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env bash - -set -e - -p="$HOME/codes/crupest/tools/Crupest.SecretTool/publish/proxy.txt" - -if [[ ! -f "$p" ]]; then -    echo "File $p does not exist!" >&2 -    exit 1 -fi - -exec vim "$p" diff --git a/tools/Crupest.SecretTool/tools/cru-proxy-log b/tools/Crupest.SecretTool/tools/cru-proxy-log deleted file mode 100755 index 6ec6ee1..0000000 --- a/tools/Crupest.SecretTool/tools/cru-proxy-log +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env bash - -set -e - -if [[ -e /proc ]]; then -    # I don't believe your system is Linux but there is no /proc. -    exec journalctl --user -u crupest-secret-tool "$@" -elif [[ "$(uname)" == "Darwin" ]]; then -    exec less "$HOME/.local/state/Crupest.SecretTool/log" -else -    echo "Not supported on systems other than macOS and Linux now." >&2 -    exit 1 -fi diff --git a/tools/Crupest.SecretTool/tools/crupest-secret-tool.service b/tools/Crupest.SecretTool/tools/crupest-secret-tool.service deleted file mode 100644 index df6d172..0000000 --- a/tools/Crupest.SecretTool/tools/crupest-secret-tool.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=crupest v2ray service - -[Service] -ExecStart=%h/.local/bin/Crupest.SecretTool - -[Install] -WantedBy=default.target diff --git a/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml b/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml deleted file mode 100644 index 9b85f13..0000000 --- a/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml +++ /dev/null @@ -1,49 +0,0 @@ -<!-- -  MIT License - -  Copyright (c) 2008-2020 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, -  Inc., Oleg Nenashev and other contributors - -  Permission is hereby granted, free of charge, to any person obtaining a copy -  of this software and associated documentation files (the "Software"), to deal -  in the Software without restriction, including without limitation the rights -  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -  copies of the Software, and to permit persons to whom the Software is -  furnished to do so, subject to the following conditions: - -  The above copyright notice and this permission notice shall be included in all -  copies or substantial portions of the Software. - -  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -  SOFTWARE. ---> - -<!-- - This is a sample configuration of the Windows Service Wrapper. - This configuration file should be placed near the WinSW executable, the name should be the same. - E.g. for myapp.exe the configuration file name should be myapp.xml -  - You can find more information about configuration options here: -https://github.com/kohsuke/winsw/blob/master/doc/xmlConfigFile.md ---> -<service> -    <id>crupest-secret-tool</id> -    <name>Crupest Secret Tool</name> -    <description>Crupest Secret Tool (powered by WinSW)</description> - -    <!-- Path to the executable, which should be started --> -    <executable>%BASE%\Crupest.SecretTool.exe</executable> - -    <onfailure action="restart" delay="10 sec" /> -    <onfailure action="restart" delay="30 sec" /> -    <onfailure action="restart" delay="50 sec" /> - -    <workingdirectory>%BASE%</workingdirectory> - -    <startmode>Automatic</startmode> -</service>
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist b/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist deleted file mode 100644 index bdfe490..0000000 --- a/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> -    <key>Label</key> -    <string>life.crupest.secret-tool</string> -    <key>ProgramArguments</key> -    <array> -        <string>/Users/crupest/.local/bin/Crupest.SecretTool</string> -    </array> -    <key>KeepAlive</key> -    <true/> -    <key>StandardOutPath</key> -    <string>/Users/crupest/.local/state/Crupest.SecretTool/log</string> -    <key>StandardErrorPath</key> -    <string>/Users/crupest/.local/state/Crupest.SecretTool/error</string> -</dict> -</plist> diff --git a/tools/scripts/neovide-listen b/tools/scripts/neovide-listen deleted file mode 100755 index 2591842..0000000 --- a/tools/scripts/neovide-listen +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -if [[ -z "$NVIM_SOCKET" ]]; then -  NVIM_SOCKET="/tmp/nvimsocket" -fi - -args=() -MY_NEOVIM_PATH="$HOME/codes/neovim/build/bin/nvim" -if [[ -e "$MY_NEOVIM_PATH" ]]; then -  echo "Found my neovim at $MY_NEOVIM_PATH" -  export VIMRUNTIME="$HOME/codes/neovim/runtime" -  args=("${args[@]}" "--neovim-bin" "$MY_NEOVIM_PATH") -fi - -listen_added=0 -for arg in "$@"; do -  args=("${args[@]}" "$arg") -  if [ "$arg" = '--' ]; then -    args=("${args[@]}" "--listen" "$NVIM_SOCKET") -    listen_added=1 -  fi -done - -if [[ $listen_added = 0 ]]; then -  args=("${args[@]}" "--" "--listen" "$NVIM_SOCKET") -fi - -NEOVIDE_BIN=neovide -MY_NEOVIDE_PATH="$HOME/codes/neovide/target/release/neovide" -if [ -e "$MY_NEOVIDE_PATH" ]; then -  echo "Found my neovide at $MY_NEOVIDE_PATH" -  NEOVIDE_BIN="$MY_NEOVIDE_PATH" -fi - -if which nvr > /dev/null; then -  echo "Detected nvr, set git editor env" -  export GIT_EDITOR='nvr -cc split --remote-wait' -fi - -args=("$NEOVIDE_BIN" "${args[@]}") -echo "Command is ${args[@]}" -exec "${args[@]}" - diff --git a/tools/scripts/neovide-listen.ps1 b/tools/scripts/neovide-listen.ps1 deleted file mode 100644 index e84f3a2..0000000 --- a/tools/scripts/neovide-listen.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -$env:NVIM_LISTEN_ADDRESS ??= "\\.\pipe\nvimsocket" - -$neovide_args = @() - -$MY_NEOVIM_PATH="$HOME/codes/neovim/build/bin/nvim.exe" -if (Get-Item $MY_NEOVIM_PATH -ErrorAction Ignore) { -    Write-Output "Found my neovim at $MY_NEOVIM_PATH." -    $env:VIMRUNTIME="$HOME/codes/neovim/runtime" -    $neovide_args += "--neovim-bin", "$MY_NEOVIM_PATH" -} - -$listen_added = $false -foreach ($arg in $args) { -    $neovide_args += $arg -    if ( $arg -eq '--') { -        $neovide_args += "--listen", $env:NVIM_LISTEN_ADDRESS -        $listen_added=$true -    } -} - -if (-not $listen_added) { -    $neovide_args += "--", "--listen", $env:NVIM_LISTEN_ADDRESS -} - -$neovide_bin = "neovide" -$my_neovide_path = "$HOME/codes/neovide/target/release/neovide.exe" -if (Get-Item $my_neovide_path -ErrorAction Ignore) { -    Write-Output "Found my neovide at $my_neovide_path." -    $neovide_bin = "$my_neovide_path" -} - -if (Get-Command nvr -ErrorAction Ignore) { -    Write-Output "Detected nvr, set git editor env." -    $env:GIT_EDITOR = "nvr -cc split --remote-wait" -} - -Write-Output "Command is $($neovide_args -join ' ')." -Start-Process $neovide_bin -ArgumentList $neovide_args -Wait diff --git a/tools/utility/rename-tree.py b/tools/utility/rename-tree.py deleted file mode 100755 index c177eb6..0000000 --- a/tools/utility/rename-tree.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import os.path -import re - -parser = argparse.ArgumentParser( -                    prog='rename-tree', -                    description='Recursively rename directories and files') - -parser.add_argument('old') -parser.add_argument('new') -parser.add_argument('dirs', nargs="+") - -args = parser.parse_args() - -old_regex = re.compile(args.old) -new = args.new - -def rename(path, isdir): -    dirname = os.path.dirname(path) -    filename = os.path.basename(path) -    new_filename = re.sub(old_regex, new, filename) -    dir_str = "/" if isdir else "" -    if new_filename != filename: -        os.rename(path, os.path.join(dirname, new_filename)) -        print(f"{path}{dir_str} -> {new_filename}{dir_str}") - -for i, d in enumerate(args.dirs): -    print(f"[{i + 1}/{len(args.dirs)}] Run for {d}:") -    for dirpath, dirnames, filenames in os.walk(d, topdown=False): -        for filename in filenames: -            rename(os.path.join(dirpath, filename), False) -        rename(dirpath, True) - -print("Done!")  | 
