freenode_merge

This commit is contained in:
hellcat 2025-12-18 16:01:13 +08:00
parent 6843f40b78
commit a93beaa48e
41 changed files with 2083 additions and 239 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ public/fn/
README.md
_env/
public/upload/
public/freenode/

View File

@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api;
//use App\Models\Article;
//use App\Services\TomTool\Http;
//use App\Services\TG\Http as TGHttp;
//use App\Models\Ship as ModelShip;
use App\Models\Option as ModelOption;
use Illuminate\Http\Request;
use Symfony\Component\Yaml\Yaml;
final class FreenodeController
{
public function sub($sSiteCode, $sClient, $sDate = "")
{
header('Content-Type: application/x-yaml; charset=utf-8');
$sDirBase = $_ENV["dir_base"];
// $aData = $request->json()->all();
// $sSiteCode = $sSiteCode ?? "base";
// $sClient = $aData["client"] ?? "";
$sDate = $sDate ?? date("Y-m-d");
//// *test
$sDate = "2025-12-22";
// $sClient = "clash";
//// test end
$aClient = [];
if ($sClient == "_all") {
$sFnClient = ModelOption::_hit("freenode_client");
$aFnClient = explode(",", $sFnClient);
foreach ($aFnClient as $sFnClient) {
$aClient[] = $sFnClient;
}
} else {
$aClient[] = $sClient;
}
$aContent = [];
foreach ($aClient as $sClient) {
$sFilePath = $this->pathReal($sSiteCode, $sClient, $sDate);
$sFileContent = file_get_contents($sFilePath); // data
$aContent[$sClient] = $sFileContent;
}
$iContentCount = count($aContent);
if ($iContentCount == 1) {
$k = array_key_first($aContent);
$sContent = $aContent[$k];
if ($k == "clash") {
header('Content-Type: application/x-yaml; charset=utf-8');
} else {
header('Content-Type: text/plain; charset=utf-8');
}
} else {
$sContent = json_encode($aContent);
}
echo $sContent;
exit;
}
private function pathReal($sSiteCode, $sClient, $sDate, $iDeep = 0)
{
if ($iDeep > 30) {
return false;
}
$sDirBase = $_ENV["dir_base"];
$sFilePath = $sDirBase."public/freenode/merge/".$sSiteCode."/".$sClient."/".$sDate.".txt";
if (file_exists($sFilePath)) {
return $sFilePath;
} else {
$oData = new \DateTime($sDate);
$oData->modify('-1 day');
$sDatePrev = $oData->format('Y-m-d');
return $this->pathReal($sSiteCode, $sClient, $sDatePrev, ++$iDeep);
}
}
}

22
app/Models/CrawlFreenode.php Executable file
View File

@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class CrawlFreenode extends Model
{
protected $table = 'crawl_freenode';
public $timestamps = false;
// public static function _listKey()
// {
// $aSelf = self::all()->toArray();
//
// foreach ($aSelf as &$aSelfRow) {
// unset($aSelfRow["content"]);
// }
//
// return $aSelf;
// }
}

12
app/Models/FreenodePool.php Executable file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class FreenodePool extends Model
{
protected $table = 'freenode_pool';
public $timestamps = false;
}

View File

@ -6,6 +6,12 @@ namespace App\Services;
use App\Services\Cron\HttpQueue as CronHttpQueue;
use App\Services\Cron\Book as CronBook;
use App\Services\Cron\FreenodeCrawl as CronFreenodeCrawl;
use App\Services\Cron\FreenodePool as CronFreenodePool;
use App\Services\Cron\FreenodeFile as CronFreenodeFile;
use App\Services\Cron\FreenodeMerge as CronFreenodeMerge;
use App\Services\Cron\FreenodeLog as CronFreenodeLog;
use App\Services\Cron\FreenodeSync as CronFreenodeSync;
use App\Services\Cron\ArticleSchema as CronArticleSchema;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
@ -18,31 +24,67 @@ final class Cron
public function run(): void
{
$h = (int) date("H");
$i = (int) date("i");
$s = (int) date("s");
try {
$h = (int) date("H");
$i = (int) date("i");
$s = (int) date("s");
if ($this->bIsTest) {
$h = (int) ($this->iTestH ?? $h);
$i = (int) ($this->iTestI ?? $i);
$s = (int) ($this->iTestS ?? $s);
}
echo "cron开始执行 \n";
if ($h % 8 === 0 && $i === 3) {
CronBook::randbox_notify();
}
if ($h % 6 === 0 && $i === 3) {
CronFreenodeCrawl::run();
CronFreenodePool::save();
CronFreenodeMerge::save();
CronFreenodeMerge::baseToSite();
CronFreenodeSync::run();
}
if ($i % 3 === 0) {
CronArticleSchema::cacheTgSave();
CronArticleSchema::pushToSlave();
CronHttpQueue::pop();
}
if ($i === 0) {
// ReportSender::handle();
}
if ($h == 5 && $i === 3) {
CronFreenodeFile::clear();
}
if ($h == 15 && $i == 3) {
CronFreenodeLog::fromLast();
}
} catch (\Throwable $e) {
$sMsg = "";
$sMsg .= "--- cron 异常 ---\n";
$sMsg .= "类型: " . get_class($e) . "\n";
$sMsg .= "信息: " . $e->getMessage() . "\n";
$sMsg .= "文件: " . $e->getFile() . " 在第 " . $e->getLine() . "\n";
$sMsg .= "代码跟踪:\n" . $e->getTraceAsString() . "\n";
$sMsg .= "-----------------------------\n";
echo $sMsg;
TeleSlave::fail()->send($sMsg);
if ($this->bIsTest) {
$h = (int) ($this->iTestH ?? $h);
$i = (int) ($this->iTestI ?? $i);
$s = (int) ($this->iTestS ?? $s);
}
echo "cron开始执行\n\n";
if ($h % 6 === 0 && $i === 3) {
CronBook::randbox_notify();
}
if ($i % 3 === 0) {
CronArticleSchema::cacheTgSave();
CronArticleSchema::pushToSlave();
CronHttpQueue::pop();
}
if ($i === 0) {
// ReportSender::handle();
}
}
public function setTestH(int $h): void

View File

@ -0,0 +1,229 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
//use Symfony\Component\Yaml\Yaml;
final class FreenodeCrawl
{
// 自有系
// 必然获取到但是密码会变暂定每天8点变意味着8袋内03分获取的是新密码
private static function getFile___my($aFrom)
{
$aFileContent = [];
foreach ($aFrom["url_file"] as $k => $sFileName) {
$sUrlSend = "https://".$aFrom["url_base"].$aFrom["url_node"].$sFileName;
$sFileContent = self::curl($sUrlSend) ?? "";
if ($sFileContent) {
$aFileContent[] = [
"type" => $k,
"file_name" => $sFileName,
"file_content" => $sFileContent
];
}
}
return $aFileContent;
}
// stairnode系
// ripaojiedian系stairnode本源
// 这个可能会没有,没有就不更新
private static function getFile__A($aFrom)
{
$aFileContent = [];
foreach ($aFrom["url_file"] as $k => $v) {
$sFileName = str_replace("{{Ymd}}", date("Ymd"), $v);
$sUrlSend = "http://".$aFrom["url_base"].$aFrom["url_node"].$sFileName;
$sFileContent = self::curl($sUrlSend) ?? "";
if (!$sFileContent) {
return [];
}
if ($k == "v2ray") {
$sFileContent = base64_decode($sFileContent);
}
if ($sFileContent) {
$aFileContent[] = [
"type" => $k,
"file_name" => $sFileName,
"file_content" => $sFileContent
];
}
}
return $aFileContent;
}
// v2rayshare系
// 这必然有,和下一天一致说明没更新(但是好像改了,现在它是一次生成很多天)
private static function getFile__B($aFrom)
{
$aFileContent = [];
$sTime = time();
$sTimeNext = strtotime("+1 day", $sTime);
$sUrlBase = $aFrom["url_base"];
$sUrlNode = $aFrom["url_node"];
$sUrlNode = str_replace("{{Y}}", date("Y"), $sUrlNode);
$sUrlNode = str_replace("{{m}}", date("m"), $sUrlNode);
$sUrlNodeNext = str_replace("{{Y}}", date("Y", $sTimeNext), $sUrlNode);
$sUrlNodeNext = str_replace("{{m}}", date("m", $sTimeNext), $sUrlNode);
foreach ($aFrom["url_file"] as $k => $v) {
$sUrlFile = str_replace("{{Ymd}}", date("Ymd"), $v);
$sUrlFileNext = str_replace("{{Ymd}}", date("Ymd"), $v);
$sHouzhui = ".".$k;
if ($k == "v2ray") {
$sHouzhui = ".txt";
} else if ($k == "clash") {
$sHouzhui = ".yaml";
}
$sFileName = date("Ymd")."-".$k.$sHouzhui;
$sUrlSend = "https://".$sUrlBase.$sUrlNode.$sUrlFile;
$sUrlSendNext = "https://".$sUrlBase.$sUrlNodeNext.$sUrlFileNext;
$sFileContent = self::curl($sUrlSend) ?? "";
// $sFileContentNext = self::curl($sUrlSendNext) ?? "";
if (!$sFileContent) {
return [];
}
if ($k == "v2ray") {
$sFileContent = base64_decode($sFileContent);
}
$sFileContentMd5 = md5($sFileContent);
// $sFileContentMd5Next = md5($sFileContentNext);
// if ($sFileContentMd5 != $sFileContentMd5Next) {
$aFileContent[] = [
"type" => $k,
"file_name" => $sFileName,
"file_content" => $sFileContent
];
// }
}
return $aFileContent;
}
private static function fileRealUpd($iId, $sType, $sFileName)
{
$oFrom = ModelCrawlFreenode::where("id", $iId)->first();
$aFileReal = json_decode($oFrom->file_real ?? "[]", true);
$aFileReal[$sType] = $sFileName;
$oFrom->file_real = $aFileReal;
return $oFrom->save();
}
public static function run()
{
echo "\nCrawlFreenode::caseA - 开始\n";
$aFromList = ModelCrawlFreenode::where("status", 1)->get()->toArray();
foreach ($aFromList as $aFrom) {
$aFrom["url_file"] = json_decode($aFrom["url_file"], true);
$sMethod = "getFile__".$aFrom["group"];
$aFileList = self::$sMethod($aFrom); // 没有就返回空数组所以不用判断直接foreach
foreach ($aFileList as $sFileRow) {
$sType = $sFileRow["type"];
$sHouzhui = "";
if ($sType == "v2ray") {
$sHouzhui = ".txt";
} else if ($sType == "clash") {
$sHouzhui = ".yaml";
}
$sFileName = date("Ymd")."-".$sType.$sHouzhui;
$sFileContent = $sFileRow["file_content"];
$sDir = "freenode/from/".$aFrom["name"];
$sPath = $sDir."/".$sFileName;
if (!is_dir($sDir)) {
mkdir($sDir, 0777, true);
}
// if ($sType == "v2ray") {
// $sFileContent = base64_decode($sFileContent);
// }
file_put_contents($sPath, $sFileContent);
self::fileRealUpd($aFrom["id"], $sType, $sFileName);
echo "- ".$aFrom["name"]."::".$sFileName."\n";
}
}
}
private static function curl($sUrl)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $sUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回内容而不是直接输出
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 如果是 https可以关闭证书验证测试用
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // 关闭主机名验证
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 关闭主机名验证
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$sContent = curl_exec($ch);
if ($sContent === false) {
$error = curl_error($ch);
$errno = curl_errno($ch);
$sMsg = "cURL error ({$errno}): {$error}";
echo $sMsg;
TeleSlave::log()->send($sMsg);
}
curl_close($ch);
return $sContent;
}
private static function fileGet($sUrl)
{
$sContent = file_get_contents($sUrl);
return $sContent;
}
}

View File

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
use App\Models\FreenodePool as ModelFreenodePool;
use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Models\SiteMap as ModelSiteMap;
use App\Models\Option as ModelOption;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
final class FreenodeFile
{
private static $aBaseProtocol = [
"clash",
"v2ray"
];
public static function clear()
{
echo "\nFreenodeFile::clear - 开始\n";
self::clear_from();
self::clear_merge__fn_a();
}
public static function clear_merge__fn_a()
{
$sDirBase = $_ENV["dir_base"];
$sPrevDay = 30;
$sTimePrevX = strtotime("-$sPrevDay days");
$sDatePrevX = date("Y-m-d", $sTimePrevX);
$aFnSiteList = ModelSiteMap::where("group_code", "fn_a")->pluck("code")->toArray();
$aFnSiteList[] = "base";
$sFnClient = ModelOption::_hit("freenode_client-fn_a");
$aFnClient = explode(",", $sFnClient);
foreach ($aFnSiteList as $sFnSiteName) {
foreach ($aFnClient as $sClient) {
$sFileDir = $sDirBase."public/freenode/merge/".$sFnSiteName."/".$sClient;
foreach (scandir($sFileDir) as $sFileName) {
if ($sFileName === '.' || $sFileName == '..') continue;
$sFilePath = $sFileDir.'/'.$sFileName;
if (preg_match('/(\d{4}-\d{2}-\d{2})/', $sFileName, $matches)) {
$sDateCode = $matches[1];
$sFileDate = \DateTime::createFromFormat('Y-m-d', $sDateCode);
if ($sFileDate && $sFileDate->getTimestamp() < $sTimePrevX) {
if (unlink($sFilePath)) {
echo " - 已删除: $sFilePath\n";
} else {
echo " - 删除失败: $sFilePath\n";
}
}
}
}
echo " - fn的merge里的".$sFnSiteName."里的".$sClient."的file清理完成 \n";
}
}
}
private static function clear_from()
{
$sDirBase = $_ENV["dir_base"];
$sPrevDay = 30;
$sTimePrevX = strtotime("-$sPrevDay days");
$sDateCodePrevX = date("Ymd", $sTimePrevX);
$oFromList = ModelCrawlFreenode::where("status", 1)->get();
foreach ($oFromList as $oFrom) {
$aFileLast = json_decode($oFrom->file_real, true);
$sFileDir = $sDirBase."public/freenode/from/".$oFrom->name;
foreach (scandir($sFileDir) as $sFileName) {
if ($sFileName === '.' || $sFileName == '..') continue;
$sFilePath = $sFileDir.'/'.$sFileName;
if (preg_match('/(\d{8})/', $sFileName, $matches)) {
$sDateCode = $matches[1]; // 提取到的日期字符串
$sFileDate = \DateTime::createFromFormat('Ymd', $sDateCode);
if ($sFileDate && $sFileDate->getTimestamp() < $sTimePrevX) {
if (unlink($sFilePath)) {
echo " - 已删除: $sFilePath\n";
} else {
echo " - 删除失败: $sFilePath\n";
}
}
}
}
echo " - fn的from的".$oFrom->name."里的file清理完成 \n";
}
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
//use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
use App\Models\CrawlFreenode as ModelCrawlFreenode;
//use Symfony\Component\Yaml\Yaml;
final class FreenodeLog
{
public static function fromLast()
{
$oFrom = ModelCrawlFreenode::where("status", 1)->get();
tomd($oFrom->toArray());
exit;
}
}

View File

@ -0,0 +1,419 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
use App\Models\CrawlFreenode as ModelCrawlFreenode;
use App\Models\FreenodePool as ModelFreenodePool;
use App\Services\Cron\FreenodePool as CronFreenodePool;
use App\Models\SiteMap as ModelSiteMap;
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 = [
"{{slogan_siteUrl}}",
"{{slogan_telegram}}",
"{{slogan_date}}",
"美国",
"日本",
"台湾",
"香港",
"新加坡",
];
private static $aCountryFlag = [
"美国" => "🇺🇸",
"日本" => "🇯🇵",
"台湾" => "🇹🇼",
"香港" => "🇭🇰",
"新加坡" => "🇸🇬",
"越南" => "🇻🇳",
"俄罗斯" => "🇷🇺",
"韩国" => "🇰🇷",
];
// public static function buildSogan($aNode)
// {
// // todo 广告位,这样就固定了
// }
private static function isSlogan($s)
{
if ($s == "{{slogan_siteUrl}}" || $s == "{{slogan_telegram}}" || $s == "{{slogan_date}}") {
return true;
}
return false;
}
public static function save()
{
echo "\nFreenodeFile::save - 开始 \n";
//// test
// $a = ["a", "b", "c"];
// $b = ["b", "c", "d"];
// $c = array_merge($a, $b);
// $c = array_unique($c);
// $c = array_values($c);
// tomd($c, 1);
//// test end
$sDirBase = $_ENV["dir_base"];
$sDate = date("Y-m-d");
// $sDate = "2025-12-12"; // *test
$oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
$aPoolContentList = json_decode($oPool->content, true);
foreach ($aPoolContentList as $sPoolClient => $aPoolContent) {
$aNodeGroupCountry = [];
$aLastNode = [];
$aLastNodeMy = [];
foreach ($aPoolContent as $sMd5 => $aNode) {
$sFromName = $aNode["fromName"];
$sCountry = $aNode["country"];
$sProtocolType = $aNode["protocolType"];
$sConfig = $aNode["config"];
$sCountryClear = self::strReplace($sCountry, $sFromName);
$aNodeGroupCountry[$sCountryClear][] = $aNode;
if ($sFromName == "_my") {
$aLastNodeMy = $aNode;
}
$aLastNode = $aNode;
}
$aSloganNode = $aLastNode;
if ($aLastNodeMy) {
$aSloganNode = $aLastNodeMy;
}
$aNodeGroupCountry["{{slogan_siteUrl}}"][] = $aSloganNode;
$aNodeGroupCountry["{{slogan_telegram}}"][] = $aSloganNode;
$aNodeGroupCountry["{{slogan_date}}"][] = $aSloganNode;
$aNodeSortCountry = [];
foreach (self::$aCountryOrder as $sCountryOrder) {
if (array_key_exists($sCountryOrder, $aNodeGroupCountry)) {
if (self::isSlogan($sCountryOrder)) {
$aNodeSortCountry[$sCountryOrder] = [
$aNodeGroupCountry[$sCountryOrder][0] // 得到多个slogan只要第一个按理不用处理保险
];
} else {
$aNodeSortCountry[$sCountryOrder] = $aNodeGroupCountry[$sCountryOrder];
}
}
}
foreach ($aNodeGroupCountry as $sCountry => $aNode) {
if (!array_key_exists($sCountry, self::$aCountryOrder)) {
$aNodeSortCountry[$sCountry] = $aNode;
}
}
$aNodeMerge = [];
$aNodeCountryList = [];
foreach ($aNodeSortCountry as $sCountry => $aNodeList) {
$i = 1;
foreach ($aNodeList as $aNode) {
if (self::isSlogan($sCountry)) {
$aNode["country"] = $sCountry;
} else {
$aNode["country"] = $sCountry." ".sprintf('%02d', $i);
}
$aNode["country"] = $aNode["country"]." :[".$aNode["fromName"]."]:";
if ($sPoolClient == "v2ray") {
$sConfig = $aNode["config"];
if (is_array($sConfig)) {
$sConfig = json_encode($sConfig);
}
$sNode = $aNode["protocolType"]."://".$sConfig."#".urlencode($aNode["country"]);
$aNodeMerge[] = $sNode;
} else if ($sPoolClient == "clash") {
$sFlagCountry = self::countryJoinFlag($aNode["country"]);
$aNode["config"]["name"] = $sFlagCountry;
$aNodeMerge[] = $aNode["config"];
$aNodeCountryList[] = $sFlagCountry;
} else {
$aNodeMerge[] = json_encode($aNode);
}
$i++;
}
}
if ($sPoolClient == "v2ray") {
$sMerge = implode("\n", $aNodeMerge);
$sMerge = base64_encode($sMerge);
} else if ($sPoolClient == "clash") {
$sFile = $sDirBase."public/freenode/template/clash.txt";
if (file_exists($sFile) && is_readable($sFile)) {
$sClashTemplate = file_get_contents($sFile); // data
} else {
$sMsg = "clash的template不存在这不应该 \n";
TeleSlave::warn()->send($sMsg);
echo $sMsg;
continue;
}
$aClashTemplate = Yaml::parse($sClashTemplate);
$aClashTemplate["proxies"] = $aNodeMerge;
$aProxyGroupList = &$aClashTemplate["proxy-groups"];
foreach ($aProxyGroupList as &$aProxyGroup) {
if (!isset($aProxyGroup["proxies"])) {
$aProxyGroup["proxies"] = [];
}
if ($aProxyGroup["name"] == "🍃 应用净化") {
continue;
}
if ($aProxyGroup["name"] == "🛑 全球拦截") {
continue;
}
if ($aProxyGroup["name"] == "🎯 全球直连") {
continue;
}
$aProxyGroup["proxies"] = self::arrMerge($aProxyGroup["proxies"], $aNodeCountryList);
}
$sMerge = Yaml::dump($aClashTemplate, 4);
}
$sFileDir = $sDirBase."public/freenode/merge/base/".$sPoolClient;
if (!is_dir($sFileDir)) {
mkdir($sFileDir, 0777, true);
}
$sFilePath = $sFileDir."/".$sDate.".txt"; // 都是txt解析的时候另处理
file_put_contents($sFilePath, $sMerge);
echo " - base::".$sFilePath." 已保存 \n";
}
}
public static function baseToSite()
{
echo "\nFreenodeMerge::baseToSite - 开始 \n";
$sDate = date("Y-m-d");
// $sDateCode = date("Ymd");
// $sDate = "2025-12-12"; // *test
$sDirBase = $_ENV["dir_base"];
$sFnClientList = ModelOption::_hit("freenode_client-fn_a");
if (!$sFnClientList) {
throw new \Exception("未设置option的freenode_client");
}
$aFnClientList = explode(",", $sFnClientList);
$aMerge = [];
foreach ($aFnClientList as $sFnClient) {
$sMergeFilePath = $sDirBase."public/freenode/merge/base/".$sFnClient."/".$sDate.".txt";
if (file_exists($sMergeFilePath)) {
$aMerge[$sFnClient] = file_get_contents($sMergeFilePath);
} else {
throw new \Exception("baseToSite里没找到merge文件 - $sMergeFilePath");
}
}
$oSiteMap = ModelSiteMap::where("group_code", "fn_a")->get();
if ($oSiteMap->isEmpty()) {
throw new \Exception("没找到siteMap的fn_a组");
}
$aSiteMap = $oSiteMap->toArray();
$aSiteMap[] = [
"code" => "_admin",
"url" => "_admin.loc",
"name" => "_admin",
];
$iH4Rand = rand(1, 21600); // 6小时
$iH4Time = time() - $iH4Rand;
$sSloganDate = date("Y-m-d H:i:s", $iH4Time);
foreach ($aSiteMap as $aSiteMapRow) {
$aSiteConfig = json_decode($aSiteMapRow["config"] ?? "", true);
$sSiteConfigTelegram = $aSiteConfig["telegram"] ?? "";
$sSiteConfigSloganUrl = $aSiteConfig["freenode_slogan"]["siteUrl"] ?? "";
$sSiteConfigSloganTelegram = $aSiteConfig["freenode_slogan"]["telegram"] ?? "";
if ($sSiteConfigSloganUrl) {
$sSlogan_siteUrl = str_replace('{{siteUrl}}', $aSiteMapRow["url"], $sSiteConfigSloganUrl);
} else {
$sSlogan_siteUrl = "美国".uniqid();
}
if ($sSiteConfigSloganTelegram) {
$sSlogan_telegram = str_replace('{{telegram}}', $sSiteConfigTelegram, $sSiteConfigSloganTelegram);
} else {
$sSlogan_telegram = "美国".uniqid();
}
foreach ($aMerge as $sClient => $sFeedContent) {
$sFileSaveDir = $sDirBase."public/freenode/merge/".$aSiteMapRow["code"]."/".$sClient;
if (!is_dir($sFileSaveDir)) {
mkdir($sFileSaveDir, 0777, true);
}
$sFileSavePath = $sFileSaveDir."/".$sDate.".txt";
if ($sClient == "v2ray") {
$sFeedContent = self::v2rayFeedToStr($sFeedContent);
}
$sFeedContent = str_replace('{{slogan_siteUrl}}', $sSlogan_siteUrl, $sFeedContent);
$sFeedContent = str_replace('{{slogan_telegram}}', $sSlogan_telegram, $sFeedContent);
$sFeedContent = str_replace('{{slogan_date}}', "更新于:".$sSloganDate, $sFeedContent);
if ($aSiteMapRow["code"] !== "_admin") {
$sFeedContent = preg_replace('/ :\[.*?\]:/su', '', $sFeedContent);
}
if ($sClient == "v2ray") {
$sFeedContent = self::v2rayFeedByStr($sFeedContent);
}
file_put_contents($sFileSavePath, $sFeedContent);
echo " - fn的".$aSiteMapRow["code"]."{$sClient}{$sDate} 保存成功 \n";
}
}
return true;
}
public static function v2rayFeedByStr($s)
{
$a = explode("\n", $s);
$aFeed = [];
foreach ($a as $sConfig) {
if (!$sConfig) {continue;}
$aTmp = CronFreenodePool::v2ray_chai($sConfig);
$sConfig = $aTmp[0];
$sName = $aTmp[1];
$sName = rawurlencode($sName);
$aFeed[] = $sConfig."#".$sName;
}
$sFeed = implode("\n", $aFeed);
$sFeed = base64_encode($sFeed);
return $sFeed;
}
public static function v2rayFeedToStr($sV2rayFeed)
{
$sV2rayFeed = base64_decode($sV2rayFeed);
$aV2rayFeed = explode("\n", $sV2rayFeed);
$aFeed = [];
foreach ($aV2rayFeed as $sConfig) {
if (!$sConfig) {continue;}
$aTmp = CronFreenodePool::v2ray_chai($sConfig);
$sConfig = $aTmp[0];
$sName = $aTmp[1];
$sName = urldecode($sName);
$aFeed[] = $sConfig."#".$sName;
}
$sFeed = implode("\n", $aFeed);
return $sFeed;
}
private static function arrMerge($aBase, $aNew)
{
$aMerge = array_merge($aBase, $aNew);
$aMerge = array_unique($aMerge);
$aMerge = array_values($aMerge);
return $aMerge;
}
public static function strReplace($sStr, $sFromName)
{
if (preg_match('/机场/u', $sStr)) {
tomd($sStr);
$sStr = "美国"; // 原本的slogan直接视为美国
}
if (preg_match('/频道/u', $sStr)) {
$sStr = "美国";
}
if ($sFromName == "ripaojiedian") {
$sStr = preg_replace('/\|\@ripaojiedian/', '', $sStr);
} else if ($sFromName == "stairnode") {
$sStr = preg_replace('/\|\@stairnode/', '', $sStr);
} else {
// 其他
}
// $sStr = preg_replace('/\d+/', '', $sStr); // 去数字
// $sStr = preg_replace('/[\x{1F1E6}-\x{1F1FF}]{2}\s*/u', '', $sStr); // 去国旗
// $sStr = preg_replace('/[a-zA-Z_]+/', '', $sStr); // 去下划线和字母
if (preg_match('/\p{Han}/u', $sStr)) {
$sStr = preg_replace('/[^\p{Han}]/u', '', $sStr);// 只留中文
} else { // 没有中文,再处理一下
$sStr = preg_replace('/^.*HK.*$/i', '香港', $sStr);
}
return $sStr;
}
private static function countryJoinFlag($str)
{
$aCountryFlagList = self::$aCountryFlag;
$sFlag = "";
foreach ($aCountryFlagList as $sCountryName => $sCountryFlag) {
if (str_contains($str, $sCountryName)) {
$sFlag = $sCountryFlag;
}
}
if ($sFlag) {
$str = $sFlag." ".$str;
}
return $str;
}
}

View File

@ -0,0 +1,498 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
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;
final class FreenodePool
{
public static function save()
{
echo "FreenodePoll::save - 开始 \n";
$sDirBase = $_ENV["dir_base"];
$sDate = date("Y-m-d");
// $sDate = "2025-12-12"; // *test
$oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
if (!$oPool) {
$oPool = new ModelFreenodePool();
$oPool->date = $sDate;
$oPool->save();
}
$oFromList = ModelCrawlFreenode::where("status", 1)->get();
$aMerge = [];
foreach ($oFromList as $oFrom) {
$aFromFileReal = json_decode($oFrom->file_real, true);
foreach ($aFromFileReal as $sFromFileRealClient => $aFromFileName) {
$sFromFilePath = $sDirBase."public/freenode/from/".$oFrom->name."/".$aFromFileName;
if (file_exists($sFromFilePath) && is_readable($sFromFilePath)) {
$sFromFileContent = file_get_contents($sFromFilePath); // data
} else {
$sMsg = "file_real不存在这不应该 -> ".$sFromFilePath." \n";
TeleSlave::warn()->send($sMsg);
echo $sMsg;
continue;
}
$aNodeFormat = [
"fromName" => $oFrom->name,
"country" => "",
"protocolType" => "",
"config" => "",
];
if ($sFromFileRealClient == "v2ray") {
$aFromFileContent = explode("\n", $sFromFileContent);
foreach ($aFromFileContent as $sNode) {
if (!$sNode) {
continue;
}
$aTmp = explode("://", $sNode, 2);
$aNodeFormat["protocolType"] = $sProtocolType = $aTmp[0]; // data
$sConfig = $aTmp[1];
if ($sProtocolType == "vmess") {
$sConfig = base64_decode($sConfig);
$aConfig = json_decode($sConfig, true);
$aNodeFormat["country"] = $sCountry = $aConfig["ps"]; // data
$aConfig["ps"] = "";
$aNodeFormat["config"] = $aConfig; // data
$sConfigMd5 = md5(json_encode($aConfig));
} else if ($sProtocolType == "ss") {
$sConfig = urldecode($sConfig);
$aTmp = self::v2ray_chai($sConfig);
$aNodeFormat["config"] = $sConfig = $aTmp[0]; // data
$aNodeFormat["country"] = $sCountry = $aTmp[1]; // data
// if ($oFrom->group == "A") { // ripaojiedian系ss协议bug修复
// $aTmp = explode("@", $sConfig);
// $qian = $aTmp[0];
// $hou = $aTmp[1];
// $qian = base64_decode($qian);
// $he = $qian."@".$hou;
// $he = base64_encode($he);
// $sConfig = $he;
// $aNodeFormat["config"] = $sConfig;
// }
$sConfigMd5 = md5($sConfig);
} else {
$sConfig = urldecode($sConfig);
$aTmp = self::v2ray_chai($sConfig);
$aNodeFormat["config"] = $sConfig = $aTmp[0]; // data
$aNodeFormat["country"] = $sCountry = $aTmp[1]; // data
$sConfigMd5 = md5($sConfig);
}
$aMerge[$sFromFileRealClient][$sConfigMd5] = $aNodeFormat;
}
} else if ($sFromFileRealClient == "clash") {
$aFromFileContent = Yaml::parse($sFromFileContent);
$aFromFileContent = $aFromFileContent["proxies"];
foreach ($aFromFileContent as $aNode) {
$aNodeFormat["protocolType"] = $aNode["type"];
$aNodeFormat["country"] = $aNode["name"]; // data
$aNode["name"] = "";
$aNodeFormat["config"] = $aNode; // data
$sConfigMd5 = md5(json_encode($aNode));
$aMerge[$sFromFileRealClient][$sConfigMd5] = $aNodeFormat;
}
}
}
}
$oPool->content = $aMerge;
$oPool->save();
echo " - freenode_pool更新完成 \n";
}
// public static function contentFormat($aContentFrom, $sClientType)
// {
// $aDataMerge = [];
// foreach ($aContentFrom as $sFromName => $aContent) {
// foreach ($aContent as $mContent) {
// $aNodeData = [];
// $sProtocolType = "";
// $mConfig = "";
// $sCountry = "";
// $sConfig = "";
//
// if ($sClientType == "v2ray") {
// $aTmp = self::v2ray_chai($mContent);
// $sTmp = $aTmp[0] ?? "none";
// $sCountry = $aTmp[1] ?? "none"; // common
// $aTmp = explode("://", $sTmp, 2);
// $sProtocolType = $aTmp[0] ?? "none"; // common
// $mConfig = $aTmp[1] ?? "none"; // common
// $sConfig = $mConfig; // common
// } else if ($sClientType == "clash") {
// $sCountry = $mContent["name"]; // common
// $sProtocolType = $mContent["type"]; // common
// $mConfig = $mContent; // common
// $mConfig["name"] = "";
// $sConfig = json_encode($mContent); // common
// }
//
// $aNodeData = [
// "from" => $sFromName,
// "clientType" => $sClientType,
// "country" => $sCountry,
// "protocolType" => $sProtocolType,
// "config" => $mConfig,
// ];
//
// $sConfigMd5 = md5($sConfig);
//
// $aDataMerge[$sConfigMd5] = [$aNodeData];
//
//// tomd($mConfig);
//
//// tomd($sContent);
// }
// }
//
// tomd($aDataMerge, 1);
// }
public static function v2ray_chai($sFromContentRow)
{
$pos = strrpos($sFromContentRow, "#");
if ($pos !== false) {
$aTmp = [
substr($sFromContentRow, 0, $pos),
substr($sFromContentRow, $pos + 1)
];
} else {
$aTmp = [$sFromContentRow]; // 没有分隔符时,返回原字符串
}
return $aTmp;
}
// public static function save_old()
// {
// self::save_custom();
// self::save_v2ray();
// self::save_clash();
// }
// public static function save_clash()
// {
// $sDate = date("Y-m-d");
//
// $oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
//
// if (!$oPool) {
// $oPool = new ModelFreenodePool();
// $oPool->date = $sDate;
// $oPool->save();
// }
//
// if (!$oPool->content_custom) {
// $oPool->content_clash = "[]";
// $oPool->save();
// }
//
// $aPoolContent = json_decode($oPool->content_clash, true);
//
// $oFromList = ModelCrawlFreenode::where("status", 1)->get();
//
// foreach ($oFromList as $oFrom) {
// $sFromName = $oFrom->name;
// $aFromFileReal = json_decode($oFrom->file_real, true);
// $sFromFileClash = $aFromFileReal["clash"];
//
// $sFromFilePath = "freenode/from/".$sFromName."/".$sFromFileClash;
//
// if (file_exists($sFromFilePath) && is_readable($sFromFilePath)) {
// $sFromFileContent = file_get_contents($sFromFilePath);
//
// } else {
// $sMsg = "file_real不存在这不应该 -> ".json_encode($oFrom->toArray());
// TeleSlave::warn()->send($sMsg);
// echo $sMsg;
// continue;
// }
//
// $aFromFileContent = Yaml::parse($sFromFileContent);
//
// $aFn = $aFromFileContent["proxies"];
//
// $aPoolContent[$sFromName] = $aFn;
//
// $oPool->content_clash = $aPoolContent;
// $oPool->save();
//
// echo " - {$sFromName}的clash更新完成 \n";
// }
//
// }
// public static function save_v2ray()
// {
// $sDate = date("Y-m-d");
//
// $oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
//
// if (!$oPool) {
// $oPool = new ModelFreenodePool();
// $oPool->date = $sDate;
// $oPool->save();
// }
//
// if (!$oPool->content_custom) {
// $oPool->content_v2ray = "[]";
// $oPool->save();
// }
//
// $aPoolContent = json_decode($oPool->content_v2ray, true);
//
// $oFromList = ModelCrawlFreenode::where("status", 1)->get();
//
// foreach ($oFromList as $oFrom) {
// $sFromName = $oFrom->name;
// $aFromFileReal = json_decode($oFrom->file_real, true);
// $sFromFileV2ray = $aFromFileReal["v2ray"];
//
// $sFromFilePath = "freenode/from/".$sFromName."/".$sFromFileV2ray;
//
// if (file_exists($sFromFilePath) && is_readable($sFromFilePath)) {
// $sFromFileContent = file_get_contents($sFromFilePath);
//
// } else {
// $sMsg = "file_real不存在这不应该 -> ".json_encode($oFrom->toArray());
// TeleSlave::warn()->send($sMsg);
// echo $sMsg;
// continue;
// }
//
// $aFromFileContent = explode("\n", $sFromFileContent);
//
// foreach ($aFromFileContent as &$sFromFileContentRow) {
// if (!$sFromFileContentRow) {
// continue;
// }
//
//// $aTmp = explode("://", $sFromFileContentRow, 2);
//// $sVtype = $aTmp[0];
//// $sVconfig = $aTmp[1];
//// if ($sVtype == "vmess") {
//// $s = base64_decode($sVconfig);
//// tomd($s, 1);
//// }
//
// $sFromFileContentRow = urldecode($sFromFileContentRow);
// }
//
// $aPoolContent[$sFromName] = $aFromFileContent;
//
// $oPool->content_v2ray = $aPoolContent;
// $oPool->save();
//
// echo " - {$sFromName}的v2ray更新完成 \n";
// }
//
// }
public static function save_custom()
{
$sDate = date("Y-m-d");
$oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
if (!$oPool) {
$oPool = new ModelFreenodePool();
$oPool->date = $sDate;
$oPool->save();
}
if (!$oPool->content_custom) {
$oPool->content_custom = "[]";
$oPool->save();
}
$aPoolContent = json_decode($oPool->content_custom, true);
$oFromList = ModelCrawlFreenode::where("status", 1)->get();
foreach ($oFromList as $oFrom) {
$sFromName = $oFrom->name;
$aFromFileReal = json_decode($oFrom->file_real, true);
$sFromFileV2ray = $aFromFileReal["v2ray"];
$sFromFilePath = "freenode/from/".$sFromName."/".$sFromFileV2ray;
if (file_exists($sFromFilePath) && is_readable($sFromFilePath)) {
$sFromFileContent = file_get_contents($sFromFilePath);
} else {
$sMsg = "file_real不存在这不应该 -> ".json_encode($oFrom->toArray());
TeleSlave::warn()->send($sMsg);
echo $sMsg;
}
$aFromFileContent = explode("\n", $sFromFileContent);
$i = 0;
$aFnNew = [];
foreach ($aFromFileContent as $sFromFileContentRow) {
if (!$sFromFileContentRow) {
continue;
}
$ss = explode("://", $sFromFileContentRow, 2);
list($sProtocolType, $sFn) = explode("://", $sFromFileContentRow, 2);
$sMethod = $sProtocolType . "ToArr";
if (method_exists(__CLASS__, $sMethod)) {
$aFn = self::$sMethod($sFn);
} else {
TeleSlave::warn()->send("未设处理方式的协议 -> ".$sFromFileContentRow);
continue;
}
$aFnNew[] = $aFn;
}
$aPoolContent[$sFromName] = $aFnNew;
$oPool->content_custom = $aPoolContent;
$oPool->save();
echo " - {$sFromName}的custom更新完成 \n";
}
}
private static function vmessToArr($sStr)
{
$sStr = base64_decode($sStr);
$a = json_decode($sStr, true);
$a["vtype"] = "vmess";
return $a;
}
private static function ssToArr($sStr)
{
// YWVzLTI1Ni1jZmI6YW1hem9uc2tyMDU@63.180.254.10:443#8%E5%85%83%E8%80%81%E7%89%8C%E4%B8%93%E7%BA%BF%E6%9C%BA%E5%9C%BA%EF%BC%9Acczzuu.top
$sStr = urldecode($sStr);
list($sPwd, $sStr) = explode("@", $sStr, 2);
$sPwd = base64_decode($sPwd);
list($sCipher, $sPwd) = explode(":", $sPwd, 2);
list($sServer, $sStr) = explode("#", $sStr, 2);
list($sServer, $sPort) = explode(":", $sServer, 2);
$aPort = explode("/?", $sPort, 2);
$sPort = $aPort[0];
$sOption = $aPort[1] ?? "";
if ($sOption) {
$aOptionList = explode(";", $sOption);
} else {
$aOptionList = [];
}
$aOptionArr = [];
foreach ($aOptionList as $sOption) {
if (!$sOption) {
continue;
}
$aTmp = explode("=", $sOption, 2);
$k = $aTmp[0] ?? "";
$v = $aTmp[1] ?? "";
$aOptionArr[$k] = $v;
}
$sName = $sStr;
$a = [
"vtype" => "ss",
"server" => $sServer,
"port" => $sPort,
"cipher" => $sCipher,
"password" => $sPwd,
"name" => $sName,
"option" => $aOptionArr,
];
return $a;
}
private static function trojanToArr($sStr)
{
// 76d630f2af6619c4a5de0ef953df3c6a@58.152.110.154:443?allowInsecure=0&sni=www.nintendogames.net#%F0%9F%87%AD%F0%9F%87%B0%20%E9%A6%99%E6%B8%AF3%7C%40stairnode
$sStr = urldecode($sStr);
$aTmp = explode("@", $sStr, 2);
$sPwd = $aTmp[0] ?? "";
$sStr = $aTmp[1] ?? "";
$aTmp = explode("#", $sStr, 2);
$sStr = $aTmp[0] ?? "";
$sName = $aTmp[1] ?? "";
$aTmp = explode("?", $sStr, 2);
$sServer = $aTmp[0] ?? "";
$sStr = $aTmp[1] ?? "";
$aTmp = explode(":", $sServer, 2);
$sServer = $aTmp[0] ?? "";
$sPort = $aTmp[1] ?? "";
$aOptionList = explode("&", $sStr, 2);
$aOptionArr = [];
foreach ($aOptionList as $sOption) {
if (!$sOption) {
continue;
}
$aTmp = explode("=", $sOption, 2);
$k = $aTmp[0] ?? "";
$v = $aTmp[1] ?? "";
$aOptionArr[$k] = $v;
}
$a = [
"vtype" => "trojan",
"name" => $sName,
"server" => $sServer,
"port" => $sPort,
"password" => $sPwd,
"option" => $aOptionArr,
];
return $a;
}
// public static function show($sDate = "")
// {
// if (!$sDate) {
// $sDate = date("Y-m-d");
// }
//
// $oPool = ModelFreenodePool::whereDate("date", $sDate)->first();
//
// if (!$oPool) {
// return "没有?";
// }
//
// return $oPool->content_custom;
// }
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
use App\Models\SiteMap as ModelSiteMap;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
use App\Services\TomTool\HttpV2;
final class FreenodeSync
{
static function run()
{
echo "\nFeenodeSync::run() - 开始 \n";
$sDirBase = $_ENV["dir_base"];
$oFnSiteList = ModelSiteMap::where("group_code", "fn_a")->get();
$aResultAll = [];
foreach ($oFnSiteList as $oFnSite) {
$aSiteConfig = json_decode($oFnSite->config, true);
$aSiteConfigFnClient = $aSiteConfig["freenode_client"] ?? [];
$aSiteApi = json_decode($oFnSite->api_path, true);
$sSiteApiFreenodeReceive = $aSiteApi["freenode_receive"] ?? "";
$aFnContent = [];
foreach ($aSiteConfigFnClient as $aSiteConfigFnClientRow) {
$sFilePath = $sDirBase."public/freenode/merge/".$oFnSite->code."/".$aSiteConfigFnClientRow."/".date("Y-m-d").".txt";
if (file_exists($sFilePath)) {
$aFnContent[$aSiteConfigFnClientRow] = file_get_contents($sFilePath);
} else {
$sMsg = "freenodeSync的run没有当天文件".$sFilePath;
TeleSlave::warn()->send($sMsg);
}
}
$sApiUrl = "https://".$oFnSite->url."/".$sSiteApiFreenodeReceive;
$sResult = HttpV2::make($sApiUrl, "post")->setDataA($aFnContent)->enableJsonSend()->send();
$aResult = json_decode($sResult, true);
if (!isset($aResult["is_done"]) || $aResult["is_done"] != true) {
$sMsg = "freenodeSync::同步失败 ->".json_encode($oFnSite->toArray())."->".json_encode($aResult);
TeleSlave::warn()->setMsgS($sMsg)->send();
continue;
}
}
echo " - ok \n";
}
}

View File

@ -34,7 +34,7 @@ class Cron extends Base {
$oServicesCron = new ServicesCron();
$oServicesCron->setTestH('06');
$oServicesCron->setTestI('00');
$oServicesCron->setTestI('03');
$oServicesCron->setTestS('00');
if ($sType == "h") {

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ class SiteMap extends Base {
$this->oDog = new Dog(new ModelSiteMap());
$this->oDog->_setDogName("siteMap");
$this->oDog->_setPrimaryKey("code"); // 可选默认id
// $this->oDog->_setJsonField(["use_from"]);
$this->oDog->_setJsonField(["config"]);
// $this->oDog->_setStrField(["content"]);
// $this->aUpdate = $aUpdate;
}

View File

@ -1,5 +1,7 @@
<?php
// 版本25-12-12
declare(strict_types=1);
namespace App\Services\TomTool;
@ -19,7 +21,7 @@ use Exception;
HttpV2::make($url)->send()
进阶:
HttpV2::make($url)->aData($aData)->setJsonAs()->setAsync()->setMethod("get")->send();
HttpV2::make($url, "get")->setDataA($aData)->enableJsonAs()->enableAsync()->optMethod("get")->send();
action:
send()

View File

@ -1,5 +1,7 @@
<?php
// 版本 25/12/12
declare(strict_types=1);
namespace App\Services\TomTool\Telegram;
@ -64,12 +66,15 @@ class Slave
public bool $enableAsync = true;
private bool $enableSend = true;
// new
public static function start(string $sBotCode = 'base', string $sChatCode = 'base'): self
{
$oInstance = new self;
$oInstance->sBotCode = $sBotCode;
$oInstance->sChatCode = $sChatCode;
$oInstance->enableSend = filter_var($_ENV["tg_send_enable"] ?? true, FILTER_VALIDATE_BOOLEAN);
return $oInstance;
}
@ -193,6 +198,11 @@ class Slave
// action
public function send($xMsg = ''): Flow
{
if ($this->enableSend == false) {
$oFlow = Flow::start();
return $oFlow->done("不发送");
}
if ($xMsg) {
if (is_array($xMsg)) {
$this->setMsgA($xMsg);

View File

@ -11,6 +11,7 @@
"irazasyed/telegram-bot-sdk": "^3.15",
"laravel/framework": "^11.31",
"laravel/tinker": "^2.9",
"symfony/yaml": "^7.4",
"voku/anti-xss": "^4.1"
},
"require-dev": {

150
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c2355b2300bdecd7969bd0274b53bde4",
"content-hash": "0276bc3b1e98a844fc7c078b38dc44f8",
"packages": [
{
"name": "brick/math",
@ -5814,6 +5814,82 @@
],
"time": "2024-11-08T15:48:14+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810",
"reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0|^8.0"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.4.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-11-16T10:14:42+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "v2.2.7",
@ -8420,78 +8496,6 @@
],
"time": "2024-10-20T05:08:20+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "099581e99f557e9f16b43c5916c26380b54abb22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22",
"reference": "099581e99f557e9f16b43c5916c26380b54abb22",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.2.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-10-23T06:56:12+00:00"
},
{
"name": "theseer/tokenizer",
"version": "1.2.3",

10
fn的tg策略.md Normal file
View File

@ -0,0 +1,10 @@
汇集还是分散?
方案1各站的自页面和fn都slogan都指向自己
方案2各站的自页面指向自己fn的slogan指向master
方案3各站的自页面和fn的slogan都指向master
感觉还是方案1合理都指向自己
好像只该指向自己因为频道发布节点时应该要发送网站链接提高点击率等于提高排名你master就没法这么做
对,这是关键票
另外github弄纯公益节点不带广告纯分享然后在fn里加slogen应该会有搞头

19
fn逻辑.md Executable file
View File

@ -0,0 +1,19 @@
1.CronFreenodeCrawl :: 爬各来源根据crawl_freenode存到public/freenode/from/xxx文件。
数据:
v2ray只是base64_decode一下便于查看
clash没动纯原始数据
动机:
保存一份原始数据,合理
逻辑:
如果没有crawl到就直接忽视pool是直接找file_real
找到就更新file_real
也就意味着即便当天没更新到pool也会直接找file_real最后有效更新的
2.CronFreenodePool :: 读各来源文件根据crawl_freenode存到freenode_pool。
动机:
合并与格式化,成为标准通用数据
3.CronFreenodeMerge :: 读freenode_pool存到public/freenode/merge/base/xxx文件。
动机:
各site定制化生成最终fn文件

View File

@ -18,6 +18,7 @@ use App\Http\Controllers\Api\GoodController;
use App\Http\Controllers\Api\GithubController;
use App\Services\OpenApi\Telegram as ServiceOpenApiTelegram;
use App\Services\Article\Schema as ServiceArticleSchema;
use App\Http\Controllers\Api\FreenodeController as ApiFreenodeController;
//Route::get('/', [ArticleListController::class, 'fn'])->middleware('test');
// /api/telegram/post
@ -25,6 +26,7 @@ use App\Services\Article\Schema as ServiceArticleSchema;
Route::any('/api/telegram/post', [ServiceOpenApiTelegram::class, 'post']);
Route::any('/api/article/schema/build', [ServiceArticleSchema::class, 'api_buildArticle']);
Route::any('/api/article/schema/build/receive', [ServiceArticleSchema::class, 'api_build_receive']);
Route::get('/sub/{siteCode}/{client}/{date?}', [ApiFreenodeController::class, "sub"]);
Route::get('/cmd', [CmdController::class, 'index']);
Route::post('/cmd/send', [CmdController::class, 'ajaxSend']);

View File

@ -10,15 +10,17 @@ return array(
'App\\Http\\Controllers\\Api\\GithubController' => $baseDir . '/app/Http/Controllers/Api/GithubController.php',
'App\\Http\\Controllers\\Api\\GoodController' => $baseDir . '/app/Http/Controllers/Api/GoodController.php',
'App\\Http\\Controllers\\Api\\ShipController' => $baseDir . '/app/Http/Controllers/Api/ShipController.php',
'App\\Http\\Controllers\\Api\\TG\\HookController' => $baseDir . '/app/Http/Controllers/Api/TG/HookController.php',
'App\\Http\\Controllers\\Api\\Tg\\HookController' => $baseDir . '/app/Http/Controllers/Api/Tg/HookController.php',
'App\\Http\\Controllers\\CmdController' => $baseDir . '/app/Http/Controllers/CmdController.php',
'App\\Http\\Controllers\\CronController' => $baseDir . '/app/Http/Controllers/CronController.php',
'App\\Http\\Controllers\\SakaiController' => $baseDir . '/app/Http/Controllers/SakaiController.php',
'App\\Http\\Middleware\\Test' => $baseDir . '/app/Http/Middleware/Test.php',
'App\\Models\\Article' => $baseDir . '/app/Models/Article.php',
'App\\Models\\ArticleCache' => $baseDir . '/app/Models/ArticleCache.php',
'App\\Models\\ArticleCacheTime' => $baseDir . '/app/Models/ArticleCacheTime.php',
'App\\Models\\ArticleConfig' => $baseDir . '/app/Models/ArticleConfig.php',
'App\\Models\\ArticleOption' => $baseDir . '/app/Models/ArticleOption.php',
'App\\Models\\ArticleSchema' => $baseDir . '/app/Models/ArticleSchema.php',
'App\\Models\\ArticleSiteMapGroup' => $baseDir . '/app/Models/ArticleSiteMapGroup.php',
'App\\Models\\Blade' => $baseDir . '/app/Models/Blade.php',
'App\\Models\\BladeJctj' => $baseDir . '/app/Models/BladeJctj.php',
'App\\Models\\BladeJctjOption' => $baseDir . '/app/Models/BladeJctjOption.php',
@ -27,6 +29,7 @@ return array(
'App\\Models\\BookOption' => $baseDir . '/app/Models/BookOption.php',
'App\\Models\\BookPage' => $baseDir . '/app/Models/BookPage.php',
'App\\Models\\BoxGithubTag' => $baseDir . '/app/Models/BoxGithubTag.php',
'App\\Models\\CrawlFreenode' => $baseDir . '/app/Models/CrawlFreenode.php',
'App\\Models\\DogOption' => $baseDir . '/app/Models/DogOption.php',
'App\\Models\\DoveSite' => $baseDir . '/app/Models/DoveSite.php',
'App\\Models\\GoodPool' => $baseDir . '/app/Models/GoodPool.php',
@ -37,39 +40,58 @@ return array(
'App\\Models\\NoteOption' => $baseDir . '/app/Models/NoteOption.php',
'App\\Models\\Option' => $baseDir . '/app/Models/Option.php',
'App\\Models\\Ship' => $baseDir . '/app/Models/Ship.php',
'App\\Models\\SiteMap' => $baseDir . '/app/Models/SiteMap.php',
'App\\Models\\SitePathMap' => $baseDir . '/app/Models/SitePathMap.php',
'App\\Models\\TelegramKey' => $baseDir . '/app/Models/TelegramKey.php',
'App\\Providers\\AppServiceProvider' => $baseDir . '/app/Providers/AppServiceProvider.php',
'App\\Services\\Article' => $baseDir . '/app/Services/Article.php',
'App\\Services\\Article\\CacheTg' => $baseDir . '/app/Services/Article/CacheTg.php',
'App\\Services\\Article\\Cmd' => $baseDir . '/app/Services/Article/Cmd.php',
'App\\Services\\Article\\Schema' => $baseDir . '/app/Services/Article/Schema.php',
'App\\Services\\Cron' => $baseDir . '/app/Services/Cron.php',
'App\\Services\\Cron\\ArticleSchema' => $baseDir . '/app/Services/Cron/ArticleSchema.php',
'App\\Services\\Cron\\CrawlFreenode' => $baseDir . '/app/Services/Cron/CrawlFreenode.php',
'App\\Services\\Cron\\HttpQueue' => $baseDir . '/app/Services/Cron/HttpQueue.php',
'App\\Services\\HttpQueue' => $baseDir . '/app/Services/HttpQueue.php',
'App\\Services\\OpenApi\\Telegram' => $baseDir . '/app/Services/OpenApi/Telegram.php',
'App\\Services\\SDK\\Master\\Telegram' => $baseDir . '/app/Services/SDK/Master/Telegram.php',
'App\\Services\\TG\\Hook\\Dog' => $baseDir . '/app/Services/TG/Hook/Dog.php',
'App\\Services\\TG\\Hook\\Mod\\Article' => $baseDir . '/app/Services/TG/Hook/Mod/Article.php',
'App\\Services\\TG\\Hook\\Mod\\Base' => $baseDir . '/app/Services/TG/Hook/Mod/Base.php',
'App\\Services\\TG\\Hook\\Mod\\Blade' => $baseDir . '/app/Services/TG/Hook/Mod/Blade.php',
'App\\Services\\TG\\Hook\\Mod\\Book' => $baseDir . '/app/Services/TG/Hook/Mod/Book.php',
'App\\Services\\TG\\Hook\\Mod\\Box' => $baseDir . '/app/Services/TG/Hook/Mod/Box.php',
'App\\Services\\TG\\Hook\\Mod\\Cron' => $baseDir . '/app/Services/TG/Hook/Mod/Cron.php',
'App\\Services\\TG\\Hook\\Mod\\Db' => $baseDir . '/app/Services/TG/Hook/Mod/Db.php',
'App\\Services\\TG\\Hook\\Mod\\Dove' => $baseDir . '/app/Services/TG/Hook/Mod/Dove.php',
'App\\Services\\TG\\Hook\\Mod\\DoveSite' => $baseDir . '/app/Services/TG/Hook/Mod/DoveSite.php',
'App\\Services\\TG\\Hook\\Mod\\Good' => $baseDir . '/app/Services/TG/Hook/Mod/Good.php',
'App\\Services\\TG\\Hook\\Mod\\Hook' => $baseDir . '/app/Services/TG/Hook/Mod/Hook.php',
'App\\Services\\TG\\Hook\\Mod\\Jctj' => $baseDir . '/app/Services/TG/Hook/Mod/Jctj.php',
'App\\Services\\TG\\Hook\\Mod\\JctjOption' => $baseDir . '/app/Services/TG/Hook/Mod/JctjOption.php',
'App\\Services\\TG\\Hook\\Mod\\Note' => $baseDir . '/app/Services/TG/Hook/Mod/Note.php',
'App\\Services\\TG\\Hook\\Mod\\Option' => $baseDir . '/app/Services/TG/Hook/Mod/Option.php',
'App\\Services\\TG\\Hook\\Mod\\Ship' => $baseDir . '/app/Services/TG/Hook/Mod/Ship.php',
'App\\Services\\TG\\Hook\\Mod\\Test1' => $baseDir . '/app/Services/TG/Hook/Mod/Test1.php',
'App\\Services\\TG\\Hook\\Mod\\Tool' => $baseDir . '/app/Services/TG/Hook/Mod/Tool.php',
'App\\Services\\TG\\Hook\\Mod\\Upload' => $baseDir . '/app/Services/TG/Hook/Mod/Upload.php',
'App\\Services\\TG\\Http' => $baseDir . '/app/Services/TG/Http.php',
'App\\Services\\SlaveMap' => $baseDir . '/app/Services/SlaveMap.php',
'App\\Services\\Tg\\Hook\\Dog' => $baseDir . '/app/Services/Tg/Hook/Dog.php',
'App\\Services\\Tg\\Hook\\Mod\\Article' => $baseDir . '/app/Services/Tg/Hook/Mod/Article.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleCache' => $baseDir . '/app/Services/Tg/Hook/Mod/ArticleCache.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleConfig' => $baseDir . '/app/Services/Tg/Hook/Mod/ArticleConfig.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleSchema' => $baseDir . '/app/Services/Tg/Hook/Mod/ArticleSchema.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleSiteMapGroup' => $baseDir . '/app/Services/Tg/Hook/Mod/ArticleSiteMapGroup.php',
'App\\Services\\Tg\\Hook\\Mod\\Base' => $baseDir . '/app/Services/Tg/Hook/Mod/Base.php',
'App\\Services\\Tg\\Hook\\Mod\\Blade' => $baseDir . '/app/Services/Tg/Hook/Mod/Blade.php',
'App\\Services\\Tg\\Hook\\Mod\\Book' => $baseDir . '/app/Services/Tg/Hook/Mod/Book.php',
'App\\Services\\Tg\\Hook\\Mod\\Box' => $baseDir . '/app/Services/Tg/Hook/Mod/Box.php',
'App\\Services\\Tg\\Hook\\Mod\\Cron' => $baseDir . '/app/Services/Tg/Hook/Mod/Cron.php',
'App\\Services\\Tg\\Hook\\Mod\\Db' => $baseDir . '/app/Services/Tg/Hook/Mod/Db.php',
'App\\Services\\Tg\\Hook\\Mod\\Dev' => $baseDir . '/app/Services/Tg/Hook/Mod/Dev.php',
'App\\Services\\Tg\\Hook\\Mod\\Dove' => $baseDir . '/app/Services/Tg/Hook/Mod/Dove.php',
'App\\Services\\Tg\\Hook\\Mod\\DoveSite' => $baseDir . '/app/Services/Tg/Hook/Mod/DoveSite.php',
'App\\Services\\Tg\\Hook\\Mod\\Good' => $baseDir . '/app/Services/Tg/Hook/Mod/Good.php',
'App\\Services\\Tg\\Hook\\Mod\\Hook' => $baseDir . '/app/Services/Tg/Hook/Mod/Hook.php',
'App\\Services\\Tg\\Hook\\Mod\\Jctj' => $baseDir . '/app/Services/Tg/Hook/Mod/Jctj.php',
'App\\Services\\Tg\\Hook\\Mod\\JctjOption' => $baseDir . '/app/Services/Tg/Hook/Mod/JctjOption.php',
'App\\Services\\Tg\\Hook\\Mod\\Note' => $baseDir . '/app/Services/Tg/Hook/Mod/Note.php',
'App\\Services\\Tg\\Hook\\Mod\\Option' => $baseDir . '/app/Services/Tg/Hook/Mod/Option.php',
'App\\Services\\Tg\\Hook\\Mod\\Ship' => $baseDir . '/app/Services/Tg/Hook/Mod/Ship.php',
'App\\Services\\Tg\\Hook\\Mod\\SiteMap' => $baseDir . '/app/Services/Tg/Hook/Mod/SiteMap.php',
'App\\Services\\Tg\\Hook\\Mod\\SitePathMap' => $baseDir . '/app/Services/Tg/Hook/Mod/SitePathMap.php',
'App\\Services\\Tg\\Hook\\Mod\\TelegramKey' => $baseDir . '/app/Services/Tg/Hook/Mod/TelegramKey.php',
'App\\Services\\Tg\\Hook\\Mod\\Test' => $baseDir . '/app/Services/Tg/Hook/Mod/Test.php',
'App\\Services\\Tg\\Hook\\Mod\\Tool' => $baseDir . '/app/Services/Tg/Hook/Mod/Tool.php',
'App\\Services\\Tg\\Hook\\Mod\\Upload' => $baseDir . '/app/Services/Tg/Hook/Mod/Upload.php',
'App\\Services\\Tg\\Http' => $baseDir . '/app/Services/Tg/Http.php',
'App\\Services\\TomTool\\Flow' => $baseDir . '/app/Services/TomTool/Flow.php',
'App\\Services\\TomTool\\Http' => $baseDir . '/app/Services/TomTool/Http.php',
'App\\Services\\TomTool\\HttpV2' => $baseDir . '/app/Services/TomTool/HttpV2.php',
'App\\Services\\TomTool\\Result' => $baseDir . '/app/Services/TomTool/Result.php',
'App\\Services\\TomTool\\TelegramVdb' => $baseDir . '/app/Services/TomTool/TelegramVdb.php',
'App\\Services\\TomTool\\Telegram\\Master' => $baseDir . '/app/Services/TomTool/Telegram/Master.php',
'App\\Services\\TomTool\\Telegram\\Sdk' => $baseDir . '/app/Services/TomTool/Telegram/Sdk.php',
'App\\Services\\TomTool\\Telegram\\Slave' => $baseDir . '/app/Services/TomTool/Telegram/Slave.php',
'App\\Services\\TomTool\\ThrowableHandler' => $baseDir . '/app/Services/TomTool/ThrowableHandler.php',
'App\\Services\\TomTool\\Tom' => $baseDir . '/app/Services/TomTool/Tom.php',
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php',
'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php',

View File

@ -543,15 +543,17 @@ class ComposerStaticInit626b9e7ddd47fb7eff9aaa53cce0c9ad
'App\\Http\\Controllers\\Api\\GithubController' => __DIR__ . '/../..' . '/app/Http/Controllers/Api/GithubController.php',
'App\\Http\\Controllers\\Api\\GoodController' => __DIR__ . '/../..' . '/app/Http/Controllers/Api/GoodController.php',
'App\\Http\\Controllers\\Api\\ShipController' => __DIR__ . '/../..' . '/app/Http/Controllers/Api/ShipController.php',
'App\\Http\\Controllers\\Api\\TG\\HookController' => __DIR__ . '/../..' . '/app/Http/Controllers/Api/TG/HookController.php',
'App\\Http\\Controllers\\Api\\Tg\\HookController' => __DIR__ . '/../..' . '/app/Http/Controllers/Api/Tg/HookController.php',
'App\\Http\\Controllers\\CmdController' => __DIR__ . '/../..' . '/app/Http/Controllers/CmdController.php',
'App\\Http\\Controllers\\CronController' => __DIR__ . '/../..' . '/app/Http/Controllers/CronController.php',
'App\\Http\\Controllers\\SakaiController' => __DIR__ . '/../..' . '/app/Http/Controllers/SakaiController.php',
'App\\Http\\Middleware\\Test' => __DIR__ . '/../..' . '/app/Http/Middleware/Test.php',
'App\\Models\\Article' => __DIR__ . '/../..' . '/app/Models/Article.php',
'App\\Models\\ArticleCache' => __DIR__ . '/../..' . '/app/Models/ArticleCache.php',
'App\\Models\\ArticleCacheTime' => __DIR__ . '/../..' . '/app/Models/ArticleCacheTime.php',
'App\\Models\\ArticleConfig' => __DIR__ . '/../..' . '/app/Models/ArticleConfig.php',
'App\\Models\\ArticleOption' => __DIR__ . '/../..' . '/app/Models/ArticleOption.php',
'App\\Models\\ArticleSchema' => __DIR__ . '/../..' . '/app/Models/ArticleSchema.php',
'App\\Models\\ArticleSiteMapGroup' => __DIR__ . '/../..' . '/app/Models/ArticleSiteMapGroup.php',
'App\\Models\\Blade' => __DIR__ . '/../..' . '/app/Models/Blade.php',
'App\\Models\\BladeJctj' => __DIR__ . '/../..' . '/app/Models/BladeJctj.php',
'App\\Models\\BladeJctjOption' => __DIR__ . '/../..' . '/app/Models/BladeJctjOption.php',
@ -560,6 +562,7 @@ class ComposerStaticInit626b9e7ddd47fb7eff9aaa53cce0c9ad
'App\\Models\\BookOption' => __DIR__ . '/../..' . '/app/Models/BookOption.php',
'App\\Models\\BookPage' => __DIR__ . '/../..' . '/app/Models/BookPage.php',
'App\\Models\\BoxGithubTag' => __DIR__ . '/../..' . '/app/Models/BoxGithubTag.php',
'App\\Models\\CrawlFreenode' => __DIR__ . '/../..' . '/app/Models/CrawlFreenode.php',
'App\\Models\\DogOption' => __DIR__ . '/../..' . '/app/Models/DogOption.php',
'App\\Models\\DoveSite' => __DIR__ . '/../..' . '/app/Models/DoveSite.php',
'App\\Models\\GoodPool' => __DIR__ . '/../..' . '/app/Models/GoodPool.php',
@ -570,39 +573,58 @@ class ComposerStaticInit626b9e7ddd47fb7eff9aaa53cce0c9ad
'App\\Models\\NoteOption' => __DIR__ . '/../..' . '/app/Models/NoteOption.php',
'App\\Models\\Option' => __DIR__ . '/../..' . '/app/Models/Option.php',
'App\\Models\\Ship' => __DIR__ . '/../..' . '/app/Models/Ship.php',
'App\\Models\\SiteMap' => __DIR__ . '/../..' . '/app/Models/SiteMap.php',
'App\\Models\\SitePathMap' => __DIR__ . '/../..' . '/app/Models/SitePathMap.php',
'App\\Models\\TelegramKey' => __DIR__ . '/../..' . '/app/Models/TelegramKey.php',
'App\\Providers\\AppServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AppServiceProvider.php',
'App\\Services\\Article' => __DIR__ . '/../..' . '/app/Services/Article.php',
'App\\Services\\Article\\CacheTg' => __DIR__ . '/../..' . '/app/Services/Article/CacheTg.php',
'App\\Services\\Article\\Cmd' => __DIR__ . '/../..' . '/app/Services/Article/Cmd.php',
'App\\Services\\Article\\Schema' => __DIR__ . '/../..' . '/app/Services/Article/Schema.php',
'App\\Services\\Cron' => __DIR__ . '/../..' . '/app/Services/Cron.php',
'App\\Services\\Cron\\ArticleSchema' => __DIR__ . '/../..' . '/app/Services/Cron/ArticleSchema.php',
'App\\Services\\Cron\\CrawlFreenode' => __DIR__ . '/../..' . '/app/Services/Cron/CrawlFreenode.php',
'App\\Services\\Cron\\HttpQueue' => __DIR__ . '/../..' . '/app/Services/Cron/HttpQueue.php',
'App\\Services\\HttpQueue' => __DIR__ . '/../..' . '/app/Services/HttpQueue.php',
'App\\Services\\OpenApi\\Telegram' => __DIR__ . '/../..' . '/app/Services/OpenApi/Telegram.php',
'App\\Services\\SDK\\Master\\Telegram' => __DIR__ . '/../..' . '/app/Services/SDK/Master/Telegram.php',
'App\\Services\\TG\\Hook\\Dog' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Dog.php',
'App\\Services\\TG\\Hook\\Mod\\Article' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Article.php',
'App\\Services\\TG\\Hook\\Mod\\Base' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Base.php',
'App\\Services\\TG\\Hook\\Mod\\Blade' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Blade.php',
'App\\Services\\TG\\Hook\\Mod\\Book' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Book.php',
'App\\Services\\TG\\Hook\\Mod\\Box' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Box.php',
'App\\Services\\TG\\Hook\\Mod\\Cron' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Cron.php',
'App\\Services\\TG\\Hook\\Mod\\Db' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Db.php',
'App\\Services\\TG\\Hook\\Mod\\Dove' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Dove.php',
'App\\Services\\TG\\Hook\\Mod\\DoveSite' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/DoveSite.php',
'App\\Services\\TG\\Hook\\Mod\\Good' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Good.php',
'App\\Services\\TG\\Hook\\Mod\\Hook' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Hook.php',
'App\\Services\\TG\\Hook\\Mod\\Jctj' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Jctj.php',
'App\\Services\\TG\\Hook\\Mod\\JctjOption' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/JctjOption.php',
'App\\Services\\TG\\Hook\\Mod\\Note' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Note.php',
'App\\Services\\TG\\Hook\\Mod\\Option' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Option.php',
'App\\Services\\TG\\Hook\\Mod\\Ship' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Ship.php',
'App\\Services\\TG\\Hook\\Mod\\Test1' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Test1.php',
'App\\Services\\TG\\Hook\\Mod\\Tool' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Tool.php',
'App\\Services\\TG\\Hook\\Mod\\Upload' => __DIR__ . '/../..' . '/app/Services/TG/Hook/Mod/Upload.php',
'App\\Services\\TG\\Http' => __DIR__ . '/../..' . '/app/Services/TG/Http.php',
'App\\Services\\SlaveMap' => __DIR__ . '/../..' . '/app/Services/SlaveMap.php',
'App\\Services\\Tg\\Hook\\Dog' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Dog.php',
'App\\Services\\Tg\\Hook\\Mod\\Article' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Article.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleCache' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/ArticleCache.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleConfig' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/ArticleConfig.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleSchema' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/ArticleSchema.php',
'App\\Services\\Tg\\Hook\\Mod\\ArticleSiteMapGroup' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/ArticleSiteMapGroup.php',
'App\\Services\\Tg\\Hook\\Mod\\Base' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Base.php',
'App\\Services\\Tg\\Hook\\Mod\\Blade' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Blade.php',
'App\\Services\\Tg\\Hook\\Mod\\Book' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Book.php',
'App\\Services\\Tg\\Hook\\Mod\\Box' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Box.php',
'App\\Services\\Tg\\Hook\\Mod\\Cron' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Cron.php',
'App\\Services\\Tg\\Hook\\Mod\\Db' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Db.php',
'App\\Services\\Tg\\Hook\\Mod\\Dev' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Dev.php',
'App\\Services\\Tg\\Hook\\Mod\\Dove' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Dove.php',
'App\\Services\\Tg\\Hook\\Mod\\DoveSite' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/DoveSite.php',
'App\\Services\\Tg\\Hook\\Mod\\Good' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Good.php',
'App\\Services\\Tg\\Hook\\Mod\\Hook' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Hook.php',
'App\\Services\\Tg\\Hook\\Mod\\Jctj' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Jctj.php',
'App\\Services\\Tg\\Hook\\Mod\\JctjOption' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/JctjOption.php',
'App\\Services\\Tg\\Hook\\Mod\\Note' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Note.php',
'App\\Services\\Tg\\Hook\\Mod\\Option' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Option.php',
'App\\Services\\Tg\\Hook\\Mod\\Ship' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Ship.php',
'App\\Services\\Tg\\Hook\\Mod\\SiteMap' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/SiteMap.php',
'App\\Services\\Tg\\Hook\\Mod\\SitePathMap' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/SitePathMap.php',
'App\\Services\\Tg\\Hook\\Mod\\TelegramKey' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/TelegramKey.php',
'App\\Services\\Tg\\Hook\\Mod\\Test' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Test.php',
'App\\Services\\Tg\\Hook\\Mod\\Tool' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Tool.php',
'App\\Services\\Tg\\Hook\\Mod\\Upload' => __DIR__ . '/../..' . '/app/Services/Tg/Hook/Mod/Upload.php',
'App\\Services\\Tg\\Http' => __DIR__ . '/../..' . '/app/Services/Tg/Http.php',
'App\\Services\\TomTool\\Flow' => __DIR__ . '/../..' . '/app/Services/TomTool/Flow.php',
'App\\Services\\TomTool\\Http' => __DIR__ . '/../..' . '/app/Services/TomTool/Http.php',
'App\\Services\\TomTool\\HttpV2' => __DIR__ . '/../..' . '/app/Services/TomTool/HttpV2.php',
'App\\Services\\TomTool\\Result' => __DIR__ . '/../..' . '/app/Services/TomTool/Result.php',
'App\\Services\\TomTool\\TelegramVdb' => __DIR__ . '/../..' . '/app/Services/TomTool/TelegramVdb.php',
'App\\Services\\TomTool\\Telegram\\Master' => __DIR__ . '/../..' . '/app/Services/TomTool/Telegram/Master.php',
'App\\Services\\TomTool\\Telegram\\Sdk' => __DIR__ . '/../..' . '/app/Services/TomTool/Telegram/Sdk.php',
'App\\Services\\TomTool\\Telegram\\Slave' => __DIR__ . '/../..' . '/app/Services/TomTool/Telegram/Slave.php',
'App\\Services\\TomTool\\ThrowableHandler' => __DIR__ . '/../..' . '/app/Services/TomTool/ThrowableHandler.php',
'App\\Services\\TomTool\\Tom' => __DIR__ . '/../..' . '/app/Services/TomTool/Tom.php',
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php',
'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php',

View File

@ -8282,31 +8282,31 @@
},
{
"name": "symfony/yaml",
"version": "v7.2.0",
"version_normalized": "7.2.0.0",
"version": "v7.4.0",
"version_normalized": "7.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "099581e99f557e9f16b43c5916c26380b54abb22"
"reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22",
"reference": "099581e99f557e9f16b43c5916c26380b54abb22",
"url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810",
"reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0"
"symfony/console": "^6.4|^7.0|^8.0"
},
"time": "2024-10-23T06:56:12+00:00",
"time": "2025-11-16T10:14:42+00:00",
"bin": [
"Resources/bin/yaml-lint"
],
@ -8337,7 +8337,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.2.0"
"source": "https://github.com/symfony/yaml/tree/v7.4.0"
},
"funding": [
{
@ -8348,6 +8348,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@ -8917,7 +8921,6 @@
"sebastian/type",
"sebastian/version",
"staabm/side-effects-detector",
"symfony/yaml",
"theseer/tokenizer"
]
}

View File

@ -3,7 +3,7 @@
'name' => 'laravel/laravel',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '01b8e3cd12bcd4f049e32ce5ec54aea8c40fae41',
'reference' => '6843f40b786f201ee5dec7fe59e77678399fbc3a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -397,7 +397,7 @@
'laravel/laravel' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '01b8e3cd12bcd4f049e32ce5ec54aea8c40fae41',
'reference' => '6843f40b786f201ee5dec7fe59e77678399fbc3a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -1293,13 +1293,13 @@
'dev_requirement' => false,
),
'symfony/yaml' => array(
'pretty_version' => 'v7.2.0',
'version' => '7.2.0.0',
'reference' => '099581e99f557e9f16b43c5916c26380b54abb22',
'pretty_version' => 'v7.4.0',
'version' => '7.4.0.0',
'reference' => '6c84a4b55aee4cd02034d1c528e83f69ddf63810',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/yaml',
'aliases' => array(),
'dev_requirement' => true,
'dev_requirement' => false,
),
'theseer/tokenizer' => array(
'pretty_version' => '1.2.3',

7
vendor/symfony/yaml/CHANGELOG.md vendored Executable file → Normal file
View File

@ -1,10 +1,17 @@
CHANGELOG
=========
7.3
---
* Add compact nested mapping support by using the `Yaml::DUMP_COMPACT_NESTED_MAPPING` flag
* Add the `Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES` flag to enforce double quotes around string values
7.2
---
* Deprecate parsing duplicate mapping keys whose value is `null`
* Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag
7.1
---

28
vendor/symfony/yaml/Command/LintCommand.php vendored Executable file → Normal file
View File

@ -58,30 +58,30 @@ class LintCommand extends Command
->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude')
->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null)
->setHelp(<<<EOF
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
the first encountered syntax error.
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
the first encountered syntax error.
You can validates YAML contents passed from STDIN:
You can validates YAML contents passed from STDIN:
<info>cat filename | php %command.full_name% -</info>
<info>cat filename | php %command.full_name% -</info>
You can also validate the syntax of a file:
You can also validate the syntax of a file:
<info>php %command.full_name% filename</info>
<info>php %command.full_name% filename</info>
Or of a whole directory:
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname</info>
The <info>--format</info> option specifies the format of the command output:
The <info>--format</info> option specifies the format of the command output:
<info>php %command.full_name% dirname --format=json</info>
<info>php %command.full_name% dirname --format=json</info>
You can also exclude one or more specific files:
You can also exclude one or more specific files:
<info>php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml"</info>
<info>php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml"</info>
EOF
EOF
)
;
}
@ -224,7 +224,7 @@ EOF
}
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
if (!\in_array($file->getExtension(), ['yml', 'yaml'])) {
if (!\in_array($file->getExtension(), ['yml', 'yaml'], true)) {
continue;
}

28
vendor/symfony/yaml/Dumper.php vendored Executable file → Normal file
View File

@ -41,6 +41,15 @@ class Dumper
* @param int-mask-of<Yaml::DUMP_*> $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
*/
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
{
if ($flags & Yaml::DUMP_NULL_AS_EMPTY && $flags & Yaml::DUMP_NULL_AS_TILDE) {
throw new \InvalidArgumentException('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.');
}
return $this->doDump($input, $inline, $indent, $flags);
}
private function doDump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0, int $nestingLevel = 0): string
{
$output = '';
$prefix = $indent ? str_repeat(' ', $indent) : '';
@ -51,11 +60,12 @@ class Dumper
}
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) {
$output .= $prefix.Inline::dump($input, $flags);
$output .= $prefix.Inline::dump($input, $flags, 0 === $nestingLevel);
} elseif ($input instanceof TaggedValue) {
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix, $nestingLevel);
} else {
$dumpAsMap = Inline::isHash($input);
$compactNestedMapping = Yaml::DUMP_COMPACT_NESTED_MAPPING & $flags && !$dumpAsMap;
foreach ($input as $key => $value) {
if ('' !== $output && "\n" !== $output[-1]) {
@ -105,10 +115,10 @@ class Dumper
}
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
$output .= ' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
} else {
$output .= "\n";
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
$output .= $this->doDump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags, $nestingLevel + 1);
}
continue;
@ -125,8 +135,8 @@ class Dumper
$output .= \sprintf('%s%s%s%s',
$prefix,
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
$willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
$willBeInlined || ($compactNestedMapping && \is_array($value) && Inline::isHash($value)) ? ' ' : "\n",
$compactNestedMapping && \is_array($value) && Inline::isHash($value) ? substr($this->doDump($value, $inline - 1, $indent + 2, $flags, $nestingLevel + 1), $indent + 2) : $this->doDump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags, $nestingLevel + 1)
).($willBeInlined ? "\n" : '');
}
}
@ -134,7 +144,7 @@ class Dumper
return $output;
}
private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string
private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix, int $nestingLevel): string
{
$output = \sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag());
@ -150,10 +160,10 @@ class Dumper
}
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
return $output.' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
}
return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
return $output."\n".$this->doDump($value->getValue(), $inline - 1, $indent, $flags, $nestingLevel + 1);
}
private function getBlockIndentationIndicator(string $value): string

36
vendor/symfony/yaml/Escaper.php vendored Executable file → Normal file
View File

@ -28,22 +28,24 @@ class Escaper
// first to ensure proper escaping because str_replace operates iteratively
// on the input arrays. This ordering of the characters avoids the use of strtr,
// which performs more slowly.
private const ESCAPEES = ['\\', '\\\\', '\\"', '"',
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\x7f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
];
private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"',
'\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
'\\x7f',
'\\N', '\\_', '\\L', '\\P',
];
private const ESCAPEES = [
'\\', '\\\\', '\\"', '"',
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\x7f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
];
private const ESCAPED = [
'\\\\', '\\"', '\\\\', '\\"',
'\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
'\\x7f',
'\\N', '\\_', '\\L', '\\P',
];
/**
* Determines if a PHP value would require double quoting in YAML.
@ -74,7 +76,7 @@ class Escaper
{
// Determines if a PHP value is entirely composed of a value that would
// require single quoting in YAML.
if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) {
if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'], true)) {
return true;
}

0
vendor/symfony/yaml/Exception/DumpException.php vendored Executable file → Normal file
View File

0
vendor/symfony/yaml/Exception/ExceptionInterface.php vendored Executable file → Normal file
View File

0
vendor/symfony/yaml/Exception/ParseException.php vendored Executable file → Normal file
View File

0
vendor/symfony/yaml/Exception/RuntimeException.php vendored Executable file → Normal file
View File

48
vendor/symfony/yaml/Inline.php vendored Executable file → Normal file
View File

@ -100,7 +100,7 @@ class Inline
*
* @throws DumpException When trying to dump PHP resource
*/
public static function dump(mixed $value, int $flags = 0): string
public static function dump(mixed $value, int $flags = 0, bool $rootLevel = false): string
{
switch (true) {
case \is_resource($value):
@ -138,7 +138,7 @@ class Inline
case \is_array($value):
return self::dumpArray($value, $flags);
case null === $value:
return self::dumpNull($flags);
return self::dumpNull($flags, $rootLevel);
case true === $value:
return 'true';
case false === $value:
@ -173,6 +173,7 @@ class Inline
case self::isBinaryString($value):
return '!!binary '.base64_encode($value);
case Escaper::requiresDoubleQuoting($value):
case Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES & $flags:
return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value):
$singleQuoted = Escaper::escapeWithSingleQuotes($value);
@ -242,23 +243,28 @@ class Inline
private static function dumpHashArray(array|\ArrayObject|\stdClass $value, int $flags): string
{
$output = [];
$keyFlags = $flags & ~Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES;
foreach ($value as $key => $val) {
if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) {
$key = (string) $key;
}
$output[] = \sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
$output[] = \sprintf('%s: %s', self::dump($key, $keyFlags), self::dump($val, $flags));
}
return \sprintf('{ %s }', implode(', ', $output));
}
private static function dumpNull(int $flags): string
private static function dumpNull(int $flags, bool $rootLevel = false): string
{
if (Yaml::DUMP_NULL_AS_TILDE & $flags) {
return '~';
}
if (Yaml::DUMP_NULL_AS_EMPTY & $flags && !$rootLevel) {
return '';
}
return 'null';
}
@ -453,7 +459,7 @@ class Inline
}
if ('!php/const' === $key || '!php/enum' === $key) {
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false);
$key .= ' '.self::parseScalar($mapping, $flags, ['(?<!:):(?!:)'], $i, false);
$key = self::evaluateScalar($key, $flags);
}
@ -708,6 +714,10 @@ class Inline
switch (true) {
case ctype_digit($scalar):
case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
if ($scalar < \PHP_INT_MIN || \PHP_INT_MAX < $scalar) {
return $scalar;
}
$cast = (int) $scalar;
return ($scalar === (string) $cast) ? $cast : $scalar;
@ -744,7 +754,7 @@ class Inline
if (false !== $scalar = $time->getTimestamp()) {
return $scalar;
}
} catch (\ValueError) {
} catch (\DateRangeError|\ValueError) {
// no-op
}
@ -823,19 +833,19 @@ class Inline
private static function getTimestampRegex(): string
{
return <<<EOF
~^
(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?
$~x
EOF;
~^
(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?
$~x
EOF;
}
/**

0
vendor/symfony/yaml/LICENSE vendored Executable file → Normal file
View File

44
vendor/symfony/yaml/Parser.php vendored Executable file → Normal file
View File

@ -198,7 +198,7 @@ class Parser
}
} elseif (
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{!].*?)) *\:(( |\t)++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (!str_contains($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))
&& (!str_contains($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"], true))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
@ -769,6 +769,11 @@ class Parser
$lines = [];
while ($this->moveToNextLine()) {
if ($this->isCurrentLineBlank()) {
$lines[] = '';
continue;
}
// unquoted strings end before the first unindented line
if (0 === $this->getCurrentLineIndentation()) {
$this->moveToPreviousLine();
@ -776,6 +781,10 @@ class Parser
break;
}
if ($this->isCurrentLineComment()) {
break;
}
$lines[] = trim($this->currentLine);
}
@ -1165,7 +1174,18 @@ class Parser
private function lexUnquotedString(int &$cursor): string
{
$offset = $cursor;
$cursor += strcspn($this->currentLine, '[]{},: ', $cursor);
while ($cursor < \strlen($this->currentLine)) {
if (\in_array($this->currentLine[$cursor], ['[', ']', '{', '}', ',', ':'], true)) {
break;
}
if (\in_array($this->currentLine[$cursor], [' ', "\t"], true) && '#' === ($this->currentLine[$cursor + 1] ?? '')) {
break;
}
++$cursor;
}
if ($cursor === $offset) {
throw new ParseException('Malformed unquoted YAML string.');
@ -1174,17 +1194,17 @@ class Parser
return substr($this->currentLine, $offset, $cursor - $offset);
}
private function lexInlineMapping(int &$cursor = 0): string
private function lexInlineMapping(int &$cursor = 0, bool $consumeUntilEol = true): string
{
return $this->lexInlineStructure($cursor, '}');
return $this->lexInlineStructure($cursor, '}', $consumeUntilEol);
}
private function lexInlineSequence(int &$cursor = 0): string
private function lexInlineSequence(int &$cursor = 0, bool $consumeUntilEol = true): string
{
return $this->lexInlineStructure($cursor, ']');
return $this->lexInlineStructure($cursor, ']', $consumeUntilEol);
}
private function lexInlineStructure(int &$cursor, string $closingTag): string
private function lexInlineStructure(int &$cursor, string $closingTag, bool $consumeUntilEol = true): string
{
$value = $this->currentLine[$cursor];
++$cursor;
@ -1204,15 +1224,19 @@ class Parser
++$cursor;
break;
case '{':
$value .= $this->lexInlineMapping($cursor);
$value .= $this->lexInlineMapping($cursor, false);
break;
case '[':
$value .= $this->lexInlineSequence($cursor);
$value .= $this->lexInlineSequence($cursor, false);
break;
case $closingTag:
$value .= $this->currentLine[$cursor];
++$cursor;
if ($consumeUntilEol && isset($this->currentLine[$cursor]) && ($whitespaces = strspn($this->currentLine, ' ', $cursor) + $cursor) < \strlen($this->currentLine) && '#' !== $this->currentLine[$whitespaces]) {
throw new ParseException(\sprintf('Unexpected token "%s".', trim(substr($this->currentLine, $cursor))));
}
return $value;
case '#':
break 2;
@ -1238,7 +1262,7 @@ class Parser
$whitespacesConsumed = 0;
do {
$whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor);
$whitespaceOnlyTokenLength = strspn($this->currentLine, " \t", $cursor);
$whitespacesConsumed += $whitespaceOnlyTokenLength;
$cursor += $whitespaceOnlyTokenLength;

View File

@ -42,8 +42,13 @@ if (!class_exists(Application::class)) {
exit(1);
}
(new Application())->add($command = new LintCommand())
->getApplication()
$command = new LintCommand();
if (method_exists($app = new Application(), 'addCommand')) {
$app->addCommand($command);
} else {
$app->add($command);
}
$app
->setDefaultCommand($command->getName(), true)
->run()
;

0
vendor/symfony/yaml/Tag/TaggedValue.php vendored Executable file → Normal file
View File

0
vendor/symfony/yaml/Unescaper.php vendored Executable file → Normal file
View File

3
vendor/symfony/yaml/Yaml.php vendored Executable file → Normal file
View File

@ -35,6 +35,9 @@ class Yaml
public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
public const DUMP_NULL_AS_TILDE = 2048;
public const DUMP_NUMERIC_KEY_AS_STRING = 4096;
public const DUMP_NULL_AS_EMPTY = 8192;
public const DUMP_COMPACT_NESTED_MAPPING = 16384;
public const DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES = 32768;
/**
* Parses a YAML file into a PHP value.

4
vendor/symfony/yaml/composer.json vendored Executable file → Normal file
View File

@ -17,11 +17,11 @@
],
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"symfony/console": "^6.4|^7.0"
"symfony/console": "^6.4|^7.0|^8.0"
},
"conflict": {
"symfony/console": "<6.4"