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 "
";
//// 访客统计 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'];
}
$aOrder = Order::whereBetween("update_time", [$sPrevTimeStart, $sPrevTimeEnd])
->where("status", "activated")
->get()
->toArray();
// 标记是否是新用户
$oUserModel = new User();
$aUserMix = [];
foreach ($aOrder as $row) {
$oUser = $oUserModel->where("id", $row["user_id"])->first();
if (isset($oUser->from)) {
$sFrom = $oUser->from;
} else {
$sFrom = "无";
}
// 初始化用户数据
if (!isset($aUserMix[$sFrom])) {
$aUserMix[$sFrom] = [
'pay_count_new' => 0,
'money_count_new' => 0,
'pay_count_old' => 0,
'money_count_old' => 0,
'pay_count' => 0,
'money_count' => 0,
];
}
// 确保时间比较的正确性
if (isset($oUser->reg_date) && ($row['update_time'] - strtotime($oUser->reg_date)) < (86400 * 30)) {
$aUserMix[$sFrom]['pay_count_new']++;
$aUserMix[$sFrom]['money_count_new'] += $row["price"];
} else {
$aUserMix[$sFrom]['pay_count_old']++;
$aUserMix[$sFrom]['money_count_old'] += $row["price"];
}
$aUserMix[$sFrom]['pay_count']++;
$aUserMix[$sFrom]['money_count'] += $row["price"];
}
$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 == 0) {
$sMsg .= '🚨【'.$oNodeRow->name."】小时未耗流,可能异常。\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 = '| 金额 | 用户ID | 用户名 | 充值时间 | '; foreach ($paylists as $paylist) { $text_html .= '
| ' . $paylist->total . ' | '; $text_html .= '' . $paylist->userid . ' | '; $text_html .= '' . (new User())->find($paylist->userid)->user_name . ' | '; $text_html .= '' . Tools::toDateTime((int) $paylist->datetime) . ' | '; $text_html .= '