#!/bin/bash # ================================================================= # 脚本名称: VPS 流量审计 Pro (V18.5 - 审计修正) # 核心逻辑: 动态重置锚点 | 流量/时间双进度对标 | 熔断机制恢复 # 优先级: 稳健性第一 | 严格保留所有日志逻辑 # ================================================================= # --- 【强制内部时区】 --- export TZ='Asia/Shanghai' # --- 【1. 自定义统一配置区】 --- TG_TOKEN="填入机器人token" CHAT_ID="填入接收日报的账号ID" HOSTNAME="Debee MO" # 服务器显示名 TRAFFIC_RESET="27 00:00" # 重置周期 (日 时:分) INIT_USAGE_GB=7.33 # 初始对齐流量 (GB) INTERFACE="eth0" # 网卡名 (查看命令: ip addr) TRAFFIC_QUOTA=1500 # 周期总配额 (单位: GB) # --------------------------------------- DATA_DIR="/root/505" DATA_FILE="$DATA_DIR/.vps_monitor_data" LOG_FILE="$DATA_DIR/vps_monitor.log" LOCK_FILE="$DATA_DIR/.vps_lock" export LC_ALL=C mkdir -p "$DATA_DIR" # 基础依赖检查 for cmd in curl awk date flock grep; do command -v $cmd &> /dev/null || exit 1 done # --- 【2. 致命错误熔断预检 (V18.2 完整版) 】 --- RESET_DAY=$(echo "$TRAFFIC_RESET" | awk '{print $1}') RESET_TIME=$(echo "$TRAFFIC_RESET" | awk '{print $2}') TEST_STAMP=$(date -d "2026-01-01 ${RESET_TIME}:00" +%s 2>/dev/null) if [[ -z "$TEST_STAMP" ]]; then echo "$(date): [致命错误] TRAFFIC_RESET 时间格式无效: ${TRAFFIC_RESET}" >> "$LOG_FILE" exit 1 fi # --- 【3. 手动重置模块 】 --- if [[ "$1" == "--reset" ]]; then rm -f "$DATA_FILE" echo -e "\n\033[32m✨ [已重置] 流量账单已删除,下次运行将重新对齐。\033[0m" exit 0 fi # --- 【4. 核心数据采集 】 --- RAW_INFO=$(grep "$INTERFACE" /proc/net/dev) [[ -z "$RAW_INFO" ]] && { echo "$(date): 错误 - 找不到网卡 $INTERFACE" >> "$LOG_FILE"; exit 1; } CURRENT_RAW=$(echo "$RAW_INFO" | awk '{print $2 + $10}') UPTIME_SEC=$(awk '{print $1}' /proc/uptime | cut -d. -f1) BOOT_TIME=$(($(date +%s) - UPTIME_SEC)) # --- 【5. 账本读取与计算逻辑 】 --- if [ ! -f "$DATA_FILE" ] || [ ! -s "$DATA_FILE" ]; then INIT_BYTES=$(awk "BEGIN {print $INIT_USAGE_GB * 1024^3}") echo "$INIT_BYTES $CURRENT_RAW 0 0 0 $INIT_BYTES $BOOT_TIME" > "$DATA_FILE" fi read LIFE_TOTAL LAST_RAW MONTH_BASE LAST_RESET_MARK LAST_REPORT_DATE DAILY_BASE LAST_BOOT_TIME < "$DATA_FILE" # 5.1 重启检查 (已恢复 V18.2 日志) HAS_REBOOTED=0 BOOT_DIFF=$((BOOT_TIME - LAST_BOOT_TIME)) [[ ${BOOT_DIFF#-} -gt 10 ]] && HAS_REBOOTED=1 # 5.2 增量计算 if [ "$HAS_REBOOTED" -eq 1 ]; then DELTA=0 echo "$(date): [系统重启] 已建立新采样基准。" >> "$LOG_FILE" else if awk "BEGIN {exit ($CURRENT_RAW >= $LAST_RAW ? 0 : 1)}"; then DELTA=$(awk "BEGIN {print $CURRENT_RAW - $LAST_RAW}") else DELTA=$CURRENT_RAW fi fi # 5.3 核心:动态重置判定 (已恢复 V18.2 日志) LIFE_TOTAL=$(awk "BEGIN {print $LIFE_TOTAL + $DELTA}") CUR_STAMP=$(date +%s) calc_reset_point() { local year_month=$1 local res=$(date -d "${year_month}-${RESET_DAY} ${RESET_TIME}:00" +%s 2>/dev/null) if [[ -z "$res" ]]; then res=$(date -d "${year_month}-01 +1 month -1 day ${RESET_TIME}:00" +%s) fi echo "$res" } CUR_MONTH_RESET=$(calc_reset_point "$(date +%Y-%m)") if [[ "$CUR_STAMP" -ge "$CUR_MONTH_RESET" ]]; then NEXT_RESET_STAMP=$(calc_reset_point "$(date -d "$(date +%Y-%m-01) +1 month" +%Y-%m)") if [[ "$LAST_RESET_MARK" -lt "$CUR_MONTH_RESET" ]]; then MONTH_BASE=$LIFE_TOTAL LAST_RESET_MARK=$CUR_MONTH_RESET echo "$(date): [重置成功] 已跨越账期节点。" >> "$LOG_FILE" fi else NEXT_RESET_STAMP=$CUR_MONTH_RESET fi # 5.4 计算百分比 (含除零保护) DAYS_LEFT=$(( (NEXT_RESET_STAMP - CUR_STAMP) / 86400 + 1 )) MTD_BYTES=$(awk "BEGIN {val=$LIFE_TOTAL - $MONTH_BASE; print (val<0 ? 0 : val)}") MTD_GB=$(awk "BEGIN {printf \"%.2f\", $MTD_BYTES / 1024^3}") REMAIN_GB=$(awk "BEGIN {printf \"%.2f\", $TRAFFIC_QUOTA - $MTD_GB}") [[ $(awk "BEGIN {print ($REMAIN_GB < 0 ? 1 : 0)}") -eq 1 ]] && REMAIN_GB="0.00" if [[ $(awk "BEGIN {print ($TRAFFIC_QUOTA > 0 ? 1 : 0)}") -eq 1 ]]; then USED_PERCENT=$(awk "BEGIN {printf \"%.2f\", ($MTD_GB / $TRAFFIC_QUOTA) * 100}") else USED_PERCENT="0.00" fi [[ $(awk "BEGIN {print ($USED_PERCENT > 100 ? 1 : 0)}") -eq 1 ]] && USED_PERCENT="100.00" REMAIN_PERCENT=$(awk "BEGIN {printf \"%.2f\", 100 - $USED_PERCENT}") # 5.5 【修改点】时间进度计算逻辑 # 确保起始锚点准确:若从未重置过(首次运行),则推算上一个重置点作为周期起点 CALC_START_MARK=$LAST_RESET_MARK if [[ "$CALC_START_MARK" -eq 0 ]]; then CALC_START_MARK=$(calc_reset_point "$(date -d "$(date +%Y-%m-01) -1 month" +%Y-%m)") fi TOTAL_CYCLE_SEC=$(awk "BEGIN {print $NEXT_RESET_STAMP - $CALC_START_MARK}") ELAPSED_SEC=$(awk "BEGIN {print $CUR_STAMP - $CALC_START_MARK}") if [[ $(awk "BEGIN {print ($TOTAL_CYCLE_SEC > 0 ? 1 : 0)}") -eq 1 ]]; then TIME_PERCENT=$(awk "BEGIN {printf \"%.2f\", ($ELAPSED_SEC / $TOTAL_CYCLE_SEC) * 100}") else TIME_PERCENT="0.00" fi # 时间百分比边界保护 [[ $(awk "BEGIN {print ($TIME_PERCENT > 100 ? 1 : 0)}") -eq 1 ]] && TIME_PERCENT="100.00" [[ $(awk "BEGIN {print ($TIME_PERCENT < 0 ? 1 : 0)}") -eq 1 ]] && TIME_PERCENT="0.00" DAILY_BYTES=$(awk "BEGIN {val=$LIFE_TOTAL - $DAILY_BASE; print (val<0 ? 0 : val)}") REALTIME_DAILY_GB=$(awk "BEGIN {printf \"%.2f\", $DAILY_BYTES / 1024^3}") # --- 【6. 推送模块 】 --- ESCAPED_HOST=$(echo "$HOSTNAME" | sed 's/&/\&/g; s//\>/g') if [[ "$1" == "--test" ]]; then echo -e "\n\033[34m🔍 [流量审计 V18.5 - 审计修正]\033[0m" echo "━━━━━━━━━━━━━━━━" echo "📅 流量日报 | ${HOSTNAME}" echo "⏳ 距离重置: ${DAYS_LEFT} 天 时间 ${TIME_PERCENT}%" echo "📊 昨日消耗: ${REALTIME_DAILY_GB} GB" echo "📈 周期累计: ${MTD_GB} GB | ${USED_PERCENT}%" echo "🚀 剩余可用: ${REMAIN_GB} GB (${REMAIN_PERCENT}%)" exit 0 fi TODAY=$(date +%F) if [[ "$1" == "--report" && "$LAST_REPORT_DATE" != "$TODAY" ]]; then MSG_TRAFFIC="📅 流量日报 | ${ESCAPED_HOST} ━━━━━━━━━━━━━━━━ ⏳ 距离重置: ${DAYS_LEFT} 天 时间 ${TIME_PERCENT}% 📊 昨日消耗: ${REALTIME_DAILY_GB} GB 📈 周期累计: ${MTD_GB} GB | ${USED_PERCENT}% 🚀 剩余可用: ${REMAIN_GB} GB (${REMAIN_PERCENT}%)" RES_REP=$(curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \ --data-urlencode "chat_id=${CHAT_ID}" \ --data-urlencode "text=${MSG_TRAFFIC}" \ --data-urlencode "parse_mode=HTML") if [[ $RES_REP == *"\"ok\":true"* ]]; then LAST_REPORT_DATE=$TODAY DAILY_BASE=$LIFE_TOTAL else echo "$(date): 日报推送失败: $RES_REP" >> "$LOG_FILE" fi fi # --- 【7. 持久化与锁 】 --- ( flock -x 9 echo "$LIFE_TOTAL $CURRENT_RAW $MONTH_BASE $LAST_RESET_MARK $LAST_REPORT_DATE $DAILY_BASE $BOOT_TIME" > "$DATA_FILE" echo "$(date): MTD ${MTD_GB}GB, Today ${REALTIME_DAILY_GB}GB" >> "$LOG_FILE" tmp_log=$(tail -n 50 "$LOG_FILE") echo "$tmp_log" > "$LOG_FILE" ) 9>"$LOCK_FILE" exit 0