no message

This commit is contained in:
hellcat 2026-08-11 11:00:00 +08:00
parent d677511bc9
commit c0eddda516
12 changed files with 791 additions and 14 deletions

File diff suppressed because one or more lines are too long

View File

@ -49,14 +49,13 @@ final class Cron
}
if ($h % 6 === 0 && $i === 3) {
ShadowRocketCrawl::run();
ShadowRocketPublish::run();
// exit;
ShadowRocketCrawl::run(); // 爬取小火箭id
ShadowRocketPublish::run(); // 小火箭id发布到site
CronFreenodeCrawl::run(); // 1.爬免费节点
CronFreenodePool::save(); // 2.格式化入库
CronFreenodeMerge::save(); // 3.加工成文件
CronFreenodeMerge::baseToSite(); // 4.文件分散为各站用
CronFreenodeMerge::baseToSite("fn_a"); // 4.文件分散为各站用
CronFreenodeMerge::baseToSite("fn_b");
}
if ($h % 3 === 0 && $i === 3) {

View File

@ -68,7 +68,12 @@ final class FnSitePublish
$sSiteFnClientNick = $sSiteFnClient;
if ($sSiteFnClient == "v2ray") {
$sSiteFnClientNick = "v2ray\shadowrocket\小火箭";
} else if ($sSiteFnClient == "singbox") {
$sSiteFnClientNick = "singbox\hiddify";
} else if ($sSiteFnClient == "clash") {
$sSiteFnClientNick = "clash\mihomo";
}
$sFeedLink = "https://".FreenodeHelperService::urlFeedToday($oSite->code_short, $sSiteFnClient);
$str = "";
$str .= "<b>".$sSiteFnClientNick."订阅链接:</b>\n\n";

View File

@ -8,6 +8,8 @@ use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
//use Symfony\Component\Yaml\Yaml;
// 第一步从哪爬run
final class FreenodeCrawl
{

View File

@ -10,6 +10,7 @@ use App\Models\SiteMap as ModelSiteMap;
use App\Models\Option as ModelOption;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
// 其实是清理文件
final class FreenodeFile
{

View File

@ -12,6 +12,7 @@ use App\Models\Option as ModelOption;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
use Symfony\Component\Yaml\Yaml;
// 应该是最终步骤,整合处理为对应站点的
final class FreenodeMerge
{
private static $aCountryOrder = [
@ -148,6 +149,11 @@ final class FreenodeMerge
$aNode["config"]["name"] = $sFlagCountry;
$aNodeMerge[] = $aNode["config"];
$aNodeCountryList[] = $sFlagCountry;
} else if ($sPoolClient == "singbox") {
$sFlagCountry = self::countryJoinFlag($aNode["country"]);
$aNode["config"]["name"] = $sFlagCountry;
$aNodeMerge[] = $aNode["config"];
$aNodeCountryList[] = $sFlagCountry;
} else {
$aNodeMerge[] = json_encode($aNode);
}
@ -203,10 +209,36 @@ final class FreenodeMerge
$sMerge = Yaml::dump($aClashTemplate, 4, 2);
$sPattern = '/(?<=^|\s)(cipher|name|type):\s+((?!\'|")[^\s"\'\n]+)/m';
$sMerge = preg_replace($sPattern, '$1: "$2"', $sMerge);
$sMerge = preg_replace($sPattern, '$1: "$2"', $sMerge); // 什么作用
// tomd($sMerge, 1);
} else if ($sPoolClient == "singbox") {
$sFile = $sDirBase."public/freenode/template/singbox.txt";
if (file_exists($sFile) && is_readable($sFile)) {
$sSingboxTemplate = file_get_contents($sFile); // data
} else {
$sMsg = "singbox的template不存在这不应该 \n";
TeleSlave::warn()->send($sMsg);
echo $sMsg;
continue;
}
$aSingboxConfig = json_decode($sSingboxTemplate, true);
foreach ($aSingboxConfig["outbounds"] as &$aOutbounds) {
if ($aOutbounds["type"] == "selector") {
$aOutbounds["outbounds"] = $aNodeCountryList;
continue;
}
}
array_push($aSingboxConfig["outbounds"], $aNodeMerge);
$sMerge = json_encode($aSingboxConfig);
} else {
$sMerge = "?";
}
$sFileDir = $sDirBase."public/freenode/merge/base/".$sPoolClient;
@ -224,7 +256,7 @@ final class FreenodeMerge
}
public static function baseToSite()
public static function baseToSite($sSiteGroupCodeTom)
{
echo "\nFreenodeMerge::baseToSite - 开始 \n";
@ -234,7 +266,7 @@ final class FreenodeMerge
$sDirBase = $_ENV["dir_base"];
$sFnClientList = ModelOption::_hit("freenode_client-fn_a");
$sFnClientList = ModelOption::_hit("freenode_client-".$sSiteGroupCodeTom);
if (!$sFnClientList) {
throw new \Exception("未设置option的freenode_client");
@ -253,7 +285,7 @@ final class FreenodeMerge
}
}
$oSiteMap = ModelSiteMap::whereIn("group_code", ["fn_a", "fn_b"])->get();
$oSiteMap = ModelSiteMap::whereIn("group_code", [$sSiteGroupCodeTom])->get();
if ($oSiteMap->isEmpty()) {
throw new \Exception("没找到siteMap的fn_a组");

View File

@ -8,7 +8,9 @@ use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Models\FreenodePool as ModelFreenodePool;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
use Symfony\Component\Yaml\Yaml;
use App\Services\ServiceFnPoolV2rayTo;
// 应该是第二步:入库先
final class FreenodePool
{
@ -100,6 +102,12 @@ final class FreenodePool
}
$aMerge[$sFromFileRealClient][$sConfigMd5] = $aNodeFormat;
$aNodeFormat_singbox = (new ServiceFnPoolV2rayTo())->singbox_row($aNodeFormat);
if (!empty($aNodeFormat_singbox)) {
$aMerge["singbox"][$sConfigMd5] = $aNodeFormat_singbox;
}
}
} else if ($sFromFileRealClient == "clash") {
@ -137,6 +145,8 @@ final class FreenodePool
}
}
}
// $aMerge["singbox"] = ServiceFnPool::SingboxByV2ray($aMerge["v2ray"]); // 免费节点都是必有v2rayclash则未必所以默认必有v2ray
$oPool->content = $aMerge;
$oPool->save();

View File

@ -9,6 +9,7 @@ use App\Services\TomTool\Telegram\Slave as TeleSlave;
use App\Services\TomTool\HttpV2;
use Illuminate\Support\Facades\Http;
// 这是向站点推送,步骤之外
final class FreenodeSync
{
static function run()

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
//use App\Services\Cron;
//use App\Models\HttpQueue as ModelHttpQueue;
//use App\Services\TomTool\Telegram\Master as TelegramMaster;
//use App\Services\TomTool\Telegram\Slave as TelegramSlave;
//use App\Services\TomTool\HttpV2;
//use Telegram\Bot\Api;
//use Telegram\Bot\Exceptions\TelegramSDKException;
//use Telegram\Bot\Exceptions\TelegramSDKException;
//use Telegram\Bot\Exceptions\TelegramResponse;
use Illuminate\Support\Facades\Http;
final class ReportTraffic
{
public function all()
{
$this->umami();
}
public function umami()
{
$r = $this->umami_checkToken();
tt($r);
}
private function umami_checkToken()
{
$oResponse = Http::post('https://api.umami.is/api/auth/login', [
'username' => "ad1473af-2b82-4e9b-a726-36bef6cc512b",
'password' => "UOtaonihouzi123#",
]);
// 工业级防御:非 2xx 响应直接抛出 Http 异常
$oResponse->throw();
// 解析 JSON
$aData = $oResponse->json();
// 检查 Key 是否符合预期(假设返回结构为 {"token": "xxx"}
if (empty($aData['token'])) {
throw new Exception('接口响应格式有误:缺失 token 字段');
}
return $aData['token'];
}
}

View File

@ -27,12 +27,13 @@ class FreenodeHelperService
return $sFeedUrl;
}
// 应该是没用上的按理应该有code和groupcode映射
public static function getClients()
{
$aClients = [
"clash",
"v2ray",
// "singbox",
"singbox",
];
return $aClients;

View File

@ -0,0 +1,514 @@
<?php
declare(strict_types=1);
namespace App\Services;
class ServiceFnPoolV2rayTo
{
public function singbox($cPoolV2ray)
{
$i = 0;
foreach ($cPoolV2ray as $aPoolV2ray) {
$i++;
// echo $i.":<br>";
if ($i <= 0) {
// continue;
}
if ($i >= 100) {
// continue;
}
$aPoolSingbox = $this->singbox_row($aPoolV2ray);
if (!$aPoolSingbox) continue; //测试用
t($aPoolV2ray);
t($aPoolSingbox);
echo "=======================================================";
}
exit;
}
public function singbox_row($aPoolV2ray)
{
$aNode = $aPoolV2ray;
if (isset($aPoolV2ray['protocolType'])) {
$aPoolV2ray['protocolType'] = $this->removeUtf8Bom($aPoolV2ray['protocolType']);
}
$sProtocol = $aPoolV2ray['protocolType'] ?? '';
if (isset($aPoolV2ray["config"]) && !is_array($aPoolV2ray["config"])) {
$aPoolV2ray["config"] = rawurldecode($aPoolV2ray["config"]);
}
$sModTo = 'mod_to_'.$sProtocol;
if (!method_exists($this, $sModTo)) {
return false; // 没做对应处理的直接排除
}
$aNode = $this->$sModTo($aPoolV2ray);
return $aNode;
}
private function mod_to_socks($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$sPwd = $this->tomBase64Decode($sPwd);
$aTmp = explode(":", $sPwd, 2);
$sUser = $aTmp[0] ?? '';
$sPwd = $aTmp[1] ?? '';
$aTmp = explode(":", $sConfig, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = (int) $aTmp[1] ?? '';
$aNode = [
"type" => "socks",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => $iPort,
"username" => $sUser,
"password" => $sPwd
];
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_trojan($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = (int) $aTmp[1] ?? '';
$aNode = [
"type" => "trojan",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => $iPort,
"password" => $sPwd
];
$aCustomYuan = explode("&", $sConfig);
$aCustom = [];
foreach ($aCustomYuan as $sRow) {
$aRow = explode("=", $sRow, 2);
$k = $aRow[0] ?? '';
$v = $aRow[1] ?? '';
$aCustom[$k] = $v;
}
$aTls = []; // 层1的tls
$aUtls = []; // 层2的utls
if (isset($aCustom["sni"])) { // 写层1的tls
$aTls["server_name"] = $this->parseServerName($aCustom["sni"], $aCustom["sni"]);
}
if (isset($aCustom["host"])) { // 写层1的tls -- host覆盖sni=host优先
$aTls["server_name"] = $this->parseServerName($aCustom["host"], $aCustom["host"]);
}
if (isset($aCustom["alpn"])) { // 写层1的tls
$aTls["alpn"] = explode(",", $aCustom["alpn"]);
}
if (isset($aCustom["allowInsecure"])) { // 写层1的tls
$aTls["insecure"] = (bool) $aCustom["allowInsecure"];
}
if (isset($aCustom["fp"])) { // 写层2的utls
$aUtls["fingerprint"] = $aCustom["fp"];
}
if (!empty($aUtls)) { // 层2的utls并入tls
$aUtls["enabled"] = true;
$aTls["utls"] = $aUtls;
}
if (!empty($aTls)) { // 层1的tlstls并入node
$aTls["enabled"] = true;
$aNode["tls"] = $aTls;
}
$aTransport = [];
if (isset($aCustom["type"]) && $aCustom["type"] != "none" && $aCustom["type"] != "tcp" && $aCustom["type"] != "raw") {
$aTransport["type"] = $aCustom["type"];
if ($aCustom["type"] == "ws" && isset($aTls["server_name"])) {
$aTransport["headers"]["Host"] = $aTls["server_name"];
}
}
if (isset($aCustom["path"])) {
$aTransport["path"] = rawurldecode($aCustom["path"]);
}
if (!empty($aTransport)) {
$aNode["transport"] = $aTransport;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_hysteria2($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = (int) $aTmp[1] ?? '';
$cCustom = explode("&", $sConfig);
$aCustom = [];
foreach ($cCustom as $sCustom) {
$aTmp = explode("=", $sCustom);
$k = $aTmp[0] ?? '';
$v = $aTmp[1] ?? '';
$aCustom[$k] = $v;
}
$aNode = [
"type" => "hysteria2",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => $iPort,
"password" => $sPwd
];
$aTls = [];
if (isset($aCustom['sni'])) {
$aTls['server_name'] = $this->parseServerName($aCustom['sni'], $aCustom['sni']);
}
if (isset($aCustom['insecure'])) {
$aTls['insecure'] = (bool) $aCustom['insecure'] ?? 0;
}
if (!empty($aTls)) {
$aTls['enabled'] = true;
$aNode["tls"] = $aTls;
}
$aObfs = [];
if (isset($aCustom["obfs"])) {
$aObfs["type"] = $aCustom["obfs"];
}
if (isset($aCustom["obfs-password"])) {
$aObfs["password"] = $aCustom["obfs-password"];
}
if (!empty($aObfs)) {
$aNode["obfs"] = $aObfs;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_ss($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$sPwd = $this->tomBase64Decode($sPwd);
$aTmp = explode(":", $sPwd, 2);
$sPwdType = $aTmp[0] ?? '';
$sPwd = $aTmp[1] ?? '';
$aTmp = explode(":", $sConfig, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = (int) $aTmp[1] ?? '';
// 推荐的现代化 SS 算法白名单
$aAllowedCiphers = [
'2022-blake3-aes-128-gcm',
'2022-blake3-aes-256-gcm',
'2022-blake3-chacha20-poly1305',
'aes-128-gcm',
'aes-192-gcm',
'aes-256-gcm',
'chacha20-ietf-poly1305',
'xchacha20-ietf-poly1305',
];
if (!in_array($sPwdType, $aAllowedCiphers, true)) {
return false;
}
$aNode = [
"type" => "shadowsocks",
"tag" => $aV2ray['country'] ?? '',
"server" => $sAddr,
"server_port" => $iPort,
"method" => $sPwdType,
"password" => $sPwd,
];
return $aNode;
}
private function mod_to_vmess($aV2ray)
{
$aNode = [];
$aConfig = $aV2ray["config"];
$aNode["type"] = "vmess";
$aNode["tag"] = $aV2ray['country'] ?? '';
$aNode["server"] = $aConfig["add"] ?? '';
$aNode["server_port"] = (int) $aConfig["port"] ?? '';
$aNode["uuid"] = $aConfig["id"] ?? '';
$aNode["security"] = $aConfig["scy"] ?? 'auto';
$aNode["alter_id"] = (int) $aConfig["aid"] ?? 0; // 猜的
$aTransport = [];
if (isset($aConfig["net"]) && $aConfig["net"] != "tcp" && $aConfig["net"] != "none" && $aConfig["net"] != "raw") {
$aTransport["type"] = $aConfig["net"];
}
if (isset($aConfig["path"])) {
$aTransport["path"] = rawurldecode($aConfig["path"]);
}
if (isset($aConfig["host"])) {
$aTransport["headers"]["Host"] = $aConfig["host"];
}
if (!empty($aTransport)) {
$aNode["transport"] = $aTransport;
}
$aTls = [];
if (isset($aConfig["tls"])) {
if ($aConfig["tls"] == "tls") {
$aTls["server_name"] = !empty($aConfig['host']) ? $aConfig['host'] : $aConfig['add'];
$aTls["server_name"] = $this->parseServerName($aTls["server_name"], $aTls["server_name"]);
}
}
if (!empty($aTls)) {
$aTls["enabled"] = true;
$aNode["tls"] = $aTls;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_vless($aV2ray)
{
$sConfig = $aV2ray['config'] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? ''; // *
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0];
$sConfig = $aTmp[1];
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? ''; // *
$sPort = $aTmp[1] ?? ''; // *
$aCustomTmp = explode("&", $sConfig);
$aCustom = [];
foreach ($aCustomTmp as $aCustomRow) {
$aTmp = explode("=", $aCustomRow, 2);
$k = $aTmp[0] ?? '';
$v = $aTmp[1] ?? '';
$aCustom[$k] = $v; // *
}
$aNode = [
'type' => 'vless',
'tag' => $aV2ray['country'] ?? '',
'server' => $sAddr,
'server_port' => (int) $sPort,
'uuid' => $sPwd,
];
if (isset($aCustom["packetEncoding"]) && $aCustom["packetEncoding"] != "none") {
$aNode["packet_encoding"] = $aCustom["packetEncoding"];
}
if (isset($aCustom["flow"])) {
$aNode["flow"] = $aCustom["flow"];
}
$aTransport = [];
if (isset($aCustom["type"]) && $aCustom["type"] != "tcp" && $aCustom["type"] != "raw") {
$aTransport["type"] = $aCustom["type"];
}
if (isset($aCustom["headerType"]) && $aCustom["headerType"] == "http") {
$aTransport["type"] = "http"; // 整个http覆盖默认tcp
}
if (isset($aCustom["path"])) {
$aTransport["path"] = rawurldecode($aCustom["path"]);
}
if (isset($aCustom["host"])) {
$aTransport["headers"]["Host"] = $aCustom["host"];
}
if (isset($aCustom["serviceName"])) {
$aTransport["service_name"] = $aCustom["serviceName"];
}
if (!empty($aTransport)) {
$aNode["transport"] = $aTransport;
}
$aTls = []; // 本体
$aUtls = []; // 二层
$aReality = []; // 二层
if (isset($aCustom["fp"])) { // 二层的utls构建
if ($aCustom["fp"] == "random") {
$aCustom["fp"] = "chrome";
}
$aUtls["fingerprint"] = $aCustom["fp"];
}
if (!empty($aUtls)) { // 二层的utls封装进tls
$aUtls["enabled"] = true;
$aTls["utls"] = $aUtls;
}
if (isset($aCustom["security"])) {
$sSecurity = $aCustom["security"];
if ($sSecurity == "reality") { // 二层的reality构建
if (isset($aCustom["pbk"])) {
$aReality["public_key"] = $aCustom["pbk"];
}
if (isset($aCustom["sid"])) {
$aReality["short_id"] = (string) $aCustom["sid"];
}
}
}
if (!empty($aReality)) { // 二层的reality封建进tls
$aReality["enabled"] = true;
$aTls["reality"] = $aReality;
}
if (isset($aCustom["alpn"])) {
$sTmp = $aCustom["alpn"] ?? "";
$aTmp = explode(",", $sTmp);
$aTls["alpn"] = $aTmp;
}
if (isset($aCustom["sni"])) { // 本体的tls构建
$aTls["server_name"] = $this->parseServerName($aCustom["sni"], $aCustom["sni"]);
}
if (isset($aCustom["encryption"]) && $aCustom["encryption"] != "none") { // 本体的tls构建
$aTls["encryption"] = $aCustom["encryption"];
}
if (!empty($aTls)) { // 本体的tls封装进node
$aTls["enabled"] = true;
$aNode["tls"] = $aTls;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function tomBase64Decode(mixed $xInput): string
{
// 1. 防御性类型检查
if (!is_string($xInput) || $xInput === '') {
return (string) $xInput;
}
// 2. 开启严格模式解码
$sDecoded = base64_decode($xInput, true);
if ($sDecoded === false) {
return $xInput;
}
// 3. 反向验证:确保它不是像 humanity 这种恰好能被解码的纯普通文本
if (base64_encode($sDecoded) !== $xInput) {
return $xInput;
}
// 4. 核心:校验解码后的数据是否为合法 UTF-8 文本,且不包含不可打印控制字符(\x00-\x08, \x0B-\x0C, \x0E-\x1F
$bIsValidUtf8 = mb_check_encoding($sDecoded, 'UTF-8');
$bHasControlChars = (bool) preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $sDecoded);
if ($bIsValidUtf8 && !$bHasControlChars) {
return $sDecoded; // 解码出来是合法的正常文本(包含中文、英文、数字等)
}
// 解码出来包含真正的二进制控制符(乱码),说明输入原本就是纯字符串,返回原值
return rawurldecode($xInput);
}
public function test_allXieyi($cPoolV2ray)
{
$aXieyi = [];
foreach ($cPoolV2ray as $aV2ray) {
$sXieyi = $aV2ray["protocolType"];
$sXieyi = $this->removeUtf8Bom($sXieyi);
$iXieyi = $aXieyi[$sXieyi] ?? 0;
$iXieyi++;
$aXieyi[$sXieyi] = $iXieyi;
}
return $aXieyi;
}
private function removeUtf8Bom(string $sInput): string
{
if (str_starts_with($sInput, "\xEF\xBB\xBF")) {
return substr($sInput, 3);
}
return $sInput;
}
private function parseServerName(string $sSni, string $sFallbackServer): string
{
$sSni = trim($sSni);
$sSni = rawurldecode($sSni);
if (empty($sSni)) {
return $sFallbackServer;
}
// 针对包含 http:// 或 https:// 的情况进行解析
if (preg_match('#^https?://#i', $sSni)) {
$aUrl = parse_url($sSni);
$sSni = $aUrl['host'] ?? '';
}
// 剔除可能残余的路径与端口号 (例如 domain.com/path 或 domain.com:8080)
if (str_contains($sSni, '/')) {
$sSni = explode('/', $sSni)[0];
}
if (str_contains($sSni, ':')) {
$sSni = explode(':', $sSni)[0];
}
// 若提取后仍为空,回退到节点自身的 server 字段
return !empty($sSni) ? $sSni : $sFallbackServer;
}
}

View File

@ -0,0 +1,119 @@
{
"log": {
"level": "error"
},
"dns": {
"servers": [
{
"tag": "local",
"address": "local"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
}
],
"final": "local",
"strategy": "prefer_ipv6"
},
"inbounds": [
{
"type": "tun",
"inet4_address": "172.19.0.1\/30",
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": true,
"udp_timeout": 60,
"platform": {
"http_proxy": {
"enabled": true,
"server": "127.0.0.1",
"server_port": 7891
}
},
"sniff": true
},
{
"type": "mixed",
"listen": "127.0.0.1",
"listen_port": 7891,
"sniff": true,
"domain_strategy": "prefer_ipv6"
}
],
"outbounds": [
{
"type": "selector",
"tag": "default",
"outbounds": []
},
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
}
],
"route": {
"rules": [
{
"clash_mode": "Direct",
"outbound": "direct"
},
{
"clash_mode": "Rule",
"outbound": "default"
},
{
"clash_mode": "Global",
"outbound": "default"
},
{
"protocol": "stun",
"outbound": "block"
},
{
"ip_is_private": true,
"outbound": "direct"
},
{
"rule_set": "geoip-cn",
"outbound": "direct"
},
{
"rule_set": "geosite-cn",
"outbound": "direct"
}
],
"rule_set": [
{
"tag": "geoip-cn",
"type": "remote",
"format": "binary",
"url": "https:\/\/fastly.jsdelivr.net\/gh\/SagerNet\/sing-geoip@rule-set\/geoip-cn.srs",
"download_detour": "direct"
},
{
"tag": "geosite-cn",
"type": "remote",
"format": "binary",
"url": "https:\/\/fastly.jsdelivr.net\/gh\/SagerNet\/sing-geosite@rule-set\/geosite-cn.srs",
"download_detour": "direct"
}
],
"auto_detect_interface": true
},
"experimental": {
"cache_file": {
"enabled": true,
"cache_id": "clashfreenode"
},
"clash_api": {
"external_controller": "127.0.0.1:9090"
}
}
}