no message

This commit is contained in:
hellcat 2025-06-17 13:19:33 +08:00
parent c6fbf984b0
commit 0fc97233b5
14 changed files with 406 additions and 379 deletions

View File

@ -12,7 +12,6 @@ final class HookController
{
public function cmd()
{
try {
$oTGHttp = new TGHttp();

View File

@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use App\Models\ArticleList;
use App\Models\Tags;
use App\Services\Time;
use App\Models\Option as ModelOption;
class ExecController extends Controller
{
@ -18,6 +19,7 @@ class ExecController extends Controller
echo 'only local';
exit;
}
}
// cron n合1
@ -64,6 +66,7 @@ class ExecController extends Controller
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$i = 0;
foreach ($aArticleCode as $iCode) {
$sUrl = $sUrlBase.$iCode;
@ -75,10 +78,11 @@ class ExecController extends Controller
$r = file_put_contents($sHtmlPath, $sArticleView);
echo $r;
echo "<br>";
$i++;
}
echo "静态化".$i."";
curl_close($ch);
}
@ -220,13 +224,24 @@ class ExecController extends Controller
}
// 节点文章批量抓取,倒数
public function freenodeCraw($day) {
// public function freenodeCraw($day) {
//
// $sDateStart = date("Ymd", strtotime("-$day day"));
//
// $iLimit = 10;
//
// for ($i=$day; $i>($day - $iLimit); $i--) {
// echo "<br>";
// echo ($i);
// echo "<br>";
// echo $this->freenodeUp(-$i);
// }
//
// }
public function freenodeCraw($iStart = 1, $iLimit = 1) {
$sDateStart = date("Ymd", strtotime("-$day day"));
$iLimit = 10;
for ($i=$day; $i>($day - $iLimit); $i--) {
for ($i=$iStart; $i>($iStart - $iLimit); $i--) {
echo "<br>";
echo ($i);
echo "<br>";
@ -248,6 +263,8 @@ class ExecController extends Controller
$sDateNext10DayCode = date("Ymd", $sTimeNext10Day);
$sFreeNodePath = "fn/".$sDateStartCode.".html";
$aOption = ModelOption::_all();
//// 生成订阅 start
$iTodayCode = ArticleList::where("code", $sDateStartCode)->first()?->code;
@ -255,11 +272,11 @@ class ExecController extends Controller
echo "article already exists.\n";
return;
}
$sV2rayTodayUrl = "https://nodefree.githubrowcontent.com/".date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".txt";
$sV2rayTodayUrl = $aOption["freenode_craw_url"].date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".txt";
$sV2rayToday = @file_get_contents($sV2rayTodayUrl);
$sV2rayNextUrl = "https://nodefree.githubrowcontent.com/".date('Y', $sTimeNext10Day)."/".date('m', $sTimeNext10Day)."/".$sDateNext10DayCode.".txt";
$sV2rayNextUrl = $aOption["freenode_craw_url"].date('Y', $sTimeNext10Day)."/".date('m', $sTimeNext10Day)."/".$sDateNext10DayCode.".txt";
$sV2rayNext = @file_get_contents($sV2rayNextUrl);
if (empty($sV2rayNext) || $sV2rayNext == $sV2rayToday) {
@ -275,8 +292,9 @@ class ExecController extends Controller
}
$sClashPath = "feed/clash-".$sDateStartCode.".yaml";
$sClashTodayUrl = "https://nodefree.githubrowcontent.com/".date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".yaml";
$sClashTodayUrl = $aOption["freenode_craw_url"].date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".yaml";
$sClashToday = @file_get_contents($sClashTodayUrl);
// tomd($aOption["freenode_craw_url"].date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".yaml", 1);
$mClashR = file_put_contents($sClashPath, $sClashToday);
if ($mClashR === false) {
@ -309,14 +327,13 @@ class ExecController extends Controller
'日本、台湾、新加坡,韩国、俄罗斯、法国、美国、德国',
'新加坡、日本、台湾、美国、韩国、德国、香港',
'香港、台湾、日本、韩国、越南、阿根廷、俄罗斯、加拿大、美国',
'上'
];
$iNodeRegionRandKey = array_rand($aNodeRegionPool);
$sNodeZone = $aNodeRegionPool[$iNodeRegionRandKey];
// 标题
$aTitlePool = [
date('Y', $sTimeStart)."年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接"
date('Y', $sTimeStart).$aOption["freenode_title"]
];
$iTitlePoolRandKey = array_rand($aTitlePool);
$sTitle = $aTitlePool[$iTitlePoolRandKey];
@ -410,8 +427,9 @@ class ExecController extends Controller
}
// 直接生成文章
public function freenodeDataAll ()
public function freenodeDataAll()
{
$aOption = ModelOption::_all();
ArticleList::where('type', 'node')->delete(); // 先删除节点文章
@ -443,14 +461,18 @@ class ExecController extends Controller
'美国、新加坡、日本、俄罗斯',
'香港、台湾、美国、日本、新加坡、德国',
'美国、新加坡、加拿大、香港、欧洲、韩国、日本',
'美国、日本、新加坡、台湾'
'美国、日本、新加坡、台湾',
'日本、香港、台湾、越南、土耳其、阿根廷、俄罗斯',
'日本、台湾、新加坡,韩国、俄罗斯、法国、美国、德国',
'新加坡、日本、台湾、美国、韩国、德国、香港',
'香港、台湾、日本、韩国、越南、阿根廷、俄罗斯、加拿大、美国',
];
$iNodeRegionRandKey = array_rand($aNodeRegionPool);
$sNodeZone = $aNodeRegionPool[$iNodeRegionRandKey];
// 标题
$aTitlePool = [
date('Y', $sTimeStart)."年最新高速SSR/V2ray/Clash/Shadowrocket免费节点订阅链接"
date('Y', $sTimeStart).$aOption["freenode_title"]
];
$iTitlePoolRandKey = array_rand($aTitlePool);
$sTitle = $aTitlePool[$iTitlePoolRandKey];

39
app/Models/Option.php Executable file
View File

@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Option extends Model
{
protected $table = 'option';
public $timestamps = false;
public static function _all()
{
$aData = self::all()->toArray();
$arr = [];
foreach ($aData as $row) {
$arr[$row['key']] = $row['value'];
}
return $arr;
}
public static function _find($sKey)
{
$oData = self::where("key", $sKey)->first();
if ($oData && $oData->value) {
$r = $oData->value;
} else {
$r = false;
}
return $r;
}
}

33
app/Services/RandBox.php Executable file
View File

@ -0,0 +1,33 @@
<?php
namespace App\Services;
class RandBox {
public function guojia()
{
$countries = [
"美国",
"日本",
"新加坡",
"韩国",
"台湾",
"香港",
"越南",
"阿根廷",
"俄罗斯",
"土耳其",
"",
"",
"",
"",
"",
"",
"",
"",
"",
];
}
}

View File

@ -1,142 +0,0 @@
<?php
namespace App\Services\TG\Hook\Mod;
use App\Models\Article as ModelArticle;
class Article extends Base {
public function List($aParam)
{
if (isset($aParam['aOption'][1])) {
$sOrderBy = $aParam['aOption'][1];
} else {
$sOrderBy = "i_in10";
}
$aArticleList = ModelArticle::orderBy($sOrderBy)->get()->toArray();
foreach ($aArticleList as &$row) { // 使用引用来修改原数组
unset($row['updated_at']); // 移除 updated_at 字段
unset($row['created_at']); // 移除 created_at 字段
$this->oTGHttp->sendToTG(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
$this->oHttp::response(200, 'ok');
}
public function Find($aParam)
{
$iId = $aParam['aArg'][1];
$aSingle = ModelArticle::find($iId)->toArray();
unset($aSingle['updated_at']);
unset($aSingle['created_at']);
return json_encode($aSingle, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
public function Like($aParams)
{
$aArticleList = ModelArticle::where($this->cmdGood($aParams['aOption'][1]), 'like', '%' . $aParams['aArg'][1] . '%')
->orderBy("user_name")
->get()
->toArray();
if (empty($aArticleList)) {
$this->oTGHttp->sendToTG("没有");
} else {
foreach ($aArticleList as &$row) { // 使用引用来修改原数组
unset($row['updated_at']); // 移除 updated_at 字段
unset($row['created_at']); // 移除 created_at 字段
$this->oTGHttp->sendToTG(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
}
$this->oHttp::response(200, 'ok');
}
public function Clone($aParam)
{
$iId = $aParam['aArg'][1];
// 查找文章
$aArticle = ModelArticle::find($iId);
if ($aArticle) {
// 复制文章
$newArticle = $aArticle->replicate();
// 如果需要,可以修改新文章的某些字段,例如标题或状态
// $newArticle->title = $newArticle->title . ' - Copy'; // 修改标题
// $newArticle->status = 'draft'; // 例如,将状态设置为草稿
// 保存新文章
$newArticle->save();
// 发送新文章的 ID 到 Telegram
$sMsg = "新文章已创建ID: " . $newArticle->id;
$this->oTGHttp->sendToTG($sMsg);
// 返回 HTTP 响应
$this->oHttp::response(200, 'ok');
} else {
throw new \Exception("没有这个id");
}
}
public function Add($aParams)
{
$newArticle = new ModelArticle();
$newArticle->user_name = $aParams['aArg'][1];
$r = $newArticle->save();
$this->oTGHttp->sendToTG(json_encode($r, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
public function Del($aParams)
{
$article = ModelArticle::find($aParams['aArg'][1]);
if ($article) {
$r = $article->delete();
} else {
$r = false;
}
$this->oTGHttp->sendToTG(json_encode($r, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
// /ArticleSet__id__user_name xxx
public function Set($aParams)
{
$sField = $this->cmdGood($aParams['aOption'][2]);
$article = ModelArticle::find($aParams['aOption'][1]);
$sSet = $aParams['aArg'][1];
if ($article) {
$article->$sField = $sSet;
$r = $article->save();
} else {
// 处理未找到的情况
throw new \Exception("Article not found");
}
$this->oTGHttp->sendToTG(json_encode($r, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
}

View File

@ -15,29 +15,18 @@ class Base {
// $this->oTGHttp->sApiChatId = env("telegram_chatid");
}
protected function cmdGood($sCmd) {
$sCmdNew = "";
switch ($sCmd) {
case "user":
$sCmdNew = "user_name";
break;
case "project":
$sCmdNew = "project_name";
break;
case "code":
$sCmdNew = "sale_code";
break;
case "condition":
$sCmdNew = "cron_condition";
break;
default:
$sCmdNew = $sCmd;
break;
public function toJson($aData)
{
return json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
public function eachSendTg($aData)
{
foreach ($aData as $row) {
$this->oTGHttp->sendToTG(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
return $sCmdNew;
$this->oHttp::response(200, 'ok');
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Services\TG\Hook\Mod;
use Illuminate\Support\Facades\DB;
class DataBase extends Base {
public function select($aParam)
{
$sql = $aParam["aArg"][1];
$sql = implode(" ", $aParam["aArg"]);
$r = DB::select($sql);
return $this->toJson($r);
}
public function sql($aParam)
{
$sql = $aParam["aArg"][1];
$sql = implode(" ", $aParam["aArg"]);
$r = DB::statement($sql);
return $this->toJson($r);
}
}

View File

@ -1,128 +0,0 @@
<?php
namespace App\Services\TG\Hook\Mod;
use App\Models\Article as ModelArticle;
class Git extends Base {
public function UrlList()
{
$aArticleList = ModelArticle::orderBy('user_name')->get()->toArray();
$sMsg = '';
foreach ($aArticleList as $v) {
$sMsg .= "\n\n";
$sMsg .= "https://github.com/".$v['user_name']."/".$v['project_name'];
}
return $sMsg;
}
public function LastTime($aParam)
{
$iId = $aParam['aArg'][1];
$aArticle = ModelArticle::find($iId);
$sProjectPath = env("BASE_DIR") . "github/" . $aArticle->user_name . "-" . $aArticle->project_name;
// 进入指定目录
if (!chdir($sProjectPath)) {
throw new \Exception("无法进入目录" . $sProjectPath);
}
// 获取远程更新
$this->runCommand('git fetch origin');
// 获取最后提交的时间
$lastCommitTime = $this->runCommand('git log origin/main -1 --format=%cd');
$lastCommitTime = \DateTime::createFromFormat('D M d H:i:s Y O', $lastCommitTime[0]);
$lastCommitTime = json_encode($lastCommitTime->format('Y-m-d H:i:s'));
// 检查是否获取到时间
if (empty($lastCommitTime)) {
throw new \Exception("无法获取最后提交时间,可能是分支不存在或没有提交。");
}
return $lastCommitTime;
}
// 没用
public function AddSshHostGithub()
{
// 定义要添加的主机
$host = 'github.com';
// 执行 ssh-keyscan 命令
$key = shell_exec("ssh-keyscan -t ed25519 $host");
// 检查是否成功获取到公钥
if ($key) {
// 获取 known_hosts 文件的路径
$knownHostsFile = '/root/.ssh/known_hosts';
// 检查 known_hosts 文件中是否已经存在该主机的公钥
if (strpos(file_get_contents($knownHostsFile), $host) === false) {
// 将公钥写入 known_hosts 文件
file_put_contents($knownHostsFile, $key, FILE_APPEND);
$sMsg = "公钥已成功添加到 $knownHostsFile\n";
} else {
$sMsg = "公钥已存在于 $knownHostsFile\n";
}
} else {
$sMsg = "无法获取 $host 的公钥\n";
}
$this->oTGHttp->sendToTG($sMsg);
$this->oHttp::response(200, 'ok');
}
// /Git#PullR 3
public function PullR($aParam)
{
$iId = $aParam['aArg'][1];
$aArticle = ModelArticle::find($iId);
$sProjectPath = env("BASE_DIR")."github/".$aArticle->user_name."-".$aArticle->project_name;
// 进入指定目录
if (!chdir($sProjectPath)) {
throw new \Exception("无法进入目录".$sProjectPath);
}
// 获取远程更新
$this->runCommand('git fetch origin');
// 硬重置到远程主分支
$this->runCommand('git reset --hard origin/main');
$this->oTGHttp->sendToTG("操作完成:已重置到远程主分支");
$this->oHttp::response(200, 'ok');
}
private function runCommand($command) {
$output = [];
$returnVar = 0;
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
$jsonOutput = json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$errorMessage = "命令失败: $command\n" . $jsonOutput . "\n";
throw new \Exception($errorMessage);
} else {
return $output;
}
}
}

View File

@ -5,30 +5,90 @@ use App\Models\Option as ModelOption;
class Option extends Base {
public function Pause($aParam)
{
$v = $aParam['aArg'][1];
// 获取第一个匹配的选项
$oOption = ModelOption::where("type", "pause")->first();
// 检查是否找到了选项
if ($oOption) {
$oOption->value = $v;
// 保存并返回结果
return json_encode($oOption->save());
}
// 如果没有找到选项,返回一个错误信息
return json_encode(['error' => 'Option not found']);
}
public function List()
{
$aOptionList = ModelOption::all()->toArray();
return json_encode($aOptionList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$oOptionList = ModelOption::all();
if (!$oOptionList) {
throw new \Exception("没有?");
}
$str = "";
foreach ($oOptionList as $row) {
$str .= "\n\n";
$str .= $row->key."::".$row->value;
}
return $str;
}
public function Add($aParam)
{
$sKey = $aParam['aArg'][1];
$oOption = new ModelOption();
$oOption->key = $sKey;
$r = $oOption->save();
return $r;
}
public function Like($aParam)
{
$sField = $aParam["aOption"][1];
$sValue = $aParam["aArg"][1];
$oOption = ModelOption::where($sField, "like", "%".$sValue."%")->get();
if (!$oOption) {
throw new \Exception("没有");
}
$this->eachSendTg($oOption);
}
public function Find($aParam)
{
$sKey = $aParam["aArg"][1];
$oOption = ModelOption::where("key", $sKey)->first();
if (!$oOption) {
throw new \Exception("没有");
}
return $this->toJson($oOption);
}
public function Option($aParam)
{
$sKey = $aParam["aOption"][1];
$sValue = $aParam["aArg"][1];
$oOption = ModelOption::where("key", $sKey)->first();
if (!$oOption) {
throw new \Exception("没有这个key");
}
$oOption->value = $sValue;
$r = $oOption->save();
return $r;
}
public function del($aParam)
{
$sKey = $aParam["aArg"][1];
$oOption = ModelOption::where("key", $sKey);
$r = $oOption->delete();
return $r;
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace App\Services\TG\Hook\Mod;
use App\Models\Article as ModelArticle;
class SaleCode extends Base {
public function SetRate($aParams)
{
$updatedRows = ModelArticle::where('sale_code', $aParams['aOption'][1])
->update(['sale_rate' => $aParams['aArg'][1]]);
$this->oTGHttp->sendToTG(json_encode($updatedRows, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
public function List()
{
$aSaleCodeList = ModelArticle::select('sale_code', 'sale_rate')
->distinct()
->orderBy("sale_code")
->get()
->toArray();
$this->oTGHttp->sendToTG(json_encode($aSaleCodeList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
public function Find($aParams)
{
$aSaleCodeList = ModelArticle::select('sale_code', 'sale_rate')
->where("sale_code", $aParams['aArg'][1])
->distinct()
->orderBy("sale_code")
->get()
->toArray();
$this->oTGHttp->sendToTG(json_encode($aSaleCodeList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
$this->oHttp::response(200, 'ok');
}
}

View File

@ -2,6 +2,7 @@
namespace App\Services\TG\Hook\Mod;
//use App\Services\TomTool\Http;
use App\Models\Option as ModelOption;
class Test extends Base {
@ -25,4 +26,22 @@ class Test extends Base {
}
public function FreenodeCrawUrl()
{
$aOption = ModelOption::_all();
$sTimeSet = 0;
$sTimeStart = strtotime($sTimeSet." day");
$sTimeNextDay = strtotime(($sTimeSet+1)." day");
$sTimeNext10Day = strtotime(($sTimeSet+10)." day");
$sTimePrevDay = strtotime(($sTimeSet-1)." day");
$sDateStartCode = date("Ymd", $sTimeStart);
$sDateNextDayCode = date("Ymd", $sTimeNextDay);
$sDateNext10DayCode = date("Ymd", $sTimeNext10Day);
$sUrl = $aOption["freenode_craw_url"].date('Y', $sTimeStart)."/".date('m', $sTimeStart)."/".$sDateStartCode.".txt";
return $sUrl;
}
}

View File

@ -0,0 +1,127 @@
<?php
namespace App\Services\TG\Hook\Mod;
use Illuminate\Support\Facades\DB;
class Tool extends Base {
// function __construct()
// {
// $this->oHttp->sMethod = "get";
// $this->oHttp->bIsJson = "true";
// /exec/html/toptags
// /exec/like/auto
// /exec/html/client
// /exec/html/article
// /exec/html/single/{code}
// /exec/data/freenode/all
// /exec/like/init
// /exec/freenode/craw/{day}
// }
public function Article_freenode_up_limit($aParam)
{
$iStart = $aParam["aArg"][1];
$iLimit = $aParam["aArg"][2];
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/freenode/craw/".$iStart."/".$iLimit;
$r = $this->oHttp->send();
return $r;
}
public function Like_init()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/like/init";
$r = $this->oHttp->send();
return $r;
}
public function Article_freenode_data_reset()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/data/freenode/all";
$r = $this->oHttp->send();
return $r;
}
public function Article_single_html($aParam)
{
$sCode = $aParam["aArg"][1];
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/html/single/".$sCode;
$r = $this->oHttp->send();
return $r;
}
public function Article_html()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/html/article";
$r = $this->oHttp->send();
return $r;
}
public function Article_client_html()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/html/client";
$r = $this->oHttp->send();
return $r;
}
public function Like_auto()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/like/auto";
$r = $this->oHttp->send();
return $r;
}
public function Toptag_html()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/html/toptags";
$r = $this->oHttp->send();
return $r;
}
public function Article_freenode_up()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/freenode/up";
$r = $this->oHttp->send();
return $r;
}
public function cron()
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/cron";
$r = $this->oHttp->send();
return $r;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Services\TG\Hook\Mod;
//use App\Services\TomTool\Http;
class ToolOld extends Base {
public function List($aParam)
{
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/tool";
$r = $this->oHttp->send();
return $r;
}
public function Exec($aParam)
{
$sExec = $aParam['aArg'][1];
$this->oHttp->sMethod = "get";
$this->oHttp->bIsJson = "true";
$this->oHttp->sToUrl = env("APP_URL")."/exec/".$sExec;
$r = $this->oHttp->send();
return $r;
}
}

View File

@ -32,7 +32,7 @@ Route::get('/client/single/{code}', [ArticleSingleController::class, 'client']);
Route::get('/fn/single/{code}', [ArticleSingleController::class, 'freenode']); // 免费节点single
Route::get('/tool', [SakaiController::class, 'tool']);
Route::get('/exec/freenode/craw/{day}', [ExecController::class, 'freenodeCraw']); // 免费节点文章批量抓取
Route::get('/exec/freenode/craw/{start}/{limit}', [ExecController::class, 'freenodeCraw']); // 免费节点文章批量抓取
Route::get('/exec/freenode/up', [ExecController::class, 'freenodeUp']); // 免费节点文章每日更新
Route::get('/exec/freenode/del', [ExecController::class, 'freenodeDelAuto']); // 免费节点自动删除
Route::get('/exec/html/toptags', [ExecController::class, 'tagCountToHtml']); // 热门标签静态生成