dd/app/Services/Article/CacheTg.php
2025-11-14 17:30:27 +08:00

373 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Services\Article;
use App\Models\ArticleCache as ModelArticleCache;
use App\Models\ArticleSchema as ModelArticleSchema;
use App\Models\ArticleCacheTime as ModelArticleCacheTime;
use App\Models\TelegramKey as ModelTelegramKey;
use App\Services\TomTool\Flow;
use App\Services\SlaveMap as ServiceSlaveMap;
use App\Services\Article\Cmd as ServiceArticleCmd;
final class CacheTg
{
public static function start()
{
return new self();
}
public function add($sGroupCode, $sContent)
{
$oResult = Flow::make("cacheAdd");
$oModelArticleCache = new ModelArticleCache();
$oModelArticleCache->content = $sContent;
$oModelArticleCache->group_code = $sGroupCode;
$oModelArticleCache->created_at = time();
$r = $oModelArticleCache->save();
if (!$r) {
return $oResult->fail("写入缓存失败");
}
$oModelArticleCacheTime = ModelArticleCacheTime::first();
$oModelArticleCacheTime->time = time();
$r = $oModelArticleCacheTime->save();
if (!$r) {
return $oResult->fail("更新cacheTime失败");
}
return $sGroupCode."写入一条缓存";
}
public function saveToSchema(): Flow
{
$oFlow = Flow::start("save");
$oArticleCache = ModelArticleCache::where("status", 1)->get();
// 第一步格式化通用tgSchema
$aDataMix["data"] = [];
foreach ($oArticleCache as $oArticleCacheRow) {
if (!isset($sFromPaichu)) { // 等于只是第一次each会赋值
$sFromPaichu = $oArticleCacheRow->group_code;
}
if ($oArticleCacheRow->group_code !== $sFromPaichu) {
continue; // 实现一次只save一种group_code
}
$aContent = json_decode($oArticleCacheRow->content, true);
$aReturn = $this->_schemaByCache_common($aContent, $oArticleCacheRow->group_code);
foreach ($aReturn as $aData) {
$aDataMix["data"][] = $aData;
}
$sGroupCode = $oArticleCacheRow->group_code; // 这里等于直接提取后一个group_code不算完美但没大问题
}
// 第二步进一步格式化为对应form的Schema
$method = '_schemaWithFrom_' . $oArticleCacheRow->group_code;
if (method_exists($this, $method)) {
$aDataMix = $this->$method($aDataMix, $sGroupCode);
} else {
// 没有对应直接不处理
}
$y = date("Y");
$sYqian = substr($y, 1, 2);
$sYhou = substr($y, 3, 1);
$sArticleCode = $sGroupCode."-".$sYqian.date("mdHis").$sYhou;
$oArticleSchema = new ModelArticleSchema();
$oArticleSchema->schema = json_encode($aDataMix["data"]);
$oArticleSchema->code = $sArticleCode;
$oArticleSchema->group_code = $sGroupCode;
$oArticleSchema->name = $aDataMix["name"] ?? "none";
$oArticleSchema->save();
ModelArticleCache::where("status", 1)->update([
"status" => 0, // 上线改0
"code" => $sArticleCode,
]);
ServiceArticleCmd::startWithO($oArticleSchema)->syncOpen()->save("new");
return $oFlow->done();
}
private function _schemaByCache_common($aContent, $sGroupCode)
{
$aDataMix = [];
if (isset($aContent["message"]["document"]["mime_type"])) { // 包含文档
$sDocumentType = $aContent["message"]["document"]["mime_type"];
$aDataTmp = []; // new data init
$aDataTmp["type"] = "str";
$aDataTmp["name"] = "str";
$aDataTmp["content"]["base"] = "";
if (strpos($sDocumentType, "image") !== false) { // 包含图片
$sDocumentName = $aContent["message"]["document"]["file_name"];
// img data init
$aDataTmp["type"] = "img";
$aDataTmp["name"] = "img_cesu";
$aDataTmp["content"]["base"] = $sDocumentName;
$aDataTmp["content"]["img"] = [
"title" => $sDocumentName,
"base" => '',
"thumb" => '',
];
$aCacheImgId = [
"base" => $aContent["message"]["document"]["file_id"],
"thumb" => $aContent["message"]["document"]["thumb"]["file_id"],
];
$sBotToken = ModelTelegramKey::botTokenByCode("master");
foreach ($aCacheImgId as $k => $aImgIdRow) {
// 第一步:获取 file_path
$getFileUrl = "https://api.telegram.org/bot".$sBotToken."/getFile?file_id=".$aImgIdRow;
$response = file_get_contents($getFileUrl);
$data = json_decode($response, true);
if (!$data['ok']) {
$aDataTmp["content"]["img"][$k] = "获取图片途径失败";
continue;
}
$filePath = $data['result']['file_path'];
// 第二步:下载文件内容
$downloadUrl = "https://api.telegram.org/file/bot".$sBotToken."/".$filePath;
$imageData = file_get_contents($downloadUrl);
if ($imageData === false) {
$aDataTmp["content"]["img"][$k] = "下载文件失败";
continue;
}
// 第三步:保存到服务器
$savePath = 'upload/';
if (!is_dir($savePath)) {
mkdir($savePath, 0777, true);
}
$fileName = basename($filePath);
$fileName = uniqid('ffq_tg-'.$k.':', true).$fileName; // 加前缀并提高熵值
file_put_contents($savePath . $fileName, $imageData);
$sSavePath = $_ENV["APP_URL"].'/'.$savePath.$fileName;
$aDataTmp["content"]["img"][$k] = $sSavePath;
}
} else { // 包含图片 end
$aDataTmp["content"]["base"] = "未处理类型{$sDocumentType}";
}
$aDataMix[] = $aDataTmp;
} // 包含文档 end
// 包含文本
if (isset($aContent["message"]["caption"])) {
$sStr = $aContent["message"]["caption"];
$aDataTmp = [];
$aDataTmp["type"] = "str";
$aDataTmp["name"] = "str";
$aDataTmp["content"]["base"] = $sStr;
$aDataMix[] = $aDataTmp;
}
if (count($aDataMix) == 0) {
$aDataTmp = [];
$aDataTmp["type"] = "loss";
$aDataTmp["name"] = "loss";
$aDataTmp["content"]["base"] = "提取失败:".json_encode($aContent);
$aDataMix[] = $aDataTmp;
}
return $aDataMix;
}
private function _schemaWithFrom_ffq_tg_a($aDataMix, $sGroupCode)
{
$aDataMixNew = [];
$aDataMixNew["name"] = '';
$aDataMixNew["title"] = '';
$aDataMixNew["data"] = [];
foreach ($aDataMix["data"] as $aDataMixRow) {
$sContent = $aDataMixRow["content"]["base"] ?? "⚠️ 没有content";
$sType = $aDataMixRow['type'] ?? "str";
if ($sType == 'str') {
$sRowName = 'str';
$aContent = $this->ffq_tg_str_fetch($sContent);
$sArticleName = $aContent["name"] ?? '';
$sStrTiqu = $aContent["tiqu"] ?? '';
$sStrBaoliu = $aContent["baoliu"] ?? '';
$sStrBaoliu = str_replace("翻翻墙", "{{sAuthorName}}", $sStrBaoliu);
$sStrBaoliu = str_replace("😀", "{{sIconText}}", $sStrBaoliu);
$sStrBaoliu = str_replace("📊", "{{sIconText}}", $sStrBaoliu);
if (!$sStrTiqu) {
$aDataMixNew["data"][] = [
"type" => "str",
"name" => "tag_tiqu",
"content" => [
"base" => $sStrTiqu,
],
];
}
if ($sArticleName) {
$aDataMixNew["name"] = $sArticleName;
$aDataMixNew["data"][] = [
"type" => "str",
"name" => "schema_name",
"content" => [
"base" => $sArticleName,
],
];
}
preg_match('/论坛[:]\s*(https?:\/\/[^\s]*?\.html)/u', $sStrBaoliu, $matches);
if (isset($matches[1])) {
$sUrl = $matches[1];
$aReturn = $this->getTaocan_ffq_tg($sUrl);
if (Flow::isFailResult($aReturn)) {
$tmp = "⚠️ 套餐table获取失败";
}
$tmp = $aReturn["sData"];
$aDataTmp = [];
$aDataTmp["type"] = "str";
$aDataTmp["name"] = "table_taocan";
$aDataTmp["content"]["base"] = $tmp;
$aDataMixNew["data"][] = $aDataTmp;
}
if (str_contains($sStrBaoliu, "评分")) {
$sRowName = "str_jieshao";
}
$aDataMixNew["data"][] = [
"type" => "str",
"name" => $sRowName,
"content" => [
"base" => $sStrBaoliu,
],
];
} else if ($sType == 'img') {
$aDataMixNew["data"][] = $aDataMixRow;
} else {
$aDataMixNew["data"][] = $aDataMixRow;
}
}
return $aDataMixNew;
}
private function ffq_tg_str_fetch($str) {
$start = strpos($str, '#');
$end = strpos($str, '📊');
$arr = [
"tiqu" => '',
"baoliu" => $str,
"name" => '',
];
if ($start !== false && $end !== false && $end > $start) {
$length = $end - $start;
$extracted = mb_substr($str, $start, $length, 'UTF-8');
$cleaned = str_replace($extracted, '', $str);
$arr["tiqu"] = $extracted;
$arr["baoliu"] = $cleaned;
}
if (!empty($arr["tiqu"])) {
$arr["tiqu"] = substr($arr["tiqu"], 1);
$tmp = explode("#", $arr["tiqu"]);
$name = $tmp[0] ?? '';
if ($name) {
$arr["name"] = $name;
}
}
return $arr;
}
private function getTaocan_ffq_tg($url)
{
$oResult = Flow::make("getTaocan_ffq_tg");
// 初始化 cURL
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/115.0 Safari/537.36',
]);
$html = curl_exec($ch);
curl_close($ch);
if ($html === false) {
return $oResult->step("html=false")->sData("⚠️ 无法读取网页内容")->done();
}
// 解析 HTML
libxml_use_internal_errors(true);
$dom = new \DOMDocument();
$dom->loadHTML($html);
libxml_clear_errors();
$xpath = new \DOMXPath($dom);
// 找到第一个 class 为 markdown-body 的 div
$markdownDiv = $xpath->query('//div[contains(@class, "markdown-body")]')->item(0);
if (!$markdownDiv) {
return $oResult->step("div loss")->sData("⚠️ 未找到 class 为 'markdown-body' 的 div")->done();
}
// 找到该 div 中的第一个 table
$table = null;
foreach ($markdownDiv->getElementsByTagName('table') as $t) {
$table = $t;
break;
}
if (!$table) {
return $oResult->step("table loss")->sData("⚠️ 未找到 table 标签")->done();
}
$sHtml = $dom->saveHTML($table);
return $oResult->sData($sHtml)->done();
}
}