aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-02-28 23:13:39 +0800
committerYuqian Yang <crupest@crupest.life>2025-02-28 23:13:39 +0800
commitdc1f0c4c0096013799416664894c5194dc7e1f52 (patch)
tree2f5d235f778cd720f4c39ec3e56b77ba6d99f375 /tools
parent7299d424d90b1effb6db69e3476ddd5af72eeba4 (diff)
downloadcrupest-dc1f0c4c0096013799416664894c5194dc7e1f52.tar.gz
crupest-dc1f0c4c0096013799416664894c5194dc7e1f52.tar.bz2
crupest-dc1f0c4c0096013799416664894c5194dc7e1f52.zip
chore(store): move everything to store.
Diffstat (limited to 'tools')
-rw-r--r--tools/Crupest.SecretTool/.gitignore7
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool.sln30
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore1
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs95
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs113
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj34
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs26
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs324
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs123
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs113
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml13
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs76
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs31
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs155
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs20
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs40
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs56
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs231
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs271
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs25
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs31
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template63
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template55
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt2
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt50
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template45
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json11
-rw-r--r--tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json14
-rwxr-xr-xtools/Crupest.SecretTool/build-secret.bash41
-rw-r--r--tools/Crupest.SecretTool/build-secret.ps125
-rwxr-xr-xtools/Crupest.SecretTool/tools/cru-proxy-edit12
-rwxr-xr-xtools/Crupest.SecretTool/tools/cru-proxy-log13
-rw-r--r--tools/Crupest.SecretTool/tools/crupest-secret-tool.service8
-rw-r--r--tools/Crupest.SecretTool/tools/crupest-secret-tool.xml49
-rw-r--r--tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist18
-rwxr-xr-xtools/scripts/neovide-listen43
-rw-r--r--tools/scripts/neovide-listen.ps138
-rwxr-xr-xtools/utility/rename-tree.py37
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!")