diff --git a/app/Http/Controllers/Api/GithubController.php b/app/Http/Controllers/Api/GithubController.php index f5de38e..1de09fa 100755 --- a/app/Http/Controllers/Api/GithubController.php +++ b/app/Http/Controllers/Api/GithubController.php @@ -52,8 +52,8 @@ class GithubController extends Controller $sCodeTrue = date("Ymd", strtotime("-1 day")); } - $sFilePathClash = "feed/clash-".$sCodeTrue.".yaml"; - $sFilePathV2ray = "feed/v2ray-".$sCodeTrue.".txt"; + $sFilePathClash = "sub/clash-".$sCodeTrue.".yaml"; + $sFilePathV2ray = "sub/v2ray-".$sCodeTrue.".txt"; $sFileClash = file_get_contents($sFilePathClash); $sFileV2ray = file_get_contents($sFilePathV2ray); diff --git a/app/Services/TomTool_new/Flow.php b/app/Services/TomTool_new/Flow.php new file mode 100755 index 0000000..fe79e9f --- /dev/null +++ b/app/Services/TomTool_new/Flow.php @@ -0,0 +1,415 @@ +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(); + } + } +} diff --git a/app/Services/TomTool_new/Http.php b/app/Services/TomTool_new/Http.php new file mode 100755 index 0000000..cf07df2 --- /dev/null +++ b/app/Services/TomTool_new/Http.php @@ -0,0 +1,93 @@ +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; + } +} diff --git a/app/Services/TomTool_new/HttpV2.php b/app/Services/TomTool_new/HttpV2.php new file mode 100755 index 0000000..c05ae1e --- /dev/null +++ b/app/Services/TomTool_new/HttpV2.php @@ -0,0 +1,261 @@ +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; +// } +} diff --git a/app/Services/TomTool_new/Telegram/Master.php b/app/Services/TomTool_new/Telegram/Master.php new file mode 100755 index 0000000..00bf4e5 --- /dev/null +++ b/app/Services/TomTool_new/Telegram/Master.php @@ -0,0 +1,173 @@ +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 = ''): self + { + $this->sFileUrl = $sFileUrl; + 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) + ->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, + "sMsg" => $this->sMsg ?? '', + "sType" => $this->sType ?? '', + ]; + } + + +} diff --git a/app/Services/TomTool_new/Telegram/Sdk.php b/app/Services/TomTool_new/Telegram/Sdk.php new file mode 100755 index 0000000..8dc1e93 --- /dev/null +++ b/app/Services/TomTool_new/Telegram/Sdk.php @@ -0,0 +1,207 @@ +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 = '') + { + $this->sFileUrl = $sFileUrl; + 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 { +//tomd($this->sFileUrl, 1); + 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) { + + $sFile = InputFile::create($this->sFileUrl, date("Y-m-d H:i:s").'.text'); + + $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, + ]; + } + +} diff --git a/app/Services/TomTool_new/Telegram/Slave.php b/app/Services/TomTool_new/Telegram/Slave.php new file mode 100755 index 0000000..b63fb84 --- /dev/null +++ b/app/Services/TomTool_new/Telegram/Slave.php @@ -0,0 +1,357 @@ +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): self + { + $this->sFileUrl = $sFileUrl; + 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 ?? '', + ]; + } + + private function getEnvSiteCode(): string + { + $sEnvSiteCode = $_ENV["site_code"] ?? "未设置env的site_code"; + + return $sEnvSiteCode; + } + +} diff --git a/app/Services/TomTool_new/TelegramV2.php b/app/Services/TomTool_new/TelegramV2.php new file mode 100755 index 0000000..35bdbe2 --- /dev/null +++ b/app/Services/TomTool_new/TelegramV2.php @@ -0,0 +1,121 @@ +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); + } + +} diff --git a/app/Services/TomTool_new/TelegramVdb_rengle.php b/app/Services/TomTool_new/TelegramVdb_rengle.php new file mode 100755 index 0000000..55a8bfe --- /dev/null +++ b/app/Services/TomTool_new/TelegramVdb_rengle.php @@ -0,0 +1,362 @@ +save($sData) + + 进阶: + TelegramVdb::make()->dataArr($aData)->setFormat("html")->save(); + TelegramVdb::make()->setFormat()->data($xData)->toJson()->save(); + + data: + data[Arr/Str/Json]($data) + data($aData)->toJson() + + action: + save() + + set: + setChatCode($code) + setBotCode($code) + setChatId($id) 覆盖code + setBotToken($token) 覆盖code + setFormat($type) + enableDate() + + */ + +final class TelegramVdb +{ + public $sBotCode; + public $sChatCode; + public $sBotToken = null; + public $iChatId = null; + public $sMsg = ''; + public $sMsgFormat = ''; + public $sMsgFormatType = 'none'; + public $bEnableDate = false; + private $bKeyBegin = false; + public $sClient = "master"; + + private const ALLOWED_FORMATS = ['none', 'html', 'markdown', 'markdownV2']; + + public static function log() + { + return self::make("log", "admin"); + } + + public static function warn() + { + return self::make("warn", "admin"); + } + + public static function fail() + { + return self::make("fail", "admin"); + } + + public static function notify() + { + return self::make("notify", "admin"); + } + + public static function make($sBotCode = "base", $sChatCode = "base") + { + $oInstance = new self(); + $oInstance->sBotCode = $sBotCode; + $oInstance->sChatCode = $sChatCode; + return $oInstance; + } + + public function send($sMsg = '') + { + return $this->_ok($sMsg, true); + } + + public function save($sMsg = '') + { + return $this->_ok($sMsg); + } + + private function sendNow() + { + $oResult = Flow::make("send"); + $oBot = new Api($this->sBotToken); + + $r = $oBot->sendMessage($this->sMsgFormat); + + if ($r->isOk()){ + return $oResult->done(); + } else { + return $oResult->fail("send失败"); + } + } + + private function HttpQueueSave() + { + $aTelegramArg = [ + "bot_token" => $this->sBotToken, + "chat_id" => $this->iChatId, + "msg" => $this->sMsgFormat, + ]; + + $aReturn = ServiceHttpQueue::start("telegram")->arg($aTelegramArg)->save()->getResult(); + + return $aReturn; + } + + public function _ok($sMsg = '', $bSendNow = false) + { + $bTgSendEnable = $_ENV["tg_send_enable"] ?? false; + if (!$bTgSendEnable) { + return false; + } + + $oResult = Flow::make("save"); + + $this->keyBegin(); + + if ($sMsg) { + if (is_array($sMsg)) { + $this->aData($sMsg); + } else { + $this->sData($sMsg); + } + } + + $this->msgFormat(); + + if ($bSendNow) { + $aReturn = $this->sendNow(); + } else { + $aReturn = $this->HttpQueueSave(); + } + + $this->sMsg = ''; + + return $aReturn; + } + + public function enableDate($b = true) + { + $this->bEnableDate = $b; + return $this; + } + + public function data($xData) + { + $this->sMsg = $xData; + return $this; + } + + public function jData($sMsg = '') + { + $this->dataJson($sMsg); + return $this; + } + + public function aData($sMsg = []) + { + $this->dataArr($sMsg); + return $this; + } + + public function sData($sMsg = '') + { + $this->dataStr($sMsg); + return $this; + } + + public function toJson() + { + $this->withDateArr(); + $this->sMsg = json_encode($this->sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + return $this; + } + + public function dataStr($sMsg = '') + { + $this->withDateStr(); + $this->sMsg = $sMsg; + return $this; + } + + public function dataArr($sMsg = []) + { + $this->withDateArr(); + $this->sMsg = json_encode($sMsg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + return $this; + } + + public function dataJson($sMsg = '') + { + $this->dataStr($sMsg); + return $this; + } + + private function withDateArr() + { + if ($this->bEnableDate) { + $this->sMsg["date"] = date("Y-m-d H:i:s"); + } + } + + private function withDateStr() + { + if ($this->bEnableDate) { + $this->sMsg = '['.date("Y-m-d H:i:s").'] '.$this->sMsg; + } + } + + public function setFormat($sMsgFormatType = "none") + { + $this->sMsgFormatType = $sMsgFormatType; + return $this; + } + + public function setBotCode($sBotCode) + { + $this->sBotCode = $sBotCode; + $this->bKeyBegin = false; + return $this; + } + + public function setChatCode($sChatCode) + { + $this->sChatCode = $sChatCode; + $this->bKeyBegin = false; + return $this; + } + + public function setBotToken($sBotToken) + { + $this->sBotToken = $sBotToken; + return $this; + } + + public function setChatId($iChatId) + { + $this->iChatId = $iChatId; + return $this; + } + + private function keyBegin() + { + if (!$this->bKeyBegin) { + if (!$this->sBotToken) { + $this->sBotToken = $this->botTokenByCode(); + } + + if (!$this->iChatId) { + $this->iChatId = $this->chatIdByCode(); + } + + if (!$this->sBotToken || !$this->iChatId) { + throw new \RuntimeException("botToken或chatId没找到,botCode={$this->sBotCode},chatCode={$this->sChatCode}"); + } + + $this->bKeyBegin = true; + } + + return $this; + } + + private function msgFormat() + { + if (!in_array($this->sMsgFormatType, self::ALLOWED_FORMATS, true)) { + throw new \RuntimeException("不支持的格式类型: {$this->sMsgFormatType}"); + } + + $method = 'msgFormat_' . $this->sMsgFormatType; + $this->$method(); + + return $this; + } + + private function botTokenByCode() + { + return ModelTelegramKey::botTokenByCode($this->sBotCode); + } + + private function chatIdByCode() + { + return ModelTelegramKey::chatIdByCode($this->sChatCode); + } + + private function msgFormat_none() + { + $this->sMsgFormat = [ + 'chat_id' => $this->iChatId, + 'text' => $this->sMsg, + 'parse_mode' => '', + 'disable_web_page_preview' => false, + 'reply_to_message_id' => null, + 'reply_markup' => null, + ]; + + return $this; + } + + private function msgFormat_html() + { + $this->sMsgFormat = [ + '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, + ]; + + return $this; + } + + private function msgFormat_markdown() + { + $this->sMsgFormat = [ + 'chat_id' => $this->iChatId, + 'text' => $this->sMsg, + 'parse_mode' => 'Markdown', + 'disable_web_page_preview' => false, + 'reply_to_message_id' => null, + 'reply_markup' => null, + ]; + + return $this; + } + + private function msgFormat_markdownV2() + { + $this->sMsgFormat = [ + 'chat_id' => $this->iChatId, + 'text' => $this->sMsg, + 'parse_mode' => 'MarkdownV2', + 'disable_web_page_preview' => false, + 'reply_to_message_id' => null, + 'reply_markup' => null, + ]; + + return $this; + } +} diff --git a/app/Services/TomTool_new/ThrowableHandler.php b/app/Services/TomTool_new/ThrowableHandler.php new file mode 100755 index 0000000..03fc2f5 --- /dev/null +++ b/app/Services/TomTool_new/ThrowableHandler.php @@ -0,0 +1,130 @@ +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(); + } +} + diff --git a/app/Services/TomTool_new/Tom.php b/app/Services/TomTool_new/Tom.php new file mode 100755 index 0000000..807e6cc --- /dev/null +++ b/app/Services/TomTool_new/Tom.php @@ -0,0 +1,21 @@ +", $sData); + + return $sData; + } +}