fuc/src/Services/Cron.php
2025-04-04 17:50:12 +08:00

1032 lines
37 KiB
PHP
Executable File
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;
use App\Models\Ann;
use App\Models\Config;
use App\Models\DetectLog;
use App\Models\EmailQueue;
use App\Models\HourlyUsage;
use App\Models\Invoice;
use App\Models\Node;
use App\Models\OnlineLog;
use App\Models\Order;
use App\Models\Paylist;
use App\Models\SubscribeLog;
use App\Models\User;
use App\Models\TgStep;
use App\Models\Zhuanfas;
use App\Models\Ducks;
use App\Models\Visitor;
use App\Models\CountVisitor;
use App\Models\CountUser;
use App\Services\TomTool\TgSendFormat;
use App\Services\IM\Telegram;
use App\Services\Tg\ReportUser;
use App\Utils\Tools;
use DateTime;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Client\ClientExceptionInterface;
use Telegram\Bot\Exceptions\TelegramSDKException;
use function array_map;
use function date;
use function in_array;
use function json_decode;
use function str_replace;
use function strtotime;
use function time;
use const PHP_EOL;
final class Cron
{
public static function cleanDb(): void
{
(new SubscribeLog())->where(
'request_time',
'<',
time() - 86400 * Config::obtain('subscribe_log_retention_days')
)->delete();
(new HourlyUsage())->where(
'date',
'<',
date('Y-m-d', time() - 86400 * Config::obtain('traffic_log_retention_days'))
)->delete();
(new DetectLog())->where('datetime', '<', time() - 86400 * 3)->delete();
(new EmailQueue())->where('time', '<', time() - 86400)->delete();
(new OnlineLog())->where('last_time', '<', time() - 86400)->delete();
echo Tools::toDateTime(time()) . ' 数据库清理完成' . PHP_EOL;
}
public static function clearTgStep(): void
{
TgStep::where("date", "<=", date('Y-m-d', strtotime('-1 day')))->delete();
}
public static function userCount(): void
{
$sPrevDate = date("Y-m-d", strtotime("-1 day"));
$sPrevDateStart = date("Y-m-d 00:00:00", strtotime("-1 day"));
$sPrevDateEnd = date("Y-m-d 23:59:59", strtotime("-1 day"));
$sPrevTimeStart = strtotime($sPrevDateStart);
$sPrevTimeEnd = strtotime($sPrevDateEnd);
$sPrevDate30 = date("Y-m-d", strtotime("-30 day"));
// echo "<pre>";
//// 访客统计 start
$aVisitorCount = Visitor::select('from', DB::raw('count(*) as count'))
->groupBy('from')
->get()
->toArray();
$aVisitorMix = [];
foreach ($aVisitorCount as $row) {
$aVisitorMix[$row['from']]['count'] = $row['count'];
}
//// 访客统计 end
// 注册数量
$aRegCount = User::select('from', DB::raw('count(*) as count'))
->whereDate('reg_date', $sPrevDate)
->groupBy('from')
->get()
->toArray();
$aRegMix = [];
foreach ($aRegCount as $row) {
if (isset($row['from'])) {
$sFrom = $row['from'];
} else {
$sFrom = '无';
}
$aRegMix[$sFrom]['count'] = $row['count'];
}
//// 付款 start
$aOrder = Order::whereBetween("update_time", [$sPrevTimeStart, $sPrevTimeEnd])->where("status", "activated")->get()->toArray();
// 标记是否是新用户
$oUserModel = new User();
$iPayCount = 0;
$iMoneyCount = 0;
$iPayCountNew = 0;
$iPayCountOld = 0;
$iMoneyCountNew = 0;
$iMoneyCountOld = 0;
$sFrom = '';
$aUserMix = [];
foreach ($aOrder as $row) {
$iPayCount ++;
$iMoneyCount += $row["price"];
$oUser = $oUserModel->where("id", $row["id"])->first();
if (isset($oUser->from)) {
$sFrom = $oUser->from;
} else {
$sFrom = "";
}
if ( ($row['update_time'] - strtotime($oUser->reg_date)) < (86400 * 30) ) {
$iPayCountNew ++;
$iMoneyCountNew += $row["price"];
} else {
$iPayCountOld ++;
$iMoneyCountOld += $row["price"];
}
$aUserMix[$sFrom]['pay_count'] = $iPayCount;
$aUserMix[$sFrom]['money_count'] = $iMoneyCount;
$aUserMix[$sFrom]['pay_count_new'] = $iPayCountNew;
$aUserMix[$sFrom]['pay_count_old'] = $iPayCountOld;
$aUserMix[$sFrom]['money_count_new'] = $iMoneyCountNew;
$aUserMix[$sFrom]['money_count_old'] = $iMoneyCountOld;
}
$aAllMix = array_merge($aVisitorMix, $aRegMix, $aUserMix);
foreach ($aAllMix as $k => $v) {
$oCountUserModel = new CountUser();
$oCountUserModel->from = $k;
$oCountUserModel->count_from = $aVisitorMix[$k]['count'] ?? 0;
$oCountUserModel->count_reg = $aRegMix[$k]['count'] ?? 0;
$oCountUserModel->count_pay = $aUserMix[$k]['pay_count'] ?? 0;
$oCountUserModel->count_pay_new = $aUserMix[$k]['pay_count_new'] ?? 0;
$oCountUserModel->count_pay_old = $aUserMix[$k]['pay_count_old'] ?? 0;
$oCountUserModel->count_money = $aUserMix[$k]['money_count'] ?? 0;
$oCountUserModel->count_money_new = $aUserMix[$k]['money_count_new'] ?? 0;
$oCountUserModel->count_money_old = $aUserMix[$k]['money_count_old'] ?? 0;
$oCountUserModel->date = $sPrevDate;
$r = $oCountUserModel->save();
}
Visitor::truncate();
$sMsg = (new ReportUser())->ByDay(1);
(new Telegram())->send(0, $sMsg);
}
// public static function visitorCount(): void
// {
// $oVisitor = Visitor::select('from', DB::raw('count(*) as count'))
// ->groupBy('from')
// ->get();
//
// foreach ($oVisitor as $row) {
//
// $oCountVisitor = new CountVisitor();
// $oCountVisitor->from = $row->from;
// $oCountVisitor->count = $row->count;
// $oCountVisitor->date = date('Y-m-d', strtotime('-1 day'));
// $oCountVisitor->save();
//
// }
//
// Visitor::truncate();
//
// // tg报表
// $aCountVisitor = CountVisitor::where('date', date('Y-m-d', strtotime('-1 day')))->get()->toArray();
//
// $sTgReport = '';
// foreach ($aCountVisitor as $row) {
//
// $sTgReport .= $row["from"]."".$row["count"]."\n";
//
// }
//
// (new Telegram())->send(0, $sTgReport);
//
// }
public static function detectInactiveUser(): void
{
$checkin_days = Config::obtain('detect_inactive_user_checkin_days');
$login_days = Config::obtain('detect_inactive_user_login_days');
$use_days = Config::obtain('detect_inactive_user_use_days');
(new User())->where('is_admin', 0)
->where('is_inactive', 0)
->where('last_check_in_time', '<', time() - 86400 * $checkin_days)
->where('last_login_time', '<', time() - 86400 * $login_days)
->where('last_use_time', '<', time() - 86400 * $use_days)
->update(['is_inactive' => 1]);
(new User())->where('is_admin', 0)
->where('is_inactive', 1)
->where('last_check_in_time', '>', time() - 86400 * $checkin_days)
->where('last_login_time', '>', time() - 86400 * $login_days)
->where('last_use_time', '>', time() - 86400 * $use_days)
->update(['is_inactive' => 0]);
echo Tools::toDateTime(time()) .
' 检测到 ' . (new User())->where('is_inactive', 1)->count() . ' 个账户处于闲置状态' . PHP_EOL;
}
public static function detectNodeOffline(): void
{
$nodes = (new Node())->where('type', 1)->get();
foreach ($nodes as $node) {
if ($node->getNodeOnlineStatus() >= 0 && $node->online === 1) {
continue;
}
if ($node->getNodeOnlineStatus() === -1 && $node->online === 1) {
echo 'Send Node Offline Email to admin users' . PHP_EOL;
try {
Notification::notifyAdmin(
$_ENV['appName'] . '-系统警告',
'管理员你好,系统发现节点 ' . $node->name . ' 掉线了,请你及时处理。'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
if (Config::obtain('telegram_node_offline')) {
$notice_text = str_replace(
'%node_name%',
$node->name,
Config::obtain('telegram_node_offline_text')
);
try {
(new Telegram())->send(0, $notice_text);
} catch (TelegramSDKException $e) {
echo $e->getMessage();
}
}
$node->online = 0;
$node->save();
continue;
}
if ($node->getNodeOnlineStatus() === 1 && $node->online === 0) {
echo 'Send Node Online Email to admin user' . PHP_EOL;
try {
Notification::notifyAdmin(
$_ENV['appName'] . '-系统提示',
'管理员你好,系统发现节点 ' . $node->name . ' 恢复上线了。'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
if (Config::obtain('telegram_node_online')) {
$notice_text = str_replace(
'%node_name%',
$node->name,
Config::obtain('telegram_node_online_text')
);
try {
(new Telegram())->send(0, $notice_text);
} catch (TelegramSDKException $e) {
echo $e->getMessage();
}
}
$node->online = 1;
$node->save();
}
}
echo Tools::toDateTime(time()) . ' 节点离线检测完成' . PHP_EOL;
}
public static function expirePaidUserAccount(): void
{
$paidUsers = (new User())->where('class', '>', 0)->get();
foreach ($paidUsers as $user) {
if (strtotime($user->class_expire) < time()) {
$text = '你好,系统发现你的账号等级已经过期了。';
$reset_traffic = $_ENV['class_expire_reset_traffic'];
if ($reset_traffic >= 0) {
$user->transfer_enable = Tools::toGB($reset_traffic);
$text .= '流量已经被重置为' . $reset_traffic . 'GB。';
}
try {
Notification::notifyUser($user, $_ENV['appName'] . '-你的账号等级已经过期了', $text);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
$user->u = 0;
$user->d = 0;
$user->transfer_today = 0;
$user->class = 0;
$user->save();
}
}
echo Tools::toDateTime(time()) . ' 付费用户过期检测完成' . PHP_EOL;
}
public static function processEmailQueue(): void
{
if ((new EmailQueue())->count() === 0) {
echo Tools::toDateTime(time()) . ' 邮件队列为空' . PHP_EOL;
} else {
//记录当前时间戳
$timestamp = time();
//邮件队列处理
while (true) {
if (time() - $timestamp > 299) {
echo Tools::toDateTime(time()) . '邮件队列处理超时,已跳过' . PHP_EOL;
break;
}
DB::beginTransaction();
$email_queues_raw = DB::select('SELECT * FROM email_queue LIMIT 1 FOR UPDATE SKIP LOCKED');
if (count($email_queues_raw) === 0) {
DB::commit();
break;
}
$email_queues = array_map(static function ($value) {
return (array) $value;
}, $email_queues_raw);
$email_queue = $email_queues[0];
echo '发送邮件至 ' . $email_queue['to_email'] . PHP_EOL;
DB::delete('DELETE FROM email_queue WHERE id = ?', [$email_queue['id']]);
if (Tools::isEmail($email_queue['to_email'])) {
try {
Mail::send(
$email_queue['to_email'],
$email_queue['subject'],
$email_queue['template'],
json_decode($email_queue['array'])
);
} catch (Exception|ClientExceptionInterface $e) {
echo $e->getMessage();
}
} else {
echo $email_queue['to_email'] . ' 邮箱格式错误,已跳过' . PHP_EOL;
}
DB::commit();
}
echo Tools::toDateTime(time()) . ' 邮件队列处理完成' . PHP_EOL;
}
}
public static function processTabpOrderActivation(): void
{
$users = User::all();
foreach ($users as $user) {
$user_id = $user->id;
// 获取用户账户等待激活的TABP订单
$pending_activation_orders = (new Order())->where('user_id', $user_id)
->where('status', 'pending_activation')
->where('product_type', 'tabp')
->orderBy('id')
->get();
// 获取用户账户已激活的TABP订单一个用户同时只能有一个已激活的TABP订单
$activated_order = (new Order())->where('user_id', $user_id)
->where('status', 'activated')
->where('product_type', 'tabp')
->orderBy('id')
->first();
// 如果用户账户中没有已激活的TABP订单且有等待激活的TABP订单则激活最早的等待激活TABP订单
if ($activated_order === null && count($pending_activation_orders) > 0) {
$order = $pending_activation_orders[0];
// 获取TABP订单内容准备激活
$content = json_decode($order->product_content);
// 激活TABP
$user->u = 0;
$user->d = 0;
$user->transfer_today = 0;
$user->transfer_enable = Tools::toGB($content->bandwidth);
$user->class = $content->class;
$old_class_expire = new DateTime();
$user->class_expire = $old_class_expire
->modify('+' . $content->class_time . ' days')->format('Y-m-d H:i:s');
$user->node_group = $content->node_group;
$user->node_speedlimit = $content->speed_limit;
$user->node_iplimit = $content->ip_limit;
$user->save();
$order->status = 'activated';
$order->update_time = time();
$order->save();
echo "TABP订单 #{$order->id} 已激活。\n";
continue;
}
// 如果用户账户中有已激活的TABP订单则判断是否过期
if ($activated_order !== null) {
$content = json_decode($activated_order->product_content);
if ($activated_order->update_time + $content->class_time * 86400 < time()) {
$activated_order->status = 'expired';
$activated_order->update_time = time();
$activated_order->save();
echo "TABP订单 #{$activated_order->id} 已过期。\n";
}
}
}
echo Tools::toDateTime(time()) . ' TABP订单激活处理完成' . PHP_EOL;
}
public static function processBandwidthOrderActivation(): void
{
$users = User::all();
foreach ($users as $user) {
$user_id = $user->id;
// 获取用户账户等待激活的流量包订单
$order = (new Order())->where('user_id', $user_id)
->where('status', 'pending_activation')
->where('product_type', 'bandwidth')
->orderBy('id')
->first();
if ($order !== null) {
// 获取流量包订单内容准备激活
$content = json_decode($order->product_content);
// 激活流量包
$user->transfer_enable += Tools::toGB($content->bandwidth);
$user->save();
$order->status = 'activated';
$order->update_time = time();
$order->save();
echo "流量包订单 #{$order->id} 已激活。\n";
}
}
echo Tools::toDateTime(time()) . ' 流量包订单激活处理完成' . PHP_EOL;
}
/**
* @throws Exception
*/
public static function processTimeOrderActivation(): void
{
$users = User::all();
foreach ($users as $user) {
$user_id = $user->id;
// 获取用户账户等待激活的时间包订单
$order = (new Order())->where('user_id', $user_id)
->where('status', 'pending_activation')
->where('product_type', 'time')
->orderBy('id')
->first();
if ($order !== null) {
$content = json_decode($order->product_content);
// 跳过当前账户等级不等于时间包等级的非免费用户订单
if ($user->class !== (int) $content->class && $user->class > 0) {
continue;
}
// 激活时间包
$user->class = $content->class;
$old_class_expire = new DateTime($user->class_expire);
$user->class_expire = $old_class_expire
->modify('+' . $content->class_time . ' days')->format('Y-m-d H:i:s');
$user->node_group = $content->node_group;
$user->node_speedlimit = $content->speed_limit;
$user->node_iplimit = $content->ip_limit;
$user->save();
$order->status = 'activated';
$order->update_time = time();
$order->save();
echo "时间包订单 #{$order->id} 已激活。\n";
}
}
echo Tools::toDateTime(time()) . ' 时间包订单激活处理完成' . PHP_EOL;
}
public static function processPendingOrder(): void
{
$pending_payment_orders = (new Order())->where('status', 'pending_payment')->get();
foreach ($pending_payment_orders as $order) {
// 检查账单支付状态
$invoice = (new Invoice())->where('order_id', $order->id)->first();
if ($invoice === null) {
continue;
}
// 标记订单为等待激活
if (in_array($invoice->status, ['paid_gateway', 'paid_balance', 'paid_admin'])) {
$order->status = 'pending_activation';
$order->update_time = time();
$order->save();
echo "已标记订单 #{$order->id} 为等待激活。\n";
continue;
}
// 取消超时未支付的订单和关联账单
if ($order->create_time + 86400 < time()) {
$order->status = 'cancelled';
$order->update_time = time();
$order->save();
echo "已取消超时订单 #{$order->id}\n";
$invoice->status = 'cancelled';
$invoice->update_time = time();
$invoice->save();
echo "已取消超时账单 #{$invoice->id}\n";
}
}
echo Tools::toDateTime(time()) . ' 等待中订单处理完成' . PHP_EOL;
}
public static function removeInactiveUserLinkAndInvite(): void
{
$inactive_users = (new User())->where('is_inactive', 1)->get();
foreach ($inactive_users as $user) {
$user->removeLink();
$user->removeInvite();
}
echo Tools::toDateTime(time()) . ' Successfully removed inactive user\'s Link and Invite' . PHP_EOL;
}
// 废弃
public static function resetNodeBandwidth(): void
{
(new Node())->where('bandwidthlimit_resetday', date('d'))->update(['node_bandwidth' => 0]);
echo Tools::toDateTime(time()) . ' 重设节点流量完成' . PHP_EOL;
}
public static function resetDuckBandwidth(): void
{
(new Ducks())->where('width_reset_day', date('d'))->update(['use_width' => 0]);
echo Tools::toDateTime(time()) . ' 重设机器流量完成' . PHP_EOL;
}
// public static function resetZhuanfaBandwidth(): void
// {
// (new Ducks())->where('end_date', date('Y-m-d'))->update(['use_width' => 0]);
//
// echo Tools::toDateTime(time()) . ' 重设机器流量完成' . PHP_EOL;
// }
public static function mergeBandWidth(): void
{
$oNodeAll = Node::where('type', 1)->get();
$aZhuanfaFlowNow = [];
$aDuckFlowNow = [];
$sMsg = "";
// 统计node流量
foreach ($oNodeAll as $oNodeRow) {
if ($oNodeRow->node_bandwidth <= 1242880) {
$sMsg .= '🚨【'.$oNodeRow->name."】小时耗流不足1mb可能有异常。\n";
}
@$aZhuanfaFlowNow[$oNodeRow->zhuanfa_code] += $oNodeRow->node_bandwidth;
@$aDuckFlowNow[$oNodeRow->duck_code] += $oNodeRow->node_bandwidth;
}
// 入库zhunafa
foreach ($aZhuanfaFlowNow as $k => $v) {
if ($v) {
Zhuanfas::where('code', $k)->increment('today_width', $v);
Zhuanfas::where('code', $k)->increment('use_width', $v);
}
}
// 入库duck
foreach ($aDuckFlowNow as $k => $v) {
if ($v) {
Ducks::where('code', $k)->increment('today_width', $v);
Ducks::where('code', $k)->increment('use_width', $v);
}
}
// 清空node流量
Node::query()->update(['node_bandwidth' => 0]);
if ($sMsg) {
// echo $sMsg;
(new Telegram())->send(0, $sMsg);
}
}
public static function reportBandWidth(): void
{
$oZhuanfas = new Zhuanfas();
$oDucks = new Ducks();
// 流量报告,转发
$oTgSendFormat = new TgSendFormat();
$oTgSendFormat->sTitleEmoji = "\n\n";
$oTgSendFormat->sTitle = "# 转发流量";
$today = new DateTime();
$oZhuanfasAll = $oZhuanfas->all();
foreach ($oZhuanfasAll as $oZhuanfasRow) {
$targetDate = new DateTime($oZhuanfasRow->end_date);
$interval = $today->diff($targetDate);
if ($today > $targetDate) {
$iDiffDay = -$interval->days;
} else {
$iDiffDay = $interval->days;
}
$oTgSendFormat->rowAdd(
$oZhuanfasRow->name,
$oZhuanfasRow->today_width,
$oZhuanfasRow->use_width,
$oZhuanfasRow->max_width,
$iDiffDay
);
}
$sTgReport = $oTgSendFormat->typeA();
// var_dump($sTgReport);
(new Telegram())->send(0, $sTgReport);
// 流量报告,机器
$oTgSendFormat = new TgSendFormat();
$oTgSendFormat->sTitleEmoji = "\n\n";
$oTgSendFormat->sTitle = "# 机器流量";
$oDucksAll = $oDucks->orderBy('code')->get();
foreach ($oDucksAll as $oDucksRow) {
if ($oDucksRow->width_reset_day == 0) {
$iWidthResetDay = 0;
} if ($oDucksRow->width_reset_day > date('d')) {
$iWidthResetDay = $oDucksRow->width_reset_day - date('d');
} else {
$date = new DateTime();
$date->modify('first day of next month');
$date->setDate((int)$date->format('Y'), (int)$date->format('m'), $oDucksRow->width_reset_day);
$sEndDate = $date;
$currentDate = new DateTime();
$interval = $currentDate->diff($sEndDate);
$iWidthResetDay = $interval->days;
}
$oTgSendFormat->rowAdd(
$oDucksRow->code,
$oDucksRow->today_width,
$oDucksRow->use_width,
$oDucksRow->max_width,
$iWidthResetDay,
$oDucksRow->memo
);
}
$sTgReport = $oTgSendFormat->typeA();
// var_dump($sTgReport);
(new Telegram())->send(0, $sTgReport);
}
public static function clearTodayWidth(): void
{
Ducks::query()->update(['today_width' => 0]);
Zhuanfas::query()->update(['today_width' => 0]);
}
public static function reportBandWidth_back(): void
{
$oNode = new Node();
$oZhuanfas = new Zhuanfas();
$oDucks = new Ducks();
$oNodeAll = $oNode->all();
foreach ($oNodeAll as $oNodeRow) {
@ $iZhuanfaFlowToday[$oNodeRow->zhuanfa_code] += $oNodeRow->node_bandwidth;
@ $iDuckFlowToday[$oNodeRow->duck_code] += $oNodeRow->node_bandwidth;
$oZhuanfas->where('code', $oNodeRow->zhuanfa_code)->increment('use_width', $oNodeRow->node_bandwidth);
$oDucks->where('code', $oNodeRow->duck_code)->increment('use_width', $oNodeRow->node_bandwidth);
$oNode->where('id', $oNodeRow->id)->update(['node_bandwidth' => 0]);
}
// 流量报告,转发
$oTgSendFormat = new TgSendFormat();
$oTgSendFormat->sTitleEmoji = "⚡️⚡️⚡️⚡️⚡️⚡️";
$oTgSendFormat->sTitle = "# 转发流量";
$today = new DateTime();
$oZhuanfasAll = $oZhuanfas->all();
foreach ($oZhuanfasAll as $oZhuanfasRow) {
$targetDate = new DateTime($oZhuanfasRow->end_date);
$interval = $today->diff($targetDate);
$oTgSendFormat->rowAdd(
$oZhuanfasRow->name,
$iZhuanfaFlowToday[$oZhuanfasRow->code],
$oZhuanfasRow->use_width,
$oZhuanfasRow->max_width,
$interval->days
);
}
$sTgReport = $oTgSendFormat->typeA();
(new Telegram())->send(0, $sTgReport);
// 流量报告,机器
$oTgSendFormat = new TgSendFormat();
$oTgSendFormat->sTitleEmoji = "\n\n";
$oTgSendFormat->sTitle = "# 机器流量";
$oDucksAll = $oDucks->orderBy('code')->get();
foreach ($oDucksAll as $oDucksRow) {
if ($oDucksRow->width_reset_day == 0) {
$iWidthResetDay = 0;
} if ($oDucksRow->width_reset_day > date('d')) {
$iWidthResetDay = $oDucksRow->width_reset_day - date('d');
} else {
$date = new DateTime();
$date->modify('first day of next month');
$date->setDate((int)$date->format('Y'), (int)$date->format('m'), $oDucksRow->width_reset_day);
$sEndDate = $date;
$currentDate = new DateTime();
$interval = $currentDate->diff($sEndDate);
$iWidthResetDay = $interval->days;
}
$oTgSendFormat->rowAdd(
$oDucksRow->code,
$iDuckFlowToday[$oDucksRow->code] ?? 0,
$oDucksRow->use_width,
$oDucksRow->max_width,
$iWidthResetDay
);
}
$sTgReport = $oTgSendFormat->typeA();
(new Telegram())->send(0, $sTgReport);
}
public static function resetTodayBandwidth(): void
{
(new User())->query()->update(['transfer_today' => 0]);
echo Tools::toDateTime(time()) . ' 重设用户每日流量完成' . PHP_EOL;
}
public static function resetFreeUserBandwidth(): void
{
$freeUsers = (new User())->where('class', 0)
->where('auto_reset_day', date('d'))->get();
foreach ($freeUsers as $user) {
try {
Notification::notifyUser(
$user,
$_ENV['appName'] . '-免费流量重置通知',
'你好,你的免费流量已经被重置为' . $user->auto_reset_bandwidth . 'GB。'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
$user->u = 0;
$user->d = 0;
$user->transfer_enable = $user->auto_reset_bandwidth * 1024 * 1024 * 1024;
$user->save();
}
echo Tools::toDateTime(time()) . ' 免费用户流量重置完成' . PHP_EOL;
}
public static function sendDailyFinanceMail(): void
{
$today = strtotime('00:00:00');
$paylists = (new Paylist())->where('status', 1)
->whereBetween('datetime', [strtotime('-1 day', $today), $today])->get();
$text_html = '<table border=1><tr><td>金额</td><td>用户ID</td><td>用户名</td><td>充值时间</td>';
foreach ($paylists as $paylist) {
$text_html .= '<tr>';
$text_html .= '<td>' . $paylist->total . '</td>';
$text_html .= '<td>' . $paylist->userid . '</td>';
$text_html .= '<td>' . (new User())->find($paylist->userid)->user_name . '</td>';
$text_html .= '<td>' . Tools::toDateTime((int) $paylist->datetime) . '</td>';
$text_html .= '</tr>';
}
$text_html .= '</table>';
$text_html .= '<br>昨日总收入笔数:' . count($paylists) . '<br>昨日总收入金额:' . $paylists->sum('total');
echo 'Sending daily finance email to admin user' . PHP_EOL;
try {
Notification::notifyAdmin(
'财务日报',
$text_html,
'finance.tpl'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
echo Tools::toDateTime(time()) . ' 成功发送财务日报' . PHP_EOL;
}
public static function sendWeeklyFinanceMail(): void
{
$today = strtotime('00:00:00');
$paylists = (new Paylist())->where('status', 1)
->whereBetween('datetime', [strtotime('-1 week', $today), $today])
->get();
$text_html = '<br>上周总收入笔数:' . count($paylists) . '<br>上周总收入金额:' . $paylists->sum('total');
echo 'Sending weekly finance email to admin user' . PHP_EOL;
try {
Notification::notifyAdmin(
'财务周报',
$text_html,
'finance.tpl'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
echo Tools::toDateTime(time()) . ' 成功发送财务周报' . PHP_EOL;
}
public static function sendMonthlyFinanceMail(): void
{
$today = strtotime('00:00:00');
$paylists = (new Paylist())->where('status', 1)
->whereBetween('datetime', [strtotime('-1 month', $today), $today])
->get();
$text_html = '<br>上月总收入笔数:' . count($paylists) . '<br>上月总收入金额:' . $paylists->sum('total');
echo 'Sending monthly finance email to admin user' . PHP_EOL;
try {
Notification::notifyAdmin(
'财务月报',
$text_html,
'finance.tpl'
);
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
echo $e->getMessage() . PHP_EOL;
}
echo Tools::toDateTime(time()) . ' 成功发送财务月报' . PHP_EOL;
}
public static function sendPaidUserUsageLimitNotification(): void
{
$paidUsers = (new User())->where('class', '>', 0)->get();
foreach ($paidUsers as $user) {
$user_traffic_left = $user->transfer_enable - $user->u - $user->d;
$under_limit = false;
$unit_text = '';
if ($_ENV['notify_limit_mode'] === 'per' &&
$user_traffic_left / $user->transfer_enable * 100 < $_ENV['notify_limit_value']
) {
$under_limit = true;
$unit_text = '%';
} elseif ($_ENV['notify_limit_mode'] === 'mb' &&
Tools::flowToMB($user_traffic_left) < $_ENV['notify_limit_value']
) {
$under_limit = true;
$unit_text = 'MB';
}
if ($under_limit && ! $user->traffic_notified) {
try {
Notification::notifyUser(
$user,
$_ENV['appName'] . '-你的剩余流量过低',
'你好,系统发现你剩余流量已经低于 ' . $_ENV['notify_limit_value'] . $unit_text . ' 。',
);
$user->traffic_notified = true;
} catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
$user->traffic_notified = false;
echo $e->getMessage() . PHP_EOL;
}
$user->save();
} elseif (! $under_limit && $user->traffic_notified) {
$user->traffic_notified = false;
$user->save();
}
}
echo Tools::toDateTime(time()) . ' 付费用户用量限制提醒完成' . PHP_EOL;
}
public static function sendDailyTrafficReport(): void
{
$users = (new User())->whereIn('daily_mail_enable', [1, 2])->get();
$ann_latest_raw = (new Ann())->orderBy('date', 'desc')->first();
if ($ann_latest_raw === null) {
$ann_latest = '<br><br>';
} else {
$ann_latest = $ann_latest_raw->content . '<br><br>';
}
foreach ($users as $user) {
$user->sendDailyNotification($ann_latest);
}
echo Tools::toDateTime(time()) . ' 成功发送每日流量报告' . PHP_EOL;
}
/**
* @throws TelegramSDKException
*/
public static function sendTelegramDailyJob(): void
{
(new Telegram())->send(0, Config::obtain('telegram_daily_job_text'));
echo Tools::toDateTime(time()) . ' 成功发送 Telegram 每日任务提示' . PHP_EOL;
}
/**
* @throws TelegramSDKException
*/
public static function sendTelegramDiary(): void
{
(new Telegram())->send(
0,
str_replace(
[
'%getTodayCheckinUser%',
'%lastday_total%',
],
[
Analytics::getTodayCheckinUser(),
Analytics::getTodayTrafficUsage(),
],
Config::obtain('telegram_diary_text')
)
);
echo Tools::toDateTime(time()) . ' 成功发送 Telegram 系统运行日志' . PHP_EOL;
}
public static function updateNodeIp(): void
{
$nodes = (new Node())->where('type', 1)->get();
foreach ($nodes as $node) {
$node->updateNodeIp();
$node->save();
}
echo Tools::toDateTime(time()) . ' 更新节点 IP 完成' . PHP_EOL;
}
}