sApiToken = Config::obtain('telegram_token'); $this->sApiChatId = Config::obtain('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'] = $aUpdate["message"]["chat"]["id"]; $aData['fromId'] = $aUpdate["message"]["from"]["id"]; if (!isset($aUpdate["message"]["text"])) { http_response_code(200); echo "not have text"; exit; } $sText = $aUpdate["message"]["text"]; // 先检查step if (strpos($sText, "/s ") === 0 && $oTgStep->fromIdHit($aData['fromId'])) { $sText = ltrim($sText, '/s'); $sText = trim($sText); $aTgStepFirst = $oTgStep->find($aData["fromId"]); if ($aTgStepFirst["cmd"] == "reply") { if ($aTgStepFirst['step'] == 1) { $oHttp = new Http(); $oHttp->sMethod = "put"; $oHttp->sToUrl = $_ENV['baseUrl']."/api/tg/ticket/".$aTgStepFirst['cache']['ticketId']; $oHttp->bIsJson = true; $aData['comment'] = $sText; $aData['admin_name'] = $aUpdate["message"]["from"]["first_name"]; $oHttp->aData = $aData; $r = @json_decode($oHttp->send(), true); // 不论失败成功都清除step $oTgStep->clearByFromId($aUpdate["message"]["from"]["id"]); if ($r["ret"] == 1) { $sMessage = "成功"; } else { $sMessage = "失败"; Http::response(500, json_encode($aData)); } $this->sendMessage($sMessage); Http::response(200, 'ok'); } } } // 打断 if ($sText === "/break") { $oTgStep->clearByFromId($aData["fromId"]); $this->sendMessage("ok"); http_response_code(200);exit; } // 命令列表 // /cmdList if (strpos($sText, '/cmdList') === 0 || strpos($sText, '\/cmdList') === 0) { $aCmdList = [ '流量报告' => 'reportWidth', '查找用户' => 'userFind 11', '查找订单' => 'orderFindByUser 11', '回复工单' => 'reply 11 + s 你好', '快捷回单' => 'ry 11 你好', '工单查找' => 'ticketFind 11', '用户报表' => 'reportUser_day_7 && reportUser_date_2025-02-02 && reportUser_date_2025-02-02_2025-03-03', 'duck添加&转发' => 'duckAdd jp02', 'duck删除&转发' => 'duckDel jp02', 'duck修改&转发' => 'duckOption|max_width|jp01 1.66#tb', 'duck列表&转发' => 'duckList', 'duck查找' => 'duckFind jp02', '转发重置' => 'zhuanfaReset-youshi 2025-02-02', 'node修改' => 'nodeOption#name#11 haha', '节点like' => 'nodeLike#duck jp02', 'node删除' => 'nodeDel 18', 'node复制' => 'nodeCopy 18', '节点列表简洁'=> 'nodeList', ]; $this->sendMessage(json_encode($aCmdList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // 流量报告 // /reportWidth if (strpos($sText, '/reportWidth') === 0 || strpos($sText, '\/reportWidth') === 0) { $jobs = new \App\Services\Cron(); $jobs->reportBandWidth(); Http::response(200, 'ok'); } // 查找用户 // /userFind 11 if (strpos($sText, '/userFind') === 0 || strpos($sText, '\/userFind') === 0) { list(, $iUserId) = explode(" ", $sText, 2); $oUser = User::find($iUserId); if ($oUser) { $aUser = $oUser->toArray(); unset($aUser['pass']); unset($aUser['passwd']); unset($aUser['uuid']); unset($aUser['method']); unset($aUser['is_admin']); unset($aUser['theme']); unset($aUser['ga_token']); unset($aUser['ga_enable']); unset($aUser['api_token']); unset($aUser['is_dark_mode']); unset($aUser['locale']); $aUser['transfer_today'] = Tools::flowToGB($aUser['transfer_today']).'GB'; $aUser['transfer_total'] = Tools::flowToGB($aUser['transfer_total']).'GB'; $aUser['transfer_enable'] = Tools::flowToGB($aUser['transfer_enable']).'GB'; $aUser['last_use_time'] = date('Y-m-d H:i:s', $aUser['last_use_time']); } else { $aUser = ["not have id" => $iUserId]; } $this->sendMessage(json_encode($aUser, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // 查找订单 // orderFindByUser 11 if (strpos($sText, '/orderFindByUser') === 0 || strpos($sText, '\/orderFindByUser') === 0) { list(, $iUserId) = explode(" ", $sText, 2); $aOrder = Order::where("user_id", $iUserId)->get()->toArray(); if ($aOrder) { $aOrderNew = []; foreach ($aOrder as $k => $v) { $v['create_time'] = date("Y-m-d H:i:m", $v['create_time']); $v['update_time'] = date("Y-m-d H:i:m", $v['update_time']); $v['product_content'] = json_decode($v['product_content']); $aOrderNew[] = $v; } } else { $aOrderNew = ["not have id" => $iUserId]; } $this->sendMessage(json_encode($aOrderNew, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // 回复工单 // /reply 11 + /s 你好 if (strpos($sText, '/reply') === 0) { // 先清除一遍,只进行一个进程 $oTgStep->clearByFromId($aData["fromId"]); $iTicketId = str_replace('/reply', '', $sText); $iTicketId = (int) trim($iTicketId); $oTgStep->add($aData['fromId'], 'reply', 1, ["ticketId" => $iTicketId]); $sMessage = "将回复工单【".$iTicketId."】,请输入回复内容。"; $this->sendMessage($sMessage); Http::response(200, 'ok'); } // 快捷回复工单 // /ry 11 你好 if (strpos($sText, '/ry') === 0 || strpos($sText, '\/ry') === 0) { try { $sText = $this->trimText($sText, '/ry'); list($iTicketId, $sText) = explode(" ", $sText, 2); $oHttp = new Http(); $oHttp->sMethod = "put"; $oHttp->sToUrl = $_ENV['baseUrl']."/api/tg/ticket/".$iTicketId; $oHttp->bIsJson = true; $aData['comment'] = $sText; $aData['admin_name'] = $aUpdate["message"]["from"]["first_name"]; $oHttp->aData = $aData; $r = @json_decode($oHttp->send(), true); // 不论失败成功都清除 step $oTgStep->clearByFromId($aUpdate["message"]["from"]["id"]); if ($r["ret"] == 1) { $sMessage = "成功"; } else { $sMessage = "失败"; Http::response(200, json_encode($aData)); } $this->sendMessage($sMessage); Http::response(200, 'ok'); } catch (Exception $e) { $sMessage = "发生错误: " . $e->getMessage(); $this->sendMessage($sMessage); Http::response(200, '内部服务器错误'); } } // 复制节点 // /nodeCopy 11 if (strpos($sText, '/nodeCopy') === 0 || strpos($sText, '\/nodeCopy') === 0) { list(, $iNodeId) = explode(" ", $sText, 2); $oHttp = new Http(); $oHttp->sMethod = "post"; $oHttp->sToUrl = $_ENV['baseUrl']."/api/tg/node/".$iNodeId."/copy"; $oHttp->bIsJson = true; $oHttp->aData = $aData; $r = @json_decode($oHttp->send(), true); if ($r["ret"] == 1) { $sMessage = "成功"; } else { $sMessage = "失败"; Http::response(500, json_encode($aData)); } $this->sendMessage($sMessage); Http::response(200, 'ok'); } // 用户报表 // /reportUser_day_7 && /reportUser_date_2025-02-02 && /reportUser_date_2025-02-02_2025-03-03 if (strpos($sText, '/reportUser') === 0 || strpos($sText, '\/reportUser') === 0) { @list(, $sOption1, $sOption2, $sOption3) = explode("_", $sText, 4); $sMsg = '?'; if ($sOption1 == 'day') { $sMsg = (new ServiceTgReportUser())->ByDay($sOption2); } if ($sOption1 == 'date') { $sMsg = (new ServiceTgReportUser())->ByDate($sOption2, $sOption3); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 优惠卷报表 // /reportCoupon#7 if (strpos($sText, '/reportCoupon') === 0 || strpos($sText, '\/reportCoupon') === 0) { try { @list($sCmd, $iDay) = explode("#", $sText, 2); // 确保 $iDay 是一个有效的数字 if (!is_numeric($iDay) || $iDay < 0) { $this->sendMessage('无效的天数参数'); Http::response(200, '内部服务器错误'); } $sStartTime = strtotime("-$iDay days"); $aOrderList = Order::where('update_time', '>=', $sStartTime) ->whereIn('status', ['activated', 'pending_activation']) // 添加状态条件 ->select('coupon', \App\Services\DB::raw('count(*) as total'), \App\Services\DB::raw('SUM(price) as total_price')) ->groupBy('coupon') ->get() ->toArray(); $sMsg = '从 '.date("Y-m-d H:i:s", $sStartTime); foreach ($aOrderList as $row) { $coupon = str_replace('.', '#', $row['coupon']); if (!$coupon) { $coupon = '无'; } $sMsg .= "\n\n"; $sMsg .= $coupon . ' - ' . $row['total_price'] . ' / ' . $row['total']; } $this->sendMessage($sMsg); Http::response(200, 'ok'); } catch (Exception $e) { // 处理异常 $errorMsg = '发生错误: ' . $e->getMessage(); $this->sendMessage($errorMsg); // 返回错误响应 Http::response(200, '内部服务器错误'); } } // 节点列表(简洁) // /nodeList if (strpos($sText, '/nodeList') === 0 || strpos($sText, '\/nodeList') === 0) { $aNodeList = Node::where('type', 1)->orderBy('name', 'asc')->get()->toArray(); $sMsg = ''; foreach ($aNodeList as $row) { $sMsg .= "\n\n"; $sMsg .= $row['name'].', id:'.$row['id'].', duck:'.$row['duck_code'].', zf:'.$row['zhuanfa_code'].', sort:'.$row['sort']; } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 添加duck // /duckAdd jp01 if (strpos($sText, '/duckAdd') === 0 || strpos($sText, '\/duckAdd') === 0) { list($sCmd, $sCode) = explode(" ", $sText, 2); try { $oDucks = new Ducks(); $oDucks->code = $sCode; if ($oDucks->save()) { $sMsg = "成功"; } else { $sMsg = "失败"; } } catch (\Exception $e) { $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 查找duck // /duckFind jp01 if (strpos($sText, '/duckFind') === 0 || strpos($sText, '\/duckFind') === 0) { list($sCmd, $sCode) = explode(" ", $sText, 2); $aData = Ducks::where('code', $sCode)->first()->toArray(); $aData['use_width'] = Tools::flowToGB($aData['use_width']).'GB'; $aData['max_width'] = Tools::flowToGB($aData['max_width']).'GB'; $aData['today_width'] = Tools::flowToGB($aData['today_width']).'GB'; $this->sendMessage(json_encode($aData, JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // 添加zhuanfa // /zhuanfaAdd jp01 if (strpos($sText, '/zhuanfaAdd') === 0 || strpos($sText, '\/zhuanfaAdd') === 0) { list($sCmd, $sCode) = explode(" ", $sText, 2); try { $oZhuanfas = new Zhuanfas(); $oZhuanfas->code = $sCode; if ($oZhuanfas->save()) { $sMsg = "成功"; } else { $sMsg = "失败"; } } catch (\Exception $e) { $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 删除duck // /duckDel jp01 if (strpos($sText, '/duckDel') === 0 || strpos($sText, '\/duckDel') === 0) { list($sCmd, $sCode) = explode(" ", $sText, 2); try { $oDucks = Ducks::where("code", $sCode)->first(); if ($oDucks) { // 尝试删除记录 $deleted = $oDucks->delete(); if ($deleted) { // 删除成功 $sMsg = "成功"; } else { // 删除失败 $sMsg = "失败"; } } else { // 找不到记录 $sMsg = "找不到记录"; } } catch (\Exception $e) { // 捕获异常并返回错误信息 $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 删除node // /duckDel jp01 if (strpos($sText, '/nodeDel') === 0 || strpos($sText, '\/nodeDel') === 0) { list($sCmd, $iId) = explode(" ", $sText, 2); $sMsg = '?'; try { $oNode = Node::where("id", $iId)->first(); if ($oNode) { // 尝试删除记录 $deleted = $oNode->delete(); if ($deleted) { // 删除成功 $sMsg = "成功"; } else { // 删除失败 $sMsg = "失败"; } } else { // 找不到记录 $sMsg = "找不到记录"; } } catch (\Exception $e) { // 捕获异常并返回错误信息 $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 删除zhuanfa // /zhuanfaDel jp01 if (strpos($sText, '/zhuanfaDel') === 0 || strpos($sText, '\/zhuanfaDel') === 0) { list($sCmd, $sCode) = explode(" ", $sText, 2); try { $oZhuanfas = Zhuanfas::where("code", $sCode)->first(); if ($oZhuanfas) { // 尝试删除记录 $deleted = $oZhuanfas->delete(); if ($deleted) { // 删除成功 $sMsg = "成功"; } else { // 删除失败 $sMsg = "失败"; } } else { // 找不到记录 $sMsg = "找不到记录"; } } catch (\Exception $e) { // 捕获异常并返回错误信息 $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 修改duck // /duckOption@end_date@jp01 2025_02_02 // /duckOption@max_width@jp01 1.66#tb if (strpos($sText, '/duckOption') === 0 || strpos($sText, '\/duckOption') === 0) { list($sCmd, $sSet) = explode(" ", $sText, 2); list(, $sField, $sCode) = explode("#", $sCmd, 3); if ($sSet) { @list($iWidth, $sWidthType) = explode("|", $sSet, 2); } else { $sMsg = "没有参数"; } if ($sWidthType == 'tb') { $sSet = Tools::tomTbToB($iWidth); } if ($sWidthType == 'gb') { $sSet = Tools::tomGbToB($iWidth); } $sMsg = '?'; try { $oDuck = Ducks::where('code', $sCode)->first(); if (!$oDuck) { $sMsg = "没有这个code"; } else { $oDuck->$sField = $sSet; if ($oDuck->save()) { $sMsg = "成功"; // 保存成功 } else { $sMsg = "失败"; // 保存失败 } } } catch (\Illuminate\Database\QueryException $e) { $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 修改zhuanfa // /zhuanfaOption-end_date-jp01 2025_02_02 // /zhuanfaOption-max_width-jp01 1.66#tb if (strpos($sText, '/zhuanfaOption') === 0 || strpos($sText, '\/zhuanfaOption') === 0) { list($sCmd, $sSet) = explode(" ", $sText, 2); list(, $sField, $sCode) = explode("-", $sCmd, 3); @list($iWidth, $sWidthType) = explode("#", $sSet, 2); if ($sWidthType == 'tb') { $sSet = Tools::tomTbToB($iWidth); } if ($sWidthType == 'gb') { $sSet = Tools::tomGbToB($iWidth); } $sMsg = '?'; try { $oZhuanfas = Zhuanfas::where('code', $sCode)->first(); if (!$oZhuanfas) { $sMsg = "没有这个code"; } else { $oZhuanfas->$sField = $sSet; if ($oZhuanfas->save()) { $sMsg = "成功"; // 保存成功 } else { $sMsg = "失败"; // 保存失败 } } } catch (\Illuminate\Database\QueryException $e) { $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 转发重置 // /zhuanfaReset-youshi 2025-02-02 if (strpos($sText, '/zhuanfaReset') === 0 || strpos($sText, '\/zhuanfaReset') === 0) { list($sCmd, $sDate) = explode(" ", $sText, 2); list(, $sCode) = explode("-", $sCmd, 2); $sMsg = '?'; try { $oZhuanfas = Zhuanfas::where('code', $sCode)->first(); if (!$oZhuanfas) { $sMsg = "没有这个code"; } else { $oZhuanfas->use_width = 0; $oZhuanfas->end_date = $sDate; if ($oZhuanfas->save()) { $sMsg = "成功"; // 保存成功 } else { $sMsg = "失败"; // 保存失败 } } } catch (\Illuminate\Database\QueryException $e) { $sMsg = json_encode($e->getMessage()); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // duck list // /duckList if (strpos($sText, '/duckList') === 0 || strpos($sText, '\/duckList') === 0) { $aDuckList = Ducks::orderBy('code')->get()->toArray(); $aMsgData = []; foreach ($aDuckList as $k => $v) { $v['use_width'] = Tools::flowToGB($v['use_width']).'g'; $v['max_width'] = Tools::flowToGB($v['max_width']).'g'; $v['today_width'] = Tools::flowToGB($v['today_width']).'g'; $aMsgData[] = $v; } $this->sendMessage(json_encode($aMsgData, JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // zhuanfa list // /zhuanfaList if (strpos($sText, '/zhuanfaList') === 0 || strpos($sText, '\/zhuanfaList') === 0) { $aZhuanfaList = Zhuanfas::orderBy('code')->get()->toArray(); $aMsgData = []; foreach ($aZhuanfaList as $k => $v) { $v['use_width'] = Tools::flowToTB($v['use_width'])."t"; $v['max_width'] = Tools::flowToTB($v['max_width']).'t'; $v['today_width'] = Tools::flowToGB($v['today_width']).'g'; $aMsgData[] = $v; } $this->sendMessage(json_encode($aMsgData, JSON_PRETTY_PRINT)); Http::response(200, 'ok'); } // 节点设置变更 // /nodeOption@name@1 haha if (strpos($sText, '/nodeOption') === 0 || strpos($sText, '\/nodeOption') === 0) { list($sCmd, $sSet) = explode(" ", $sText, 2); list(, $sField, $iNodeId) = explode("#", $sCmd, 3); switch ($sField) { case "config": $sField = "custom_config"; break; case "duck": $sField = "duck_code"; break; case "zhuanfa": $sField = "zhuanfa_code"; break; default: $sField = $sField; break; } $sMsg = '?'; try { $oNode = Node::find($iNodeId); if (!$oNode) { $sMsg = "没有这个id"; } else { $oNode->$sField = $sSet; $oNode->save(); $sMsg = "ok"; } } catch (\Illuminate\Database\QueryException $e) { $sMsg = "数据库操作失败"; } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 节点搜索 // /nodeLike_duck_type1 jp02 if (strpos($sText, '/nodeLike') === 0 || strpos($sText, '\/nodeLike') === 0) { @list($sCmd, $sLike) = explode(" ", $sText); @list(, $sField, $sMod) = explode("#", $sCmd, 3); switch ($sField) { case "port": $sField = "custom_config"; break; case "config": $sField = "custom_config"; break; case "duck": $sField = "duck_code"; break; case "zhuanfa": $sField = "zhuanfa_code"; break; default: $sField = $sField; break; } $sMsg = '?'; try { $oNodeList = Node::where($sField, 'like', '%' . $sLike . '%'); if ($sMod == 'type1') { $oNodeList->where('type', 1); } $aNodeList = $oNodeList->orderBy('type', 'desc')->orderBy('name', 'asc')->get()->toArray(); } catch (\Illuminate\Database\QueryException $e) { $sMsg = '字段不存在'; } if (empty($aNodeList)) { $sMsg = "没有"; } else { $aNodeListNew = []; foreach ($aNodeList as $row) { $row['custom_config'] = json_decode($row['custom_config']); $row['dynamic_rate_config'] = json_decode($row['dynamic_rate_config']); $row['node_bandwidth_limit'] = Tools::flowToGB($row['node_bandwidth_limit']).'GB'; $row['node_bandwidth'] = Tools::flowToGB($row['node_bandwidth']).'GB'; $row['node_heartbeat'] = date('Y-m-d H:i:s', $row['node_heartbeat']); unset($row['is_dynamic_rate']); unset($row['dynamic_rate_type']); unset($row['dynamic_rate_config']); unset($row['bandwidthlimit_resetday']); unset($row['is_dynamic_rate']); unset($row['password']); // $aNodeListNew[] = $row; $sMsg = json_encode($row, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); $this->sendMessage($sMsg); } // $sMsg = json_encode($aNodeListNew, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); Http::response(200, 'ok'); } $this->sendMessage($sMsg); Http::response(200, 'ok'); } // 工单查找 // /ticketFind 11 if (strpos($sText, '/ticketFind') === 0) { $iTicketId = $this->trimText($sText, '/ticketFind'); $aTicket = Ticket::where("id", $iTicketId)->first()->toArray(); $sMsg = "title:".$aTicket['title']."。\n"; $aTicketContent = json_decode($aTicket['content'], true); if (!is_array($aTicketContent[0])) { $aTicketContent[] = $aTicketContent; } foreach ($aTicketContent as $row) { $sMsg .= "\n"; $sMsg .= "name:".$row['commenter_name']."\n"; $sMsg .= "comment:".$row['comment']."\n"; } $this->sendMessage($sMsg); Http::response(200, 'ok'); } Http::response(200, '无'); } private function trimText($sText, $sTrim) { $sText = ltrim($sText, $sTrim); $sText = trim($sText); return $sText; } private function sendMessage($sText = '?') { if ($_ENV['is_loc'] === true) { echo "
";
var_dump($sText);
} else {
$url = "https://api.telegram.org/bot$this->sApiToken/sendMessage?chat_id=$this->sApiChatId&text=" . urlencode($sText);
file_get_contents($url);
}
}
}