1
This commit is contained in:
commit
c69d35d1bb
18
.editorconfig
Executable file
18
.editorconfig
Executable file
@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
66
.env.example
Executable file
66
.env.example
Executable file
@ -0,0 +1,66 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_TIMEZONE=UTC
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
6
.gitignore
vendored
Executable file
6
.gitignore
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
.env
|
||||
public/fn/
|
||||
README.md
|
||||
_env/
|
||||
public/upload/
|
||||
public/freenode/
|
||||
94
app/Http/Controllers/Api/FreenodeController.php
Executable file
94
app/Http/Controllers/Api/FreenodeController.php
Executable 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
169
app/Http/Controllers/Api/Tg/HookController.php
Executable file
169
app/Http/Controllers/Api/Tg/HookController.php
Executable file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\Tg;
|
||||
|
||||
//use App\Models\Article;
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Tg\Http as TGHttp;
|
||||
use App\Models\Hook as ModelHook;
|
||||
use App\Services\TomTool\ThrowableHandler;
|
||||
//use App\Services\TomTool\TelegramVdb;
|
||||
use App\Services\TomTool\Telegram\Slave as TelegramSlave;
|
||||
|
||||
final class HookController
|
||||
{
|
||||
|
||||
private $sCustomText = '';
|
||||
|
||||
public function cmd_ffq_article()
|
||||
{
|
||||
try {
|
||||
|
||||
$sUpdate = file_get_contents("php://input");
|
||||
$aUpdate = json_decode($sUpdate, true);
|
||||
$sText = $aUpdate["message"]["text"] ?? "没有text";
|
||||
if (!str_starts_with($sText, '/')) {
|
||||
// $this->sCustomCmd = "article";
|
||||
// $this->sCustomFunc = "cache";
|
||||
// $this->aCustomOption[1] = "ffq";
|
||||
$this->sCustomText = "/article#cacheAdd__ffq_tg_a"." ".$sUpdate;
|
||||
}
|
||||
|
||||
$this->cmd();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
$aDataTg = ThrowableHandler::make($e)->enableTrace()->fetch();
|
||||
TelegramSlave::fail()->enableSlaveQueue(false)->setMsgA([
|
||||
"msg" => $aDataTg,
|
||||
"data" => $sUpdate,
|
||||
])->send();
|
||||
|
||||
$aDataCmd = $aDataTg;
|
||||
$sDataCmd = json_encode($aDataCmd, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$sDataCmd = str_replace("\\n", "<br>", $sDataCmd);
|
||||
echo $sDataCmd;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function cmd()
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
$oTGHttp = new TGHttp();
|
||||
|
||||
$oTGHttp->sApiToken = env('telegram_token');
|
||||
$oTGHttp->sApiChatId = env('telegram_chatid');
|
||||
|
||||
$sUpdate = file_get_contents("php://input");
|
||||
|
||||
$aUpdate = json_decode($sUpdate, true);
|
||||
|
||||
// @file_put_contents('/www/_tg/bot_log.txt', json_encode($aUpdate, JSON_PRETTY_PRINT));
|
||||
|
||||
$aData['chatId'] = (string) $aUpdate["message"]["chat"]["id"];
|
||||
$aData['fromId'] = (string) $aUpdate["message"]["from"]["id"];
|
||||
|
||||
if ($this->sCustomText) {
|
||||
$aUpdate["message"]["text"] = $this->sCustomText;
|
||||
}
|
||||
|
||||
if (!isset($aUpdate["message"]["text"])) {
|
||||
throw new \Exception(json_encode($aUpdate));
|
||||
}
|
||||
|
||||
$sText = $aUpdate["message"]["text"];
|
||||
|
||||
// hook优先
|
||||
// 非is_hook,并且,首字符非/
|
||||
if ((!isset($aUpdate["is_hook"]) || $aUpdate["is_hook"] == false) && substr($sText, 0, 1) !== "/") {
|
||||
|
||||
$sHookKey = ModelHook::where("tg_user_id", $aData["fromId"])->first()?->key;
|
||||
|
||||
if ($sHookKey) {
|
||||
if ($sText == "run") {
|
||||
$sText = $sHookKey;
|
||||
} else {
|
||||
$sText = $sHookKey.$sText;
|
||||
}
|
||||
$aUpdate["is_hook"] = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@$aArg = explode(" ", $sText) ?? [];
|
||||
@$aOption = explode("__", $aArg[0]) ?? [];
|
||||
|
||||
$sCmd = $this->cleanCmd($aOption[0]);
|
||||
|
||||
// 大于3个说明是hook或dove之类
|
||||
$aCmd = explode("#", $sCmd);
|
||||
// if (count($aCmd) >= 3) {
|
||||
// $sHook = $aCmd[0];
|
||||
// $sClass = $sHook;
|
||||
// $sFunc = "go";
|
||||
// } else {
|
||||
$sClass = $aCmd[0] ?? false;
|
||||
$sFunc = $aCmd[1] ?? false;
|
||||
// }
|
||||
|
||||
if (strpos($sClass, "dove$") !== false) {
|
||||
$sClass = "dove";
|
||||
$sFunc = "go";
|
||||
}
|
||||
|
||||
$sUClass = ucfirst($sClass);
|
||||
$sUFunc = ucfirst($sFunc);
|
||||
|
||||
unset($aOption[0]);
|
||||
unset($aArg[0]);
|
||||
|
||||
$aParams = [];
|
||||
$aParams['aOption'] = $aOption;
|
||||
$aParams['aArg'] = $aArg;
|
||||
|
||||
$sClassPath = "App\\Services\\Tg\\Hook\\Mod\\".$sClass;
|
||||
$sUClassPath = "App\\Services\\Tg\\Hook\\Mod\\".$sUClass;
|
||||
|
||||
$oInstans = new $sUClassPath($aUpdate);
|
||||
$r = $oInstans->$sFunc($aParams, $aUpdate);
|
||||
|
||||
$oTGHttp->send($r, $aUpdate);
|
||||
|
||||
echo $r;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
$aDataTg = ThrowableHandler::make($e)->enableTrace()->fetch();
|
||||
TelegramSlave::fail()->enableSlaveQueue(false)->setMsgA($aDataTg)->send();
|
||||
|
||||
$aDataCmd = $aDataTg;
|
||||
$sDataCmd = json_encode($aDataCmd, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$sDataCmd = str_replace("\\n", "<br>", $sDataCmd);
|
||||
echo $sDataCmd;
|
||||
exit;
|
||||
|
||||
// old
|
||||
// $sMessage = "错误(文件: " . $e->getFile() . ",行: " . $e->getLine() . "): " . $e->getMessage();
|
||||
// $oTGHttp->send($sMessage, $sUpdate);
|
||||
// //Http::response(200, 'error'); 注释的
|
||||
// old end
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanCmd($input) {
|
||||
|
||||
$input = str_replace('\\', '', $input);
|
||||
$input = str_replace('/', '', $input);
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
7
app/Http/Kernel.php
Executable file
7
app/Http/Kernel.php
Executable file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
protected $routeMiddleware = [
|
||||
// 其他中间件...
|
||||
// 'test' => \App\Http\Middleware\Test::class,
|
||||
];
|
||||
|
||||
?>
|
||||
21
app/Http/Middleware/Test.php
Executable file
21
app/Http/Middleware/Test.php
Executable file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
//use voku\helper\AntiXSS;
|
||||
|
||||
class Test
|
||||
{
|
||||
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$sSearch = request('search');
|
||||
|
||||
// echo $sSearch;exit;
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
12
app/Models/BoxGithubTag.php
Executable file
12
app/Models/BoxGithubTag.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BoxGithubTag extends Model
|
||||
{
|
||||
protected $table = 'box_github_tag';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
||||
22
app/Models/CrawlFreenode.php
Executable file
22
app/Models/CrawlFreenode.php
Executable 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;
|
||||
// }
|
||||
}
|
||||
51
app/Models/DogOption.php
Executable file
51
app/Models/DogOption.php
Executable file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DogOption extends Model
|
||||
{
|
||||
protected $table = 'dog_option';
|
||||
public $timestamps = false;
|
||||
|
||||
public static function _hit($sDogName, $k)
|
||||
{
|
||||
$v = self::where("dog_name", $sDogName)->where("k", $k)->first()?->v;
|
||||
// tomd($sDogName);
|
||||
return $v;
|
||||
}
|
||||
|
||||
public static function _list($sDogName)
|
||||
{
|
||||
$oSelf = self::where("dog_name", $sDogName)->get();
|
||||
|
||||
return self::_filterList($oSelf);
|
||||
}
|
||||
|
||||
// public static function _limit($sDogName, $iSkip, $iTake)
|
||||
// {
|
||||
// $oSelf = self::where("dog_name", $sDogName)->skip($iSkip)->take($iTake)->get();
|
||||
//
|
||||
// return self::__filterList($oSelf);
|
||||
// }
|
||||
|
||||
public static function _filter($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
$arr[$oOption->dog_name."#".$oOption->k] = $oOption->v;
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function _filterList($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
foreach ($oOption as $oOptionRow) {
|
||||
$arr[] = self::_filter($oOptionRow);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
}
|
||||
24
app/Models/DoveSite.php
Executable file
24
app/Models/DoveSite.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DoveSite extends Model
|
||||
{
|
||||
protected $table = 'dove_site';
|
||||
public $timestamps = false;
|
||||
|
||||
public static function _getMap()
|
||||
{
|
||||
$oSelf = self::all();
|
||||
|
||||
$aMap = [];
|
||||
foreach ($oSelf as $oSelfRow) {
|
||||
$aMap[$oSelfRow["code"]] = $oSelfRow["www"].$oSelfRow["api_path"];
|
||||
}
|
||||
|
||||
return $aMap;
|
||||
}
|
||||
|
||||
}
|
||||
12
app/Models/FreenodePool.php
Executable file
12
app/Models/FreenodePool.php
Executable 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;
|
||||
|
||||
}
|
||||
11
app/Models/Hook.php
Executable file
11
app/Models/Hook.php
Executable file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Hook extends Model
|
||||
{
|
||||
protected $table = 'hook';
|
||||
public $timestamps = false;
|
||||
}
|
||||
15
app/Models/HttpQueue.php
Executable file
15
app/Models/HttpQueue.php
Executable file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class HttpQueue extends Model
|
||||
{
|
||||
protected $table = 'http_queue';
|
||||
public $timestamps = false;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
47
app/Models/Option.php
Executable file
47
app/Models/Option.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Option extends Model
|
||||
{
|
||||
protected $table = 'option';
|
||||
public $timestamps = false;
|
||||
protected $primaryKey = 'k';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
public static function _hit($k)
|
||||
{
|
||||
$v = self::where("k", $k)->first()?->v;
|
||||
|
||||
return $v;
|
||||
}
|
||||
|
||||
public static function _list()
|
||||
{
|
||||
$oSelf = self::get();
|
||||
|
||||
return self::_filterList($oSelf);
|
||||
}
|
||||
|
||||
public static function _filter($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
$arr[$oOption->k] = $oOption->v;
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function _filterList($oOption)
|
||||
{
|
||||
$arr = [];
|
||||
foreach ($oOption as $oOptionRow) {
|
||||
$arr[] = self::_filter($oOptionRow);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
}
|
||||
12
app/Models/SiteMap.php
Executable file
12
app/Models/SiteMap.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SiteMap extends Model
|
||||
{
|
||||
protected $table = 'site_map';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
||||
12
app/Models/SitePathMap.php
Executable file
12
app/Models/SitePathMap.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SitePathMap extends Model
|
||||
{
|
||||
protected $table = 'site_path_map';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
||||
37
app/Models/TelegramKey.php
Executable file
37
app/Models/TelegramKey.php
Executable file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Services\TomTool\Telegram\Slave as TeleSlave;
|
||||
|
||||
class TelegramKey extends Model
|
||||
{
|
||||
protected $table = 'telegram_key';
|
||||
public $timestamps = false;
|
||||
|
||||
public static function botTokenByCode($sBotCode)
|
||||
{
|
||||
$sBotToken = self::where("type", "bot_token")->where("key", $sBotCode)->first()?->value;
|
||||
|
||||
if (!$sBotToken) {
|
||||
// TeleSlave::notify()->send("未映射botCode:".$sBotCode.",使用默认base。");
|
||||
$sBotToken = self::where("type", "bot_token")->where("key", "base")->first()?->value;
|
||||
}
|
||||
|
||||
return $sBotToken;
|
||||
}
|
||||
|
||||
public static function chatIdByCode($sChatCode)
|
||||
{
|
||||
$iChatId = self::where("type", "chat_id")->where("key", $sChatCode)->first()?->value;
|
||||
|
||||
if (!$iChatId) {
|
||||
// TeleSlave::notify()->send("未映射chatCode:".$sChatCode.",使用默认base。");
|
||||
$iChatId = self::where("type", "chat_id")->where("key", "base")->first()?->value;
|
||||
}
|
||||
|
||||
return (int) $iChatId;
|
||||
}
|
||||
|
||||
}
|
||||
24
app/Providers/AppServiceProvider.php
Executable file
24
app/Providers/AppServiceProvider.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
122
app/Services/Cron.php
Executable file
122
app/Services/Cron.php
Executable file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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\Cron\FnTgPublish as CronFnTgPublish;
|
||||
use App\Services\TomTool\Telegram\Slave as TeleSlave;
|
||||
|
||||
final class Cron
|
||||
{
|
||||
public bool $bIsTest = false;
|
||||
public ?int $iTestH = null;
|
||||
public ?int $iTestI = null;
|
||||
public ?int $iTestS = null;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
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 === 0) {
|
||||
// // ReportSender::handle();
|
||||
// }
|
||||
|
||||
if ($h == 5 && $i === 3) {
|
||||
CronFreenodeFile::clear();
|
||||
}
|
||||
|
||||
// if ($h == 15 && $i == 3) {
|
||||
//// CronFreenodeLog::fromLast();
|
||||
// }
|
||||
|
||||
if ($h == 11 && $i == 3) { // 中午12点03发布fn的tg
|
||||
CronFnTgPublish::run();
|
||||
}
|
||||
|
||||
if ($i % 3 === 0) {
|
||||
CronArticleSchema::cacheTgSave();
|
||||
CronArticleSchema::pushToSlave();
|
||||
CronHttpQueue::pop();
|
||||
}
|
||||
|
||||
} 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;
|
||||
|
||||
$sMsg = "";
|
||||
$sMsg .= "--- cron 异常 ---\n";
|
||||
$sMsg .= "类型: " . get_class($e) . "\n";
|
||||
$sMsg .= "信息: " . $e->getMessage() . "\n";
|
||||
$sMsg .= "文件: " . $e->getFile() . " 在第 " . $e->getLine() . " 行\n";
|
||||
|
||||
TeleSlave::fail()->send($sMsg);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function setTestH(int $h): void
|
||||
{
|
||||
$this->enableTestMode();
|
||||
$this->iTestH = $h;
|
||||
}
|
||||
|
||||
public function setTestI(int $i): void
|
||||
{
|
||||
$this->enableTestMode();
|
||||
$this->iTestI = $i;
|
||||
}
|
||||
|
||||
public function setTestS(int $s): void
|
||||
{
|
||||
$this->enableTestMode();
|
||||
$this->iTestS = $s;
|
||||
}
|
||||
|
||||
public function enableTestMode(bool $enable = true): void
|
||||
{
|
||||
$this->bIsTest = $enable;
|
||||
}
|
||||
}
|
||||
299
app/Services/Cron/FreenodeCrawl.php
Executable file
299
app/Services/Cron/FreenodeCrawl.php
Executable file
@ -0,0 +1,299 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
// 通用系,有就拿,没有就不拿,暂时没有date
|
||||
private static function getFile__common($aFrom)
|
||||
{
|
||||
$aFileContent = [];
|
||||
|
||||
foreach ($aFrom["url_file"] as $k => $v) {
|
||||
|
||||
$sFileName = $v;
|
||||
|
||||
$sUrlSend = "https://".$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;
|
||||
}
|
||||
|
||||
// stairnode系
|
||||
// ripaojiedian系,stairnode本源
|
||||
// 这个可能会没有,没有就不更新
|
||||
private static function getFile__ripaojiedian($aFrom)
|
||||
{
|
||||
$aFileContent = [];
|
||||
|
||||
foreach ($aFrom["url_file"] as $k => $v) {
|
||||
|
||||
$sFileName = str_replace("{{Ymd}}", date("Ymd"), $v);
|
||||
|
||||
$sUrlSend = "https://".$aFrom["url_base"].$aFrom["url_node"].$sFileName;
|
||||
|
||||
$sFileContent = self::curl($sUrlSend) ?? "";
|
||||
// tomd($sUrlSend);
|
||||
// tomd($sFileContent, 1);
|
||||
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__v2rayshare($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::run - 开始\n";
|
||||
|
||||
$sDirBase = $_ENV["dir_base"];
|
||||
|
||||
$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
|
||||
|
||||
if (empty($aFileList)) {
|
||||
$sMsg = " - CrawlFreenode爬from返回空 -> ".json_encode($aFrom)." \n";
|
||||
echo $sMsg;
|
||||
TeleSlave::log()->send($sMsg);
|
||||
}
|
||||
|
||||
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 = $sDirBase."public/freenode/from/".$aFrom["name"];
|
||||
// tomd($sDir, 1);
|
||||
$sPath = $sDir."/".$sFileName;
|
||||
|
||||
if (!is_dir($sDir)) {
|
||||
mkdir($sDir, 0777, true);
|
||||
}
|
||||
|
||||
// if ($sType == "v2ray") {
|
||||
// $sFileContent = base64_decode($sFileContent);
|
||||
// }
|
||||
|
||||
// file_put_contents($sPath, $sFileContent);
|
||||
self::save_utf8_file($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;
|
||||
}
|
||||
|
||||
private static function save_utf8_file($filename, $content) {
|
||||
// 自动检测编码
|
||||
$encoding = mb_detect_encoding(
|
||||
$content,
|
||||
['UTF-8', 'GBK', 'BIG5', 'ISO-8859-1'],
|
||||
true
|
||||
);
|
||||
|
||||
// 如果不是 UTF-8,就转成 UTF-8
|
||||
if ($encoding !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
|
||||
}
|
||||
|
||||
// 确保文件头是 UTF-8(可选)
|
||||
$content = "\xEF\xBB\xBF" . $content; // 如果需要 BOM,可以加上
|
||||
|
||||
|
||||
file_put_contents($filename, $content);
|
||||
|
||||
@chmod($filename, 0777);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
105
app/Services/Cron/FreenodeFile.php
Executable file
105
app/Services/Cron/FreenodeFile.php
Executable 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
23
app/Services/Cron/FreenodeLog.php
Executable file
23
app/Services/Cron/FreenodeLog.php
Executable 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;
|
||||
}
|
||||
|
||||
}
|
||||
423
app/Services/Cron/FreenodeMerge.php
Executable file
423
app/Services/Cron/FreenodeMerge.php
Executable file
@ -0,0 +1,423 @@
|
||||
<?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);
|
||||
|
||||
@chmod($sFilePath, 0777);
|
||||
|
||||
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);
|
||||
|
||||
@chmod($sFileSavePath, 0777);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
501
app/Services/Cron/FreenodePool.php
Executable file
501
app/Services/Cron/FreenodePool.php
Executable file
@ -0,0 +1,501 @@
|
||||
<?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") {
|
||||
|
||||
$bom = pack('H*','EFBBBF');
|
||||
$sFromFileContent = preg_replace("/^$bom/", '', $sFromFileContent);
|
||||
$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;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
57
app/Services/Cron/FreenodeSync.php
Executable file
57
app/Services/Cron/FreenodeSync.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?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::同步失败 ->".$oFnSite->name."->".$sResult;
|
||||
echo $sMsg;
|
||||
TeleSlave::warn()->setMsgS($sMsg)->send();
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo " - ok \n";
|
||||
}
|
||||
}
|
||||
25
app/Services/Cron/FreenodeWatch.php
Executable file
25
app/Services/Cron/FreenodeWatch.php
Executable file
@ -0,0 +1,25 @@
|
||||
<?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 FreenodeWatch
|
||||
{
|
||||
static function run()
|
||||
{
|
||||
echo "\n\nFreenodeWatch::run() - 开始";
|
||||
|
||||
$sDirBase = $_ENV["dir_base"];
|
||||
|
||||
$oFnSiteList = ModelSiteMap::where("group_code", "fn_a")->get();
|
||||
|
||||
// foreach ()
|
||||
|
||||
echo "\n - ok";
|
||||
}
|
||||
}
|
||||
91
app/Services/Cron/HttpQueue.php
Executable file
91
app/Services/Cron/HttpQueue.php
Executable file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Cron;
|
||||
|
||||
//use App\Services\Cron;
|
||||
use App\Models\HttpQueue as ModelHttpQueue;
|
||||
use App\Services\TomTool\Telegram\Master as TelegramMaster;
|
||||
use App\Services\TomTool\Telegram\Slave as TelegramSlave;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
use Telegram\Bot\Api;
|
||||
//use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use Telegram\Bot\Exceptions\TelegramResponse;
|
||||
|
||||
final class HttpQueue
|
||||
{
|
||||
|
||||
public static function pop()
|
||||
{
|
||||
echo "\nhttpQueue::pop - 开始\n";
|
||||
$oHttpQueue = ModelHttpQueue::where("status", 1)->get();
|
||||
|
||||
foreach ($oHttpQueue as $oHttpQueueRow) {
|
||||
|
||||
$aArg = json_decode($oHttpQueueRow->arg, true);
|
||||
|
||||
$sBotCode = $aArg["sBotCode"] ?? '';
|
||||
$sChatCode = $aArg["sChatCode"] ?? '';
|
||||
$sBotToken = $aArg["sBotToken"] ?? '';
|
||||
$iChatId = $aArg["iChatId"] ?? 0;
|
||||
$sMsg = $aArg["sMsg"] ?? '';
|
||||
$sPhotoUrl = $aArg["sPhotoUrl"] ?? '';
|
||||
$iPhotoWidth = $aArg["iPhotoWidth"] ?? 0;
|
||||
$iPhotoHeight = $aArg["iPhotoHeight"] ?? 0;
|
||||
$enableMaster = $aArg["enableMaster"] ?? null;
|
||||
$sType = $aArg["sType"] ?? '';
|
||||
|
||||
if ($enableMaster) {
|
||||
$oTgSlave = TelegramSlave::start()
|
||||
->enableDetail(false)
|
||||
->enableMaster(true)
|
||||
->enableAsync(false)
|
||||
->enableSlaveQueue(false)
|
||||
->setBotCode($sBotCode)
|
||||
->setChatCode($sChatCode)
|
||||
->setBotToken($sBotToken)
|
||||
->setChatId($iChatId)
|
||||
->setMsgS($sMsg)
|
||||
->setPhotoUrl($sPhotoUrl, $iPhotoWidth, $iPhotoHeight)
|
||||
->setType($sType)
|
||||
->send();
|
||||
|
||||
if ($oTgSlave->isFail()) {
|
||||
echo "失败:从telegram_local发送到master失败\n";
|
||||
var_dump($oTgSlave->getResult());
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "发送到master成功\n";
|
||||
} else { // 直接发送到tg,必须有token和id
|
||||
$oTgSlave = TelegramSlave::start()
|
||||
->enableDetail(false)
|
||||
->enableMaster(false)
|
||||
->setBotCode($sBotCode)
|
||||
->setChatCode($sChatCode)
|
||||
->setBotToken($sBotToken)
|
||||
->setChatId($iChatId)
|
||||
->setMsgS($sMsg)
|
||||
->setPhotoUrl($sPhotoUrl, $iPhotoWidth, $iPhotoHeight)
|
||||
->setType($sType)
|
||||
->send();
|
||||
|
||||
if ($oTgSlave->isFail()) {
|
||||
echo "失败:从telegram_local发送到tg失败\n";
|
||||
var_dump($oTgSlave->getResult());
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "发送到tg成功\n";
|
||||
}
|
||||
|
||||
$oHttpQueueRow->delete();
|
||||
}
|
||||
|
||||
echo "ok\n";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
55
app/Services/HttpQueue.php
Executable file
55
app/Services/HttpQueue.php
Executable file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\HttpQueue as ModelHttpQueue;
|
||||
use App\Services\TomTool\Flow;
|
||||
|
||||
final class HttpQueue
|
||||
{
|
||||
public $sName;
|
||||
public $aArg = [];
|
||||
public $sUrl;
|
||||
|
||||
public static function start(string $sName = ''): self
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->sName = $sName;
|
||||
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function url($sUrl)
|
||||
{
|
||||
$this->sUrl = $sUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function arg($aArg)
|
||||
{
|
||||
$this->aArg = $aArg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function save(): Flow
|
||||
{
|
||||
$oResult = Flow::start("httpQueue");
|
||||
|
||||
$oModelHttpQueue = new ModelHttpQueue();
|
||||
$oModelHttpQueue->name = $this->sName;
|
||||
$oModelHttpQueue->url = $this->sUrl;
|
||||
$oModelHttpQueue->arg = json_encode($this->aArg, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|
||||
$r = $oModelHttpQueue->save();
|
||||
|
||||
if (!$r) {
|
||||
return $oResult->fail("进入队列失败");
|
||||
}
|
||||
|
||||
return $oResult->done("成功");
|
||||
}
|
||||
|
||||
}
|
||||
120
app/Services/SlaveMap.php
Executable file
120
app/Services/SlaveMap.php
Executable file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\SiteMap as ModelSiteMap;
|
||||
use App\Models\SitePathMap as ModelSitePathMap;
|
||||
use App\Models\ArticleSiteMapGroup as ModelArticleSiteMapGroup;
|
||||
use App\Services\TomTool\Flow;
|
||||
|
||||
final class SlaveMap
|
||||
{
|
||||
|
||||
public static function siteByGroup(string $sSiteGroupCode): array
|
||||
{
|
||||
$aSiteList = ModelSiteMap::where("group_code", $sSiteGroupCode)->pluck("code")->toArray();
|
||||
|
||||
return $aSiteList;
|
||||
}
|
||||
|
||||
public static function siteGroupByArticleGroup(string $sArticleGoupCode)
|
||||
{
|
||||
$aSiteGroupList = ModelArticleSiteMapGroup::where("article_group_code", $sArticleGoupCode)->pluck("site_group_code")->toArray();
|
||||
|
||||
return $aSiteGroupList;
|
||||
}
|
||||
|
||||
// public static function siteByArticleGroup(string $sArticleGoupCode)
|
||||
// {
|
||||
// $aSiteGroupList = self::siteGroupByArticleGroup($sArticleGoupCode);
|
||||
//
|
||||
// $aSiteList = ModelSiteMap::whereIn("group_code", $aSiteGroupList)->orderBy("group_code")->get()->toArray();
|
||||
//
|
||||
// return $aSiteList;
|
||||
// }
|
||||
|
||||
public static function cmdListByArticleGroup(string $sArticleGoupCode): array
|
||||
{
|
||||
$aSiteGroupList = self::siteGroupByArticleGroup($sArticleGoupCode);
|
||||
|
||||
$aCmdList = [];
|
||||
foreach ($aSiteGroupList as $sSiteGroupCode) {
|
||||
|
||||
$aSiteList = self::siteByGroup($sSiteGroupCode);
|
||||
|
||||
foreach ($aSiteList as $sSiteCode) {
|
||||
$sCmdKey = "|{$sSiteGroupCode}|{$sSiteCode}|";
|
||||
$aCmdList[] = $sCmdKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $aCmdList;
|
||||
}
|
||||
|
||||
// public static function cmdTreeByArticleGroup(string $sArticleGoupCode, $xDefaultCmd = null): array
|
||||
// {
|
||||
// $aSiteGroupList = self::siteGroupByArticleGroup($sArticleGoupCode);
|
||||
//
|
||||
// $aTree = [];
|
||||
// foreach ($aSiteGroupList as $sSiteGroupCode) {
|
||||
//
|
||||
// $aSiteList = self::siteByGroup($sSiteGroupCode);
|
||||
//
|
||||
// foreach ($aSiteList as $sSiteCode) {
|
||||
// $aTree[$sSiteGroupCode][$sSiteCode] = $xDefaultCmd;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// return $aTree;
|
||||
// }
|
||||
|
||||
public static function sitePathByGroup(string $sSiteGroupCode, string $sPathGroupCode): Flow
|
||||
{
|
||||
$oFlow = Flow::start("sitePathByGroup");
|
||||
|
||||
$oSiteList = ModelSiteMap::where("group_code", $sSiteGroupCode)->get();
|
||||
|
||||
if ($oSiteList->isEmpty()) {
|
||||
return $oFlow->fail("no have site");
|
||||
}
|
||||
|
||||
$sPath = ModelSitePathMap::where("site_group_code", $sSiteGroupCode)->where("path_group_code", $sPathGroupCode)->first()?->path;
|
||||
|
||||
if (!$sPath) {
|
||||
return $oFlow->fail("no have path");
|
||||
}
|
||||
|
||||
$aSitePath = [];
|
||||
foreach ($oSiteList as $oSite) {
|
||||
$aSitePath[] = $oSite->url.$sPath;
|
||||
}
|
||||
|
||||
return $oFlow->aData($aSitePath)->done();
|
||||
}
|
||||
|
||||
public static function sitePathByCode(string $sSiteCode, string $sPathGroupCode): Flow
|
||||
{
|
||||
$oFlow = Flow::start("sitePathByCode");
|
||||
|
||||
$oSite = ModelSiteMap::where("code", $sSiteCode)->first();
|
||||
|
||||
if (!$oSite) {
|
||||
return $oFlow->fail("no have site");
|
||||
}
|
||||
|
||||
$sPath = ModelSitePathMap::where("site_group_code", $oSite->group_code)->where("path_group_code", $sPathGroupCode)->first()?->path;
|
||||
|
||||
if (!$sPath) {
|
||||
return $oFlow->fail("no have path");
|
||||
}
|
||||
|
||||
$sSitePath = $oSite->url.$sPath;
|
||||
|
||||
return $oFlow->sData($sSitePath)->done();
|
||||
}
|
||||
|
||||
}
|
||||
1052
app/Services/Tg/Hook/Dog.php
Executable file
1052
app/Services/Tg/Hook/Dog.php
Executable file
File diff suppressed because it is too large
Load Diff
57
app/Services/Tg/Hook/Mod/Base.php
Executable file
57
app/Services/Tg/Hook/Mod/Base.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
//use App\Services\TomTool\Http;
|
||||
//use App\Models\Article as ModelArticle;
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Tg\Http as TGHttp;
|
||||
|
||||
class Base {
|
||||
|
||||
public function __construct() {
|
||||
$this->oHttp = new Http();
|
||||
$this->oTGHttp = new TGHttp();
|
||||
// $this->oTGHttp->sApiToken = env("telegram_token");
|
||||
// $this->oTGHttp->sApiChatId = env("telegram_chatid");
|
||||
}
|
||||
|
||||
public function toJson($aData)
|
||||
{
|
||||
return json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
public function eachSendTg($aData)
|
||||
{
|
||||
foreach ($aData as $row) {
|
||||
$this->oTGHttp->sendToTG(json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
$this->oHttp::response(200, 'ok');
|
||||
}
|
||||
|
||||
// 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;
|
||||
// }
|
||||
//
|
||||
// return $sCmdNew;
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
70
app/Services/Tg/Hook/Mod/Cron.php
Executable file
70
app/Services/Tg/Hook/Mod/Cron.php
Executable file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Cron as ServicesCron;
|
||||
use App\Services\Cron\HttpQueue as CronHttpQueue;
|
||||
|
||||
class Cron extends Base {
|
||||
|
||||
public function httpQueuePop()
|
||||
{
|
||||
$r = CronHttpQueue::pop();
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function test($aParam)
|
||||
{
|
||||
$sType = $aParam["aOption"][1] ?? "i";
|
||||
$sValue = $aParam["aArg"][1] ?? "03";
|
||||
|
||||
if (!$sType) {
|
||||
return "需要his之一";
|
||||
}
|
||||
|
||||
if (!$sValue) {
|
||||
return "需要value";
|
||||
}
|
||||
|
||||
if ($sValue == "?") {
|
||||
return "#test__[his] [value]";
|
||||
}
|
||||
|
||||
$oServicesCron = new ServicesCron();
|
||||
|
||||
$oServicesCron->setTestH('06');
|
||||
$oServicesCron->setTestI('03');
|
||||
$oServicesCron->setTestS('00');
|
||||
|
||||
if ($sType == "h") {
|
||||
$oServicesCron->setTestH($sValue);
|
||||
} else if ($sType == "i") {
|
||||
$oServicesCron->setTestI($sValue);
|
||||
} else if ($sType == "s") {
|
||||
$oServicesCron->setTestS($sValue);
|
||||
}
|
||||
|
||||
return $oServicesCron->run();
|
||||
}
|
||||
|
||||
public function testOld($aParam)
|
||||
{
|
||||
$sType = $aParam["aArg"][1] ?? false;
|
||||
$sValue = $aParam["aArg"][2] ?? false;
|
||||
|
||||
$oHttp = new Http();
|
||||
$oHttp->sMethod = "get";
|
||||
$oHttp->bIsJson = true;
|
||||
$oHttp->sToUrl = env("APP_URL")."/cron";
|
||||
|
||||
if ($sType && $sValue) {
|
||||
$oHttp->aData = ["k" => $sType, "v" => $sValue];
|
||||
}
|
||||
|
||||
$r = $oHttp->send();
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
243
app/Services/Tg/Hook/Mod/Dove.php
Executable file
243
app/Services/Tg/Hook/Mod/Dove.php
Executable file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
//use App\Models\Hook as ModelHook;
|
||||
use App\Models\DoveSite as ModelDoveSite;
|
||||
use App\Services\TomTool\Http;
|
||||
use App\Services\Tg\Http as TGHttp;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
|
||||
class Dove extends Base {
|
||||
|
||||
private $oDog;
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
|
||||
$this->oDog = new Dog(new ModelDoveSite());
|
||||
$this->oDog->_setDogName("dove"); // 必须,me用,正常就是类名,只不过后期没准改名,所以就这里写死
|
||||
$this->oDog->_setPrimaryKey("code"); // 可选,默认id
|
||||
// $this->oDog->_setStrField(["content"]); // 可选,指定长文本字段
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
|
||||
}
|
||||
|
||||
public function go($aParam, $aData)
|
||||
{
|
||||
$oDoveSiteCheckedList = ModelDoveSite::where("dove_checked", 1)->get();
|
||||
|
||||
list(, $sSubCmd) = explode("$", $aData['message']['text'], 2);
|
||||
|
||||
$aData['message']['text'] = $sSubCmd;
|
||||
|
||||
$aData['is_dove'] = 1;
|
||||
|
||||
$aData['dove_token'] = env('dove_token');
|
||||
|
||||
$oTGHttp = new TGHttp();
|
||||
$oHttp = new Http();
|
||||
$oHttp->sMethod = "post";
|
||||
$oHttp->bIsJson = true;
|
||||
$r = [];
|
||||
foreach ($oDoveSiteCheckedList as $row) {
|
||||
|
||||
$sApiUrl = "https://".$row->www.$row->api_path;
|
||||
$oHttp->sToUrl = $sApiUrl;
|
||||
$oHttp->aData = $aData;
|
||||
$tmp = $oHttp->send();
|
||||
|
||||
$r = "***".$row->code."*** \n\n".$tmp;
|
||||
$oTGHttp->sendToTG($r);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Http::response(200);
|
||||
}
|
||||
|
||||
// public function del($aParam)
|
||||
// {
|
||||
// $sCode = $aParam["aArg"][1] ?? false;
|
||||
//
|
||||
// if (!$sCode) {
|
||||
// return "要code";
|
||||
// }
|
||||
//
|
||||
// $oDove = ModelDoveSite::where("code", $sCode)->first();
|
||||
//
|
||||
// if (!$oDove) {
|
||||
// return "没有";
|
||||
// }
|
||||
//
|
||||
// $oDove->delete();
|
||||
//
|
||||
// return $this->list();
|
||||
// }
|
||||
|
||||
// public function add($aParam)
|
||||
// {
|
||||
// $sCode = $aParam["aArg"][1];
|
||||
//
|
||||
// $oDove = new ModelDoveSite();
|
||||
//
|
||||
// $oDove->code = $sCode;
|
||||
//
|
||||
// $oDove->save();
|
||||
//
|
||||
// return $this->list();
|
||||
// }
|
||||
|
||||
// public function set($aParam)
|
||||
// {
|
||||
// $sCode = $aParam["aOption"][1] ?? false;
|
||||
// $k = $aParam["aOption"][2] ?? false;
|
||||
// $v = $aParam["aArg"][1] ?? false;
|
||||
//
|
||||
// if (!$sCode) {
|
||||
// return "需要code";
|
||||
// }
|
||||
//
|
||||
// $oDove = ModelDoveSite::where("code", $sCode)->first();
|
||||
//
|
||||
// if (!$oDove) {
|
||||
// return "没有";
|
||||
// }
|
||||
//
|
||||
// $oDove->$k = $v;
|
||||
//
|
||||
// $oDove->save();
|
||||
//
|
||||
// return $this->toJson($oDove);
|
||||
// }
|
||||
|
||||
// public function list()
|
||||
// {
|
||||
// $oDove = ModelDoveSite::all();
|
||||
//
|
||||
// $r = $this->toJson($oDove);
|
||||
//
|
||||
// return $r;
|
||||
// }
|
||||
|
||||
// public function class($aParam)
|
||||
// {
|
||||
// $sClass = $aParam["aArg"][1] ?? false;
|
||||
//
|
||||
// if ($sClass == "?") {
|
||||
// return "/dove#class <class>";
|
||||
// }
|
||||
//
|
||||
// if (!$sClass) {
|
||||
// return "需要class";
|
||||
// }
|
||||
//
|
||||
// $aParam["aOption"][1] = "class";
|
||||
// $aParam["aArg"][1] = $sClass;
|
||||
//
|
||||
// return $this->where($aParam);
|
||||
// }
|
||||
|
||||
// public function where($aParam)
|
||||
// {
|
||||
// $sField = $aParam["aOption"][1] ?? false;
|
||||
// $sValue = $aParam["aArg"][1] ?? false;
|
||||
//
|
||||
// if ($sValue == "?") {
|
||||
// return "/dove#where__<firld> <value>";
|
||||
// }
|
||||
//
|
||||
// if (!$sField) {
|
||||
// return "需要field";
|
||||
// }
|
||||
//
|
||||
// if (!$sValue) {
|
||||
// return "需要value";
|
||||
// }
|
||||
//
|
||||
// $oDoveSite = ModelDoveSite::where($sField, $sValue)->get();
|
||||
//
|
||||
// if ($oDove->isEmpty()) {
|
||||
// return "没找到这个";
|
||||
// }
|
||||
//
|
||||
// return $this->toJson($oDoveSite);
|
||||
// }
|
||||
|
||||
public function check($aParam)
|
||||
{
|
||||
$iSetValue = $aParam['iSetValue'] ?? 1;
|
||||
$sCode = $aParam['aArg'][1];
|
||||
|
||||
$oDoveSite = ModelDoveSite::where("code", $sCode)->first();
|
||||
|
||||
if (!$oDoveSite) {
|
||||
throw new \Exception("没有这个code");
|
||||
}
|
||||
|
||||
$oDoveSite->dove_checked = $iSetValue;
|
||||
|
||||
$r = $oDoveSite->save();
|
||||
|
||||
if (!$r) {
|
||||
throw new \Exception("没check上?");
|
||||
}
|
||||
|
||||
$r = $this->now();
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function checkOne($aParam)
|
||||
{
|
||||
ModelDoveSite::query()->update(["dove_checked" => 0]);
|
||||
|
||||
return $this->check($aParam);
|
||||
}
|
||||
|
||||
public function checkClear()
|
||||
{
|
||||
|
||||
$r = ModelDoveSite::query()->update(["dove_checked" => 0]);
|
||||
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function checkUn($aParam)
|
||||
{
|
||||
$sCode = $aParam["aArg"][1];
|
||||
|
||||
$r = ModelDoveSite::where("code", $sCode)->update(["dove_checked" => 0]);
|
||||
|
||||
return $this->now();
|
||||
}
|
||||
|
||||
public function now()
|
||||
{
|
||||
$aDoveSiteNow = ModelDoveSite::where("dove_checked", 1)->get()->toArray();
|
||||
|
||||
// if ($aDoveSiteNow) {
|
||||
// $r = "";
|
||||
// foreach ($aDoveSiteNow as $row) {
|
||||
// $r .= "\n";
|
||||
// $r .= $row['code']."::".$row['www']."::".$row['class'];
|
||||
// }
|
||||
// } else {
|
||||
// $r = "没有";
|
||||
// }
|
||||
|
||||
if (!$aDoveSiteNow) {
|
||||
return "没有";
|
||||
}
|
||||
|
||||
$r = $this->toJson($aDoveSiteNow);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
69
app/Services/Tg/Hook/Mod/DoveSite.php
Executable file
69
app/Services/Tg/Hook/Mod/DoveSite.php
Executable file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Models\DoveSite as ModelDoveSite;
|
||||
|
||||
class DoveSite extends Base {
|
||||
|
||||
public function add($aParam)
|
||||
{
|
||||
$sCode = $aParam['aArg'][1];
|
||||
$oDoveSite = new ModelDoveSite();
|
||||
|
||||
$oDoveSite->code = $sCode;
|
||||
$r = $oDoveSite->save();
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function del($aParam)
|
||||
{
|
||||
$sCode = $aParam['aArg'][1];
|
||||
|
||||
$r = ModelDoveSite::where("code", $sCode)->delete();
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function limit($aParam)
|
||||
{
|
||||
$iLimit = $aParam['aArg'][1];
|
||||
|
||||
$oDoveSite = ModelDoveSite::orderBy("code")->limit($iLimit)->get();
|
||||
|
||||
$this->eachSendTg($oDoveSite);
|
||||
}
|
||||
|
||||
public function like($aParam)
|
||||
{
|
||||
$sField = $aParam['aOption'][1];
|
||||
$sValue = $aParam['aArg'][1];
|
||||
|
||||
$oDoveSiteLike = ModelDoveSite::where($sField, "like", "%".$sValue."%")->get();
|
||||
|
||||
$this->eachSendTg($oDoveSiteLike);
|
||||
|
||||
}
|
||||
|
||||
public function Set($aParam)
|
||||
{
|
||||
$sCode = $aParam['aOption'][1];
|
||||
$sField = $aParam['aOption'][2];
|
||||
$sValue = $aParam['aArg'][1];
|
||||
|
||||
$oDoveSite = ModelDoveSite::where("code", $sCode)->first();
|
||||
|
||||
if (!$oDoveSite) {
|
||||
throw new \Exception("没有这个code");
|
||||
}
|
||||
|
||||
$oDoveSite->$sField = $sValue;
|
||||
$r = $oDoveSite->save();
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
66
app/Services/Tg/Hook/Mod/Freenode.php
Executable file
66
app/Services/Tg/Hook/Mod/Freenode.php
Executable file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
//use App\Services\TomTool\Flow;
|
||||
use App\Services\TomTool\ThrowableHandler;
|
||||
//use App\Services\Cron\FreenodeMerge as CronFreenodeMerge;
|
||||
use App\Models\FreenodePool as ModelFreenodePool;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
use App\Services\TomTool\Map;
|
||||
use App\Models\SiteMap as ModelSiteMap;
|
||||
use App\Services\TomTool\Telegram\Slave as TeleSlave;
|
||||
|
||||
class Freenode extends Base {
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelFreenodePool());
|
||||
$this->oDog->_setDogName("Freenode");
|
||||
$this->oDog->_setJsonField(["content"]);
|
||||
// $this->oDog->_setStrField(["content"]);
|
||||
$this->aUpdate = $aUpdate;
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
public function show($aParam)
|
||||
{
|
||||
$sSiteCode = $aParam["aArg"][1] ?? "cfn";
|
||||
$sClient = $aParam["aOption"][1] ?? "clash";
|
||||
|
||||
if ($sSiteCode === "?") {
|
||||
return "/show#__[client:clash] [siteCodeShort]";
|
||||
}
|
||||
|
||||
$oSite = ModelSiteMap::where("code_short", $sSiteCode)->first();
|
||||
|
||||
if (!$oSite) {
|
||||
return "没有这个site";
|
||||
}
|
||||
|
||||
$sClientSfx = Map::fnClientToSfx($sClient);
|
||||
|
||||
$sFileName = date("Ymd")."-".$sClient.".".$sClientSfx;
|
||||
$sFileUrl = "https://".$oSite->url."/sub/".$sFileName;
|
||||
// $sFileUrl = "https://clashv2rayfree.com/sub/".$sFileName; // test
|
||||
|
||||
$oFlow = TeleSlave::start("admin", "admin")
|
||||
->enableSlaveQueue(false)
|
||||
->enableAsync(false)
|
||||
->enableDetail(false)
|
||||
->setFileUrl($sFileUrl, $sSiteCode."的".$sClient.".txt")
|
||||
->send();
|
||||
|
||||
if ($oFlow->isFail()) {
|
||||
var_dump($oFlow->getResult());
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
29
app/Services/Tg/Hook/Mod/FreenodeFrom.php
Executable file
29
app/Services/Tg/Hook/Mod/FreenodeFrom.php
Executable file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\TomTool\ThrowableHandler;
|
||||
//use App\Services\Cron\FreenodeMerge as CronFreenodeMerge;
|
||||
//use App\Models\FreenodePool as ModelFreenodePool;
|
||||
use App\Models\CrawlFreenode as ModelCrawlFreenode;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
|
||||
class FreenodeFrom extends Base {
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelCrawlFreenode());
|
||||
$this->oDog->_setDogName("crawlFreenode");
|
||||
$this->oDog->_setJsonField(["url_file", "file_real"]);
|
||||
// $this->oDog->_setStrField(["content"]);
|
||||
$this->aUpdate = $aUpdate;
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
}
|
||||
187
app/Services/Tg/Hook/Mod/FreenodePool.php
Executable file
187
app/Services/Tg/Hook/Mod/FreenodePool.php
Executable file
File diff suppressed because one or more lines are too long
48
app/Services/Tg/Hook/Mod/Hook.php
Executable file
48
app/Services/Tg/Hook/Mod/Hook.php
Executable file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Models\Hook as ModelHook;
|
||||
//use App\Models\SiteList as ModelSiteList;
|
||||
//use App\Services\TomTool\Http;
|
||||
|
||||
class Hook extends Base {
|
||||
|
||||
public function set($aParam, $aData)
|
||||
{
|
||||
$sKey = $aParam['aArg'][1];
|
||||
$iFromId = $aData["message"]["from"]["id"];
|
||||
|
||||
ModelHook::where("tg_user_id", $iFromId)->truncate();
|
||||
|
||||
$oModelHook = new ModelHook();
|
||||
$oModelHook->key = $sKey;
|
||||
$oModelHook->tg_user_id = $iFromId;
|
||||
$r = $oModelHook->save();
|
||||
|
||||
return $this->now($aParam, $aData);
|
||||
}
|
||||
|
||||
public function un($aParam, $aData)
|
||||
{
|
||||
$iFromId = $aData["message"]["from"]["id"];
|
||||
ModelHook::where("tg_user_id", $iFromId)->truncate();
|
||||
|
||||
return $this->now($aParam, $aData);
|
||||
}
|
||||
|
||||
public function now($aParam, $aData)
|
||||
{
|
||||
$iFromId = $aData["message"]["from"]["id"];
|
||||
$sHookKey = ModelHook::where("tg_user_id", $iFromId)->first()?->key;
|
||||
|
||||
if ($sHookKey) {
|
||||
$r = $sHookKey;
|
||||
} else {
|
||||
$r = "hook未挂载";
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
30
app/Services/Tg/Hook/Mod/HttpQueue.php
Executable file
30
app/Services/Tg/Hook/Mod/HttpQueue.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
use App\Models\HttpQueue as ModelHttpQueue;
|
||||
|
||||
class HttpQueue extends Base {
|
||||
|
||||
private $oDog;
|
||||
// private $aUpdate;
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelHttpQueue());
|
||||
$this->oDog->_setDogName("HttpQueue");
|
||||
// $this->oDog->_setPrimaryKey("code"); // 可选,默认id
|
||||
// $this->oDog->_setJsonField(["config"]);
|
||||
// $this->oDog->_setStrField(["content"]);
|
||||
// $this->aUpdate = $aUpdate;
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
49
app/Services/Tg/Hook/Mod/Option.php
Executable file
49
app/Services/Tg/Hook/Mod/Option.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
use App\Models\Option as ModelOption;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
|
||||
class Option extends Base {
|
||||
|
||||
private $oDog;
|
||||
|
||||
public function __construct($aUpdate = [])
|
||||
{
|
||||
$this->oDog = new Dog(new ModelOption());
|
||||
$this->oDog->_setPrimaryKey("k"); // 可选,默认id
|
||||
$this->oDog->_setDogName("option"); // 必须
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// }
|
||||
|
||||
}
|
||||
30
app/Services/Tg/Hook/Mod/SiteMap.php
Executable file
30
app/Services/Tg/Hook/Mod/SiteMap.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
use App\Models\SiteMap as ModelSiteMap;
|
||||
|
||||
class SiteMap extends Base {
|
||||
|
||||
private $oDog;
|
||||
// private $aUpdate;
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelSiteMap());
|
||||
$this->oDog->_setDogName("siteMap");
|
||||
$this->oDog->_setPrimaryKey("code"); // 可选,默认id
|
||||
$this->oDog->_setJsonField(["config", "api_path"]);
|
||||
// $this->oDog->_setStrField(["content"]);
|
||||
// $this->aUpdate = $aUpdate;
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
30
app/Services/Tg/Hook/Mod/SitePathMap.php
Executable file
30
app/Services/Tg/Hook/Mod/SitePathMap.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Models\SitePathMap as ModelSitePathMap;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
|
||||
class SitePathMap extends Base {
|
||||
|
||||
private $oDog;
|
||||
// private $aUpdate;
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelSitePathMap());
|
||||
$this->oDog->_setDogName("sitePathMap");
|
||||
// $this->oDog->_setPrimaryKey("code"); // 可选,默认id
|
||||
// $this->oDog->_setJsonField(["use_from"]);
|
||||
// $this->oDog->_setStrField(["content"]);
|
||||
// $this->aUpdate = $aUpdate;
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
31
app/Services/Tg/Hook/Mod/TelegramKey.php
Executable file
31
app/Services/Tg/Hook/Mod/TelegramKey.php
Executable file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg\Hook\Mod;
|
||||
|
||||
use App\Models\TelegramKey as ModelTelegramKey;
|
||||
//use App\Models\ArticleCache as ModelArticleCache;
|
||||
//use App\Models\ArticleCacheTime as ModelArticleCacheTime;
|
||||
use App\Services\Tg\Hook\Dog;
|
||||
//use App\Services\Article as ServiceArticle;
|
||||
//use App\Services\TomTool\Flow;
|
||||
//use App\Services\HttpQueue;
|
||||
//use App\Services\TomTool\TelegramVdb;
|
||||
|
||||
class TelegramKey extends Base {
|
||||
|
||||
private $oDog;
|
||||
|
||||
public function __construct($aUpdate)
|
||||
{
|
||||
$this->oDog = new Dog(new ModelTelegramKey());
|
||||
$this->oDog->_setDogName("telegram_key"); // 必须,me用,正常就是类名,只不过后期没准改名,所以就这里写死,要在最前面
|
||||
}
|
||||
|
||||
public function __call($sName, $aArg)
|
||||
{
|
||||
|
||||
return $this->oDog->$sName($aArg[0]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
108
app/Services/Tg/Http.php
Executable file
108
app/Services/Tg/Http.php
Executable file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Tg;
|
||||
use App\Services\TomTool\Http as TomHttp;
|
||||
|
||||
class Http {
|
||||
|
||||
public $sApiToken;
|
||||
public $sApiChatId;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->sApiToken = env('telegram_token');
|
||||
$this->sApiChatId = env('telegram_chatid');
|
||||
}
|
||||
|
||||
public function send($sMsg = "msg空", $aUpdate = [])
|
||||
{
|
||||
|
||||
$bSendTg = true;
|
||||
|
||||
$sShell = $aUpdate["shell"] ?? false;
|
||||
$bIsDove = $aUpdate["is_dove"] ?? false;
|
||||
|
||||
if ($sShell == "cmd") {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if ($bIsDove == true) {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if (env("is_loc") == true) {
|
||||
$bSendTg = false;
|
||||
}
|
||||
|
||||
if (is_array($sMsg)) {
|
||||
if ($bSendTg) {
|
||||
foreach ($sMsg as $sMsgRow) {
|
||||
$this->sendTg($sMsgRow);
|
||||
}
|
||||
} else {
|
||||
echo json_encode($sMsg, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
} else {
|
||||
if ($bSendTg) {
|
||||
$this->sendTg($sMsg);
|
||||
} else {
|
||||
echo $sMsg;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bSendTg) {
|
||||
TomHttp::response(200);
|
||||
} else {
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function sendTg($sMsg)
|
||||
{
|
||||
$oHttp = new TomHttp();
|
||||
$oHttp->sMethod = "post";
|
||||
$oHttp->sToUrl = "https://api.telegram.org/bot$this->sApiToken/sendMessage";
|
||||
// $oHttp->bIsJson = true;
|
||||
$oHttp->aData = [
|
||||
"chat_id" => $this->sApiChatId,
|
||||
"text" => $sMsg
|
||||
];
|
||||
|
||||
$oHttp->send();
|
||||
}
|
||||
|
||||
public function sendToTG($sMsg, $aUpdate = [])
|
||||
{
|
||||
|
||||
if (isset($aUpdate["shell"]) && $aUpdate["shell"] == "cmd") {
|
||||
|
||||
echo $sMsg;
|
||||
|
||||
} else if (isset($aUpdate["is_dove"]) && $aUpdate["is_dove"] == true) {
|
||||
|
||||
echo $sMsg;
|
||||
|
||||
} else {
|
||||
|
||||
if (env("is_loc") == true) {
|
||||
echo $sMsg;
|
||||
} else {
|
||||
$oHttp = new TomHttp();
|
||||
$oHttp->sMethod = "post";
|
||||
$oHttp->sToUrl = "https://api.telegram.org/bot$this->sApiToken/sendMessage";
|
||||
// $oHttp->bIsJson = true;
|
||||
$oHttp->aData = [
|
||||
"chat_id" => $this->sApiChatId,
|
||||
"text" => $sMsg
|
||||
];
|
||||
|
||||
$oHttp->send();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
415
app/Services/TomTool/Flow.php
Executable file
415
app/Services/TomTool/Flow.php
Executable file
@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use App\Services\TomTool\TelegramVdb;
|
||||
|
||||
/**
|
||||
|
||||
默认:
|
||||
name:none
|
||||
isDone:false
|
||||
|
||||
new:
|
||||
$oResult = Flow::make("name");
|
||||
|
||||
快速:
|
||||
Flow::make("name")->fail("kkkkkkk");
|
||||
|
||||
进阶:
|
||||
Flow::make("name")->noDone()->msg("哟哟哟哟哟哟")->sendTg()->fetch();
|
||||
|
||||
业务:
|
||||
step()
|
||||
msg()
|
||||
data()
|
||||
aData()
|
||||
jData()
|
||||
sData()
|
||||
code()
|
||||
put()
|
||||
merge()
|
||||
toDone()
|
||||
noDone()
|
||||
step("start")
|
||||
|
||||
action:
|
||||
reset()
|
||||
done(?$msg)
|
||||
fail(?$msg, ?$aParentResult)
|
||||
sendTg() 伪action
|
||||
fetch() or get()
|
||||
fetchAll() or geAll()
|
||||
|
||||
set:
|
||||
enableTg();
|
||||
parent($aParentResult);
|
||||
|
||||
with:
|
||||
withTraceId();
|
||||
withTimestamp();
|
||||
withStep();
|
||||
|
||||
判断:
|
||||
::isDone($aResult);
|
||||
::isFail($aResult);
|
||||
|
||||
*/
|
||||
|
||||
class Flow
|
||||
{
|
||||
protected array $aStep = [];
|
||||
protected array $aParent = [];
|
||||
protected bool $isToTg = false;
|
||||
protected bool $isToTgOne = false;
|
||||
protected array $aResult = [
|
||||
'isDone' => false,
|
||||
];
|
||||
protected string $sName;
|
||||
private bool $isChain = false;
|
||||
|
||||
public function __construct(string $name = 'none')
|
||||
{
|
||||
$this->sName = $name;
|
||||
$this->aStep[] = "{$this->sName}::start";
|
||||
}
|
||||
|
||||
// new
|
||||
public static function make(string $name = 'none'): self
|
||||
{
|
||||
return new self($name);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function start($sName = 'none')
|
||||
{
|
||||
$oInstance = new self($sName);
|
||||
$oInstance->isChain = true;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
// option
|
||||
public function enableTg(bool $flag = true): self
|
||||
{
|
||||
$this->isToTg = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// option
|
||||
public function enableTgOne(bool $flag = true): self
|
||||
{
|
||||
$this->enableTg(true);
|
||||
$this->isToTgOne = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// option
|
||||
public function parent(array $aParentResult): self
|
||||
{
|
||||
$this->aParent = $aParentResult;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function step(string $step): self
|
||||
{
|
||||
$this->aStep[] = "{$this->sName}::{$step}";
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function msg($msg = "msg未设置"): self
|
||||
{
|
||||
if (is_array($msg)) {
|
||||
$msg = json_encode($msg);
|
||||
}
|
||||
$this->put('sMsg', $msg);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function aData(array $data): self
|
||||
{
|
||||
$this->put('aData', $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function aDataPut($k, $v)
|
||||
{
|
||||
$this->aResult["aData"][$k] = $v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function sData(string $data): self
|
||||
{
|
||||
$this->put('sData', $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function code(string $code): self
|
||||
{
|
||||
$this->put('sCode', $code);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function put(string $key, mixed $value, $sDataKey = ''): self
|
||||
{
|
||||
if ($sDataKey) {
|
||||
$this->aResult[$sDataKey][$key] = $value;
|
||||
} else {
|
||||
$this->aResult[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function merge(array $data): self
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
$this->put($k, $v);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function setDone(bool $b = true): self
|
||||
{
|
||||
$this->aResult['isDone'] = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// data
|
||||
public function setFail(string $step = ''): self
|
||||
{
|
||||
if ($step) {
|
||||
$this->step($step);
|
||||
}
|
||||
$this->aResult['isDone'] = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withTraceId(string $id = null): self
|
||||
{
|
||||
$this->aResult['sTraceId'] = $id ?? uniqid('trace_', true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withTimestamp(): self
|
||||
{
|
||||
$this->aResult['sTimestamp'] = (new \DateTime())->format(\DateTime::ATOM);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// with
|
||||
public function withStep(bool $flag = true): self
|
||||
{
|
||||
if ($flag) {
|
||||
$this->withParentStep();
|
||||
$this->aResult['aStep'] = $this->aStep;
|
||||
} else {
|
||||
unset($this->aResult['aStep']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// action
|
||||
public function reset(): self
|
||||
{
|
||||
$this->aStep = [$this->sName . '::start'];
|
||||
$this->aParent = [];
|
||||
$this->isToTg = false;
|
||||
$this->isToTgOne = false;
|
||||
$this->aResult = ['isDone' => false];
|
||||
return $this;
|
||||
}
|
||||
|
||||
// action
|
||||
public function done(mixed $msg = null)
|
||||
{
|
||||
$this->setDone();
|
||||
if ($msg) {
|
||||
$this->msg($msg);
|
||||
}
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// action
|
||||
public function fail(mixed $msg = null, array $aParentResult = [])
|
||||
{
|
||||
$this->setFail('fail');
|
||||
if ($aParentResult) {
|
||||
$this->aParent = $aParentResult;
|
||||
}
|
||||
if ($msg) {
|
||||
$this->msg($msg);
|
||||
}
|
||||
return $this->getFull();
|
||||
}
|
||||
|
||||
// action
|
||||
// public function sendTg() // 伪装成非set
|
||||
// {
|
||||
// $this->enableTgOne();
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
// action
|
||||
public function sendTg($sBotCode = 'base', $sChatCode = 'base')
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getDataA()
|
||||
{
|
||||
return $this->aResult["aData"] ?? [];
|
||||
}
|
||||
|
||||
public function getDataS()
|
||||
{
|
||||
return (string) $this->aResult["sData"] ?? '';
|
||||
}
|
||||
|
||||
public function getDataI()
|
||||
{
|
||||
return (int) $this->aResult["iData"] ?? 0;
|
||||
}
|
||||
|
||||
public function getDataB()
|
||||
{
|
||||
return (bool) $this->aResult["bData"] ?? false;
|
||||
}
|
||||
|
||||
public function getDataX()
|
||||
{
|
||||
return $this->aResult["xData"] ?? '';
|
||||
}
|
||||
|
||||
public function setDataA(array $aData): self
|
||||
{
|
||||
$this->aData($aData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataB(bool $bData): self
|
||||
{
|
||||
$this->aResult["bData"] = $bData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataS(string $sData): self
|
||||
{
|
||||
$this->aResult["sData"] = $sData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataI(int $iData): self
|
||||
{
|
||||
$this->aResult["iData"] = $iData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataX($xData): self
|
||||
{
|
||||
$this->aResult["xData"] = $xData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResult(): array
|
||||
{
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// action
|
||||
public function get()
|
||||
{
|
||||
if ($this->isToTg) {
|
||||
$this->_sendTg();
|
||||
}
|
||||
|
||||
if ($this->isToTgOne) {
|
||||
$this->isToTg = false;
|
||||
$this->isToTgOne = false;
|
||||
}
|
||||
|
||||
if ($this->isChain) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// action
|
||||
public function getFull()
|
||||
{
|
||||
$this->withStep();
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// action
|
||||
public function toArray()
|
||||
{
|
||||
return $this->aResult;
|
||||
}
|
||||
|
||||
// helper
|
||||
public function isDone()
|
||||
{
|
||||
return $this->aResult["isDone"] ?? false;
|
||||
}
|
||||
|
||||
// helper
|
||||
public function isFail()
|
||||
{
|
||||
return !$this->isDone();
|
||||
}
|
||||
|
||||
// helper
|
||||
public static function isDoneResult(array $aResult): bool
|
||||
{
|
||||
return $aResult['isDone'] ?? false;
|
||||
}
|
||||
|
||||
// helper
|
||||
public static function isFailResult(array $aResult): bool
|
||||
{
|
||||
return !self::isDoneResult($aResult);
|
||||
}
|
||||
|
||||
// shell
|
||||
public function fetch()
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
// shell
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->getFull();
|
||||
}
|
||||
|
||||
private function _sendTg()
|
||||
{
|
||||
TelegramVdb::make()->dataArr($this->aResult)->save();
|
||||
}
|
||||
|
||||
protected function addStepParent(): void
|
||||
{
|
||||
$this->aStep[] = $this->aParent;
|
||||
}
|
||||
|
||||
protected function withParentStep(): void
|
||||
{
|
||||
if ($this->aParent) {
|
||||
$this->addStepParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
app/Services/TomTool/Http.php
Executable file
93
app/Services/TomTool/Http.php
Executable file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Http
|
||||
{
|
||||
public $sMethod = 'get';
|
||||
public $aData = [];
|
||||
public $sToUrl; // 必须
|
||||
public $bIsJson = false;
|
||||
|
||||
public function send($sUrl = false): string
|
||||
{
|
||||
// 两种传递方式
|
||||
if ($sUrl) {
|
||||
$this->sToUrl = $sUrl;
|
||||
}
|
||||
|
||||
// 初始化 cURL 会话
|
||||
$ch = curl_init();
|
||||
|
||||
// 设置请求 URL
|
||||
curl_setopt($ch, CURLOPT_URL, $this->sToUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应而不是输出
|
||||
|
||||
// 根据请求方法设置 cURL 选项
|
||||
switch (strtolower($this->sMethod)) {
|
||||
case 'post':
|
||||
curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->aData)); // 设置 POST 数据为 JSON
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json', // 设置内容类型为 JSON
|
||||
'Content-Length: ' . strlen(json_encode($this->aData)) // 设置内容长度
|
||||
]);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->aData)); // 设置 POST 数据为普通表单格式
|
||||
}
|
||||
break;
|
||||
|
||||
case 'put':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // 设置为 PUT 请求
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->aData)); // 设置 PUT 数据为 JSON
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json', // 设置内容类型为 JSON
|
||||
'Content-Length: ' . strlen(json_encode($this->aData)) // 设置内容长度
|
||||
]);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->aData)); // 设置 PUT 数据为普通表单格式
|
||||
}
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
// 默认使用 GET 请求
|
||||
curl_setopt($ch, CURLOPT_HTTPGET, true); // 设置为 GET 请求
|
||||
curl_setopt($ch, CURLOPT_URL, $this->sToUrl . '?' . http_build_query($this->aData)); // 设置请求 URL
|
||||
if ($this->bIsJson) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: application/json' // 设置接受的内容类型为 JSON
|
||||
]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 禁用 SSL 验证
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证对等证书
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名
|
||||
|
||||
// 执行 cURL 请求
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// 检查是否有错误
|
||||
if (curl_errno($ch)) {
|
||||
return 'Error: ' . curl_error($ch);
|
||||
}
|
||||
|
||||
// 关闭 cURL 会话
|
||||
curl_close($ch);
|
||||
|
||||
// 返回响应
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function response($iCode, $sMsg = '')
|
||||
{
|
||||
http_response_code($iCode);
|
||||
echo $sMsg;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
261
app/Services/TomTool/HttpV2.php
Executable file
261
app/Services/TomTool/HttpV2.php
Executable file
@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
// 版本:25-12-12
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use App\Services\TomTool\Flow;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
|
||||
默认:
|
||||
method:post
|
||||
|
||||
new:
|
||||
HttpV2::make($url)
|
||||
|
||||
基本:
|
||||
HttpV2::make($url)->send()
|
||||
|
||||
进阶:
|
||||
HttpV2::make($url, "get")->setDataA($aData)->enableJsonAs()->enableAsync()->optMethod("get")->send();
|
||||
|
||||
action:
|
||||
send()
|
||||
|
||||
data:
|
||||
xData()
|
||||
sData()
|
||||
jData()
|
||||
sData()
|
||||
|
||||
set:
|
||||
optMethod()
|
||||
enableJsonAs()
|
||||
enableJsonSend()
|
||||
enableJsonReturn()
|
||||
|
||||
@note:
|
||||
默认关闭 SSL 验证(适用于开发环境),生产环境建议开启
|
||||
异步模式下不会返回响应内容,仅快速触发请求
|
||||
|
||||
*/
|
||||
|
||||
|
||||
class HttpV2
|
||||
{
|
||||
protected string $method;
|
||||
protected $data = [];
|
||||
protected string $url;
|
||||
protected bool $sendJson = false;
|
||||
protected bool $returnJson = false;
|
||||
protected bool $async = false;
|
||||
// private bool $isChain = false;
|
||||
// private array $aResult = [
|
||||
// "isDone" = false
|
||||
// ];
|
||||
|
||||
public static function make(string $url, string $method = 'post'): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->url = $url;
|
||||
$instance->optMethod($method);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
// public static function start(string $sUrl, string $sMethod = 'post'): self
|
||||
// {
|
||||
// $oInstance = new self();
|
||||
// $oInstance->url = $sUrl;
|
||||
// $oInstance->method = $sMethod;
|
||||
// $oInstance->isChain = true;
|
||||
// return $oInstance;
|
||||
// }
|
||||
//
|
||||
// public function isDone()
|
||||
// {
|
||||
// return $this->aResult["isDone"] ?? false;
|
||||
// }
|
||||
//
|
||||
// public function isFail()
|
||||
// {
|
||||
// return !$this->isDone();
|
||||
// }
|
||||
|
||||
// public function getResult()
|
||||
// {
|
||||
// return $this->aResult;
|
||||
// }
|
||||
|
||||
public function optMethod(string $method): self
|
||||
{
|
||||
$this->method = strtolower($method);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataX($xData)
|
||||
{
|
||||
$this->setData($xData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataA($aData)
|
||||
{
|
||||
$this->setData($aData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataS($sData)
|
||||
{
|
||||
$this->setData($sData);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData($xData)
|
||||
{
|
||||
// if (is_array($xData)) {
|
||||
// json_encode($xData)
|
||||
// }
|
||||
$this->data = $xData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// public function aData($aData = [])
|
||||
// {
|
||||
// $this->data($aData);
|
||||
// return $this;
|
||||
// }
|
||||
//
|
||||
// public function sData($sData = '')
|
||||
// {
|
||||
// $this->data($sData);
|
||||
// return $this;
|
||||
// }
|
||||
//
|
||||
// public function jData($jData = '')
|
||||
// {
|
||||
// $this->data($jData);
|
||||
// return $this;
|
||||
// }
|
||||
//
|
||||
// public function xData($xData = '')
|
||||
// {
|
||||
// $this->data($xData);
|
||||
// return $this;
|
||||
// }
|
||||
//
|
||||
// public function data($data = ''): self
|
||||
// {
|
||||
// $this->data = $data;
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
public function enableJsonSend(bool $flag = true): self
|
||||
{
|
||||
$this->sendJson = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableJsonReturn(bool $flag = true): self
|
||||
{
|
||||
$this->returnJson = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableJsonAs(): self
|
||||
{
|
||||
$this->sendJson = true;
|
||||
$this->returnJson = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableAsync(bool $flag = true): self
|
||||
{
|
||||
$this->async = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send(): string
|
||||
{
|
||||
$oFlow = Flow::make("HttpV2::send");
|
||||
|
||||
$ch = curl_init();
|
||||
$headers = [];
|
||||
|
||||
// 构建 URL 和请求体
|
||||
if ($this->method === 'get') {
|
||||
$url = $this->url . '?' . http_build_query($this->data);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
$payload = $this->sendJson ? json_encode($this->data) : http_build_query($this->data);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
|
||||
if ($this->method === 'post') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method));
|
||||
}
|
||||
|
||||
if ($this->sendJson) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers[] = 'Content-Length: ' . strlen($payload);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->returnJson) {
|
||||
$headers[] = 'Accept: application/json';
|
||||
}
|
||||
|
||||
if (!empty($headers)) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
|
||||
// SSL 设置
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
// 异步发送
|
||||
if ($this->async) {
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 200);
|
||||
curl_setopt($ch, CURLOPT_NOSIGNAL, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return 'sent';
|
||||
}
|
||||
|
||||
// 阻塞执行
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception("cURL Error: {$error}");
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
// $this->aResult = $oFlow->sData($response)->done();
|
||||
//
|
||||
// if ($this->isChain) {
|
||||
// return $this;
|
||||
// }
|
||||
//
|
||||
// return $this->aResult;
|
||||
}
|
||||
|
||||
// public static function response(int $code, string $message = ''): void
|
||||
// {
|
||||
// http_response_code($code);
|
||||
// echo $message;
|
||||
// exit;
|
||||
// }
|
||||
}
|
||||
26
app/Services/TomTool/Map.php
Executable file
26
app/Services/TomTool/Map.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Map
|
||||
{
|
||||
public static function fnClientToSfx($sClient)
|
||||
{
|
||||
$sfx = "";
|
||||
|
||||
switch ($sClient) {
|
||||
case "clash":
|
||||
$sfx = "yaml";
|
||||
break;
|
||||
case "v2ray":
|
||||
$sfx = "txt";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $sfx;
|
||||
}
|
||||
}
|
||||
177
app/Services/TomTool/Telegram/Master.php
Executable file
177
app/Services/TomTool/Telegram/Master.php
Executable file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
|
||||
use App\Services\HttpQueue as ServiceHttpQueue;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\TomTool\Telegram\Sdk as ServiceTelegramSdk;
|
||||
use App\Models\TelegramKey as ModelTelegramKey;
|
||||
|
||||
/**
|
||||
|
||||
注意是master服务器端专用的,这里会对code找map,slave不会。
|
||||
|
||||
new:
|
||||
start(?$sBodeCode, ?$sChatCode)
|
||||
|
||||
set:
|
||||
setBotCode()
|
||||
setChatCode()
|
||||
setBotToken()
|
||||
setChatId()
|
||||
setMsgS()
|
||||
|
||||
action:
|
||||
send()
|
||||
|
||||
*/
|
||||
|
||||
final class Master
|
||||
{
|
||||
public string $sBotCode = "base";
|
||||
public string $sChatCode = "base";
|
||||
public string $sBotToken = '';
|
||||
public int $iChatId = 0;
|
||||
|
||||
public string $sMsg = '';
|
||||
public string $sPhotoUrl = '';
|
||||
public int $iPhotoWidth = 0;
|
||||
public int $iPhotoHeight = 0;
|
||||
|
||||
public string $sFileUrl = '';
|
||||
public string $sFileName = '';
|
||||
|
||||
public string $sType = '';
|
||||
|
||||
public bool $enableQueue = false; // 应该用不到了
|
||||
|
||||
public bool $bKeyBegin = false;
|
||||
|
||||
public static function start($sBotCode = 'base', $sChatCode = 'base'): self
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->sBotCode = $sBotCode;
|
||||
$oInstance->sChatCode = $sChatCode;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function setBotCode(string $sBotCode): self
|
||||
{
|
||||
$this->sBotCode = $sBotCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatCode(string $sChatCode): self
|
||||
{
|
||||
$this->sChatCode = $sChatCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBotToken(string $sBotToken): self
|
||||
{
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatId(int $iChatId): self
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMsgS(string $sMsg): self
|
||||
{
|
||||
$this->sMsg = $sMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhotoUrl(string $sPhotoUrl, int $iPhotoWidth = 0, int $iPhotoHeight = 0): self
|
||||
{
|
||||
$this->sPhotoUrl = $sPhotoUrl;
|
||||
$this->iPhotoWidth = $iPhotoWidth;
|
||||
$this->iPhotoHeight = $iPhotoHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(string $sType): self
|
||||
{
|
||||
$this->sType = $sType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFileUrl(string $sFileUrl = '', string $sFileName = ''): self
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send($sMsg = ''): Flow
|
||||
{
|
||||
if ($sMsg) {
|
||||
$this->sMsg = $sMsg;
|
||||
}
|
||||
|
||||
if (!$this->sMsg) {
|
||||
// throw new \Exception("未设置msg");
|
||||
}
|
||||
|
||||
$this->keyBegin();
|
||||
|
||||
if ($this->enableQueue) {
|
||||
$aData = $this->buildDataTom();
|
||||
$oFlow = ServiceHttpQueue::start("telegram_local")->arg($aData)->save();
|
||||
} else {
|
||||
$oFlow = ServiceTelegramSdk::start()
|
||||
->setBotToken($this->sBotToken)
|
||||
->setChatId($this->iChatId)
|
||||
->setMsgS($this->sMsg)
|
||||
->setPhotoUrl($this->sPhotoUrl, $this->iPhotoWidth, $this->iPhotoHeight)
|
||||
->setFileUrl($this->sFileUrl, $this->sFileName)
|
||||
->setType($this->sType)
|
||||
->send();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
private function keyBegin()
|
||||
{
|
||||
if (!$this->bKeyBegin) {
|
||||
if (!$this->sBotToken) {
|
||||
$this->sBotToken = ModelTelegramKey::botTokenByCode($this->sBotCode);
|
||||
}
|
||||
|
||||
if (!$this->iChatId) {
|
||||
$this->iChatId = ModelTelegramKey::chatIdByCode($this->sChatCode);
|
||||
}
|
||||
|
||||
if (!$this->sBotToken || !$this->iChatId) {
|
||||
throw new \RuntimeException("botToken或chatId没找到,botCode={$this->sBotCode},chatCode={$this->sChatCode}");
|
||||
}
|
||||
|
||||
$this->bKeyBegin = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildDataTom(): array
|
||||
{
|
||||
return [
|
||||
"sBotCode" => $this->sBotCode ?? '',
|
||||
"sChatCode" => $this->sChatCode ?? '',
|
||||
"sBotToken" => $this->sBotToken ?? '',
|
||||
"iChatId" => $this->iChatId ?? '',
|
||||
"sPhotoUrl" => $this->sPhotoUrl ?? '',
|
||||
"iPhotoWidth" => $this->iPhotoWidth,
|
||||
"iPhotoHeight" => $this->iPhotoHeight,
|
||||
"sFileUrl" => $this->sFileUrl,
|
||||
"sFileName" => $this->sFileName,
|
||||
"sMsg" => $this->sMsg ?? '',
|
||||
"sType" => $this->sType ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
215
app/Services/TomTool/Telegram/Sdk.php
Executable file
215
app/Services/TomTool/Telegram/Sdk.php
Executable file
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
use Telegram\Bot\Api;
|
||||
use Telegram\Bot\FileUpload\InputFile;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use App\Services\TomTool\Flow;
|
||||
//use Intervention\Image\Laravel\Facades\Image;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Intervention\Image\Drivers\Gd\Driver;
|
||||
use App\Services\TomTool\HttpV2;
|
||||
|
||||
class Sdk
|
||||
{
|
||||
public $sMsgFormat = [];
|
||||
public $sMsgFormatType = 'none';
|
||||
public $sBotToken = '';
|
||||
public $iChatId = 0;
|
||||
public $sPhotoUrl = '';
|
||||
public $iPhotoWidth = 0;
|
||||
public $iPhotoHeight = 0;
|
||||
public $sFileUrl = '';
|
||||
public $sFileName = '';
|
||||
public $sMsg;
|
||||
public $oApi;
|
||||
|
||||
private const ALLOWED_FORMATS = ['none', 'html', 'markdown', 'markdownV2'];
|
||||
|
||||
public static function start()
|
||||
{
|
||||
$oInstance = new self();
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function setBotToken($sBotToken)
|
||||
{
|
||||
$this->oApi = new Api($sBotToken);
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setChatId($iChatId)
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMsgS($sMsg = '')
|
||||
{
|
||||
$this->sMsg = $sMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhotoUrl($sUrl = '', $iPhotoWidth = 0, $iPhotoHeight = 0)
|
||||
{
|
||||
$this->sPhotoUrl = $sUrl;
|
||||
$this->iPhotoWidth = $iPhotoWidth;
|
||||
$this->iPhotoHeight = $iPhotoHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFileUrl($sFileUrl = '', $sFileName = '')
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType($sType)
|
||||
{
|
||||
if ($sType) {
|
||||
$this->sMsgFormatType = $sType;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send()
|
||||
{
|
||||
$oFlow = Flow::start("tg_sdk_send");
|
||||
|
||||
$this->msgFormat();
|
||||
|
||||
// try {
|
||||
|
||||
if ($this->sPhotoUrl) {
|
||||
$r = $this->oApi->sendPhoto($this->sMsgFormat);
|
||||
} else if ($this->sFileUrl) {
|
||||
$r = $this->oApi->sendDocument($this->sMsgFormat);
|
||||
} else {
|
||||
$r = $this->oApi->sendMessage($this->sMsgFormat);
|
||||
}
|
||||
|
||||
if (!($r && $r->getMessageId())) {
|
||||
return $oFlow->fail($r);
|
||||
}
|
||||
// } catch (TelegramSDKException $e) {
|
||||
// return $oFlow->fail(['error' => $e->getMessage()]);
|
||||
// }
|
||||
|
||||
return $oFlow->done();
|
||||
}
|
||||
|
||||
public function getMsgFormat()
|
||||
{
|
||||
$this->msgFormat();
|
||||
|
||||
return $this->sMsgFormat;
|
||||
}
|
||||
|
||||
public function msgFormat()
|
||||
{
|
||||
if (!in_array($this->sMsgFormatType, self::ALLOWED_FORMATS, true)) {
|
||||
throw new \RuntimeException("不支持的格式类型: {$this->sMsgFormatType}");
|
||||
}
|
||||
|
||||
$method = 'msgFormat_' . $this->sMsgFormatType;
|
||||
$this->sMsgFormat = $this->$method();
|
||||
|
||||
if ($this->sPhotoUrl) {
|
||||
|
||||
$oManager = new ImageManager(new Driver());
|
||||
|
||||
$sTempPath = $_ENV['dir_base'].'public/upload';
|
||||
$sTempPath .= "/tg_output.png";
|
||||
|
||||
$sImg = file_get_contents($this->sPhotoUrl);
|
||||
$oImage = $oManager->read($sImg);
|
||||
|
||||
if ($this->iPhotoWidth && $this->iPhotoHeight) {
|
||||
$oImage = $oImage->cover($this->iPhotoWidth, $this->iPhotoHeight);
|
||||
}
|
||||
|
||||
$oImage->toJpg(80)->save($sTempPath);
|
||||
|
||||
$photo = InputFile::create($sTempPath, date("Y-m-d").'.jpg');
|
||||
|
||||
$this->sMsgFormat["photo"] = $photo;
|
||||
|
||||
$this->sMsgFormat["caption"] = $this->sMsgFormat["text"];
|
||||
unset($this->sMsgFormat["text"]);
|
||||
|
||||
} else if ($this->sFileUrl) {
|
||||
|
||||
if (!$this->sFileName) {
|
||||
$sFileName = date("Y-m-d H:i:s").'.txt';
|
||||
} else {
|
||||
$sFileName = $this->sFileName;
|
||||
}
|
||||
|
||||
$sFile = InputFile::create($this->sFileUrl, $sFileName);
|
||||
|
||||
$this->sMsgFormat["document"] = $sFile;
|
||||
$this->sMsgFormat["caption"] = $this->sMsgFormat["text"];
|
||||
unset($this->sMsgFormat["text"]);
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function msgFormat_none()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_html()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => strip_tags($this->sMsg, [
|
||||
'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del', 'span', 'tg-spoiler', 'a', 'tg-emoji',
|
||||
'code', 'pre',
|
||||
]),
|
||||
'parse_mode' => 'HTML',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_markdown()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function msgFormat_markdownV2()
|
||||
{
|
||||
return [
|
||||
'chat_id' => $this->iChatId,
|
||||
'text' => $this->sMsg,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
361
app/Services/TomTool/Telegram/Slave.php
Executable file
361
app/Services/TomTool/Telegram/Slave.php
Executable file
@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
// 版本 25/12/12
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool\Telegram;
|
||||
|
||||
use App\Services\TomTool\HttpV2;
|
||||
use App\Services\TomTool\Telegram\Sdk as ServiceTelegramSdk;
|
||||
use App\Services\TomTool\Flow;
|
||||
use App\Services\HttpQueue as ServiceHttpQueue;
|
||||
|
||||
/**
|
||||
|
||||
提醒:
|
||||
master自身已经没有队列了,收到slave消息直接转发
|
||||
|
||||
默认:
|
||||
走本地队列,发送到master
|
||||
走异步
|
||||
|
||||
new:
|
||||
start()
|
||||
log()
|
||||
warn()
|
||||
fail()
|
||||
notify()
|
||||
|
||||
opt:
|
||||
enableMaster(:true)
|
||||
enableMasterQueue(:true)
|
||||
enableSlaveQueue(:true)
|
||||
enableDetail(:true)
|
||||
|
||||
set:
|
||||
setMsgS()
|
||||
setMsgA()
|
||||
setBotCode()
|
||||
setChatCode()
|
||||
setBotToken()
|
||||
setChatId()
|
||||
|
||||
action:
|
||||
send()
|
||||
sendMaster()
|
||||
sendLocal()
|
||||
|
||||
*/
|
||||
|
||||
class Slave
|
||||
{
|
||||
public string $sBotCode;
|
||||
public string $sChatCode;
|
||||
|
||||
public string $sBotToken = '';
|
||||
public int $iChatId = 0;
|
||||
|
||||
public string $sMsg = '';
|
||||
public string $sPhotoUrl = '';
|
||||
public int $iPhotoWidth = 0;
|
||||
public int $iPhotoHeight = 0;
|
||||
|
||||
public string $sFileUrl = '';
|
||||
public string $sFileName = '';
|
||||
|
||||
public string $sType = '';
|
||||
|
||||
public bool $enableMaster = true;
|
||||
public bool $enableMasterQueue = false; // 这里之前想错了,不会有master队列,只在slave有,master转发就行了
|
||||
public bool $enableSlaveQueue = false;
|
||||
|
||||
public bool $enableDetail = true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// new
|
||||
public static function log(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("log", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function warn(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("warn", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function fail(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("fail", $sChatCode);
|
||||
}
|
||||
|
||||
// new
|
||||
public static function notify(string $sChatCode = 'admin'): self
|
||||
{
|
||||
return self::start("fail", $sChatCode);
|
||||
}
|
||||
|
||||
|
||||
// opt
|
||||
public function enableAsync(bool $b = true): self
|
||||
{
|
||||
$this->enableAsync = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableDetail(bool $b = true): self
|
||||
{
|
||||
$this->enableDetail = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableSlaveQueue(bool $b = true): self
|
||||
{
|
||||
$this->enableSlaveQueue = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableMaster(bool $b = true): self
|
||||
{
|
||||
$this->enableMaster = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// opt
|
||||
public function enableMasterQueue(bool $b = true): self
|
||||
{
|
||||
$this->enableMasterQueue = $b;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setPhotoUrl(string $sPhotoUrl, int $iWidth = 0, int $iHeight = 0): self
|
||||
{
|
||||
$this->sPhotoUrl = $sPhotoUrl;
|
||||
$this->iPhotoWidth = $iWidth;
|
||||
$this->iPhotoHeight = $iHeight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setFileUrl(string $sFileUrl, string $sFileName = ''): self
|
||||
{
|
||||
$this->sFileUrl = $sFileUrl;
|
||||
$this->sFileName = $sFileName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setType(string $sType): self
|
||||
{
|
||||
$this->sType = $sType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setMsgS(string $sMsg = ''): self
|
||||
{
|
||||
if ($this->enableDetail) {
|
||||
$sMsg = "[ ".$this->getEnvSiteCode()." | ".date("d:H:i:s")." ] ".$sMsg;
|
||||
}
|
||||
|
||||
$this->sMsg = $sMsg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setMsgA(array $aMsg = []): self
|
||||
{
|
||||
$aMsgNew = $aMsg;
|
||||
|
||||
if ($this->enableDetail) {
|
||||
$aMsgNew = [];
|
||||
$aMsgNew["site_code"] = $this->getEnvSiteCode();
|
||||
$aMsgNew["date"] = date("Y-m-d H:i:s");
|
||||
$aMsgNew["data"] = $aMsg;
|
||||
}
|
||||
|
||||
$this->sMsg = json_encode($aMsgNew, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setBotCode(string $sBotCode): self
|
||||
{
|
||||
$this->sBotCode = $sBotCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setChatCode(string $sChatCode): self
|
||||
{
|
||||
$this->sChatCode = $sChatCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setBotToken(string $sBotToken): self
|
||||
{
|
||||
$this->sBotToken = $sBotToken;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// set
|
||||
public function setChatId(int $iChatId): self
|
||||
{
|
||||
$this->iChatId = $iChatId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// 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);
|
||||
} else {
|
||||
$this->setMsgS($xMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->sMsg) {
|
||||
// throw new \Exception("未设置msg");
|
||||
}
|
||||
|
||||
if ($this->enableMaster) {
|
||||
$oFlow = $this->_send_master();
|
||||
} else {
|
||||
$oFlow = $this->_send_local();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
// action
|
||||
public function sendMaster($xMsg = ''): Flow
|
||||
{
|
||||
$this->enableMaster(true);
|
||||
return $this->send($xMsg);
|
||||
}
|
||||
|
||||
// action
|
||||
public function sendLocal($xMsg = ''): Flow
|
||||
{
|
||||
$this->enableMaster(false);
|
||||
return $this->send($xMsg);
|
||||
}
|
||||
|
||||
private function _send_master(): Flow
|
||||
{
|
||||
$aData = $this->buildDataTom();
|
||||
|
||||
if ($this->enableSlaveQueue) {
|
||||
$oFlow = ServiceHttpQueue::start("telegram_master")->arg($aData)->save();
|
||||
} else {
|
||||
|
||||
$oFlow = Flow::start("_send_master");
|
||||
$sUrlMaSterBase = $_ENV["url_master_base"] ?? "";
|
||||
|
||||
if (!$sUrlMaSterBase) {
|
||||
throw new \Exception("未设置env的url_master_base");
|
||||
}
|
||||
|
||||
$sUrlMasterPost = "https://".$sUrlMaSterBase."/api/telegram/post";
|
||||
|
||||
try {
|
||||
|
||||
$oHttpV2 = HttpV2::make($sUrlMasterPost);
|
||||
|
||||
if ($this->enableAsync) {
|
||||
$oHttpV2 = $oHttpV2->enableAsync();
|
||||
}
|
||||
|
||||
$r = $oHttpV2->enableJsonAs()->setData($aData)->send();
|
||||
|
||||
if ($this->enableAsync) {
|
||||
return $oFlow->sData("async")->done("async");
|
||||
} else {
|
||||
$a = json_decode($r, true);
|
||||
$isDone = $a["isDone"] ?? false;
|
||||
|
||||
if ($isDone) {
|
||||
return $oFlow->done();
|
||||
} else {
|
||||
return $oFlow->fail($r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$oFlow = $oFlow->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
// 直接从本地发到tg,几乎用不上,都走master了
|
||||
private function _send_local(): Flow
|
||||
{
|
||||
if ($this->enableSlaveQueue) {
|
||||
$aData = $this->buildDataTom();
|
||||
$oFlow = ServiceHttpQueue::start("telegram_local")->arg($aData)->save();
|
||||
} else {
|
||||
$oFlow = ServiceTelegramSdk::start($this->sBotToken, $this->iChatId)->setMsgS($this->sMsg)->send();
|
||||
}
|
||||
|
||||
return $oFlow;
|
||||
}
|
||||
|
||||
private function buildDataTom(): array
|
||||
{
|
||||
return [
|
||||
"sBotCode" => $this->sBotCode ?? '',
|
||||
"sChatCode" => $this->sChatCode ?? '',
|
||||
"sBotToken" => $this->sBotToken ?? '',
|
||||
"iChatId" => $this->iChatId ?? '',
|
||||
"enableMasterQueue" => $this->enableMasterQueue ?? '',
|
||||
"enableMaster" => $this->enableMaster,
|
||||
"sMsg" => $this->sMsg ?? '',
|
||||
"sPhotoUrl" => $this->sPhotoUrl ?? '',
|
||||
"iPhotoWidth" => $this->iPhotoWidth ?? 0,
|
||||
"iPhotoHeight" => $this->iPhotoHeight ?? 0,
|
||||
"sType" => $this->sType ?? '',
|
||||
"sFileUrl" => $this->sFileUrl ?? '',
|
||||
"sFileName" => $this->sFileName ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
private function getEnvSiteCode(): string
|
||||
{
|
||||
$sEnvSiteCode = $_ENV["site_code"] ?? "未设置env的site_code";
|
||||
|
||||
return $sEnvSiteCode;
|
||||
}
|
||||
|
||||
}
|
||||
121
app/Services/TomTool/TelegramV2.php
Executable file
121
app/Services/TomTool/TelegramV2.php
Executable file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\IM;
|
||||
|
||||
use App\Models\Config;
|
||||
use Telegram\Bot\Api;
|
||||
use Telegram\Bot\Exceptions\TelegramSDKException;
|
||||
use function strip_tags;
|
||||
|
||||
// 没有必须,默认使用base配置
|
||||
final class TelegramV2
|
||||
{
|
||||
private Api $bot;
|
||||
public $iTo;
|
||||
public $sToken;
|
||||
public $sMsgType = false;
|
||||
|
||||
public function __construct($sToken = "base", $sTo = "base")
|
||||
{
|
||||
$this->setToken($sToken);
|
||||
|
||||
$this->setTo($sTo);
|
||||
|
||||
// $this->bot = new Api($this->sToken);
|
||||
}
|
||||
|
||||
public function setMsgType($sMsgType)
|
||||
{
|
||||
$this->sMsgType = $sMsgType;
|
||||
}
|
||||
|
||||
public function setTo($xTo, $sType = "code")
|
||||
{
|
||||
$iChatId = $xTo;
|
||||
|
||||
if ($sType == "code") {
|
||||
// $sEnvKey = "tgChat_".$xTo;
|
||||
$iChatId = $_ENV['tgChat'][$iChatId];
|
||||
}
|
||||
|
||||
$this->iTo = $iChatId;
|
||||
}
|
||||
|
||||
public function setToken($xToken, $sType = "code")
|
||||
{
|
||||
$sToken = $xToken;
|
||||
|
||||
if ($sType == "code") {
|
||||
// $sEnvKey = "tgToken_".$xToken;
|
||||
$sToken = $_ENV['tgToken'][$sToken];
|
||||
}
|
||||
|
||||
$this->sToken = $sToken;
|
||||
|
||||
$this->bot = new Api($this->sToken);
|
||||
}
|
||||
|
||||
public function send($sMsg, $sMsgType = 'str')
|
||||
{
|
||||
if ($sMsgType == "arr") {
|
||||
$sMsg = json_encode($sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
if ($this->sMsgType === false) {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "html") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => strip_tags(
|
||||
$sMsg,
|
||||
['b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike','del', 'span','tg-spoiler', 'a', 'tg-emoji',
|
||||
'code', 'pre',
|
||||
]
|
||||
),
|
||||
'parse_mode' => 'HTML',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "markdown") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => 'Markdown',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else if ($this->sMsgType === "markdownV2") {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
} else {
|
||||
$sendMessage = [
|
||||
'chat_id' => $this->iTo,
|
||||
'text' => $sMsg,
|
||||
'parse_mode' => '',
|
||||
'disable_web_page_preview' => false,
|
||||
'reply_to_message_id' => null,
|
||||
'reply_markup' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->bot->sendMessage($sendMessage);
|
||||
}
|
||||
|
||||
}
|
||||
130
app/Services/TomTool/ThrowableHandler.php
Executable file
130
app/Services/TomTool/ThrowableHandler.php
Executable file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
use Throwable as ThrowableBase;
|
||||
use Illuminate\Database\QueryException;
|
||||
use App\Services\TomTool\TelegramVdb;
|
||||
|
||||
/**
|
||||
|
||||
new:
|
||||
$oThrowableHandler = ThrowableHandler::make($e);
|
||||
|
||||
快速:
|
||||
$aResult = ThrowableHandler::make($e)->toArr();
|
||||
|
||||
action:
|
||||
fetch() or get()
|
||||
|
||||
set:
|
||||
enableTg()
|
||||
enableTrace()
|
||||
setMaxDepth()
|
||||
|
||||
*/
|
||||
|
||||
class ThrowableHandler
|
||||
{
|
||||
protected bool $tgEnable = false;
|
||||
protected bool $showTrace = false;
|
||||
protected int $maxDepth = 3;
|
||||
protected ThrowableBase $e;
|
||||
|
||||
public static function make(ThrowableBase $e): self
|
||||
{
|
||||
$oInstance = new self();
|
||||
$oInstance->e = $e;
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$data = $this->buildExceptionData($this->e, 0);
|
||||
|
||||
if ($this->tgEnable) {
|
||||
$this->sendTelegram($data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function toArr(): array
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->get(), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function enableTg(bool $enable = true): self
|
||||
{
|
||||
$this->tgEnable = $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableTrace(bool $enable = true): self
|
||||
{
|
||||
$this->showTrace = $enable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxDepth(int $depth): self
|
||||
{
|
||||
$this->maxDepth = max(1, $depth);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function buildExceptionData(ThrowableBase $e, int $depth): array
|
||||
{
|
||||
$data = [
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
];
|
||||
|
||||
$code = $e->getCode();
|
||||
if ($code !== null && $code !== 0) {
|
||||
$data['code'] = $code;
|
||||
}
|
||||
|
||||
if ($this->showTrace) {
|
||||
$filteredTrace = array_filter($e->getTrace(), function ($frame) {
|
||||
return empty($frame['file']) || !str_contains($frame['file'], '/vendor/');
|
||||
});
|
||||
|
||||
$traceLines = [];
|
||||
foreach ($filteredTrace as $frame) {
|
||||
$file = $frame['file'] ?? '[internal]';
|
||||
$line = $frame['line'] ?? '';
|
||||
$func = isset($frame['class'], $frame['function'])
|
||||
? "{$frame['class']}{$frame['type']}{$frame['function']}"
|
||||
: ($frame['function'] ?? '[unknown]');
|
||||
$traceLines[] = "{$file}:{$line} {$func}()";
|
||||
}
|
||||
|
||||
$data['trace'] = implode("\n", $traceLines);
|
||||
}
|
||||
|
||||
if ($depth < $this->maxDepth && $e->getPrevious() instanceof ThrowableBase) {
|
||||
$data['previous'] = $this->buildExceptionData($e->getPrevious(), $depth + 1);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function sendTelegram(array $data): void
|
||||
{
|
||||
TelegramVdb::make()->aData($data)->save();
|
||||
}
|
||||
}
|
||||
|
||||
21
app/Services/TomTool/Tom.php
Executable file
21
app/Services/TomTool/Tom.php
Executable file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TomTool;
|
||||
|
||||
class Tom
|
||||
{
|
||||
public static function toJson($aData)
|
||||
{
|
||||
return json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
public static function toJsonBr($aData)
|
||||
{
|
||||
$sData = self::toJson($aData);
|
||||
$sData = str_replace("\\n", "<br>", $sData);
|
||||
|
||||
return $sData;
|
||||
}
|
||||
}
|
||||
15
artisan
Executable file
15
artisan
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
$status = (require_once __DIR__.'/bootstrap/app.php')
|
||||
->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
24
bootstrap/app.php
Executable file
24
bootstrap/app.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->alias([
|
||||
//'test' => \App\Http\Middleware\Test::class,
|
||||
]);
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
'api/*',
|
||||
'cmd/*',
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
})->create();
|
||||
2
bootstrap/cache/.gitignore
vendored
Executable file
2
bootstrap/cache/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
5
bootstrap/providers.php
Executable file
5
bootstrap/providers.php
Executable file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
];
|
||||
76
composer.json
Executable file
76
composer.json
Executable file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"intervention/image": "^3.11",
|
||||
"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": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/pail": "^1.1",
|
||||
"laravel/pint": "^1.13",
|
||||
"laravel/sail": "^1.26",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.1",
|
||||
"phpunit/phpunit": "^11.0.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi",
|
||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
||||
"@php artisan migrate --graceful --ansi"
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
8704
composer.lock
generated
Executable file
8704
composer.lock
generated
Executable file
File diff suppressed because it is too large
Load Diff
126
config/app.php
Executable file
126
config/app.php
Executable file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application, which will be used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| other UI elements where an application name needs to be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => env('APP_TIMEZONE', 'UTC'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
||||
115
config/auth.php
Executable file
115
config/auth.php
Executable file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default authentication "guard" and password
|
||||
| reset "broker" for your application. You may change these values
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTH_GUARD', 'web'),
|
||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| which utilizes session storage plus the Eloquent user provider.
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| providers to represent the model / table. These providers may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options specify the behavior of Laravel's password
|
||||
| reset functionality, including the table utilized for token storage
|
||||
| and the user provider that is invoked to actually retrieve users.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the amount of seconds before a password confirmation
|
||||
| window expires and users are asked to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||
|
||||
];
|
||||
108
config/cache.php
Executable file
108
config/cache.php
Executable file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
||||
|
||||
];
|
||||
173
config/database.php
Executable file
173
config/database.php
Executable file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
'driver' => 'mariadb',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => 'prefer',
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
77
config/filesystems.php
Executable file
77
config/filesystems.php
Executable file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application for file storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure as many filesystem disks as necessary, and you
|
||||
| may even configure multiple disks for the same driver. Examples for
|
||||
| most supported storage drivers are configured here for reference.
|
||||
|
|
||||
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
132
config/logging.php
Executable file
132
config/logging.php
Executable file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
116
config/mail.php
Executable file
116
config/mail.php
Executable file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send all email
|
||||
| messages unless another mailer is explicitly specified when sending
|
||||
| the message. All additional mailers can be configured within the
|
||||
| "mailers" array. Examples of each type of mailer are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||
| when delivering an email. You may specify which one you're using for
|
||||
| your mailers below. You may also add additional mailers if needed.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "resend", "log", "array",
|
||||
| "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'transport' => 'resend',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all emails sent by your application to be sent from
|
||||
| the same address. Here you may specify a name and address that is
|
||||
| used globally for all emails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
];
|
||||
112
config/queue.php
Executable file
112
config/queue.php
Executable file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue supports a variety of backends via a single, unified
|
||||
| API, giving you convenient access to each backend using identical
|
||||
| syntax for each. The default queue connection is defined below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection options for every queue backend
|
||||
| used by your application. An example configuration is provided for
|
||||
| each backend supported by Laravel. You're also free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||
'queue' => env('DB_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Batching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options configure the database and table that store job
|
||||
| batching information. These options can be updated to any database
|
||||
| connection and table which has been defined by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'batching' => [
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'job_batches',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control how and where failed jobs are stored. Laravel ships with
|
||||
| support for storing failed jobs in a simple file or in a database.
|
||||
|
|
||||
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
38
config/services.php
Executable file
38
config/services.php
Executable file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'postmark' => [
|
||||
'token' => env('POSTMARK_TOKEN'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'key' => env('RESEND_KEY'),
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'notifications' => [
|
||||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
217
config/session.php
Executable file
217
config/session.php
Executable file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines the default session driver that is utilized for
|
||||
| incoming requests. Laravel supports a variety of storage options to
|
||||
| persist session data. Database storage is a great default choice.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "apc",
|
||||
| "memcached", "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to expire immediately when the browser is closed then you may
|
||||
| indicate that via the expire_on_close configuration option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it's stored. All encryption is performed
|
||||
| automatically by Laravel and you may use the session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => env('SESSION_ENCRYPT', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the "file" session driver, the session files are placed
|
||||
| on disk. The default storage location is defined here; however, you
|
||||
| are free to provide another location where they should be stored.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table to
|
||||
| be used to store sessions. Of course, a sensible default is defined
|
||||
| for you; however, you're welcome to change this to another table.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => env('SESSION_TABLE', 'sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using one of the framework's cache driven session backends, you may
|
||||
| define the cache store which should be used to store the session data
|
||||
| between requests. This must match one of your defined cache stores.
|
||||
|
|
||||
| Affects: "apc", "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the session cookie that is created by
|
||||
| the framework. Typically, you should not need to change this value
|
||||
| since doing so does not grant a meaningful security improvement.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application, but you're free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('SESSION_PATH', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the domain and subdomains the session cookie is
|
||||
| available to. By default, the cookie will be available to the root
|
||||
| domain and all subdomains. Typically, this shouldn't be changed.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. It's unlikely you should disable this option.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" to permit secure cross-site requests.
|
||||
|
|
||||
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||
|
||||
];
|
||||
1
database/.gitignore
vendored
Executable file
1
database/.gitignore
vendored
Executable file
@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
44
database/factories/UserFactory.php
Executable file
44
database/factories/UserFactory.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
database/migrations/0001_01_01_000000_create_users_table.php
Executable file
49
database/migrations/0001_01_01_000000_create_users_table.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
35
database/migrations/0001_01_01_000001_create_cache_table.php
Executable file
35
database/migrations/0001_01_01_000001_create_cache_table.php
Executable file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
57
database/migrations/0001_01_01_000002_create_jobs_table.php
Executable file
57
database/migrations/0001_01_01_000002_create_jobs_table.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
23
database/seeders/DatabaseSeeder.php
Executable file
23
database/seeders/DatabaseSeeder.php
Executable file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
}
|
||||
}
|
||||
17
package.json
Executable file
17
package.json
Executable file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.4",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^1.0",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"vite": "^6.0"
|
||||
}
|
||||
}
|
||||
33
phpunit.xml
Executable file
33
phpunit.xml
Executable file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory>tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
|
||||
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
6
postcss.config.js
Executable file
6
postcss.config.js
Executable file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
29
public/index.php
Executable file
29
public/index.php
Executable file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
function tomd($aData, $iType = 0) {
|
||||
|
||||
if (env("is_loc") == 1) {
|
||||
echo "<pre>";
|
||||
var_dump($aData);
|
||||
|
||||
if ($iType == 1) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
(require_once __DIR__.'/../bootstrap/app.php')
|
||||
->handleRequest(Request::capture());
|
||||
2
public/js/jquery.min.js
vendored
Executable file
2
public/js/jquery.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
3
resources/css/app.css
Executable file
3
resources/css/app.css
Executable file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
1
resources/js/app.js
Executable file
1
resources/js/app.js
Executable file
@ -0,0 +1 @@
|
||||
import './bootstrap';
|
||||
4
resources/js/bootstrap.js
vendored
Executable file
4
resources/js/bootstrap.js
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
8
routes/console.php
Executable file
8
routes/console.php
Executable file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote')->hourly();
|
||||
44
routes/web.php
Executable file
44
routes/web.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Article\ListController as ArticleListController;
|
||||
use App\Http\Controllers\Article\SingleController as ArticleSingleController;
|
||||
use App\Http\Controllers\ExecController;
|
||||
use App\Http\Controllers\ClientController;
|
||||
use App\Http\Controllers\HelperController;
|
||||
use App\Http\Controllers\HtmlController;
|
||||
use App\Http\Controllers\SakaiController;
|
||||
use App\Http\Controllers\CronController;
|
||||
use App\Http\Controllers\CmdController;
|
||||
use App\Http\Controllers\Api\ShipController;
|
||||
//use App\Http\Controllers\Api\GithubController;
|
||||
use App\Http\Controllers\Api\Tg\HookController;
|
||||
use App\Http\Controllers\Api\CountryController;
|
||||
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::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']);
|
||||
Route::get('/cmd/image/upload', [CmdController::class, 'imageUpload']);
|
||||
Route::post('/cmd/image/upload/exec', [CmdController::class, 'imageUploadExec']);
|
||||
|
||||
Route::post('/api/open/telegram/queue/add', [ServiceTelegram::class, 'queueAdd']);
|
||||
Route::post('/api/github/get', [GithubController::class, 'get']);
|
||||
Route::get('/api/good/rand/{class}', [GoodController::class, 'rand']);
|
||||
Route::get('/api/freenode/country/rand/{case?}', [CountryController::class, 'rand']);
|
||||
Route::get('/api/ship/get/{shipCode}/{clientClass}/{clientCode?}', [ShipController::class, 'getShipByCode']);
|
||||
|
||||
Route::get('/cron', [CronController::class, 'index']);
|
||||
|
||||
Route::any('/api/tg/hook', [HookController::class, 'cmd']);
|
||||
Route::any('/api/tg/hook/cmd_ffq_article', [HookController::class, 'cmd_ffq_article']);
|
||||
|
||||
Route::get('/tool', [SakaiController::class, 'tool']);
|
||||
14
scripts/cron.php
Executable file
14
scripts/cron.php
Executable file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL & ~E_DEPRECATED);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
$app = require_once __DIR__ . '/../bootstrap/app.php';
|
||||
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
|
||||
$service = $app->make(App\Services\Cron::class);
|
||||
//$service->setTestI(5);
|
||||
$result = $service->run();
|
||||
|
||||
print_r($result);
|
||||
4
storage/app/.gitignore
vendored
Executable file
4
storage/app/.gitignore
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!private/
|
||||
!public/
|
||||
!.gitignore
|
||||
2
storage/app/private/.gitignore
vendored
Executable file
2
storage/app/private/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
storage/app/public/.gitignore
vendored
Executable file
2
storage/app/public/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
9
storage/framework/.gitignore
vendored
Executable file
9
storage/framework/.gitignore
vendored
Executable file
@ -0,0 +1,9 @@
|
||||
compiled.php
|
||||
config.php
|
||||
down
|
||||
events.scanned.php
|
||||
maintenance.php
|
||||
routes.php
|
||||
routes.scanned.php
|
||||
schedule-*
|
||||
services.json
|
||||
3
storage/framework/cache/.gitignore
vendored
Executable file
3
storage/framework/cache/.gitignore
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
*
|
||||
!data/
|
||||
!.gitignore
|
||||
2
storage/framework/cache/data/.gitignore
vendored
Executable file
2
storage/framework/cache/data/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
storage/framework/sessions/.gitignore
vendored
Executable file
2
storage/framework/sessions/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
storage/framework/testing/.gitignore
vendored
Executable file
2
storage/framework/testing/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
storage/framework/views/.gitignore
vendored
Executable file
2
storage/framework/views/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
storage/logs/.gitignore
vendored
Executable file
2
storage/logs/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
20
tailwind.config.js
Executable file
20
tailwind.config.js
Executable file
@ -0,0 +1,20 @@
|
||||
import defaultTheme from 'tailwindcss/defaultTheme';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
|
||||
'./storage/framework/views/*.php',
|
||||
'./resources/**/*.blade.php',
|
||||
'./resources/**/*.js',
|
||||
'./resources/**/*.vue',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
19
tests/Feature/ExampleTest.php
Executable file
19
tests/Feature/ExampleTest.php
Executable file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
// use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* A basic test example.
|
||||
*/
|
||||
public function test_the_application_returns_a_successful_response(): void
|
||||
{
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user