├── traffic_monitor.sh ├── 交互式脚本.png ├── 端口流量tg推送实例图片.jpg ├── ali-20g ├── ali-200g ├── ali-1T ├── asia-300g ├── gcp-200g ├── alice-1500g ├── tg_notifier_config.txt.example ├── gcp-625g ├── az-115g ├── traffic_monitor_config.txt.example ├── az-15g ├── .gitignore ├── remove_traffic_limit.sh ├── test_port_traffic_notifications.sh ├── PORT_TRAFFIC_ISSUE.md ├── set_daily_time.sh ├── OPTIMIZATION_SUMMARY.md ├── PROXY_TRAFFIC_MONITORING.md ├── port_traffic_helper.sh ├── machine_limit_manager.sh ├── view_port_traffic.sh ├── trafficcop-manager.sh ├── README.md ├── serverchan_notifier.sh ├── pushplus_notifier.sh ├── trafficcop.sh ├── README_EN.md └── tg_notifier.sh /traffic_monitor.sh: -------------------------------------------------------------------------------- 1 | /root/TrafficCop/trafficcop.sh -------------------------------------------------------------------------------- /交互式脚本.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ypq123456789/TrafficCop/HEAD/交互式脚本.png -------------------------------------------------------------------------------- /端口流量tg推送实例图片.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ypq123456789/TrafficCop/HEAD/端口流量tg推送实例图片.jpg -------------------------------------------------------------------------------- /ali-20g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=max 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=20 4 | TRAFFIC_TOLERANCE=3 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=eth0 8 | -------------------------------------------------------------------------------- /ali-200g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=max 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=200 4 | TRAFFIC_TOLERANCE=30 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=eth0 8 | -------------------------------------------------------------------------------- /ali-1T: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=out 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=1024 4 | TRAFFIC_TOLERANCE=150 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=eth0 8 | LIMIT_MODE=tc 9 | -------------------------------------------------------------------------------- /asia-300g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=total 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=300 4 | TRAFFIC_TOLERANCE=40 5 | PERIOD_START_DAY=27 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=eth0 8 | # 这家流量计费周期起始日是账单日,记得自行更换! 9 | -------------------------------------------------------------------------------- /gcp-200g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=out 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=200 4 | TRAFFIC_TOLERANCE=30 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=ens4 8 | LIMIT_MODE=tc 9 | # 白嫖标准路由200g流量就跑 10 | -------------------------------------------------------------------------------- /alice-1500g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=total 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=1500 4 | TRAFFIC_TOLERANCE=200 5 | PERIOD_START_DAY=18 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=eth0 8 | LIMIT_MODE=tc 9 | # 这家的流量计费周期起始日是账单日,记得自己修改! 10 | -------------------------------------------------------------------------------- /tg_notifier_config.txt.example: -------------------------------------------------------------------------------- 1 | # Telegram Bot 配置示例 2 | # 复制此文件为 tg_notifier_config.txt 并填写实际值 3 | 4 | BOT_TOKEN="YOUR_BOT_TOKEN_HERE" 5 | CHAT_ID="YOUR_CHAT_ID_HERE" 6 | DAILY_REPORT_TIME=08:00 7 | MACHINE_NAME="Your Server Name" 8 | -------------------------------------------------------------------------------- /gcp-625g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=out 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=625 4 | TRAFFIC_TOLERANCE=80 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=20 7 | MAIN_INTERFACE=ens4 8 | LIMIT_MODE=tc 9 | # 详见:https://www.nodeseek.com/post-115166-1 GCP追求大流量的极致解法 10 | -------------------------------------------------------------------------------- /az-115g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=out 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=115 4 | TRAFFIC_TOLERANCE=5 5 | PERIOD_START_DAY=1 6 | LIMIT_SPEED=1024 7 | MAIN_INTERFACE=eth0 8 | LIMIT_MODE=tc 9 | # 因为az国外教育版本身就只有115g,流量很少,容差可以小一点 10 | # 因为az国外教育版即使超出国外的115g免费额度还有100美元的赠金,所以流量限速没必要太慢 11 | -------------------------------------------------------------------------------- /traffic_monitor_config.txt.example: -------------------------------------------------------------------------------- 1 | # 流量监控配置示例 2 | # 复制此文件为 traffic_monitor_config.txt 并根据需要修改 3 | 4 | TRAFFIC_MODE=total 5 | TRAFFIC_PERIOD=monthly 6 | TRAFFIC_LIMIT=1000 7 | TRAFFIC_TOLERANCE=20 8 | PERIOD_START_DAY=1 9 | LIMIT_SPEED=20 10 | MAIN_INTERFACE=eth0 11 | LIMIT_MODE=tc 12 | -------------------------------------------------------------------------------- /az-15g: -------------------------------------------------------------------------------- 1 | TRAFFIC_MODE=out 2 | TRAFFIC_PERIOD=monthly 3 | TRAFFIC_LIMIT=15 4 | TRAFFIC_TOLERANCE=0 5 | PERIOD_START_DAY=15 6 | LIMIT_SPEED=1024 7 | MAIN_INTERFACE=eth0 8 | LIMIT_MODE=tc 9 | # 因为az国内教育版本身就只有15g,流量很少,没必要设置容差了 10 | # 因为az国内教育版即使超出国内的15g免费额度还有100美元的赠金,所以流量限速没必要太慢 11 | # 国内版计费周期起始日是自己获得订阅的日期,不是1号!按照自己的情况自行修改 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 忽略配置文件(包含敏感信息) 2 | tg_notifier_config.txt 3 | traffic_monitor_config.txt 4 | port_traffic_config.txt 5 | ports_traffic_config.json 6 | 7 | # 保留示例配置文件 8 | !*.example 9 | 10 | # 忽略日志文件 11 | *.log 12 | last_traffic_notification 13 | 14 | # 忽略缓存文件 15 | port_traffic_cache.json 16 | 17 | # 忽略备份文件 18 | *.backup* 19 | *.bak 20 | 21 | # 忽略IDE文件 22 | .vscode/ 23 | *.code-workspace 24 | 25 | # 忽略临时文件 26 | *.tmp 27 | *.temp 28 | CLAUDE.md -------------------------------------------------------------------------------- /remove_traffic_limit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TrafficCop 解除机器限速脚本 v2.0 4 | # 使用新的管理方式,完全停止监控而不是设置超大值 5 | 6 | echo "============================================" 7 | echo " TrafficCop 机器限速解除脚本 v2.0" 8 | echo "============================================" 9 | echo "" 10 | 11 | # 下载并运行新的管理器 12 | echo "正在下载机器限速管理器..." 13 | curl -fsSL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/machine_limit_manager.sh -o /tmp/machine_limit_manager.sh 14 | 15 | if [ $? -eq 0 ]; then 16 | chmod +x /tmp/machine_limit_manager.sh 17 | echo "" 18 | echo "使用新的管理方式禁用机器限速..." 19 | bash /tmp/machine_limit_manager.sh --disable 20 | 21 | echo "" 22 | echo "✓ 机器限速已通过新方式完全禁用" 23 | echo "" 24 | echo "说明:" 25 | echo "- 监控进程已停止" 26 | echo "- TC限速规则已清除" 27 | echo "- 定时任务已移除" 28 | echo "- 原配置已备份,可随时恢复" 29 | echo "" 30 | echo "如需恢复监控,请运行:" 31 | echo "bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/machine_limit_manager.sh)" 32 | 33 | # 清理临时文件 34 | rm -f /tmp/machine_limit_manager.sh 35 | else 36 | echo "ERROR: 无法下载管理器,使用传统方式..." 37 | echo "" 38 | 39 | # 传统方式作为备用方案 40 | WORK_DIR="/root/TrafficCop" 41 | CONFIG_FILE="$WORK_DIR/traffic_monitor_config.txt" 42 | 43 | if [ -f "$CONFIG_FILE" ]; then 44 | # 备份原始文件 45 | cp "$CONFIG_FILE" "$CONFIG_FILE.bak" 46 | 47 | # 修改配置为超大值 (传统方式) 48 | sed -i 's/TRAFFIC_LIMIT=[0-9]*/TRAFFIC_LIMIT=1000000/' "$CONFIG_FILE" 49 | sed -i 's/LIMIT_SPEED=[0-9]*/LIMIT_SPEED=1000000/' "$CONFIG_FILE" 50 | 51 | echo "已使用传统方式设置超大限制值" 52 | echo "配置文件已备份为: $CONFIG_FILE.bak" 53 | else 54 | echo "未找到配置文件,无需处理" 55 | fi 56 | fi 57 | 58 | echo "" 59 | echo "操作完成!" 60 | -------------------------------------------------------------------------------- /test_port_traffic_notifications.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 测试端口流量通知功能 4 | # 这个脚本用于验证定时推送的端口流量是否正确显示 5 | 6 | WORK_DIR="/root/TrafficCop" 7 | cd "$WORK_DIR" || exit 1 8 | 9 | echo "=== 端口流量通知测试 ===" 10 | echo "测试时间: $(date)" 11 | echo "" 12 | 13 | # 检查必要文件是否存在 14 | echo "1. 检查必要文件..." 15 | if [ ! -f "ports_traffic_config.json" ]; then 16 | echo "❌ 错误: ports_traffic_config.json 不存在" 17 | exit 1 18 | fi 19 | 20 | if [ ! -f "view_port_traffic.sh" ]; then 21 | echo "❌ 错误: view_port_traffic.sh 不存在" 22 | exit 1 23 | fi 24 | 25 | if [ ! -f "port_traffic_helper.sh" ]; then 26 | echo "❌ 错误: port_traffic_helper.sh 不存在" 27 | exit 1 28 | fi 29 | 30 | echo "✅ 所有必要文件都存在" 31 | echo "" 32 | 33 | # 测试端口配置 34 | echo "2. 检查端口配置..." 35 | port_count=$(jq -r '.ports | length' "ports_traffic_config.json" 2>/dev/null) 36 | if [ -z "$port_count" ] || [ "$port_count" -eq 0 ]; then 37 | echo "❌ 错误: 没有配置端口" 38 | exit 1 39 | fi 40 | 41 | echo "✅ 已配置 $port_count 个端口" 42 | echo "" 43 | 44 | # 测试 view_port_traffic.sh 的JSON输出 45 | echo "3. 测试 view_port_traffic.sh JSON输出..." 46 | port_data=$(bash "view_port_traffic.sh" --json 2>/dev/null) 47 | if [ -z "$port_data" ]; then 48 | echo "❌ 错误: view_port_traffic.sh 没有返回数据" 49 | echo "尝试直接运行:" 50 | bash "view_port_traffic.sh" --json 51 | exit 1 52 | fi 53 | 54 | echo "✅ view_port_traffic.sh JSON输出正常" 55 | echo "输出数据: $port_data" 56 | echo "" 57 | 58 | # 测试 port_traffic_helper.sh 函数 59 | echo "4. 测试 port_traffic_helper.sh 函数..." 60 | source "port_traffic_helper.sh" 61 | 62 | if ! command -v get_port_traffic_summary &> /dev/null; then 63 | echo "❌ 错误: get_port_traffic_summary 函数不可用" 64 | exit 1 65 | fi 66 | 67 | summary=$(get_port_traffic_summary 5) 68 | echo "✅ port_traffic_helper.sh 函数可用" 69 | echo "摘要输出: $summary" 70 | echo "" 71 | 72 | # 测试 tg_notifier.sh 的端口流量获取 73 | echo "5. 测试 tg_notifier.sh 端口流量获取..." 74 | if [ -f "tg_notifier.sh" ]; then 75 | # 临时source tg_notifier.sh 来测试函数 76 | source "tg_notifier.sh" 2>/dev/null 77 | 78 | if command -v get_port_traffic_summary_for_tg &> /dev/null; then 79 | tg_summary=$(get_port_traffic_summary_for_tg) 80 | echo "✅ tg_notifier.sh 端口流量获取正常" 81 | echo "TG格式输出: $tg_summary" 82 | else 83 | echo "❌ 错误: get_port_traffic_summary_for_tg 函数不可用" 84 | fi 85 | else 86 | echo "⚠️ 警告: tg_notifier.sh 不存在" 87 | fi 88 | 89 | echo "" 90 | echo "=== 测试完成 ===" 91 | -------------------------------------------------------------------------------- /PORT_TRAFFIC_ISSUE.md: -------------------------------------------------------------------------------- 1 | # 端口流量统计问题完整解决方案 2 | 3 | ## 问题描述 4 | 在UFW防火墙环境下,端口流量统计严重偏低,只能统计到入站流量,出站流量显示为0。 5 | 6 | ## 根本原因(深度分析) 7 | 8 | ### 第一层问题:UFW链结构 9 | UFW的OUTPUT链结构如下: 10 | ``` 11 | OUTPUT链 12 | ├─ ufw-before-output 13 | │ ├─ lo接口规则 (位置1) 14 | │ ├─ ESTABLISHED/RELATED规则 (位置2) ⚠️ 关键! 15 | │ └─ ufw-user-output (位置3) 16 | ├─ ufw-after-output 17 | └─ ... 18 | ``` 19 | 20 | ### 第二层问题:ESTABLISHED规则优先 21 | 在 `ufw-before-output` 链中,第2条规则会接受所有 ESTABLISHED 和 RELATED 状态的连接: 22 | ```bash 23 | ACCEPT ctstate RELATED,ESTABLISHED # 所有已建立连接的流量在这里就被接受了 24 | ``` 25 | 26 | ### 第三层问题:规则永不触发 27 | - 几乎所有的出站流量都是已建立连接的响应流量(ESTABLISHED状态) 28 | - 这些流量在到达 `ufw-user-output` 之前就被第2条规则接受了 29 | - 导致添加在 `ufw-user-output` 中的统计规则**永远不会被触发** 30 | 31 | ### 实际影响示例 32 | 以端口11710为例: 33 | - 原始统计:入站 0.23 MB + 出站 0 MB = 总计 0.23 MB 34 | - 修复后统计:入站 0.23 MB + 出站 245 MB = **总计 245 MB** 35 | - **出站流量是入站的1000倍,但之前完全漏统计!** 36 | 37 | ## 解决方案(v3.2) 38 | 39 | ### 核心修复 40 | 将出站统计规则添加到 `ufw-before-output` 链的**第2个位置**(在ESTABLISHED规则之前): 41 | 42 | ```bash 43 | # 在正确位置插入规则 44 | iptables -I ufw-before-output 2 -o eth0 -p tcp --sport PORT -j ACCEPT 45 | iptables -I ufw-before-output 2 -o eth0 -p udp --sport PORT -j ACCEPT 46 | ``` 47 | 48 | ### 修改的函数 49 | 1. **`init_iptables_rules()`** - 在正确位置添加规则 50 | 2. **`get_port_traffic_usage()`** - 从正确的链读取流量数据 51 | 52 | ### 读取优先级 53 | ``` 54 | 出站流量读取顺序: 55 | 1. ufw-before-output (UFW环境,正确位置) 56 | 2. ufw-user-output (兼容性,旧规则) 57 | 3. OUTPUT (标准iptables环境) 58 | ``` 59 | 60 | ## 验证方法 61 | 62 | ### 检查规则位置 63 | ```bash 64 | # 查看ufw-before-output链的规则顺序 65 | iptables -L ufw-before-output -v -n --line-numbers 66 | 67 | # 确认统计规则在ESTABLISHED规则之前 68 | # 正确示例: 69 | # 1 ACCEPT lo接口 70 | # 2 ACCEPT tcp spt:YOUR_PORT ✓ 统计规则 71 | # 3 ACCEPT udp spt:YOUR_PORT 72 | # ... 73 | # 6 ACCEPT ESTABLISHED,RELATED ✓ ESTABLISHED在后面 74 | ``` 75 | 76 | ### 查看实时流量 77 | ```bash 78 | # 查看端口流量 79 | bash /root/TrafficCop/view_port_traffic.sh 80 | 81 | # 手动验证 82 | iptables -L ufw-before-output -v -n -x | grep "spt:YOUR_PORT" 83 | ``` 84 | 85 | ## 历史数据说明 86 | - ⚠️ 只能统计修复后的流量,修复前的历史流量无法追溯 87 | - 建议:修复后重置计数器,从头开始统计(可选) 88 | ```bash 89 | iptables -Z # 重置所有计数器 90 | ``` 91 | 92 | ## 技术细节 93 | - **UFW入站**:规则在 `ufw-user-input` 链,由 `ufw allow` 命令创建 94 | - **UFW出站**:必须手动添加到 `ufw-before-output` 链的正确位置 95 | - **关键位置**:必须在 ESTABLISHED 规则之前,否则规则不会被触发 96 | - **流量类型**:大部分出站流量都是 ESTABLISHED 状态 97 | 98 | ## 更新日志 99 | - v3.0: 初始版本,规则添加到 ufw-user-output(不工作) 100 | - v3.1: 修复读取逻辑,支持UFW链(仍然不工作) 101 | - **v3.2**: ✅ 最终修复,规则添加到 ufw-before-output 正确位置 102 | -------------------------------------------------------------------------------- /set_daily_time.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Telegram通知脚本 - 时间设置工具 4 | # 用法: bash set_daily_time.sh HH:MM 5 | 6 | WORK_DIR="/root/TrafficCop" 7 | CONFIG_FILE="$WORK_DIR/tg_notifier_config.txt" 8 | CRON_LOG="$WORK_DIR/tg_notifier_cron.log" 9 | 10 | if [ $# -ne 1 ]; then 11 | echo "用法: $0 HH:MM" 12 | echo "示例: $0 09:30" 13 | exit 1 14 | fi 15 | 16 | new_time="$1" 17 | 18 | # 验证时间格式 19 | if [[ ! $new_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then 20 | echo "错误: 无效的时间格式。请使用 HH:MM 格式 (如: 09:30)" 21 | exit 1 22 | fi 23 | 24 | echo "正在修改每日报告时间为: $new_time" 25 | 26 | # 备份配置文件 27 | cp "$CONFIG_FILE" "$CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)" 28 | 29 | # 使用awk修改配置 30 | awk -v new_time="$new_time" ' 31 | /^DAILY_REPORT_TIME=/ { print "DAILY_REPORT_TIME=" new_time; next } 32 | { print } 33 | ' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" 34 | 35 | echo "每日报告时间已更新为: $new_time" 36 | 37 | # 立即刷新端口流量缓存 38 | echo "正在刷新端口流量缓存..." 39 | 40 | if [ -f "$WORK_DIR/view_port_traffic.sh" ]; then 41 | cd "$WORK_DIR" 42 | 43 | # 执行端口流量收集并保存到缓存 44 | cache_file="/tmp/port_traffic_cache_$(date '+%Y-%m-%d_%H:%M:%S')_manual.json" 45 | 46 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [手动设置时间] 开始刷新缓存" >> "$CRON_LOG" 47 | 48 | # 获取端口流量数据 49 | PATH="/usr/sbin:/usr/bin:/sbin:/bin:$PATH" bash view_port_traffic.sh --json > "$cache_file.tmp" 2>&1 50 | exit_code=$? 51 | 52 | if [ $exit_code -eq 0 ] && [ -s "$cache_file.tmp" ]; then 53 | # 添加元数据 54 | { 55 | echo "{" 56 | echo " \"timestamp\": \"$(date '+%Y-%m-%d %H:%M:%S')\"," 57 | echo " \"source\": \"manual_time_change\"," 58 | echo " \"data\": $(cat "$cache_file.tmp")" 59 | echo "}" 60 | } > "$cache_file" 61 | 62 | rm -f "$cache_file.tmp" 63 | 64 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [手动设置时间] 缓存已刷新: $cache_file" >> "$CRON_LOG" 65 | echo "✅ 缓存已刷新,定时推送将使用最新数据" 66 | 67 | # 显示缓存的数据摘要 68 | ports_summary=$(cat "$cache_file" | jq -r '.data.ports[] | "\(.port):\(.usage)GB"' 2>/dev/null | tr '\n' ' ') 69 | if [ -n "$ports_summary" ]; then 70 | echo "端口流量数据: $ports_summary" 71 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [手动设置时间] 端口数据摘要: $ports_summary" >> "$CRON_LOG" 72 | fi 73 | else 74 | echo "❌ 缓存刷新失败,但时间已修改" 75 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [手动设置时间] 缓存刷新失败 (exit code: $exit_code)" >> "$CRON_LOG" 76 | if [ -f "$cache_file.tmp" ]; then 77 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [手动设置时间] 错误输出: $(cat "$cache_file.tmp")" >> "$CRON_LOG" 78 | rm -f "$cache_file.tmp" 79 | fi 80 | fi 81 | else 82 | echo "⚠️ 未找到 view_port_traffic.sh,时间已修改但未刷新缓存" 83 | fi 84 | 85 | echo 86 | echo "修改完成!新的每日报告时间: $new_time" 87 | echo "定时任务将在下次执行时使用新时间。" 88 | -------------------------------------------------------------------------------- /OPTIMIZATION_SUMMARY.md: -------------------------------------------------------------------------------- 1 | # TrafficCop 优化总结 2 | 3 | ## 本次优化内容 4 | 5 | ### 1. 项目清理(已完成 ✅) 6 | - 删除备份文件: 7 | - `port_traffic_limit.sh.v1.bak` (525行) 8 | - `trafficcop-manager-fixed.sh` (405行) 9 | - `update_vps.sh` (335行) 10 | - 共删除 1265 行冗余代码 11 | 12 | - 更新 `.gitignore`: 13 | - 排除个人配置文件:`port_traffic_config.txt`, `ports_traffic_config.json` 14 | - 允许示例文件:`!*.example` 15 | 16 | - 创建配置模板: 17 | - `tg_notifier_config.txt.example` 18 | - `traffic_monitor_config.txt.example` 19 | 20 | ### 2. UFW 防火墙流量监控修复(已完成 ✅) 21 | 22 | #### 问题发现 23 | 端口流量统计始终显示 0 GB 24 | 25 | #### 根本原因 26 | UFW 防火墙使用特殊的 iptables 链结构: 27 | - `ufw-before-input` / `ufw-before-output` - 优先级最高的规则链 28 | - 位置2有 ESTABLISHED 规则接受所有已建立连接(77GB入站,63GB出站) 29 | - 自定义规则必须插入到 ESTABLISHED 规则**之前**(position 2) 30 | 31 | #### 解决方案 32 | 修改 `init_iptables_rules()` 函数: 33 | ```bash 34 | # 在 ufw-before-input 和 ufw-before-output 的位置2插入规则(ESTABLISHED规则之前) 35 | iptables -I ufw-before-input 2 -i "$interface" -p tcp --dport "$port" -j ACCEPT 36 | iptables -I ufw-before-input 2 -i "$interface" -p udp --dport "$port" -j ACCEPT 37 | iptables -I ufw-before-output 2 -o "$interface" -p tcp --sport "$port" -j ACCEPT 38 | iptables -I ufw-before-output 2 -o "$interface" -p udp --sport "$port" -j ACCEPT 39 | ``` 40 | 41 | #### 验证结果 42 | - 修复前:0.000 GB 43 | - 修复后:2.318 GB(入站 3.81 MB,出站 2369 MB) 44 | 45 | ### 3. 代理场景流量监控优化(已完成 ✅) 46 | 47 | #### 问题分析 48 | 对于代理服务器(xray/v2ray),iptables 按端口监控存在根本性局限: 49 | 50 | **可以监控:** 51 | - ✅ 客户端 → 服务器11710端口(入站 dport) 52 | - ✅ 服务器11710端口 → 客户端(出站 sport) 53 | 54 | **无法监控:** 55 | - ❌ 服务器随机端口 → 目标网站(出站) 56 | - ❌ 目标网站 → 服务器随机端口(入站) 57 | 58 | #### 实际测量数据(2025-11-19) 59 | 60 | **服务器总流量(vnstat):** 61 | - 入站:5.88 GiB 62 | - 出站:6.02 GiB 63 | - 比例:1.02:1 ✓ 64 | 65 | **端口11710流量(iptables原方案):** 66 | - 入站:3.81 MB(捕获率 0.06%) 67 | - 出站:2369 MB(捕获率 38.4%) 68 | - 比例:1:620 ✗ 69 | 70 | #### 解决方案:估算法 71 | 72 | **原理:** 客户端请求多少数据,服务器就需要下载并转发相应数据 73 | 74 | **计算公式:** 75 | ``` 76 | 入站流量(dport) = 客户端实际请求量(可准确测量) 77 | 出站流量(sport) = 入站流量(估算值) 78 | 总流量 = 入站流量 × 2(估算值) 79 | ``` 80 | 81 | **代码实现:** 82 | ```bash 83 | # 获取入站流量(准确值) 84 | local in_bytes=$(iptables -L ufw-before-input -v -n -x | grep "dpt:$port" | awk '{sum+=$2}') 85 | local in_gb=$(printf "%.2f" $(echo "scale=2; $in_bytes / 1024 / 1024 / 1024" | bc)) 86 | 87 | # 代理场景:出站 = 入站(估算) 88 | local out_gb="$in_gb" 89 | 90 | # 总流量 = 入站 × 2(估算) 91 | local total_gb=$(printf "%.2f" $(echo "scale=2; $in_gb * 2" | bc)) 92 | ``` 93 | 94 | #### 用户说明 95 | 96 | 在脚本输出中添加说明: 97 | ``` 98 | ⚠ 代理监控说明: 99 | 入站: 实际测量值(客户端→服务器) 100 | 出站: 估算值 = 入站(服务器→客户端,无法直接测量) 101 | 总计: 估算值 = 入站 × 2(实际略高10-20%因协议开销) 102 | ``` 103 | 104 | #### 精度评估 105 | 106 | - **理想情况**:总流量 ≈ 入站 × 2.0 107 | - **实际情况**:总流量 ≈ 入站 × 2.1 到 2.3 108 | - **误差范围**:±10-20% 109 | - **适用场景**:流量限制、费用预估 110 | 111 | 对于流量监控和限制的使用场景,这个精度**完全可接受**。 112 | 113 | ## 提交历史 114 | 115 | 1. **项目优化与清理** (commit: 93b9d64) 116 | - 删除备份文件 117 | - 更新 .gitignore 118 | - 创建配置模板 119 | 120 | 2. **UFW出站流量监控修复 v3.2** (commit: 30e70ff) 121 | - 修复 ufw-before-output 链规则位置 122 | 123 | 3. **完整修复:UFW入站+出站流量统计 v3.3** (commit: 0e6200e) 124 | - 同时修复入站和出站监控 125 | - 规则插入到 ESTABLISHED 之前 126 | 127 | 4. **代理场景流量监控优化 v3.4** (commit: 894efc6) 128 | - 实现估算法 129 | - 添加用户说明 130 | - 创建详细文档 131 | 132 | ## 技术文档 133 | 134 | - **PROXY_TRAFFIC_MONITORING.md** - 代理流量监控原理、问题分析和解决方案详解 135 | 136 | ## 备选方案(精确监控) 137 | 138 | 如需精确监控代理总流量,可使用以下方案: 139 | 140 | ### 方案1:cgroup v2 + iptables 141 | 追踪进程的所有流量(推荐) 142 | 143 | ### 方案2:conntrack 标记 144 | 使用连接跟踪标记关联的返回包 145 | 146 | ### 方案3:进程级监控工具 147 | 使用 nethogs、iftop 等工具(不支持限速集成) 148 | 149 | ## 最终效果 150 | 151 | ✅ 项目结构清晰,无冗余文件 152 | ✅ UFW 环境下流量统计正常工作 153 | ✅ 代理场景流量估算合理准确 154 | ✅ 用户界面清晰易懂,有明确说明 155 | ✅ 技术文档完整,便于后续维护 156 | 157 | --- 158 | 159 | **优化完成日期**:2025-11-19 160 | **版本**:v3.4 161 | **状态**:已推送到 GitHub 162 | -------------------------------------------------------------------------------- /PROXY_TRAFFIC_MONITORING.md: -------------------------------------------------------------------------------- 1 | # 代理服务器流量监控说明 2 | 3 | ## 问题背景 4 | 5 | 在使用 iptables 按端口监控代理服务器(如 xray/v2ray)流量时,会遇到一个根本性的限制问题。 6 | 7 | ## 代理流量的工作原理 8 | 9 | 以 xray 代理服务器(监听 11710 端口)为例,当客户端访问 YouTube 时的流量路径: 10 | 11 | ``` 12 | 客户端 (36.161.108.71) 13 | ↓ 请求 (入站到11710) 14 | 代理服务器 (104.194.87.91:11710) 15 | ↓ 请求 (出站,随机源端口 52658) 16 | YouTube (149.154.175.55:443) 17 | ↓ 响应 (入站,目标端口 52658) 18 | 代理服务器 (104.194.87.91:52658) 19 | ↓ 响应 (出站从11710) 20 | 客户端 (36.161.108.71) 21 | ``` 22 | 23 | ## iptables 端口监控的局限性 24 | 25 | ### 可以监控的流量 26 | 27 | 使用 `--dport 11710` 和 `--sport 11710` 规则只能捕获: 28 | 29 | ✅ **客户端 → 服务器11710端口** (入站,dport:11710) 30 | ✅ **服务器11710端口 → 客户端** (出站,sport:11710) 31 | 32 | ### 无法监控的流量 33 | 34 | ❌ **服务器随机端口 → YouTube** (出站,sport:52658) 35 | ❌ **YouTube → 服务器随机端口** (入站,dport:52658) 36 | 37 | ## 实际测量数据 38 | 39 | 以下是 2025-11-19 的实际测量对比: 40 | 41 | ### 服务器总流量(vnstat) 42 | - 入站:5.88 GiB 43 | - 出站:6.02 GiB 44 | - 比例:**1.02:1** ✓ 符合代理预期 45 | 46 | ### 端口 11710 流量(iptables 原始方案) 47 | - 入站:3.81 MB (0.0037 GiB) 48 | - 出站:2369 MB (2.313 GiB) 49 | - 比例:**1:620** ✗ 严重不对称 50 | 51 | ### 流量捕获率 52 | - 出站捕获率:2.313 / 6.02 = **38.4%** 53 | - 入站捕获率:0.0037 / 5.88 = **0.06%** 54 | 55 | ### 原因分析 56 | 57 | - **入站流量**:只捕获了客户端的请求头/握手流量(~4MB),未捕获服务器从目标网站接收的响应流量(~5.88GB) 58 | - **出站流量**:只捕获了服务器发送给客户端的响应流量(~2.3GB),未捕获服务器向目标网站发起的请求流量 59 | 60 | ## 解决方案 61 | 62 | ### 方案选择 63 | 64 | 由于完整监控代理流量需要使用 cgroup/conntrack 等复杂技术,我们采用**估算法**: 65 | 66 | **核心原理**:客户端请求多少数据,服务器就需要下载并转发相应数据 67 | 68 | ### 计算公式 69 | 70 | ```bash 71 | 入站流量(dport) = 客户端实际请求量(可准确测量) 72 | 出站流量(sport) = 入站流量(估算值) 73 | 总流量 = 入站流量 × 2(估算值) 74 | ``` 75 | 76 | ### 实现代码 77 | 78 | ```bash 79 | # 获取入站流量(字节) 80 | local in_bytes=$(iptables -L ufw-before-input -v -n -x | grep "dpt:$port" | awk '{sum+=$2} END {printf "%.0f", sum+0}') 81 | 82 | # 转换为GB 83 | local in_gb=$(printf "%.2f" $(echo "scale=2; $in_bytes / 1024 / 1024 / 1024" | bc)) 84 | 85 | # 代理场景:出站流量 = 入站流量(估算) 86 | local out_gb="$in_gb" 87 | 88 | # 总流量 = 入站 × 2(估算) 89 | local total_gb=$(printf "%.2f" $(echo "scale=2; $in_gb * 2" | bc)) 90 | 91 | echo "$in_gb,$out_gb,$total_gb" 92 | ``` 93 | 94 | ## 用户说明 95 | 96 | 在脚本输出中添加以下说明: 97 | 98 | ``` 99 | ⚠ 代理监控说明: 100 | 入站: 实际测量值(客户端→服务器) 101 | 出站: 估算值 = 入站(服务器→客户端,无法直接测量) 102 | 总计: 估算值 = 入站 × 2(实际略高10-20%因协议开销) 103 | ``` 104 | 105 | ## 估算精度 106 | 107 | ### 误差来源 108 | 109 | 1. **协议开销**:TCP/IP 头部、TLS 握手、重传等(+5-10%) 110 | 2. **压缩因素**:某些内容可能经过压缩(±5-15%) 111 | 3. **连接复用**:HTTP/2、QUIC 等协议的连接复用影响 112 | 113 | ### 预期精度 114 | 115 | - **理想情况**:总流量 ≈ 入站 × 2.0 116 | - **实际情况**:总流量 ≈ 入站 × 2.1 到 2.3 117 | - **误差范围**:±10-20% 118 | 119 | 对于流量限制场景,这个精度完全可以接受。 120 | 121 | ## 精确监控方案(备选) 122 | 123 | 如果需要精确监控代理总流量,可以使用以下方案: 124 | 125 | ### 方案1:cgroup v2 + iptables 126 | 127 | ```bash 128 | # 创建 cgroup 129 | mkdir -p /sys/fs/cgroup/xray 130 | echo [xray-pid] > /sys/fs/cgroup/xray/cgroup.procs 131 | 132 | # 使用 iptables cgroup 匹配 133 | iptables -I INPUT -m cgroup --path xray -j ACCOUNT 134 | iptables -I OUTPUT -m cgroup --path xray -j ACCOUNT 135 | ``` 136 | 137 | **优点**:可以追踪进程的所有流量 138 | **缺点**:配置复杂,需要 cgroup v2 支持 139 | 140 | ### 方案2:conntrack 标记 141 | 142 | ```bash 143 | # 标记 xray 发出的包 144 | iptables -t mangle -I OUTPUT -m owner --cmd-owner xray-linux-amd64 -j MARK --set-mark 11710 145 | iptables -t mangle -A OUTPUT -m mark --mark 11710 -j CONNMARK --save-mark 146 | 147 | # 标记关联的返回包 148 | iptables -t mangle -A INPUT -m connmark --mark 11710 -j MARK --restore-mark 149 | 150 | # 统计流量 151 | iptables -I INPUT -m mark --mark 11710 -j ACCOUNT 152 | iptables -I OUTPUT -m mark --mark 11710 -j ACCOUNT 153 | ``` 154 | 155 | **优点**:可以追踪连接的完整流量 156 | **缺点**:需要 conntrack 支持,配置复杂 157 | 158 | ### 方案3:进程级监控工具 159 | 160 | 使用 `nethogs`、`iftop` 等工具直接监控进程流量。 161 | 162 | **优点**:简单直观 163 | **缺点**:无法与 iptables 限速集成 164 | 165 | ## 结论 166 | 167 | 对于代理场景的流量监控和限制: 168 | 169 | 1. **推荐方案**:使用估算法(入站 × 2),简单实用,精度可接受 170 | 2. **显示说明**:在输出中明确告知用户这是估算值 171 | 3. **精确需求**:如果需要精确监控,使用 cgroup 或 conntrack 方案 172 | 173 | --- 174 | 175 | **最后更新**:2025-11-19 176 | **版本**:v1.0 177 | -------------------------------------------------------------------------------- /port_traffic_helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Port Traffic Helper Functions 4 | # 用于通知脚本获取端口流量信息的通用函数 5 | 6 | WORK_DIR="/root/TrafficCop" 7 | PORTS_CONFIG_FILE="$WORK_DIR/ports_traffic_config.json" 8 | 9 | # 获取端口流量摘要(简短版本,用于警告通知) 10 | get_port_traffic_summary() { 11 | local max_ports=${1:-5} # 默认显示最多5个端口 12 | local summary="" 13 | 14 | if [ ! -f "$PORTS_CONFIG_FILE" ] || [ ! -f "$WORK_DIR/view_port_traffic.sh" ]; then 15 | echo "" 16 | return 17 | fi 18 | 19 | local port_data=$(bash "$WORK_DIR/view_port_traffic.sh" --json 2>/dev/null) 20 | 21 | if [ -z "$port_data" ]; then 22 | echo "" 23 | return 24 | fi 25 | 26 | local port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null) 27 | 28 | if [ "$port_count" -eq 0 ]; then 29 | echo "" 30 | return 31 | fi 32 | 33 | summary="端口流量:" 34 | 35 | local i=0 36 | while [ $i -lt $port_count ] && [ $i -lt $max_ports ]; do 37 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 38 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 39 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 40 | 41 | if [ -n "$port" ] && [ "$port" != "null" ]; then 42 | local port_percentage=0 43 | if (( $(echo "$port_limit > 0" | bc -l 2>/dev/null) )); then 44 | port_percentage=$(echo "scale=0; ($port_usage / $port_limit) * 100" | bc 2>/dev/null) 45 | fi 46 | summary="${summary}\n端口${port}: ${port_usage}/${port_limit}GB (${port_percentage}%)" 47 | fi 48 | 49 | i=$((i + 1)) 50 | done 51 | 52 | if [ "$port_count" -gt $max_ports ]; then 53 | summary="${summary}\n...及其他$((port_count - max_ports))个端口" 54 | fi 55 | 56 | echo "$summary" 57 | } 58 | 59 | # 获取端口流量详情(详细版本,用于每日报告) 60 | get_port_traffic_details() { 61 | local details="" 62 | 63 | if [ ! -f "$PORTS_CONFIG_FILE" ] || [ ! -f "$WORK_DIR/view_port_traffic.sh" ]; then 64 | echo "" 65 | return 66 | fi 67 | 68 | local port_data=$(bash "$WORK_DIR/view_port_traffic.sh" --json 2>/dev/null) 69 | 70 | if [ -z "$port_data" ]; then 71 | echo "" 72 | return 73 | fi 74 | 75 | local port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null) 76 | 77 | if [ "$port_count" -eq 0 ]; then 78 | echo "" 79 | return 80 | fi 81 | 82 | details="🔌 端口流量详情:" 83 | 84 | local i=0 85 | while [ $i -lt $port_count ]; do 86 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 87 | local port_desc=$(echo "$port_data" | jq -r ".ports[$i].description" 2>/dev/null) 88 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 89 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 90 | 91 | if [ -n "$port" ] && [ "$port" != "null" ]; then 92 | local port_percentage=0 93 | if (( $(echo "$port_limit > 0" | bc -l 2>/dev/null) )); then 94 | port_percentage=$(echo "scale=1; ($port_usage / $port_limit) * 100" | bc 2>/dev/null) 95 | fi 96 | 97 | # 根据使用率选择状态图标 98 | local status_icon="✅" 99 | if (( $(echo "$port_percentage >= 90" | bc -l 2>/dev/null) )); then 100 | status_icon="🔴" 101 | elif (( $(echo "$port_percentage >= 75" | bc -l 2>/dev/null) )); then 102 | status_icon="🟡" 103 | fi 104 | 105 | details="${details}\n${status_icon} 端口 ${port} (${port_desc}):${port_usage}GB / ${port_limit}GB (${port_percentage}%)" 106 | fi 107 | 108 | i=$((i + 1)) 109 | done 110 | 111 | echo "$details" 112 | } 113 | 114 | # 检查是否有端口流量配置 115 | has_port_config() { 116 | if [ -f "$PORTS_CONFIG_FILE" ]; then 117 | local port_count=$(cat "$PORTS_CONFIG_FILE" 2>/dev/null | jq -r '.ports | length' 2>/dev/null) 118 | [ "$port_count" -gt 0 ] 119 | return $? 120 | fi 121 | return 1 122 | } 123 | -------------------------------------------------------------------------------- /machine_limit_manager.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TrafficCop 机器限速管理脚本 v2.0 4 | # 提供完整的启用/禁用/恢复机器限速功能 5 | 6 | WORK_DIR="/root/TrafficCop" 7 | CONFIG_FILE="$WORK_DIR/traffic_monitor_config.txt" 8 | BACKUP_CONFIG_FILE="$CONFIG_FILE.disabled.backup" 9 | SCRIPT_PATH="$WORK_DIR/trafficcop.sh" 10 | CRON_COMMENT="# TrafficCop Monitor" 11 | 12 | # 颜色定义 13 | RED='\033[0;31m' 14 | GREEN='\033[0;32m' 15 | YELLOW='\033[0;33m' 16 | BLUE='\033[0;34m' 17 | CYAN='\033[0;36m' 18 | NC='\033[0m' 19 | 20 | # 检查网络接口 21 | get_main_interface() { 22 | ip route | grep default | awk '{print $5}' | head -n1 23 | } 24 | 25 | # 清除TC限速规则 26 | clear_tc_rules() { 27 | local interface=$(get_main_interface) 28 | if [ -n "$interface" ]; then 29 | echo "清除网络接口 $interface 的TC限速规则..." 30 | tc qdisc del dev "$interface" root 2>/dev/null || true 31 | echo "✓ TC限速规则已清除" 32 | fi 33 | } 34 | 35 | # 停止监控进程 36 | stop_monitor_process() { 37 | echo "停止TrafficCop监控进程..." 38 | 39 | # 杀死相关进程 40 | pkill -f "trafficcop.sh" 2>/dev/null || true 41 | pkill -f "traffic_monitor.sh" 2>/dev/null || true 42 | 43 | echo "✓ 监控进程已停止" 44 | } 45 | 46 | # 移除定时任务 47 | remove_cron_job() { 48 | echo "移除定时任务..." 49 | 50 | # 备份当前crontab 51 | crontab -l > /tmp/crontab_backup.txt 2>/dev/null || true 52 | 53 | # 移除TrafficCop相关的定时任务 54 | crontab -l 2>/dev/null | grep -v "trafficcop.sh\|traffic_monitor.sh" | crontab - 2>/dev/null || true 55 | 56 | echo "✓ 定时任务已移除" 57 | } 58 | 59 | # 添加定时任务 60 | add_cron_job() { 61 | echo "添加定时任务..." 62 | 63 | # 检查是否已存在 64 | if crontab -l 2>/dev/null | grep -q "trafficcop.sh"; then 65 | echo "! 定时任务已存在" 66 | return 67 | fi 68 | 69 | # 添加新的定时任务 70 | (crontab -l 2>/dev/null; echo "*/5 * * * * cd $WORK_DIR && bash trafficcop.sh --cron $CRON_COMMENT") | crontab - 71 | 72 | echo "✓ 定时任务已添加" 73 | } 74 | 75 | # 完全禁用机器限速 76 | disable_machine_limit() { 77 | echo -e "${YELLOW}==================== 禁用机器限速 ====================${NC}" 78 | echo "" 79 | 80 | # 1. 停止监控进程 81 | stop_monitor_process 82 | 83 | # 2. 清除TC限速规则 84 | clear_tc_rules 85 | 86 | # 3. 移除定时任务 87 | remove_cron_job 88 | 89 | # 4. 备份并标记配置文件 90 | if [ -f "$CONFIG_FILE" ]; then 91 | echo "备份当前配置..." 92 | cp "$CONFIG_FILE" "$BACKUP_CONFIG_FILE" 93 | echo "DISABLED=true" >> "$CONFIG_FILE" 94 | echo "DISABLED_TIME=$(date '+%Y-%m-%d %H:%M:%S')" >> "$CONFIG_FILE" 95 | echo "✓ 配置已备份并标记为禁用" 96 | fi 97 | 98 | # 5. 取消可能的关机计划 99 | shutdown -c 2>/dev/null || true 100 | echo "✓ 已取消关机计划" 101 | 102 | echo "" 103 | echo -e "${GREEN}✓ 机器限速已完全禁用${NC}" 104 | echo -e "${CYAN}说明: 原配置已备份,可随时恢复${NC}" 105 | } 106 | 107 | # 启用机器限速 108 | enable_machine_limit() { 109 | echo -e "${YELLOW}==================== 启用机器限速 ====================${NC}" 110 | echo "" 111 | 112 | if [ ! -f "$CONFIG_FILE" ]; then 113 | echo -e "${RED}错误: 未找到配置文件 $CONFIG_FILE${NC}" 114 | echo "请先运行 trafficcop.sh 进行初始配置" 115 | return 1 116 | fi 117 | 118 | # 1. 恢复配置文件(移除DISABLED标记) 119 | if grep -q "DISABLED=true" "$CONFIG_FILE" 2>/dev/null; then 120 | echo "恢复配置文件..." 121 | grep -v "DISABLED\|DISABLED_TIME" "$CONFIG_FILE" > "$CONFIG_FILE.tmp" 122 | mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" 123 | echo "✓ 配置文件已恢复" 124 | fi 125 | 126 | # 2. 添加定时任务 127 | add_cron_job 128 | 129 | # 3. 立即执行一次监控(测试配置) 130 | echo "启动TrafficCop监控测试..." 131 | cd "$WORK_DIR" 132 | bash "$SCRIPT_PATH" --cron 133 | 134 | echo "" 135 | echo -e "${GREEN}✓ 机器限速已启用${NC}" 136 | echo -e "${CYAN}监控将通过定时任务每5分钟执行一次${NC}" 137 | echo -e "${CYAN}刚才已执行一次测试,可在日志中查看结果${NC}" 138 | } 139 | 140 | # 恢复之前的配置 141 | restore_machine_limit() { 142 | echo -e "${YELLOW}==================== 恢复机器限速 ====================${NC}" 143 | echo "" 144 | 145 | if [ ! -f "$BACKUP_CONFIG_FILE" ]; then 146 | echo -e "${RED}错误: 未找到备份配置文件${NC}" 147 | echo "无法恢复,请手动重新配置" 148 | return 1 149 | fi 150 | 151 | # 恢复配置文件 152 | echo "恢复备份配置..." 153 | cp "$BACKUP_CONFIG_FILE" "$CONFIG_FILE" 154 | echo "✓ 配置已恢复" 155 | 156 | # 启用监控 157 | enable_machine_limit 158 | } 159 | 160 | # 查看当前状态 161 | show_status() { 162 | echo -e "${CYAN}==================== 当前状态 ====================${NC}" 163 | echo "" 164 | 165 | # 检查配置文件 166 | if [ -f "$CONFIG_FILE" ]; then 167 | if grep -q "DISABLED=true" "$CONFIG_FILE" 2>/dev/null; then 168 | local disabled_time=$(grep "DISABLED_TIME=" "$CONFIG_FILE" | cut -d'=' -f2) 169 | echo -e "配置状态: ${RED}已禁用${NC} (禁用时间: $disabled_time)" 170 | else 171 | echo -e "配置状态: ${GREEN}已启用${NC}" 172 | fi 173 | else 174 | echo -e "配置状态: ${YELLOW}未配置${NC}" 175 | fi 176 | 177 | # 检查进程状态(检查最近的执行记录而不是实时进程) 178 | local last_run=$(grep "当前版本" "$WORK_DIR/traffic_monitor.log" 2>/dev/null | tail -1 | awk '{print $1, $2}') 179 | if [ -n "$last_run" ]; then 180 | local last_run_timestamp=$(date -d "$last_run" +%s 2>/dev/null || echo "0") 181 | local current_timestamp=$(date +%s) 182 | local time_diff=$((current_timestamp - last_run_timestamp)) 183 | 184 | if [ $time_diff -lt 600 ]; then # 10分钟内有执行记录 185 | echo -e "监控进程: ${GREEN}运行中${NC} (最后执行: $last_run)" 186 | else 187 | echo -e "监控进程: ${YELLOW}空闲中${NC} (最后执行: $last_run)" 188 | fi 189 | else 190 | echo -e "监控进程: ${RED}未运行${NC}" 191 | fi 192 | 193 | # 检查定时任务 194 | if crontab -l 2>/dev/null | grep -q "trafficcop.sh"; then 195 | echo -e "定时任务: ${GREEN}已设置${NC}" 196 | else 197 | echo -e "定时任务: ${RED}未设置${NC}" 198 | fi 199 | 200 | # 检查TC规则 201 | local interface=$(get_main_interface) 202 | if [ -n "$interface" ] && tc qdisc show dev "$interface" | grep -q "tbf"; then 203 | echo -e "TC限速: ${YELLOW}已激活${NC}" 204 | else 205 | echo -e "TC限速: ${GREEN}未激活${NC}" 206 | fi 207 | 208 | # 检查备份 209 | if [ -f "$BACKUP_CONFIG_FILE" ]; then 210 | echo -e "配置备份: ${GREEN}存在${NC}" 211 | else 212 | echo -e "配置备份: ${YELLOW}不存在${NC}" 213 | fi 214 | } 215 | 216 | # 详细状态检查 217 | show_detailed_status() { 218 | echo -e "${CYAN}==================== 详细状态 ====================${NC}" 219 | echo "" 220 | 221 | # 基本状态 222 | show_status 223 | echo "" 224 | 225 | # 检查配置文件内容 226 | echo -e "${CYAN}配置文件内容:${NC}" 227 | if [ -f "$CONFIG_FILE" ]; then 228 | cat "$CONFIG_FILE" 229 | else 230 | echo -e "${RED}配置文件不存在${NC}" 231 | fi 232 | echo "" 233 | 234 | # 检查定时任务详情 235 | echo -e "${CYAN}定时任务详情:${NC}" 236 | crontab -l 2>/dev/null | grep -v "^#" | grep "trafficcop\|traffic_monitor" || echo "无相关定时任务" 237 | echo "" 238 | 239 | # 检查最近的日志 240 | echo -e "${CYAN}最近的监控日志 (最后10行):${NC}" 241 | if [ -f "$WORK_DIR/traffic_monitor.log" ]; then 242 | tail -10 "$WORK_DIR/traffic_monitor.log" 243 | else 244 | echo "日志文件不存在" 245 | fi 246 | echo "" 247 | 248 | # 检查当前流量使用 249 | echo -e "${CYAN}当前流量统计:${NC}" 250 | if command -v vnstat >/dev/null 2>&1; then 251 | vnstat -i $(get_main_interface) --oneline 2>/dev/null | head -1 || echo "无法获取流量统计" 252 | else 253 | echo "vnstat 未安装" 254 | fi 255 | } 256 | 257 | # 主菜单 258 | show_menu() { 259 | clear 260 | echo -e "${BLUE}╔════════════════════════════════════════╗${NC}" 261 | echo -e "${BLUE}║ TrafficCop 机器限速管理 ║${NC}" 262 | echo -e "${BLUE}╚════════════════════════════════════════╝${NC}" 263 | echo "" 264 | show_status 265 | echo "" 266 | echo "选择操作:" 267 | echo "1) 禁用机器限速 (完全停止监控)" 268 | echo "2) 启用机器限速 (恢复监控)" 269 | echo "3) 恢复之前配置 (从备份恢复)" 270 | echo "4) 查看详细状态" 271 | echo "5) 清除TC限速规则 (仅清除当前限速)" 272 | echo "0) 退出" 273 | echo "" 274 | } 275 | 276 | # 主程序 277 | main() { 278 | # 创建工作目录 279 | mkdir -p "$WORK_DIR" 280 | 281 | if [ "$1" = "--disable" ]; then 282 | disable_machine_limit 283 | exit 0 284 | elif [ "$1" = "--enable" ]; then 285 | enable_machine_limit 286 | exit 0 287 | elif [ "$1" = "--status" ]; then 288 | show_status 289 | exit 0 290 | fi 291 | 292 | while true; do 293 | show_menu 294 | read -p "请选择 [0-5]: " choice 295 | 296 | case $choice in 297 | 1) 298 | echo "" 299 | read -p "确认禁用机器限速?这将停止所有监控 [y/N]: " confirm 300 | if [[ $confirm =~ ^[Yy]$ ]]; then 301 | disable_machine_limit 302 | read -p "按回车键继续..." 303 | fi 304 | ;; 305 | 2) 306 | echo "" 307 | enable_machine_limit 308 | read -p "按回车键继续..." 309 | ;; 310 | 3) 311 | echo "" 312 | restore_machine_limit 313 | read -p "按回车键继续..." 314 | ;; 315 | 4) 316 | echo "" 317 | show_detailed_status 318 | read -p "按回车键继续..." 319 | ;; 320 | 5) 321 | echo "" 322 | clear_tc_rules 323 | read -p "按回车键继续..." 324 | ;; 325 | 0) 326 | echo "退出" 327 | exit 0 328 | ;; 329 | *) 330 | echo -e "${RED}无效选择${NC}" 331 | sleep 1 332 | ;; 333 | esac 334 | done 335 | } 336 | 337 | main "$@" 338 | -------------------------------------------------------------------------------- /view_port_traffic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # View Port Traffic - 查看端口流量使用情况脚本 4 | # 版本 1.3 5 | 6 | SCRIPT_VERSION="1.3" 7 | 8 | # 设置时区为上海(东八区) 9 | export TZ='Asia/Shanghai' 10 | 11 | WORK_DIR="/root/TrafficCop" 12 | PORTS_CONFIG_FILE="$WORK_DIR/ports_traffic_config.json" 13 | 14 | # 颜色定义 15 | RED='\033[0;31m' 16 | GREEN='\033[0;32m' 17 | YELLOW='\033[0;33m' 18 | BLUE='\033[0;34m' 19 | PURPLE='\033[0;35m' 20 | CYAN='\033[0;36m' 21 | NC='\033[0m' # No Color 22 | 23 | # 获取端口流量使用情况 24 | get_port_traffic_usage() { 25 | local port=$1 26 | local interface=$2 27 | local traffic_mode=$3 28 | 29 | # ==================== 重要说明 ==================== 30 | # 对于代理服务器(如xray/v2ray)场景: 31 | # 32 | # 问题:iptables按端口监控只能捕获客户端↔服务器的流量(监听端口) 33 | # 无法捕获服务器↔目标网站的流量(随机源端口) 34 | # 35 | # 例如:客户端通过11710端口连接代理服务器访问YouTube 36 | # ✓ 可监控: 客户端 → 服务器11710端口 (入站dport) 37 | # ✓ 可监控: 服务器11710端口 → 客户端 (出站sport) 38 | # ✗ 无法监控: 服务器随机端口 → YouTube (出站) 39 | # ✗ 无法监控: YouTube → 服务器随机端口 (入站) 40 | # 41 | # 解决方案: 42 | # - 入站流量(dport): 可准确统计客户端请求量 43 | # - 出站流量(sport): 使用入站流量估算 (出站 ≈ 入站) 44 | # - 总流量: 入站 × 2 (近似值,实际略高因协议开销) 45 | # 46 | # 如需精确监控代理总流量,请使用进程级监控(cgroup/conntrack) 47 | # ================================================= 48 | 49 | # 获取入站流量(字节)- UFW环境下需要从ufw-before-input读取 50 | # 首先检查ufw-before-input(UFW环境下的正确位置) 51 | local rx_bytes=$(iptables -L ufw-before-input -v -n -x 2>/dev/null | grep "dpt:$port" | awk '{sum+=$2} END {printf "%.0f", sum+0}') 52 | # 如果为空,尝试ufw-user-input(兼容性) 53 | if [ -z "$rx_bytes" ] || [ "$rx_bytes" = "0" ]; then 54 | rx_bytes=$(iptables -L ufw-user-input -v -n -x 2>/dev/null | grep "dpt:$port" | awk '{sum+=$2} END {printf "%.0f", sum+0}') 55 | fi 56 | # 最后尝试标准INPUT链 57 | if [ -z "$rx_bytes" ] || [ "$rx_bytes" = "0" ]; then 58 | rx_bytes=$(iptables -L INPUT -v -n -x 2>/dev/null | grep "dpt:$port" | awk '{sum+=$2} END {printf "%.0f", sum+0}') 59 | fi 60 | 61 | # 代理场景:出站流量 = 入站流量(估算值) 62 | # 原因:客户端请求多少数据,服务器就需要下载并转发相应数据 63 | local tx_bytes="$rx_bytes" 64 | 65 | local usage_bytes 66 | case $traffic_mode in 67 | out) usage_bytes=$tx_bytes ;; 68 | in) usage_bytes=$rx_bytes ;; 69 | total) 70 | # 总流量 = 入站 × 2(估算值) 71 | usage_bytes=$(echo "$rx_bytes * 2" | bc 2>/dev/null || echo "0") 72 | ;; 73 | max) usage_bytes=$(echo "$rx_bytes $tx_bytes" | tr ' ' '\n' | sort -rn | head -n1) ;; 74 | *) 75 | # 默认按total模式计算 76 | usage_bytes=$(echo "$rx_bytes * 2" | bc 2>/dev/null || echo "0") 77 | ;; 78 | esac 79 | 80 | if [ -n "$usage_bytes" ] && [ "$usage_bytes" -gt 0 ]; then 81 | # 使用 awk 确保显示前导零(例如 0.02 而不是 .02) 82 | # 对 bc 的调用加上 stderr 重定向并在出错时返回 0,避免出现 "(standard_in) 1: syntax error" 83 | local gb_value=$(echo "scale=6; $usage_bytes/1024/1024/1024" | bc 2>/dev/null || echo "0") 84 | printf "%.3f" $(echo "$gb_value" | awk '{printf "%.6f", $1}') 85 | else 86 | echo "0.000" 87 | fi 88 | } 89 | 90 | # 获取端口配置 91 | get_port_config() { 92 | local port=$1 93 | local config=$(cat "$PORTS_CONFIG_FILE" 2>/dev/null || echo '{"ports":[]}') 94 | echo "$config" | jq -r ".ports[] | select(.port==$port)" 95 | } 96 | 97 | # 计算使用百分比 98 | calculate_percentage() { 99 | local usage=$1 100 | local limit=$2 101 | 102 | # 使用 bc 时屏蔽 stderr 并在出错时返回默认值,防止语法错误输出 103 | if (( $(echo "$limit > 0" | bc -l 2>/dev/null || echo "0") )); then 104 | # 使用 printf 确保显示前导零 105 | printf "%.2f" $(echo "scale=2; ($usage / $limit) * 100" | bc 2>/dev/null || echo "0") 106 | else 107 | echo "0.00" 108 | fi 109 | } 110 | 111 | # 获取状态颜色 112 | get_status_color() { 113 | local percentage=$1 114 | 115 | if (( $(echo "$percentage >= 90" | bc -l 2>/dev/null || echo "0") )); then 116 | echo "$RED" 117 | elif (( $(echo "$percentage >= 75" | bc -l 2>/dev/null || echo "0") )); then 118 | echo "$YELLOW" 119 | else 120 | echo "$GREEN" 121 | fi 122 | } 123 | 124 | # 显示进度条 125 | show_progress_bar() { 126 | local percentage=$1 127 | local width=30 128 | # 计算填充长度,屏蔽 bc stderr 并在出错时使用 0 129 | local filled=$(echo "scale=0; ($percentage * $width) / 100" | bc 2>/dev/null || echo 0) 130 | 131 | # 确保filled不为负数且不超过width 132 | if [ "$filled" -lt 0 ]; then 133 | filled=0 134 | elif [ "$filled" -gt "$width" ]; then 135 | filled=$width 136 | fi 137 | 138 | local color=$(get_status_color "$percentage") 139 | 140 | # 使用 printf 正确显示 ANSI 颜色代码 141 | printf "%b" "${color}[" 142 | for ((i=0; i/dev/null || echo 0) 170 | local percentage=$(calculate_percentage "$current_usage" "$traffic_limit") 171 | 172 | # 确定状态 173 | local status 174 | local status_color 175 | if (( $(echo "$current_usage > $limit_threshold" | bc -l 2>/dev/null || echo "0") )); then 176 | status="⚠️ 已限制" 177 | status_color="$RED" 178 | elif (( $(echo "$current_usage > ($traffic_limit * 0.8)" | bc -l 2>/dev/null || echo "0") )); then 179 | status="⚡ 接近限制" 180 | status_color="$YELLOW" 181 | else 182 | status="✓ 正常" 183 | status_color="$GREEN" 184 | fi 185 | 186 | # 翻译模式 187 | local mode_text 188 | case $traffic_mode in 189 | out) mode_text="出站" ;; 190 | in) mode_text="入站" ;; 191 | total) mode_text="总计" ;; 192 | max) mode_text="最大" ;; 193 | esac 194 | 195 | local period_text 196 | case $period in 197 | monthly) period_text="月度" ;; 198 | quarterly) period_text="季度" ;; 199 | yearly) period_text="年度" ;; 200 | esac 201 | 202 | local limit_mode_text 203 | if [ "$limit_mode" = "tc" ]; then 204 | limit_mode_text="限速 ${limit_speed}kbit/s" 205 | else 206 | limit_mode_text="阻断" 207 | fi 208 | 209 | # 显示信息 210 | echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 211 | echo -e "${BLUE}端口 $port${NC} - ${PURPLE}$description${NC}" 212 | echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 213 | echo -e "状态: ${status_color}${status}${NC}" 214 | echo -e "流量使用: ${YELLOW}${current_usage} GB${NC} / ${GREEN}${traffic_limit} GB${NC} (${percentage}%)" 215 | show_progress_bar "$percentage" 216 | echo -e "限制阈值: ${YELLOW}${limit_threshold} GB${NC} (扣除容错 ${traffic_tolerance} GB)" 217 | echo -e "统计模式: ${mode_text} | 周期: ${period_text} | 上次重置: ${last_reset}" 218 | echo -e "限制方式: ${limit_mode_text}" 219 | echo "" 220 | } 221 | 222 | # 显示所有端口信息 223 | show_all_ports() { 224 | # 非实时模式才清屏(实时模式在realtime_monitor中清屏) 225 | if [ "$1" != "--no-clear" ]; then 226 | clear 227 | fi 228 | 229 | if [ ! -f "$PORTS_CONFIG_FILE" ]; then 230 | echo -e "${RED}未找到端口配置文件${NC}" 231 | return 232 | fi 233 | 234 | local port_count=$(cat "$PORTS_CONFIG_FILE" | jq -r '.ports | length') 235 | 236 | if [ "$port_count" -eq 0 ]; then 237 | echo -e "${YELLOW}当前没有配置任何端口${NC}" 238 | return 239 | fi 240 | 241 | clear 242 | echo -e "${PURPLE}╔════════════════════════════════════════╗${NC}" 243 | echo -e "${PURPLE}║ 端口流量监控 - 实时查看工具 v${SCRIPT_VERSION} ║${NC}" 244 | echo -e "${PURPLE}╚════════════════════════════════════════╝${NC}" 245 | echo "" 246 | # 确保使用北京时间 247 | echo -e "更新时间: ${CYAN}$(TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M:%S')${NC}" 248 | echo -e "已配置端口: ${GREEN}${port_count}${NC}" 249 | echo "" 250 | # 代理场景说明 251 | echo -e "${YELLOW}⚠ 代理监控说明:${NC}" 252 | echo -e " ${CYAN}入站${NC}: 实际测量值(客户端→服务器)" 253 | echo -e " ${CYAN}出站${NC}: 估算值 = 入站(服务器→客户端,无法直接测量)" 254 | echo -e " ${CYAN}总计${NC}: 估算值 = 入站 × 2(实际略高10-20%因协议开销)" 255 | echo "" 256 | 257 | # 获取所有端口并显示 258 | local ports=$(cat "$PORTS_CONFIG_FILE" | jq -r '.ports[].port') 259 | 260 | for port in $ports; do 261 | show_port_info "$port" 262 | done 263 | 264 | # 显示总计信息 265 | echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 266 | echo -e "${BLUE}总计统计${NC}" 267 | echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 268 | 269 | local total_usage=0 270 | local total_limit=0 271 | 272 | for port in $ports; do 273 | local port_config=$(get_port_config "$port") 274 | local interface=$(echo "$port_config" | jq -r '.main_interface') 275 | local traffic_mode=$(echo "$port_config" | jq -r '.traffic_mode') 276 | local traffic_limit=$(echo "$port_config" | jq -r '.traffic_limit') 277 | 278 | local current_usage=$(get_port_traffic_usage "$port" "$interface" "$traffic_mode") 279 | total_usage=$(echo "$total_usage + $current_usage" | bc 2>/dev/null || echo "$total_usage") 280 | total_limit=$(echo "$total_limit + $traffic_limit" | bc 2>/dev/null || echo "$total_limit") 281 | done 282 | 283 | local total_percentage=$(calculate_percentage "$total_usage" "$total_limit") 284 | 285 | # 格式化总使用量,确保显示前导零 286 | local total_usage_formatted=$(printf "%.3f" $(echo "$total_usage" | awk '{printf "%.6f", $1}')) 287 | 288 | echo -e "所有端口总使用: ${YELLOW}${total_usage_formatted} GB${NC} / ${GREEN}${total_limit} GB${NC} (${total_percentage}%)" 289 | show_progress_bar "$total_percentage" 290 | echo "" 291 | } 292 | 293 | # 实时监控模式 294 | realtime_monitor() { 295 | while true; do 296 | clear 297 | show_all_ports 298 | echo -e "${YELLOW}[实时监控模式] 按 Ctrl+C 退出${NC}" 299 | sleep 5 300 | done 301 | } 302 | 303 | # 导出为JSON 304 | export_json() { 305 | if [ ! -f "$PORTS_CONFIG_FILE" ]; then 306 | echo -e "${RED}未找到端口配置文件${NC}" 307 | return 308 | fi 309 | 310 | local ports=$(cat "$PORTS_CONFIG_FILE" | jq -r '.ports[].port') 311 | local export_file="$WORK_DIR/port_traffic_report_$(date +%Y%m%d_%H%M%S).json" 312 | 313 | echo '{' > "$export_file" 314 | echo ' "timestamp": "'$(date '+%Y-%m-%d %H:%M:%S')'",' >> "$export_file" 315 | echo ' "ports": [' >> "$export_file" 316 | 317 | local first=true 318 | for port in $ports; do 319 | local port_config=$(get_port_config "$port") 320 | local interface=$(echo "$port_config" | jq -r '.main_interface') 321 | local traffic_mode=$(echo "$port_config" | jq -r '.traffic_mode') 322 | local traffic_limit=$(echo "$port_config" | jq -r '.traffic_limit') 323 | local description=$(echo "$port_config" | jq -r '.description') 324 | 325 | local current_usage=$(get_port_traffic_usage "$port" "$interface" "$traffic_mode") 326 | local percentage=$(calculate_percentage "$current_usage" "$traffic_limit") 327 | 328 | if [ "$first" = false ]; then 329 | echo ' ,' >> "$export_file" 330 | fi 331 | first=false 332 | 333 | cat >> "$export_file" << EOF 334 | { 335 | "port": $port, 336 | "description": "$description", 337 | "current_usage": $current_usage, 338 | "traffic_limit": $traffic_limit, 339 | "percentage": $percentage, 340 | "traffic_mode": "$traffic_mode" 341 | } 342 | EOF 343 | done 344 | 345 | echo ' ]' >> "$export_file" 346 | echo '}' >> "$export_file" 347 | 348 | echo -e "${GREEN}报告已导出到: ${export_file}${NC}" 349 | } 350 | 351 | # 主函数 352 | main() { 353 | if [ "$1" = "--realtime" ] || [ "$1" = "-r" ]; then 354 | realtime_monitor 355 | elif [ "$1" = "--export" ] || [ "$1" = "-e" ]; then 356 | export_json 357 | elif [ "$1" = "--json" ] || [ "$1" = "-j" ]; then 358 | # 输出JSON格式,用于其他脚本调用 359 | if [ ! -f "$PORTS_CONFIG_FILE" ]; then 360 | echo '{"ports":[]}' 361 | return 362 | fi 363 | 364 | local ports=$(cat "$PORTS_CONFIG_FILE" | jq -r '.ports[].port') 365 | echo '{"ports":[' 366 | 367 | local first=true 368 | for port in $ports; do 369 | local port_config=$(get_port_config "$port") 370 | local interface=$(echo "$port_config" | jq -r '.main_interface') 371 | local traffic_mode=$(echo "$port_config" | jq -r '.traffic_mode') 372 | local traffic_limit=$(echo "$port_config" | jq -r '.traffic_limit') 373 | local description=$(echo "$port_config" | jq -r '.description') 374 | 375 | local current_usage=$(get_port_traffic_usage "$port" "$interface" "$traffic_mode") 376 | 377 | if [ "$first" = false ]; then 378 | echo ',' 379 | fi 380 | first=false 381 | 382 | echo -n "{\"port\":\"$port\",\"description\":\"$description\",\"usage\":$current_usage,\"limit\":$traffic_limit}" 383 | done 384 | 385 | echo ']}' 386 | elif [ "$1" = "--help" ] || [ "$1" = "-h" ]; then 387 | echo "用法: $0 [选项]" 388 | echo "" 389 | echo "选项:" 390 | echo " 无参数 显示所有端口流量信息" 391 | echo " -r, --realtime 实时监控模式(每5秒刷新)" 392 | echo " -e, --export 导出为JSON报告" 393 | echo " -j, --json 输出JSON格式(用于脚本调用)" 394 | echo " -h, --help 显示帮助信息" 395 | echo "" 396 | echo "示例:" 397 | echo " $0 # 查看所有端口" 398 | echo " $0 --realtime # 实时监控" 399 | echo " $0 --export # 导出报告" 400 | else 401 | show_all_ports 402 | fi 403 | } 404 | 405 | main "$@" 406 | -------------------------------------------------------------------------------- /trafficcop-manager.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TrafficCop 管理器 - 交互式管理工具 4 | # 版本 2.4 5 | # 最后更新:2025-10-19 20:00 6 | 7 | SCRIPT_VERSION="2.4" 8 | LAST_UPDATE="2025-10-19 20:00" 9 | 10 | # 颜色定义 11 | RED='\033[0;31m' 12 | GREEN='\033[0;32m' 13 | YELLOW='\033[0;33m' 14 | BLUE='\033[0;34m' 15 | PURPLE='\033[0;35m' 16 | CYAN='\033[0;36m' 17 | NC='\033[0m' # No Color 18 | 19 | # 基础目录 20 | WORK_DIR="/root/TrafficCop" 21 | REPO_URL="https://raw.githubusercontent.com/ypq123456789/TrafficCop/main" 22 | 23 | # 检查root权限 24 | check_root() { 25 | if [ "$(id -u)" -ne 0 ]; then 26 | echo -e "${RED}此脚本必须以root权限运行${NC}" 27 | exit 1 28 | fi 29 | } 30 | 31 | # 创建工作目录 32 | create_work_dir() { 33 | mkdir -p "$WORK_DIR" 34 | cd "$WORK_DIR" 35 | } 36 | 37 | # 下载脚本 38 | install_script() { 39 | local script_name="$1" 40 | echo -e "${YELLOW}正在下载 $script_name...${NC}" 41 | curl -fsSL "$REPO_URL/$script_name" -o "$WORK_DIR/$script_name" 42 | chmod +x "$WORK_DIR/$script_name" 43 | } 44 | 45 | # 运行脚本 46 | run_script() { 47 | local script_path="$1" 48 | bash "$script_path" 49 | } 50 | 51 | # 安装流量监控 52 | install_monitor() { 53 | echo -e "${CYAN}正在安装流量监控功能...${NC}" 54 | install_script "trafficcop.sh" 55 | run_script "$WORK_DIR/trafficcop.sh" 56 | echo -e "${GREEN}流量监控功能安装完成!${NC}" 57 | read -p "按回车键继续..." 58 | } 59 | 60 | # 安装Telegram通知 61 | install_tg_notifier() { 62 | echo -e "${CYAN}正在安装Telegram通知功能...${NC}" 63 | install_script "tg_notifier.sh" 64 | run_script "$WORK_DIR/tg_notifier.sh" 65 | echo -e "${GREEN}Telegram通知功能安装完成!${NC}" 66 | read -p "按回车键继续..." 67 | } 68 | 69 | # 安装PushPlus通知 70 | install_pushplus_notifier() { 71 | echo -e "${CYAN}正在安装PushPlus通知功能...${NC}" 72 | 73 | # 检查pushplus_notifier.sh是否在仓库中,如果不在,使用本地的 74 | if curl -s --head "$REPO_URL/pushplus_notifier.sh" | grep "HTTP/2 200\|HTTP/1.1 200" > /dev/null; then 75 | install_script "pushplus_notifier.sh" 76 | else 77 | echo -e "${YELLOW}从仓库下载失败,使用本地文件...${NC}" 78 | # 复制当前目录下的pushplus_notifier.sh到工作目录 79 | if [ -f "pushplus_notifier.sh" ]; then 80 | cp "pushplus_notifier.sh" "$WORK_DIR/pushplus_notifier.sh" 81 | chmod +x "$WORK_DIR/pushplus_notifier.sh" 82 | else 83 | echo -e "${RED}本地pushplus_notifier.sh文件不存在!${NC}" 84 | read -p "按回车键继续..." 85 | return 86 | fi 87 | fi 88 | run_script "$WORK_DIR/pushplus_notifier.sh" 89 | echo -e "${GREEN}PushPlus通知功能安装完成!${NC}" 90 | read -p "按回车键继续..." 91 | } 92 | 93 | # 安装Server酱通知 94 | install_serverchan_notifier() { 95 | echo -e "${CYAN}正在安装Server酱通知功能...${NC}" 96 | 97 | # 检查serverchan_notifier.sh是否在仓库中,如果不在,使用本地的 98 | if curl -s --head "$REPO_URL/serverchan_notifier.sh" | grep "HTTP/2 200\|HTTP/1.1 200" > /dev/null; then 99 | install_script "serverchan_notifier.sh" 100 | else 101 | echo -e "${YELLOW}从仓库下载失败,使用本地文件...${NC}" 102 | # 复制当前目录下的serverchan_notifier.sh到工作目录 103 | if [ -f "serverchan_notifier.sh" ]; then 104 | cp "serverchan_notifier.sh" "$WORK_DIR/serverchan_notifier.sh" 105 | chmod +x "$WORK_DIR/serverchan_notifier.sh" 106 | else 107 | echo -e "${RED}本地serverchan_notifier.sh文件不存在!${NC}" 108 | read -p "按回车键继续..." 109 | return 110 | fi 111 | fi 112 | run_script "$WORK_DIR/serverchan_notifier.sh" 113 | echo -e "${GREEN}Server酱通知功能安装完成!${NC}" 114 | read -p "按回车键继续..." 115 | } 116 | 117 | # 安装端口流量限制 118 | install_port_traffic_limit() { 119 | echo -e "${CYAN}正在安装端口流量限制功能...${NC}" 120 | 121 | # 安装主配置脚本 122 | install_script "port_traffic_limit.sh" 123 | 124 | # 安装端口流量查看脚本 125 | echo -e "${YELLOW}正在下载端口流量查看脚本...${NC}" 126 | install_script "view_port_traffic.sh" 127 | 128 | # 安装辅助函数库 129 | echo -e "${YELLOW}正在下载辅助函数库...${NC}" 130 | install_script "port_traffic_helper.sh" 131 | 132 | # 运行配置向导 133 | run_script "$WORK_DIR/port_traffic_limit.sh" 134 | 135 | echo -e "${GREEN}端口流量限制功能安装完成!${NC}" 136 | echo -e "${CYAN}提示:使用选项5可管理端口配置,支持序号快速选择${NC}" 137 | read -p "按回车键继续..." 138 | } 139 | 140 | # 管理端口配置 141 | manage_port_config() { 142 | echo -e "${CYAN}端口配置管理${NC}" 143 | 144 | if [ ! -f "$WORK_DIR/port_traffic_limit.sh" ]; then 145 | echo -e "${RED}端口流量限制脚本不存在,请先安装${NC}" 146 | read -p "按回车键继续..." 147 | return 148 | fi 149 | 150 | run_script "$WORK_DIR/port_traffic_limit.sh" 151 | } 152 | 153 | # 查看端口流量状态 154 | view_port_traffic() { 155 | echo -e "${CYAN}正在查看端口流量状态...${NC}" 156 | 157 | # 确保脚本存在 158 | if [ ! -f "$WORK_DIR/view_port_traffic.sh" ]; then 159 | echo -e "${YELLOW}端口流量查看脚本不存在,正在下载...${NC}" 160 | install_script "view_port_traffic.sh" 161 | fi 162 | 163 | if [ -f "$WORK_DIR/view_port_traffic.sh" ]; then 164 | # 运行脚本并等待用户输入 165 | bash "$WORK_DIR/view_port_traffic.sh" 166 | echo "" 167 | echo -e "${CYAN}端口流量状态查看完成${NC}" 168 | read -p "按回车键返回主菜单..." 169 | else 170 | echo -e "${RED}无法下载端口流量查看脚本${NC}" 171 | read -p "按回车键继续..." 172 | fi 173 | } 174 | 175 | # 解除端口流量限制 176 | remove_port_traffic_limit() { 177 | echo -e "${CYAN}正在解除端口流量限制...${NC}" 178 | if [ -f "$WORK_DIR/port_traffic_limit.sh" ]; then 179 | bash "$WORK_DIR/port_traffic_limit.sh" --remove 180 | echo -e "${GREEN}端口流量限制已解除!${NC}" 181 | echo -e "${YELLOW}注意:配置文件和查看脚本仍保留,可继续使用选项12/13管理${NC}" 182 | else 183 | echo -e "${RED}端口流量限制脚本不存在${NC}" 184 | fi 185 | read -p "按回车键继续..." 186 | } 187 | 188 | # 查看日志 189 | view_logs() { 190 | echo -e "${CYAN}查看日志${NC}" 191 | echo "1) 流量监控日志" 192 | echo "2) Telegram通知日志" 193 | echo "3) PushPlus通知日志" 194 | echo "4) Server酱通知日志" 195 | echo "5) 端口流量监控日志" 196 | echo "0) 返回主菜单" 197 | 198 | read -p "请选择要查看的日志 [0-5]: " log_choice 199 | 200 | case $log_choice in 201 | 1) 202 | if [ -f "$WORK_DIR/traffic_monitor.log" ]; then 203 | tail -50 "$WORK_DIR/traffic_monitor.log" 204 | else 205 | echo -e "${RED}流量监控日志不存在${NC}" 206 | fi 207 | ;; 208 | 2) 209 | if [ -f "$WORK_DIR/tg_notifier.log" ]; then 210 | tail -20 "$WORK_DIR/tg_notifier.log" 211 | else 212 | echo -e "${RED}Telegram通知日志不存在${NC}" 213 | fi 214 | ;; 215 | 3) 216 | if [ -f "$WORK_DIR/pushplus_notifier.log" ]; then 217 | tail -20 "$WORK_DIR/pushplus_notifier.log" 218 | else 219 | echo -e "${RED}PushPlus通知日志不存在${NC}" 220 | fi 221 | ;; 222 | 4) 223 | if [ -f "$WORK_DIR/serverchan_notifier.log" ]; then 224 | tail -20 "$WORK_DIR/serverchan_notifier.log" 225 | else 226 | echo -e "${RED}Server酱通知日志不存在${NC}" 227 | fi 228 | ;; 229 | 5) 230 | if [ -f "$WORK_DIR/port_traffic_monitor.log" ]; then 231 | tail -20 "$WORK_DIR/port_traffic_monitor.log" 232 | else 233 | echo -e "${RED}端口流量监控日志不存在${NC}" 234 | fi 235 | ;; 236 | 0) 237 | return 238 | ;; 239 | *) 240 | echo -e "${RED}无效的选择${NC}" 241 | ;; 242 | esac 243 | 244 | read -p "按回车键继续..." 245 | } 246 | 247 | # 查看当前配置 248 | view_config() { 249 | echo -e "${CYAN}查看当前配置${NC}" 250 | echo "1) 流量监控配置" 251 | echo "2) Telegram通知配置" 252 | echo "3) PushPlus通知配置" 253 | echo "4) Server酱通知配置" 254 | echo "0) 返回主菜单" 255 | 256 | read -p "请选择要查看的配置类型 [0-4]: " config_choice 257 | 258 | case $config_choice in 259 | 1) 260 | if [ -f "$WORK_DIR/traffic_monitor_config.txt" ]; then 261 | cat "$WORK_DIR/traffic_monitor_config.txt" 262 | else 263 | echo -e "${RED}流量监控配置不存在${NC}" 264 | fi 265 | ;; 266 | 2) 267 | if [ -f "$WORK_DIR/tg_notifier_config.txt" ]; then 268 | cat "$WORK_DIR/tg_notifier_config.txt" 269 | else 270 | echo -e "${RED}Telegram通知配置不存在${NC}" 271 | fi 272 | ;; 273 | 3) 274 | if [ -f "$WORK_DIR/pushplus_notifier_config.txt" ]; then 275 | cat "$WORK_DIR/pushplus_notifier_config.txt" 276 | else 277 | echo -e "${RED}PushPlus通知配置不存在${NC}" 278 | fi 279 | ;; 280 | 4) 281 | if [ -f "$WORK_DIR/serverchan_notifier_config.txt" ]; then 282 | cat "$WORK_DIR/serverchan_notifier_config.txt" 283 | else 284 | echo -e "${RED}Server酱通知配置不存在${NC}" 285 | fi 286 | ;; 287 | 0) 288 | return 289 | ;; 290 | *) 291 | echo -e "${RED}无效的选择${NC}" 292 | ;; 293 | esac 294 | 295 | read -p "按回车键继续..." 296 | } 297 | 298 | # 使用预设配置 299 | use_preset_config() { 300 | echo -e "${CYAN}使用预设配置${NC}" 301 | echo "" 302 | echo "可用的预设配置:" 303 | echo "1) ali-20g - 阿里云轻量 20G配置" 304 | echo "2) ali-200g - 阿里云轻量 200G配置" 305 | echo "3) ali-1T - 阿里云轻量 1T配置" 306 | echo "4) asia-300g - 亚洲地区 300G配置" 307 | echo "5) az-15g - Azure 15G配置" 308 | echo "6) az-115g - Azure 115G配置" 309 | echo "7) gcp-200g - Google Cloud 200G配置" 310 | echo "8) gcp-625g - Google Cloud 625G配置" 311 | echo "9) alice-1500g - Alice 1500G配置" 312 | echo "0) 返回主菜单" 313 | echo "" 314 | 315 | read -p "请选择预设配置 [0-9]: " preset_choice 316 | 317 | local config_file="" 318 | case $preset_choice in 319 | 1) config_file="ali-20g" ;; 320 | 2) config_file="ali-200g" ;; 321 | 3) config_file="ali-1T" ;; 322 | 4) config_file="asia-300g" ;; 323 | 5) config_file="az-15g" ;; 324 | 6) config_file="az-115g" ;; 325 | 7) config_file="gcp-200g" ;; 326 | 8) config_file="gcp-625g" ;; 327 | 9) config_file="alice-1500g" ;; 328 | 0) return ;; 329 | *) 330 | echo -e "${RED}无效的选择${NC}" 331 | read -p "按回车键继续..." 332 | return 333 | ;; 334 | esac 335 | 336 | echo -e "${YELLOW}正在下载并应用预设配置 $config_file...${NC}" 337 | 338 | # 下载预设配置文件 339 | if curl -fsSL "$REPO_URL/$config_file" -o "$WORK_DIR/traffic_monitor_config.txt"; then 340 | echo -e "${GREEN}预设配置已应用!${NC}" 341 | echo -e "${CYAN}配置内容:${NC}" 342 | cat "$WORK_DIR/traffic_monitor_config.txt" 343 | 344 | # 提示用户是否立即启动监控 345 | echo "" 346 | read -p "是否立即启动流量监控?[y/N]: " start_monitor 347 | if [[ $start_monitor =~ ^[Yy]$ ]]; then 348 | install_script "trafficcop.sh" 349 | run_script "$WORK_DIR/trafficcop.sh" 350 | fi 351 | else 352 | echo -e "${RED}下载预设配置失败${NC}" 353 | fi 354 | 355 | read -p "按回车键继续..." 356 | } 357 | 358 | # 停止所有服务 359 | stop_all_services() { 360 | echo -e "${CYAN}正在停止所有TrafficCop服务...${NC}" 361 | 362 | # 停止流量监控进程 363 | pkill -f "trafficcop.sh" 2>/dev/null 364 | pkill -f "traffic_monitor.sh" 2>/dev/null 365 | echo "✓ 流量监控进程已停止" 366 | 367 | # 移除cron任务 368 | crontab -l 2>/dev/null | grep -v "trafficcop.sh\|traffic_monitor.sh" | crontab - 2>/dev/null 369 | echo "✓ 定时任务已清理" 370 | 371 | # 清除TC规则 372 | local interface=$(ip route | grep default | awk '{print $5}' | head -n1) 373 | if [ -n "$interface" ]; then 374 | tc qdisc del dev "$interface" root 2>/dev/null 375 | echo "✓ TC限速规则已清除" 376 | fi 377 | 378 | # 取消关机计划 379 | shutdown -c 2>/dev/null 380 | echo "✓ 关机计划已取消" 381 | 382 | echo -e "${GREEN}所有服务已停止!${NC}" 383 | read -p "按回车键继续..." 384 | } 385 | 386 | # 更新所有脚本 387 | update_all_scripts() { 388 | echo -e "${CYAN}正在更新所有脚本到最新版本...${NC}" 389 | 390 | local scripts=("trafficcop.sh" "tg_notifier.sh" "pushplus_notifier.sh" "serverchan_notifier.sh" 391 | "port_traffic_limit.sh" "view_port_traffic.sh" "port_traffic_helper.sh" 392 | "remove_traffic_limit.sh" "machine_limit_manager.sh") 393 | 394 | for script in "${scripts[@]}"; do 395 | if curl -fsSL "$REPO_URL/$script" -o "$WORK_DIR/$script.new" 2>/dev/null; then 396 | mv "$WORK_DIR/$script.new" "$WORK_DIR/$script" 397 | chmod +x "$WORK_DIR/$script" 398 | echo -e "${GREEN}✓ $script 已更新${NC}" 399 | else 400 | echo -e "${YELLOW}! $script 更新失败或不存在${NC}" 401 | fi 402 | done 403 | 404 | echo -e "${GREEN}脚本更新完成!${NC}" 405 | read -p "按回车键继续..." 406 | } 407 | 408 | # 机器限速管理 409 | manage_machine_limit() { 410 | echo -e "${CYAN}正在启动机器限速管理器...${NC}" 411 | 412 | # 下载并运行机器限速管理器 413 | echo -e "${YELLOW}正在下载机器限速管理器...${NC}" 414 | install_script "machine_limit_manager.sh" 415 | 416 | if [ -f "$WORK_DIR/machine_limit_manager.sh" ]; then 417 | run_script "$WORK_DIR/machine_limit_manager.sh" 418 | else 419 | echo -e "${RED}无法下载机器限速管理器${NC}" 420 | echo -e "${YELLOW}尝试使用旧方式解除限速...${NC}" 421 | remove_traffic_limit 422 | fi 423 | } 424 | 425 | # 显示主菜单 426 | show_main_menu() { 427 | clear 428 | echo -e "${BLUE}╔════════════════════════════════════════╗${NC}" 429 | echo -e "${BLUE}║ TrafficCop 管理工具 v${SCRIPT_VERSION} ║${NC}" 430 | echo -e "${BLUE}╚════════════════════════════════════════╝${NC}" 431 | echo -e "${PURPLE}====================================${NC}" 432 | echo -e "${CYAN}最后更新: ${LAST_UPDATE}${NC}" 433 | echo "" 434 | echo -e "${YELLOW}1) 安装/管理流量监控${NC}" 435 | echo -e "${YELLOW}2) 安装/管理Telegram通知${NC}" 436 | echo -e "${YELLOW}3) 安装/管理PushPlus通知${NC}" 437 | echo -e "${YELLOW}4) 安装/管理Server酱通知${NC}" 438 | echo -e "${YELLOW}5) 安装/管理端口流量限制${NC}" 439 | echo -e "${CYAN}6) 查看端口流量状态${NC}" 440 | echo -e "${GREEN}7) 机器限速管理 (启用/禁用)${NC}" 441 | echo -e "${YELLOW}8) 查看日志${NC}" 442 | echo -e "${YELLOW}9) 查看当前配置${NC}" 443 | echo -e "${YELLOW}10) 使用预设配置${NC}" 444 | echo -e "${YELLOW}11) 停止所有服务${NC}" 445 | echo -e "${GREEN}12) 更新所有脚本到最新版本${NC}" 446 | echo -e "${YELLOW}0) 退出${NC}" 447 | echo -e "${PURPLE}====================================${NC}" 448 | echo "" 449 | } 450 | 451 | # 主函数 452 | main() { 453 | check_root 454 | create_work_dir 455 | 456 | while true; do 457 | show_main_menu 458 | read -p "请选择操作 [0-12]: " choice 459 | 460 | case $choice in 461 | 1) 462 | install_monitor 463 | ;; 464 | 2) 465 | install_tg_notifier 466 | ;; 467 | 3) 468 | install_pushplus_notifier 469 | ;; 470 | 4) 471 | install_serverchan_notifier 472 | ;; 473 | 5) 474 | manage_port_config 475 | ;; 476 | 6) 477 | view_port_traffic 478 | ;; 479 | 7) 480 | manage_machine_limit 481 | ;; 482 | 8) 483 | view_logs 484 | ;; 485 | 9) 486 | view_config 487 | ;; 488 | 10) 489 | use_preset_config 490 | ;; 491 | 11) 492 | stop_all_services 493 | ;; 494 | 12) 495 | update_all_scripts 496 | ;; 497 | 0) 498 | echo -e "${GREEN}感谢使用TrafficCop管理工具!${NC}" 499 | exit 0 500 | ;; 501 | *) 502 | echo -e "${RED}无效的选择,请重新输入${NC}" 503 | sleep 1 504 | ;; 505 | esac 506 | done 507 | } 508 | 509 | # 启动主程序 510 | main "$@" 511 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrafficCop - 智能流量监控与限制脚本 2 | [English](README_EN.md) | 中文 3 | 4 | [netjett](https://netjett.com/aff.php?aff=115)赞助了本项目 5 | 6 | [![image](https://github.com/user-attachments/assets/b1712474-1c08-49f9-a252-43caa33809d1)](https://yxvm.com/aff.php?aff=718) 7 | 8 | [NodeSupport](https://github.com/NodeSeekDev/NodeSupport)赞助了本项目 9 | 10 | [VTEXS](https://console.vtexs.com/?affid=1554)赞助了本项目 11 | 12 | [![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com?aff=SwiftPuppy719 "Powered by DartNode - Free VPS for Open Source") 13 | 14 | ## 注意事项 15 | 16 | 1. 本脚本是基于vnstat的流量统计,vnstat只会在安装后开始统计流量! 17 | 18 | 2. TC模式无法防止DDoS消耗流量,流量消耗速度仍然较快!欢迎PR修复(如果可以修复的话)。 19 | 20 | 3. 如果遇到GitHub API速率限制问题,可以尝试以下解决方案: 21 | - 使用原始内容URL下载脚本 22 | - 等待API限制重置(通常1小时) 23 | - 使用个人访问令牌增加API限额 24 | - 手动下载脚本并运行 25 | 26 | 4. 脚本默认使用root权限运行。如需非root用户运行,请确保该用户有sudo权限,并将所有命令前加sudo。 27 | 28 | 5. 如果遇到问题,可以查看日志文件(/root/TrafficCop/traffic_monitor.log)获取更多信息。 29 | 30 | 6. 定期检查脚本更新以获取新功能和bug修复。 31 | 32 | 7. 对于特定VPS提供商,可能需要调整配置以适应其计费模式。 33 | 34 | 8. TC模式下的速度限制可能不是精确的,实际速度可能略有偏差。 35 | 36 | 9. 关机模式会完全切断网络连接,请谨慎使用。 37 | 38 | 10. 建议定期备份配置文件(traffic_monitor_config.txt)。 39 | 40 | ## 常见问题 41 | 42 | Q: 为什么我的流量统计似乎不准确? 43 | A: 确保vnstat已正确安装并运行一段时间。新安装的vnstat需要时间来收集准确的数据。 44 | 45 | Q: 如何更改已设置的配置? 46 | A: 重新运行脚本,它会提示你是否要修改现有配置。 47 | 48 | Q: TC模式下SSH连接变慢怎么办? 49 | A: 尝试增加TC模式下的速度限制值。 50 | 51 | Q: 如何完全卸载脚本? 52 | A: 使用以下命令: 53 | ``` 54 | sudo pkill -f traffic_monitor.sh 55 | sudo rm -rf /root/TrafficCop 56 | sudo tc qdisc del dev $(ip route | grep default | cut -d ' ' -f 5) root 57 | ``` 58 | 59 | 60 | 61 | ## 一键安装脚本 62 | ### 一键安装交互式脚本 63 | ``` 64 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 65 | ``` 66 | #### 功能 67 | 68 | 1. 安装流量监控 - 下载并安装基础的流量监控功能 69 | 2. 安装Telegram通知功能 - 添加Telegram推送通知 70 | 3. 安装PushPlus通知功能 - 添加PushPlus推送通知 71 | 4. 安装Server酱通知功能 - 添加Server酱推送通知 72 | 5. 安装端口流量限制 - 为多个端口设置独立的流量限制(支持多端口 2.0) 73 | 6. 查看端口流量状态 - 实时查看所有配置端口的流量使用情况(新增 2.0) 74 | 7. 机器限速管理 - 完整的机器级限速启用/禁用/恢复功能(新增 v2.4) 75 | 8. 查看日志 - 查看各种服务的日志文件 76 | 9. 查看当前配置 - 查看各种服务的配置文件 77 | 10. 使用预设配置 - 应用针对不同服务商优化的预设配置 78 | 11. 停止所有服务 - 停止所有TrafficCop相关服务 79 | 12. 更新所有脚本到最新版本 - 一键更新所有组件 80 | 81 | #### 优势 82 | 1. 一站式管理 - 用户只需记住一个命令,即可管理所有TrafficCop功能 83 | 2. 交互式体验 - 通过数字菜单选择,无需记忆复杂命令 84 | 3. 可视化界面 - 使用彩色输出增强用户体验 85 | 4. 灵活操作 - 完成一项操作后返回主菜单,可继续选择其他操作 86 | 5. 用户友好 - 每个操作都有确认提示,避免误操作 87 | 88 | ![image](交互式脚本.png) 89 | 90 | 91 | ### 一键全家桶TG推送(调用api,版本最新,可能会403): 92 | ``` 93 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 94 | ``` 95 | ### 一键全家桶TG推送(从原始内容下载,版本可能落后): 96 | ``` 97 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 98 | ``` 99 | ### 一键全家桶pushplus推送(调用api,版本最新,可能会403): 100 | ``` 101 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/pushplus_notifier.sh" | tr -d '\r' > /root/TrafficCop/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && bash /root/TrafficCop/pushplus_notifier.sh 102 | ``` 103 | ### 一键全家桶pushplus推送(从原始内容下载,版本可能落后): 104 | ``` 105 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/pushplus_notifier.sh" | tr -d '\r' > /root/TrafficCop/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && bash /root/TrafficCop/pushplus_notifier.sh 106 | ``` 107 | ### 我只要监控,不要推送: 108 | ``` 109 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh 110 | ``` 111 | ## 实用命令 112 | ### 查看日志: 113 | ``` 114 | sudo tail -f -n 30 /root/TrafficCop/traffic_monitor.log 115 | ``` 116 | ### 查看当前配置: 117 | ``` 118 | sudo cat /root/TrafficCop/traffic_monitor_config.txt 119 | ``` 120 | ### 紧急停止所有traffic_monitor进程(用于脚本出现问题时): 121 | ``` 122 | sudo pkill -f traffic_monitor.sh 123 | ``` 124 | ### 一键解除限速 125 | ``` 126 | sudo curl -sSL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/remove_traffic_limit.sh | sudo bash 127 | ``` 128 | 129 | ## 脚本逻辑 130 | - 自动检测并选择主要网卡进行流量限制。 131 | - 用户选择流量统计模式(四种选项)。 132 | - 用户设置流量计算周期(月/季/年)和起始日期。 133 | - 用户输入流量限制和容错范围。 134 | - 用户选择限制模式(TC模式或关机模式)。 135 | - 对于TC模式,用户可设置限速值。 136 | - 脚本每分钟检测流量消耗,达到限制时执行相应操作。 137 | - 在新的流量周期开始时自动解除限制。 138 | 139 | ## 脚本特色 140 | - 四种全面的流量统计模式,适应各种VPS计费方式。 141 | - 自定义流量计算周期和起始日。 142 | - 自定义流量容错范围。 143 | - 交互式配置,可随时修改参数。 144 | - 实时流量统计提示。 145 | - TC模式保证SSH连接可用。 146 | - 关机模式提供更严格的流量控制。 147 | - 自定义限速带宽(TC模式)。 148 | 149 | ## Telegram Bot 集成 150 | TrafficCop 现在集成了 Telegram Bot 功能,可以发送以下通知: 151 | 152 | - 限速警告 153 | - 限速解除通知 154 | - 新周期开始通知 155 | - 关机警告 156 | - 每日流量报告 157 | 158 | **支持自定义主机名,一个机器人就可以统一管理你的所有小鸡!** 159 | 160 | **支持自定义每日流量报告的时间,你想每个小鸡什么时候通知就什么时候,当然你也可以设置都是一样的时间,一下子冒出来一堆,享受坐拥数🐔的快感** 161 | 162 | 要使用此功能,请在脚本配置过程中提供你的 Telegram Bot Token 和 Chat ID。 163 | 164 | Telegram Bot Token 在你创建机器人时会显示。 165 | 166 | Chat ID获取方法:https://api.telegram.org/bot${BOT_TOKEN}/getUpdates 167 | 168 | ${BOT_TOKEN}是你的 Telegram Bot Token 169 | 170 | Chat ID还可以通过bot获取,更简单,比如[username_to_id_bot](https://t.me/username_to_id_bot) 171 | 172 | ### 相关命令 173 | 一键推送脚本(调用api,版本最新,可能会403): 174 | ``` 175 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 176 | ``` 177 | 一键推送脚本(从原始内容下载,版本可能落后): 178 | ``` 179 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 180 | ``` 181 | 查看tg推送定时执行日志 182 | ``` 183 | sudo tail -f -n 30 /root/TrafficCop/tg_notifier_cron.log 184 | ``` 185 | 查看当前状态 186 | ``` 187 | sudo tail -f -n 30 /root/TrafficCop/last_traffic_notification 188 | ``` 189 | 杀死所有TG推送进程 190 | ``` 191 | sudo pkill -f tg_notifier.sh && crontab -l | grep -v "tg_notifier.sh" | crontab - 192 | ``` 193 | 194 | ### 推送示意: 195 | 196 | **机器总流量推送:** 197 | ![image](https://github.com/ypq123456789/TrafficCop/assets/114487221/7674bb25-2771-47e3-a999-8701ef160c7c) 198 | 199 | **端口流量推送(新增):** 200 | ![端口流量推送](端口流量tg推送实例图片.jpg) 201 | 202 | 端口流量推送展示了: 203 | - 🔌 端口流量详情 204 | - ✅ 实时使用量(精确到 0.01GB) 205 | - 📊 流量限制对比 206 | - 🎯 状态图标(✅绿色正常 / 🟡黄色警告 / 🔴红色超限) 207 | 208 | ## pushplus 集成 209 | TrafficCop 现在集成了pushplus推送功能。 210 | 211 | 可发送的通知类型同上、支持自定义主机名、支持自定义每日流量报告的时间。 212 | 213 | 要使用此功能,请在脚本配置过程中提供你的pushplus token。 214 | 215 | ### 相关命令 216 | 一键推送脚本(调用api,版本最新,可能会403): 217 | ``` 218 | sudo bash -c "mkdir -p /root/TrafficCop && curl -sSfL -H 'Accept: application/vnd.github.v3.raw' -o /root/TrafficCop/pushplus_notifier.sh https://api.github.com/repos/ypq123456789/TrafficCop/contents/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && /root/TrafficCop/pushplus_notifier.sh" 219 | ``` 220 | 一键推送脚本(从原始内容下载,版本可能落后): 221 | ``` 222 | sudo mkdir -p /root/TrafficCop && curl -sSfL -o /root/TrafficCop/pushplus_notifier.sh https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && /root/TrafficCop/pushplus_notifier.sh 223 | ``` 224 | 查看pushplus推送定时执行日志 225 | ``` 226 | sudo tail -f -n 30 /root/TrafficCop/pushplus_notifier_cron.log 227 | ``` 228 | 查看当前状态 229 | ``` 230 | sudo tail -f -n 30 /root/TrafficCop/last_pushplus_notification 231 | ``` 232 | 杀死所有pushplus推送进程 233 | ``` 234 | sudo pkill -f pushplus_notifier.sh && crontab -l | grep -v "pushplus_notifier.sh" | crontab - 235 | ``` 236 | 237 | 推送示意如下: 238 | ![Screenshot_20240707_022328_com tencent mm](https://github.com/ypq123456789/TrafficCop/assets/114487221/c32c1ba1-1082-4f01-a26c-25608e9e3c29) 239 | 240 | ## 端口流量限制功能 2.0(新增 - 支持多端口) 241 | 242 | TrafficCop 现在支持为**多个端口**同时设置独立的流量限制!这个功能非常适合需要对特定服务(如Web服务器、代理服务、SSH等)进行精细化流量管理的场景。 243 | 244 | ### 功能特点 245 | 246 | 1. **多端口流量管理** - 同时监控和限制多个端口的流量,使用JSON格式存储配置 247 | 2. **独立端口流量统计** - 使用iptables精确统计每个端口的入站和出站流量 248 | 3. **实时流量查看** - 彩色可视化界面,显示所有端口的流量使用情况和进度条 249 | 4. **智能配置同步** - 自动同步机器配置,也支持自定义配置 250 | 5. **灵活的限制策略** - 支持两种限制模式: 251 | - TC模式:对端口流量进行限速 252 | - 阻断模式:超限后完全阻断端口流量 253 | 6. **配置验证** - 确保端口流量限制不超过机器总流量限制 254 | 7. **推送通知集成** - 所有推送服务(Telegram、PushPlus、ServerChan)自动包含端口流量信息 255 | 8. **自动化管理** - 支持定时任务自动监控和限制 256 | 257 | ### 使用逻辑 258 | 259 | #### 场景一:机器未限制流量 260 | 当机器尚未配置流量限制时,为指定端口设置流量限制会: 261 | 1. 创建端口流量配置 262 | 2. 询问是否同步配置到机器流量限制 263 | 3. 如果选择同步,端口配置将自动应用到机器级别 264 | 265 | #### 场景二:机器已限制流量 266 | 当机器已配置流量限制时,为指定端口设置流量限制会: 267 | 1. 检查端口流量限制是否小于等于机器流量限制 268 | 2. 默认继承机器的其他配置(统计模式、周期、限制模式等) 269 | 3. 允许自定义配置以满足特殊需求 270 | 271 | ### 安装和配置 272 | 273 | #### 方法一:通过管理器脚本(推荐) 274 | ```bash 275 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 276 | ``` 277 | 选择 "5) 安装端口流量限制" 278 | 279 | #### 方法二:直接运行脚本 280 | ```bash 281 | sudo mkdir -p /root/TrafficCop && \ 282 | curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/port_traffic_limit.sh" | tr -d '\r' > /root/TrafficCop/port_traffic_limit.sh && \ 283 | chmod +x /root/TrafficCop/port_traffic_limit.sh && \ 284 | bash /root/TrafficCop/port_traffic_limit.sh 285 | ``` 286 | 287 | ### 配置选项 288 | 289 | 在配置过程中,您需要提供: 290 | 291 | 1. **端口号** - 要限制流量的端口(1-65535) 292 | 2. **流量限制** - 端口允许使用的最大流量(GB) 293 | 3. **容错范围** - 触发限制前的缓冲区(GB) 294 | 4. **配置方式** - 选择使用机器配置或自定义配置 295 | 296 | 如果选择自定义配置,还需要设置: 297 | - 流量统计模式(出站/入站/总计/最大值) 298 | - 统计周期(月/季/年) 299 | - 周期起始日 300 | - 限制模式(TC限速/阻断) 301 | - 限速值(仅TC模式) 302 | 303 | ### 相关命令 304 | 305 | #### 查看所有端口流量(推荐) 306 | ```bash 307 | # 普通查看 308 | sudo bash /root/TrafficCop/view_port_traffic.sh 309 | 310 | # 实时监控(每5秒刷新) 311 | sudo bash /root/TrafficCop/view_port_traffic.sh --realtime 312 | 313 | # 导出JSON报告 314 | sudo bash /root/TrafficCop/view_port_traffic.sh --export 315 | ``` 316 | 317 | #### 管理端口配置 318 | ```bash 319 | # 打开交互式配置菜单 320 | sudo bash /root/TrafficCop/port_traffic_limit.sh 321 | 322 | # 删除特定端口 323 | sudo bash /root/TrafficCop/port_traffic_limit.sh --remove 80 324 | 325 | # 删除所有端口配置 326 | sudo bash /root/TrafficCop/port_traffic_limit.sh --remove 327 | ``` 328 | 329 | #### 通过管理器访问(推荐) 330 | ```bash 331 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 332 | # 选择 12) 查看端口流量 333 | # 选择 13) 管理端口配置 334 | ``` 335 | 336 | #### 查看配置文件 337 | ```bash 338 | # 查看JSON配置(多端口) 339 | sudo cat /root/TrafficCop/ports_traffic_config.json 340 | 341 | # 美化输出 342 | sudo cat /root/TrafficCop/ports_traffic_config.json | jq 343 | ``` 344 | 345 | ### 使用示例 346 | 347 | #### 场景:为多个服务配置独立流量限制 348 | 349 | 假设您的机器有1TB的总流量限制,您想为不同服务设置独立的流量配额: 350 | 351 | **配置多个端口:** 352 | 1. 运行端口配置脚本 353 | 2. 添加端口80(Web服务):200GB限制,10GB容错 354 | 3. 添加端口443(HTTPS):300GB限制,15GB容错 355 | 4. 添加端口22(SSH):50GB限制,5GB容错 356 | 357 | **实时查看所有端口流量:** 358 | ```bash 359 | sudo bash /root/TrafficCop/view_port_traffic.sh 360 | ``` 361 | 362 | 输出示例: 363 | ``` 364 | ════════════════════════════════════════════════════════════ 365 | 端口流量监控 - 2025-10-18 15:30:45 366 | ════════════════════════════════════════════════════════════ 367 | 368 | ✅ 端口 80 (Web Server) 369 | 已用: 150.2 GB / 200 GB (75.1%) 370 | [████████████████████████ ] 限速: 20kbit/s 371 | 372 | 🟡 端口 443 (HTTPS) 373 | 已用: 280.5 GB / 300 GB (93.5%) 374 | [████████████████████████████ ] 限速: 50kbit/s 375 | 376 | ✅ 端口 22 (SSH) 377 | 已用: 15.3 GB / 50 GB (30.6%) 378 | [█████████ ] 限速: 10kbit/s 379 | 380 | ════════════════════════════════════════════════════════════ 381 | 总计: 3个端口 | 总用量: 446.0 GB | 总限制: 550 GB 382 | ════════════════════════════════════════════════════════════ 383 | ``` 384 | 385 | **推送通知示例(Telegram):** 386 | ``` 387 | 📊 [MyServer]每日流量报告 388 | 389 | 🖥️ 机器总流量: 390 | 当前使用:650.5 GB 391 | 流量限制:1000 GB 392 | 393 | 🔌 端口流量详情: 394 | ✅ 端口 80 (Web Server):150.2GB / 200GB 395 | 🟡 端口 443 (HTTPS):280.5GB / 300GB 396 | ✅ 端口 22 (SSH):15.3GB / 50GB 397 | ``` 398 | 399 | 当某个端口流量达到限制时: 400 | - **TC模式**:端口速度将被限制到设定值(如20kbit/s) 401 | - **阻断模式**:端口将被完全阻断,无法接收或发送数据 402 | - **自动通知**:所有配置的推送服务会发送警告通知 403 | 404 | ### 技术原理 405 | 406 | 端口流量限制功能使用以下技术实现: 407 | 408 | 1. **iptables** - 创建规则统计特定端口的流量 409 | 2. **tc (Traffic Control)** - 实现端口级别的流量控制和限速 410 | 3. **HTB (Hierarchical Token Bucket)** - 分层流量控制,为不同端口分配不同的带宽 411 | 4. **Packet Marking** - 使用mangle表标记数据包,实现精确的流量分类 412 | 413 | ### 配置文件格式(JSON) 414 | 415 | ```json 416 | { 417 | "ports": [ 418 | { 419 | "port": 80, 420 | "description": "Web Server", 421 | "traffic_limit": 200, 422 | "traffic_tolerance": 10, 423 | "traffic_mode": "total", 424 | "traffic_period": "monthly", 425 | "period_start_day": 1, 426 | "limit_speed": 20, 427 | "main_interface": "eth0", 428 | "limit_mode": "tc", 429 | "created_at": "2025-10-18 12:00:00", 430 | "last_reset": "2025-10-01" 431 | } 432 | ] 433 | } 434 | ``` 435 | 436 | ### 注意事项 437 | 438 | 1. **依赖要求**:需要jq工具(脚本会自动安装)和iptables 439 | 2. **流量统计**:端口流量统计从配置时开始,不包含历史流量 440 | 3. **先决条件**:建议先运行主流量监控脚本,以确保依赖已安装 441 | 4. **性能影响**:TC模式可能对端口性能有轻微影响 442 | 5. **谨慎使用**:阻断模式会完全禁止端口通信,请谨慎使用 443 | 6. **多端口支持**:✅ 2.0版本已支持同时配置和管理多个端口 444 | 7. **推送集成**:端口流量信息已集成到所有推送通知中 445 | 446 | ## 预设配置 447 | ### 阿里云CDT 200G: 448 | ``` 449 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-200g && cat /root/TrafficCop/traffic_monitor_config.txt 450 | ``` 451 | ### 阿里云CDT 20G: 452 | ``` 453 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-20g && cat /root/TrafficCop/traffic_monitor_config.txt 454 | ``` 455 | ### 阿里云轻量 1T: 456 | ``` 457 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-1T && cat /root/TrafficCop/traffic_monitor_config.txt 458 | ``` 459 | ### azure学生 15G: 460 | ``` 461 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/az-15g && cat /root/TrafficCop/traffic_monitor_config.txt 462 | ``` 463 | ### azure学生 115G: 464 | ``` 465 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/az-115g && cat /root/TrafficCop/traffic_monitor_config.txt 466 | ``` 467 | 468 | ### GCP 625G[大流量极致解法](https://www.nodeseek.com/post-115166-1): 469 | ``` 470 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/GCP-625g && cat /root/TrafficCop/traffic_monitor_config.txt 471 | ``` 472 | ### GCP 200G(白嫖标准路由200g流量就跑): 473 | ``` 474 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/GCP-200g && cat /root/TrafficCop/traffic_monitor_config.txt 475 | ``` 476 | ### alice 1500G: 477 | ``` 478 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/alice-1500g && cat /root/TrafficCop/traffic_monitor_config.txt 479 | ``` 480 | ### 亚洲云 300G: 481 | ``` 482 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/asia-300g && cat /root/TrafficCop/traffic_monitor_config.txt 483 | ``` 484 | ## Star History 485 | 486 | [![Star History Chart](https://api.star-history.com/svg?repos=ypq123456789/TrafficCop&type=Date)](https://star-history.com/#ypq123456789/TrafficCop&Date) 487 | 488 | ## 交流TG群: 489 | https://t.me/+ydvXl1_OBBBiZWM1 490 | 491 | ## 支持作者 492 | 非常感谢您对本项目的兴趣!维护开源项目确实需要大量时间和精力投入。若您认为这个项目为您带来了价值,希望您能考虑给予一些支持,哪怕只是一杯咖啡的费用。 493 | 您的慷慨相助将激励我继续完善这个项目,使其更加实用。它还能让我更专心地参与开源社区的工作。如果您愿意提供赞助,可通过下列渠道: 494 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 |
微信支付宝
微信支付宝
-------------------------------------------------------------------------------- /serverchan_notifier.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 设置工作目录 4 | WORK_DIR="/root/TrafficCop" 5 | mkdir -p "$WORK_DIR" 6 | 7 | # 导入端口流量辅助函数 8 | if [ -f "$WORK_DIR/port_traffic_helper.sh" ]; then 9 | source "$WORK_DIR/port_traffic_helper.sh" 10 | fi 11 | 12 | # 更新文件路径 13 | CONFIG_FILE="$WORK_DIR/serverchan_notifier_config.txt" 14 | LOG_FILE="$WORK_DIR/traffic_monitor.log" 15 | LAST_NOTIFICATION_FILE="$WORK_DIR/last_serverchan_notification" 16 | SCRIPT_PATH="$WORK_DIR/serverchan_notifier.sh" 17 | CRON_LOG="$WORK_DIR/serverchan_notifier_cron.log" 18 | 19 | # 切换到工作目录 20 | cd "$WORK_DIR" || exit 1 21 | 22 | # 设置时区为上海(东八区) 23 | export TZ='Asia/Shanghai' 24 | 25 | echo "----------------------------------------------"| tee -a "$CRON_LOG" 26 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 版本号:1.0" 27 | 28 | # 检查是否有同名的 crontab 正在执行: 29 | check_running() { 30 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查是否有其他实例运行" >> "$CRON_LOG" 31 | if pidof -x "$(basename "\$0")" -o $$ > /dev/null; then 32 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 另一个脚本实例正在运行,退出脚本" >> "$CRON_LOG" 33 | echo "另一个脚本实例正在运行,退出脚本" 34 | exit 1 35 | fi 36 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有其他实例运行,继续执行" >> "$CRON_LOG" 37 | } 38 | 39 | # 读取配置 40 | read_config() { 41 | if [ ! -f "$CONFIG_FILE" ] || [ ! -s "$CONFIG_FILE" ]; then 42 | echo "配置文件不存在或为空,需要进行初始化配置。" 43 | return 1 44 | fi 45 | 46 | # 读取配置文件 47 | source "$CONFIG_FILE" 48 | 49 | # 检查必要的配置项是否都存在 50 | if [ -z "$SENDKEY" ] || [ -z "$MACHINE_NAME" ] || [ -z "$DAILY_REPORT_TIME" ]; then 51 | echo "配置文件不完整,需要重新进行配置。" 52 | return 1 53 | fi 54 | 55 | return 0 56 | } 57 | 58 | # 写入配置 59 | write_config() { 60 | cat > "$CONFIG_FILE" << EOF 61 | SENDKEY="$SENDKEY" 62 | DAILY_REPORT_TIME="$DAILY_REPORT_TIME" 63 | MACHINE_NAME="$MACHINE_NAME" 64 | EOF 65 | echo "配置已保存到 $CONFIG_FILE" 66 | } 67 | 68 | # 初始配置 69 | initial_config() { 70 | echo "======================================" 71 | echo " 修改 Server酱 通知配置" 72 | echo "======================================" 73 | echo "" 74 | echo "提示:按 Enter 保留当前配置,输入新值则更新配置" 75 | echo "" 76 | 77 | local new_sendkey new_machine_name new_daily_report_time 78 | 79 | # SendKey 80 | if [ -n "$SENDKEY" ]; then 81 | local key_display="${SENDKEY:0:10}...${SENDKEY: -4}" 82 | echo "请输入Server酱发送密钥 [当前: $key_display]: " 83 | else 84 | echo "请输入Server酱发送密钥 (SendKey): " 85 | fi 86 | read -r new_sendkey 87 | if [[ -z "$new_sendkey" ]] && [[ -n "$SENDKEY" ]]; then 88 | new_sendkey="$SENDKEY" 89 | echo " → 保留原配置" 90 | fi 91 | while [[ -z "$new_sendkey" ]]; do 92 | echo "SendKey不能为空。请重新输入: " 93 | read -r new_sendkey 94 | done 95 | 96 | # 机器名称 97 | if [ -n "$MACHINE_NAME" ]; then 98 | echo "请输入机器名称 [当前: $MACHINE_NAME]: " 99 | else 100 | echo "请输入机器名称: " 101 | fi 102 | read -r new_machine_name 103 | if [[ -z "$new_machine_name" ]] && [[ -n "$MACHINE_NAME" ]]; then 104 | new_machine_name="$MACHINE_NAME" 105 | echo " → 保留原配置" 106 | fi 107 | while [[ -z "$new_machine_name" ]]; do 108 | echo "机器名称不能为空。请重新输入: " 109 | read -r new_machine_name 110 | done 111 | 112 | # 每日报告时间 113 | if [ -n "$DAILY_REPORT_TIME" ]; then 114 | echo "请输入每日报告时间 [当前: $DAILY_REPORT_TIME,格式 HH:MM]: " 115 | else 116 | echo "请输入每日报告时间 (时区已经固定为东八区,输入格式为 HH:MM,例如 01:00): " 117 | fi 118 | read -r new_daily_report_time 119 | if [[ -z "$new_daily_report_time" ]] && [[ -n "$DAILY_REPORT_TIME" ]]; then 120 | new_daily_report_time="$DAILY_REPORT_TIME" 121 | echo " → 保留原配置" 122 | fi 123 | while [[ ! $new_daily_report_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; do 124 | echo "时间格式不正确。请重新输入 (HH:MM): " 125 | read -r new_daily_report_time 126 | done 127 | 128 | # 更新配置文件(使用write_config函数确保格式正确) 129 | SENDKEY="$new_sendkey" 130 | MACHINE_NAME="$new_machine_name" 131 | DAILY_REPORT_TIME="$new_daily_report_time" 132 | 133 | write_config 134 | 135 | echo "" 136 | echo "======================================" 137 | echo "配置已更新成功!" 138 | echo "======================================" 139 | echo "" 140 | read_config 141 | } 142 | 143 | # 发送限速警告 144 | send_throttle_warning() { 145 | local url="https://sctapi.ftqq.com/${SENDKEY}.send" 146 | local title="⚠️ [${MACHINE_NAME}]限速警告" 147 | local desp="流量已达到限制,已启动 TC 模式限速。" 148 | 149 | # 添加端口流量摘要 150 | if command -v get_port_traffic_summary &> /dev/null; then 151 | local port_summary=$(get_port_traffic_summary 3) 152 | if [ -n "$port_summary" ]; then 153 | desp="${desp}%0A%0A${port_summary}" 154 | fi 155 | fi 156 | 157 | curl -s -X POST "$url" -d "title=$title" -d "desp=$desp" 158 | } 159 | 160 | # 发送限速解除通知 161 | send_throttle_lifted() { 162 | local url="https://sctapi.ftqq.com/${SENDKEY}.send" 163 | local title="✅ [${MACHINE_NAME}]限速解除" 164 | local desp="流量已恢复正常,所有限制已清除。" 165 | 166 | # 添加端口流量摘要 167 | if command -v get_port_traffic_summary &> /dev/null; then 168 | local port_summary=$(get_port_traffic_summary 3) 169 | if [ -n "$port_summary" ]; then 170 | desp="${desp}%0A%0A${port_summary}" 171 | fi 172 | fi 173 | 174 | curl -s -X POST "$url" -d "title=$title" -d "desp=$desp" 175 | } 176 | 177 | # 发送新周期开始通知 178 | send_new_cycle_notification() { 179 | local url="https://sctapi.ftqq.com/${SENDKEY}.send" 180 | local title="🔄 [${MACHINE_NAME}]新周期开始" 181 | local desp="新的流量统计周期已开始,之前的限速(如果有)已自动解除。" 182 | curl -s -X POST "$url" -d "title=$title" -d "desp=$desp" 183 | } 184 | 185 | # 发送关机警告 186 | send_shutdown_warning() { 187 | local url="https://sctapi.ftqq.com/${SENDKEY}.send" 188 | local title="🚨 [${MACHINE_NAME}]关机警告" 189 | local desp="流量已达到严重限制,系统将在 1 分钟后关机!" 190 | 191 | # 添加端口流量摘要 192 | if command -v get_port_traffic_summary &> /dev/null; then 193 | local port_summary=$(get_port_traffic_summary 3) 194 | if [ -n "$port_summary" ]; then 195 | desp="${desp}%0A%0A${port_summary}" 196 | fi 197 | fi 198 | 199 | curl -s -X POST "$url" -d "title=$title" -d "desp=$desp" 200 | } 201 | 202 | # 测试 Server酱 通知 203 | test_serverchan_notification() { 204 | local title="🔔 [${MACHINE_NAME}]测试消息" 205 | local desp="如果您收到这条消息,说明Server酱通知功能正常工作。" 206 | local response 207 | response=$(curl -s -X POST "https://sctapi.ftqq.com/${SENDKEY}.send" \ 208 | -d "title=${title}" \ 209 | -d "desp=${desp}") 210 | 211 | if echo "$response" | grep -q '"code":0'; then 212 | echo "✅ [${MACHINE_NAME}]测试消息已成功发送,请检查您的微信。" 213 | else 214 | echo "❌ [${MACHINE_NAME}]发送测试消息失败。请检查您的SENDKEY设置。" 215 | fi 216 | } 217 | 218 | check_and_notify() { 219 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查流量状态..."| tee -a "$CRON_LOG" 220 | 221 | local current_status="未知" 222 | local current_time=$(date '+%Y-%m-%d %H:%M:%S') 223 | local relevant_log="" 224 | 225 | # 从后往前读取日志文件,找到第一个包含相关信息的行 226 | relevant_log=$(tac "$LOG_FILE" | grep -m 1 -E "流量超出限制|使用 TC 模式限速|新的流量周期开始|流量正常,清除所有限制") 227 | 228 | # 记录相关的日志内容 229 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 相关的日志内容: $relevant_log"| tee -a "$CRON_LOG" 230 | 231 | # 确定当前状态 232 | if echo "$relevant_log" | grep -q "流量超出限制,系统将在 1 分钟后关机"; then 233 | current_status="关机" 234 | elif echo "$relevant_log" | grep -q "流量超出限制"; then 235 | current_status="限速" 236 | elif echo "$relevant_log" | grep -q "新的流量周期开始,重置限制"; then 237 | current_status="新周期" 238 | elif echo "$relevant_log" | grep -q "流量正常,清除所有限制"; then 239 | current_status="正常" 240 | fi 241 | 242 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前检测到的状态: $current_status"| tee -a "$CRON_LOG" 243 | 244 | local last_status="" 245 | if [ -f "$LAST_NOTIFICATION_FILE" ]; then 246 | last_status=$(tail -n 1 "$LAST_NOTIFICATION_FILE" | cut -d' ' -f3-) 247 | fi 248 | 249 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 上次记录的状态: $last_status"| tee -a "$CRON_LOG" 250 | 251 | # 根据状态调用相应的通知函数 252 | if [ "$current_status" = "限速" ] && [ "$last_status" != "限速" ]; then 253 | send_throttle_warning 254 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_warning"| tee -a "$CRON_LOG" 255 | elif [ "$current_status" = "正常" ] && [ "$last_status" = "限速" ]; then 256 | send_throttle_lifted 257 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_lifted"| tee -a "$CRON_LOG" 258 | elif [ "$current_status" = "新周期" ]; then 259 | send_new_cycle_notification 260 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_new_cycle_notification"| tee -a "$CRON_LOG" 261 | elif [ "$current_status" = "关机" ]; then 262 | send_shutdown_warning 263 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_shutdown_warning"| tee -a "$CRON_LOG" 264 | elif [ "$current_status" = "未知" ]; then 265 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法识别当前状态,不发送通知"| tee -a "$CRON_LOG" 266 | else 267 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无需发送通知"| tee -a "$CRON_LOG" 268 | fi 269 | 270 | # 追加新状态到状态文件 271 | echo "$current_time $current_status" >> "$LAST_NOTIFICATION_FILE" 272 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已追加新状态到状态文件"| tee -a "$CRON_LOG" 273 | 274 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 流量检查完成。"| tee -a "$CRON_LOG" 275 | } 276 | 277 | # 设置定时任务 278 | setup_cron() { 279 | local correct_entry="* * * * * $SCRIPT_PATH -cron" 280 | local current_crontab=$(crontab -l 2>/dev/null) 281 | local serverchan_notifier_entries=$(echo "$current_crontab" | grep "serverchan_notifier.sh") 282 | local correct_entries_count=$(echo "$serverchan_notifier_entries" | grep -F "$correct_entry" | wc -l) 283 | 284 | if [ "$correct_entries_count" -eq 1 ]; then 285 | echo "正确的 crontab 项已存在且只有一个,无需修改。" 286 | else 287 | # 删除所有包含 serverchan_notifier.sh 的条目 288 | new_crontab=$(echo "$current_crontab" | grep -v "serverchan_notifier.sh") 289 | 290 | # 添加一个正确的条目 291 | new_crontab="${new_crontab} 292 | $correct_entry" 293 | 294 | # 更新 crontab 295 | echo "$new_crontab" | crontab - 296 | 297 | echo "已更新 crontab。删除了所有旧的 serverchan_notifier.sh 条目,并添加了一个每分钟执行的条目。" 298 | fi 299 | 300 | # 显示当前的 crontab 内容 301 | echo "当前的 crontab 内容:" 302 | crontab -l 303 | } 304 | 305 | # 每日报告 306 | daily_report() { 307 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始生成每日报告"| tee -a "$CRON_LOG" 308 | echo "$(date '+%Y-%m-%d %H:%M:%S') : DAILY_REPORT_TIME=$DAILY_REPORT_TIME"| tee -a "$CRON_LOG" 309 | echo "$(date '+%Y-%m-%d %H:%M:%S') : SENDKEY=${SENDKEY:0:5}..."| tee -a "$CRON_LOG" 310 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 日志文件路径: $LOG_FILE"| tee -a "$CRON_LOG" 311 | 312 | # 反向读取日志文件,查找第一个包含"当前使用流量"和"限制流量"的行 313 | local usage_line=$(tac "$LOG_FILE" | grep -m 1 -E "当前使用流量:.*限制流量:") 314 | 315 | if [[ -z "$usage_line" ]]; then 316 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法在日志中找到同时包含当前使用流量和限制流量的行"| tee -a "$CRON_LOG" 317 | return 1 318 | fi 319 | 320 | local current_usage=$(echo "$usage_line" | grep -oP '当前使用流量:\s*\K[0-9.]+ [GBMKgbmk]+') 321 | local limit=$(echo "$usage_line" | grep -oP '限制流量:\s*\K[0-9.]+ [GBMKgbmk]+') 322 | 323 | if [[ -z "$current_usage" || -z "$limit" ]]; then 324 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法从行中提取流量信息"| tee -a "$CRON_LOG" 325 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 问题行: $usage_line"| tee -a "$CRON_LOG" 326 | return 1 327 | fi 328 | 329 | local title="📊 [${MACHINE_NAME}]每日流量报告" 330 | local desp="## 📊 每日流量报告%0A%0A### 🖥️ 机器总流量%0A- **当前使用**:$current_usage%0A- **流量限制**:$limit" 331 | 332 | # 检查是否有端口流量配置 333 | local ports_config_file="$WORK_DIR/ports_traffic_config.json" 334 | local view_script="$WORK_DIR/view_port_traffic.sh" 335 | 336 | if [ -f "$ports_config_file" ]; then 337 | local port_count=$(jq -r '.ports | length' "$ports_config_file" 2>/dev/null || echo "0") 338 | 339 | if [ "$port_count" -gt 0 ]; then 340 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到 $port_count 个端口流量配置,添加端口信息"| tee -a "$CRON_LOG" 341 | 342 | # 如果有 view_port_traffic.sh 脚本,使用它 343 | if [ -f "$view_script" ]; then 344 | local port_data=$(bash "$view_script" --json 2>/dev/null) 345 | else 346 | local port_data="" 347 | fi 348 | 349 | if [ -n "$port_data" ] && echo "$port_data" | jq -e '.ports' >/dev/null 2>&1; then 350 | local actual_port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null || echo "0") 351 | 352 | if [ "$actual_port_count" -gt 0 ]; then 353 | desp="${desp}%0A%0A### 🔌 端口流量详情%0A%0A| 端口 | 描述 | 使用/限制 | 百分比 | 状态 |%0A|------|------|-----------|--------|------|" 354 | 355 | # 遍历每个端口 356 | local i=0 357 | while [ $i -lt $actual_port_count ]; do 358 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 359 | local port_desc=$(echo "$port_data" | jq -r ".ports[$i].description" 2>/dev/null) 360 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 361 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 362 | 363 | if [ -n "$port" ] && [ "$port" != "null" ] && [ "$port_usage" != "null" ]; then 364 | # 格式化流量显示(保留2位小数) 365 | local port_usage_formatted=$(printf "%.2f" "$port_usage" 2>/dev/null || echo "$port_usage") 366 | local port_limit_formatted=$(printf "%.2f" "$port_limit" 2>/dev/null || echo "$port_limit") 367 | 368 | # 计算使用百分比 369 | local port_percentage=0 370 | if [ -n "$port_limit" ] && [ "$port_limit" != "null" ] && (( $(echo "$port_limit > 0" | bc -l 2>/dev/null || echo "0") )); then 371 | port_percentage=$(printf "%.2f" $(echo "scale=2; ($port_usage / $port_limit) * 100" | bc 2>/dev/null || echo "0")) 372 | fi 373 | 374 | # 根据使用率选择状态 375 | local status="✅ 正常" 376 | if (( $(echo "$port_percentage >= 90" | bc -l 2>/dev/null || echo "0") )); then 377 | status="🔴 告警" 378 | elif (( $(echo "$port_percentage >= 75" | bc -l 2>/dev/null || echo "0") )); then 379 | status="🟡 警告" 380 | fi 381 | 382 | desp="${desp}%0A| $port | $port_desc | ${port_usage_formatted}GB / ${port_limit_formatted}GB | ${port_percentage}%% | $status |" 383 | fi 384 | 385 | i=$((i + 1)) 386 | done 387 | 388 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已添加 $actual_port_count 个端口的流量信息"| tee -a "$CRON_LOG" 389 | else 390 | echo "$(date '+%Y-%m-%d %H:%M:%S') : JSON数据中没有端口信息"| tee -a "$CRON_LOG" 391 | fi 392 | else 393 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法获取有效的端口流量JSON数据"| tee -a "$CRON_LOG" 394 | fi 395 | else 396 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有配置端口流量监控"| tee -a "$CRON_LOG" 397 | fi 398 | else 399 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 端口配置文件不存在"| tee -a "$CRON_LOG" 400 | fi 401 | 402 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 准备发送消息: $title $desp"| tee -a "$CRON_LOG" 403 | 404 | local url="https://sctapi.ftqq.com/${SENDKEY}.send" 405 | local response 406 | 407 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 尝试发送Server酱消息"| tee -a "$CRON_LOG" 408 | 409 | response=$(curl -s -X POST "$url" -d "title=$title" -d "desp=$desp") 410 | 411 | if echo "$response" | grep -q '"code":0'; then 412 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功"| tee -a "$CRON_LOG" 413 | return 0 414 | else 415 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败. 响应: $response"| tee -a "$CRON_LOG" 416 | return 1 417 | fi 418 | } 419 | 420 | # 主任务 421 | main() { 422 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 进入主任务" >> "$CRON_LOG" 423 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 参数数量: $#" >> "$CRON_LOG" 424 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 所有参数: $@" >> "$CRON_LOG" 425 | 426 | check_running 427 | 428 | if [[ "$*" == *"-cron"* ]]; then 429 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到-cron参数, 进入cron模式" >> "$CRON_LOG" 430 | if read_config; then 431 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 成功读取配置文件" >> "$CRON_LOG" 432 | check_and_notify "false" 433 | 434 | # 检查是否需要发送每日报告 435 | current_time=$(TZ='Asia/Shanghai' date +%H:%M) 436 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间: $current_time, 设定的报告时间: $DAILY_REPORT_TIME" >> "$CRON_LOG" 437 | if [ "$current_time" == "$DAILY_REPORT_TIME" ]; then 438 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 时间匹配,准备发送每日报告" >> "$CRON_LOG" 439 | if daily_report; then 440 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功" >> "$CRON_LOG" 441 | else 442 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败" >> "$CRON_LOG" 443 | fi 444 | else 445 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间与报告时间不匹配,不发送报告" >> "$CRON_LOG" 446 | fi 447 | else 448 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 配置文件不存在或不完整,跳过检查" >> "$CRON_LOG" 449 | exit 1 450 | fi 451 | else 452 | # 交互模式 453 | echo "进入交互模式" 454 | if ! read_config; then 455 | echo "需要进行初始化配置。" 456 | initial_config 457 | fi 458 | 459 | setup_cron 460 | 461 | # 直接显示当前配置摘要 462 | echo "当前配置摘要:" 463 | echo "机器名称: $MACHINE_NAME" 464 | echo "每日报告时间: $DAILY_REPORT_TIME" 465 | echo "SENDKEY: ${SENDKEY:0:10}..." # 只显示前10个字符 466 | 467 | echo "脚本正在运行中。按 'q' 退出,按 'c' 检查流量,按 'd' 手动发送每日报告,按 'r' 重新加载配置,按 't' 发送测试消息,按 'm' 修改配置,按 'h' 修改每日报告时间。" 468 | while true; do 469 | read -n 1 -t 1 input 470 | if [ -n "$input" ]; then 471 | echo 472 | case $input in 473 | q|Q) 474 | echo "退出脚本。" 475 | exit 0 476 | ;; 477 | c|C) 478 | check_and_notify 479 | ;; 480 | d|D) 481 | daily_report 482 | ;; 483 | r|R) 484 | read_config 485 | echo "配置已重新加载。" 486 | ;; 487 | t|T) 488 | test_serverchan_notification 489 | ;; 490 | m|M) 491 | initial_config 492 | ;; 493 | h|H) 494 | echo "请输入新的每日报告时间 (HH:MM): " 495 | read -r new_time 496 | if [[ $new_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then 497 | sed -i "s/DAILY_REPORT_TIME=.*/DAILY_REPORT_TIME=$new_time/" "$CONFIG_FILE" 498 | echo "每日报告时间已更新为 $new_time" 499 | else 500 | echo "无效的时间格式。未更改。" 501 | fi 502 | ;; 503 | *) 504 | echo "无效的输入: $input" 505 | ;; 506 | esac 507 | 508 | echo "脚本正在运行中。按 'q' 退出,按 'c' 检查流量,按 'd' 手动发送每日报告,按 'r' 重新加载配置,按 't' 发送测试消息,按 'm' 修改配置,按 'h' 修改每日报告时间。" 509 | fi 510 | done 511 | fi 512 | } 513 | 514 | # 执行主函数 515 | main "$@" 516 | echo "----------------------------------------------"| tee -a "$CRON_LOG" -------------------------------------------------------------------------------- /pushplus_notifier.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 设置新的工作目录 4 | WORK_DIR="/root/TrafficCop" 5 | mkdir -p "$WORK_DIR" 6 | 7 | # 导入端口流量辅助函数 8 | if [ -f "$WORK_DIR/port_traffic_helper.sh" ]; then 9 | source "$WORK_DIR/port_traffic_helper.sh" 10 | fi 11 | 12 | # 更新文件路径 13 | CONFIG_FILE="$WORK_DIR/pushplus_notifier_config.txt" 14 | LOG_FILE="$WORK_DIR/traffic_monitor.log" 15 | LAST_NOTIFICATION_FILE="$WORK_DIR/last_pushplus_notification" 16 | SCRIPT_PATH="$WORK_DIR/pushplus_notifier.sh" 17 | CRON_LOG="$WORK_DIR/pushplus_notifier_cron.log" 18 | 19 | # 切换到工作目录 20 | cd "$WORK_DIR" || exit 1 21 | 22 | # 设置时区为上海(东八区) 23 | export TZ='Asia/Shanghai' 24 | 25 | echo "----------------------------------------------"| tee -a "$CRON_LOG" 26 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 版本号:1.9" 27 | 28 | # 检查是否有同名的 crontab 正在执行: 29 | check_running() { 30 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查是否有其他实例运行" >> "$CRON_LOG" 31 | if pidof -x "$(basename "\$0")" -o $$ > /dev/null; then 32 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 另一个脚本实例正在运行,退出脚本" >> "$CRON_LOG" 33 | echo "另一个脚本实例正在运行,退出脚本" 34 | exit 1 35 | fi 36 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有其他实例运行,继续执行" >> "$CRON_LOG" 37 | } 38 | 39 | 40 | # 函数:获取非空输入 41 | get_valid_input() { 42 | local prompt="${1:-"请输入:"}" 43 | local input="" 44 | while true; do 45 | read -p "${prompt}" input 46 | if [[ -n "${input}" ]]; then 47 | echo "${input}" 48 | return 49 | else 50 | echo "输入不能为空,请重新输入。" 51 | fi 52 | done 53 | } 54 | 55 | # 读取配置 56 | read_config() { 57 | if [ ! -f "$CONFIG_FILE" ] || [ ! -s "$CONFIG_FILE" ]; then 58 | echo "配置文件不存在或为空,需要进行初始化配置。" 59 | return 1 60 | fi 61 | 62 | # 读取配置文件 63 | source "$CONFIG_FILE" 64 | 65 | # 检查必要的配置项是否都存在 66 | if [ -z "$PUSHPLUS_TOKEN" ] || [ -z "$MACHINE_NAME" ] || [ -z "$DAILY_REPORT_TIME" ]; then 67 | echo "配置文件不完整,需要重新进行配置。" 68 | return 1 69 | fi 70 | 71 | return 0 72 | } 73 | 74 | # 写入配置 75 | write_config() { 76 | cat > "$CONFIG_FILE" << EOF 77 | PUSHPLUS_TOKEN="$PUSHPLUS_TOKEN" 78 | DAILY_REPORT_TIME="$DAILY_REPORT_TIME" 79 | MACHINE_NAME="$MACHINE_NAME" 80 | EOF 81 | echo "配置已保存到 $CONFIG_FILE" 82 | } 83 | 84 | # 初始配置 85 | initial_config() { 86 | echo "======================================" 87 | echo " 修改 PushPlus 通知配置" 88 | echo "======================================" 89 | echo "" 90 | echo "提示:按 Enter 保留当前配置,输入新值则更新配置" 91 | echo "" 92 | 93 | local new_token new_machine_name new_daily_report_time 94 | 95 | # PushPlus Token 96 | if [ -n "$PUSHPLUS_TOKEN" ]; then 97 | local token_display="${PUSHPLUS_TOKEN:0:10}...${PUSHPLUS_TOKEN: -4}" 98 | echo "请输入PushPlus Token [当前: $token_display]: " 99 | else 100 | echo "请输入PushPlus Token: " 101 | fi 102 | read -r new_token 103 | if [[ -z "$new_token" ]] && [[ -n "$PUSHPLUS_TOKEN" ]]; then 104 | new_token="$PUSHPLUS_TOKEN" 105 | echo " → 保留原配置" 106 | fi 107 | while [[ -z "$new_token" ]]; do 108 | echo "PushPlus Token 不能为空。请重新输入: " 109 | read -r new_token 110 | done 111 | 112 | # 机器名称 113 | if [ -n "$MACHINE_NAME" ]; then 114 | echo "请输入机器名称 [当前: $MACHINE_NAME]: " 115 | else 116 | echo "请输入机器名称: " 117 | fi 118 | read -r new_machine_name 119 | if [[ -z "$new_machine_name" ]] && [[ -n "$MACHINE_NAME" ]]; then 120 | new_machine_name="$MACHINE_NAME" 121 | echo " → 保留原配置" 122 | fi 123 | while [[ -z "$new_machine_name" ]]; do 124 | echo "机器名称不能为空。请重新输入: " 125 | read -r new_machine_name 126 | done 127 | 128 | # 每日报告时间 129 | if [ -n "$DAILY_REPORT_TIME" ]; then 130 | echo "请输入每日报告时间 [当前: $DAILY_REPORT_TIME,格式 HH:MM]: " 131 | else 132 | echo "请输入每日报告时间 (时区已经固定为东八区,输入格式为 HH:MM,例如 01:00): " 133 | fi 134 | read -r new_daily_report_time 135 | if [[ -z "$new_daily_report_time" ]] && [[ -n "$DAILY_REPORT_TIME" ]]; then 136 | new_daily_report_time="$DAILY_REPORT_TIME" 137 | echo " → 保留原配置" 138 | fi 139 | while [[ ! $new_daily_report_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; do 140 | echo "时间格式不正确。请重新输入 (HH:MM): " 141 | read -r new_daily_report_time 142 | done 143 | 144 | # 更新配置文件(使用引号防止空格等特殊字符问题) 145 | PUSHPLUS_TOKEN="$new_token" 146 | MACHINE_NAME="$new_machine_name" 147 | DAILY_REPORT_TIME="$new_daily_report_time" 148 | 149 | write_config 150 | 151 | echo "" 152 | echo "======================================" 153 | echo "配置已更新成功!" 154 | echo "======================================" 155 | echo "" 156 | read_config 157 | 158 | } 159 | 160 | 161 | # 发送限速警告 162 | send_throttle_warning() { 163 | local title="⚠️ [${MACHINE_NAME}]限速警告" 164 | local content="流量已达到限制,已启动 TC 模式限速。" 165 | 166 | # 添加端口流量摘要 167 | if command -v get_port_traffic_summary &> /dev/null; then 168 | local port_summary=$(get_port_traffic_summary 3) 169 | if [ -n "$port_summary" ]; then 170 | content="${content}

${port_summary}" 171 | fi 172 | fi 173 | 174 | local url="http://www.pushplus.plus/send" 175 | local response 176 | 177 | response=$(curl -s -X POST "$url" \ 178 | -H "Content-Type: application/json" \ 179 | -d "{ 180 | \"token\": \"$PUSHPLUS_TOKEN\", 181 | \"title\": \"$title\", 182 | \"content\": \"$content\", 183 | \"template\": \"html\" 184 | }") 185 | 186 | if echo "$response" | grep -q '"code":200'; then 187 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送成功"| tee -a "$CRON_LOG" 188 | return 0 189 | else 190 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送失败. 响应: $response"| tee -a "$CRON_LOG" 191 | return 1 192 | fi 193 | } 194 | 195 | # 发送限速解除通知 196 | send_throttle_lifted() { 197 | local title="✅ [${MACHINE_NAME}]限速解除" 198 | local content="流量已恢复正常,所有限制已清除。" 199 | 200 | # 添加端口流量摘要 201 | if command -v get_port_traffic_summary &> /dev/null; then 202 | local port_summary=$(get_port_traffic_summary 3) 203 | if [ -n "$port_summary" ]; then 204 | content="${content}

${port_summary}" 205 | fi 206 | fi 207 | 208 | local url="http://www.pushplus.plus/send" 209 | local response 210 | 211 | response=$(curl -s -X POST "$url" \ 212 | -H "Content-Type: application/json" \ 213 | -d "{ 214 | \"token\": \"$PUSHPLUS_TOKEN\", 215 | \"title\": \"$title\", 216 | \"content\": \"$content\", 217 | \"template\": \"html\" 218 | }") 219 | 220 | if echo "$response" | grep -q '"code":200'; then 221 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送成功"| tee -a "$CRON_LOG" 222 | return 0 223 | else 224 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送失败. 响应: $response"| tee -a "$CRON_LOG" 225 | return 1 226 | fi 227 | } 228 | 229 | # 发送新周期开始通知 230 | send_new_cycle_notification() { 231 | local title="🔄 [${MACHINE_NAME}]新周期开始" 232 | local content="新的流量统计周期已开始,之前的限速(如果有)已自动解除。" 233 | local url="http://www.pushplus.plus/send" 234 | local response 235 | 236 | response=$(curl -s -X POST "$url" \ 237 | -H "Content-Type: application/json" \ 238 | -d "{ 239 | \"token\": \"$PUSHPLUS_TOKEN\", 240 | \"title\": \"$title\", 241 | \"content\": \"$content\", 242 | \"template\": \"html\" 243 | }") 244 | 245 | if echo "$response" | grep -q '"code":200'; then 246 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送成功"| tee -a "$CRON_LOG" 247 | return 0 248 | else 249 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送失败. 响应: $response"| tee -a "$CRON_LOG" 250 | return 1 251 | fi 252 | } 253 | 254 | # 发送关机警告 255 | send_shutdown_warning() { 256 | local title="🚨 [${MACHINE_NAME}]关机警告" 257 | local content="流量已达到严重限制,系统将在 1 分钟后关机!" 258 | 259 | # 添加端口流量摘要 260 | if command -v get_port_traffic_summary &> /dev/null; then 261 | local port_summary=$(get_port_traffic_summary 3) 262 | if [ -n "$port_summary" ]; then 263 | content="${content}

${port_summary}" 264 | fi 265 | fi 266 | 267 | local url="http://www.pushplus.plus/send" 268 | local response 269 | 270 | response=$(curl -s -X POST "$url" \ 271 | -H "Content-Type: application/json" \ 272 | -d "{ 273 | \"token\": \"$PUSHPLUS_TOKEN\", 274 | \"title\": \"$title\", 275 | \"content\": \"$content\", 276 | \"template\": \"html\" 277 | }") 278 | 279 | if echo "$response" | grep -q '"code":200'; then 280 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送成功"| tee -a "$CRON_LOG" 281 | return 0 282 | else 283 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送失败. 响应: $response"| tee -a "$CRON_LOG" 284 | return 1 285 | fi 286 | } 287 | 288 | test_pushplus_notification() { 289 | local title="🔔 [${MACHINE_NAME}]测试消息" 290 | local content="这是一条测试消息。如果您收到这条消息,说明PushPlus通知功能正常工作。" 291 | local url="http://www.pushplus.plus/send" 292 | local response 293 | 294 | response=$(curl -s -X POST "$url" \ 295 | -H "Content-Type: application/json" \ 296 | -d "{ 297 | \"token\": \"$PUSHPLUS_TOKEN\", 298 | \"title\": \"$title\", 299 | \"content\": \"$content\", 300 | \"template\": \"html\" 301 | }") 302 | 303 | if echo "$response" | grep -q '"code":200'; then 304 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送成功"| tee -a "$CRON_LOG" 305 | echo "✅ [${MACHINE_NAME}]测试消息已成功发送,请检查您的PushPlus。" 306 | return 0 307 | else 308 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PushPlus 通知发送失败. 响应: $response"| tee -a "$CRON_LOG" 309 | echo "❌ [${MACHINE_NAME}]发送测试消息失败。请检查您的PUSHPLUS_TOKEN设置。" 310 | return 1 311 | fi 312 | } 313 | 314 | 315 | check_and_notify() { 316 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查流量状态..."| tee -a "$CRON_LOG" 317 | 318 | local current_status="未知" 319 | local current_time=$(date '+%Y-%m-%d %H:%M:%S') 320 | local relevant_log="" 321 | 322 | # 从后往前读取日志文件,找到第一个包含相关信息的行 323 | relevant_log=$(tac "$LOG_FILE" | grep -m 1 -E "流量超出限制|使用 TC 模式限速|新的流量周期开始|流量正常,清除所有限制") 324 | 325 | # 记录相关的日志内容 326 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 相关的日志内容: $relevant_log"| tee -a "$CRON_LOG" 327 | 328 | # 确定当前状态 329 | if echo "$relevant_log" | grep -q "流量超出限制,系统将在 1 分钟后关机"; then 330 | current_status="关机" 331 | elif echo "$relevant_log" | grep -q "流量超出限制"; then 332 | current_status="限速" 333 | elif echo "$relevant_log" | grep -q "新的流量周期开始,重置限制"; then 334 | current_status="新周期" 335 | elif echo "$relevant_log" | grep -q "流量正常,清除所有限制"; then 336 | current_status="正常" 337 | fi 338 | 339 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前检测到的状态: $current_status"| tee -a "$CRON_LOG" 340 | 341 | local last_status="" 342 | if [ -f "$LAST_NOTIFICATION_FILE" ]; then 343 | last_status=$(tail -n 1 "$LAST_NOTIFICATION_FILE" | cut -d' ' -f3-) 344 | fi 345 | 346 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 上次记录的状态: $last_status"| tee -a "$CRON_LOG" 347 | 348 | # 根据状态调用相应的通知函数 349 | if [ "$current_status" = "限速" ] && [ "$last_status" != "限速" ]; then 350 | send_throttle_warning 351 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_warning"| tee -a "$CRON_LOG" 352 | elif [ "$current_status" = "正常" ] && [ "$last_status" = "限速" ]; then 353 | send_throttle_lifted 354 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_lifted"| tee -a "$CRON_LOG" 355 | elif [ "$current_status" = "新周期" ]; then 356 | send_new_cycle_notification 357 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_new_cycle_notification"| tee -a "$CRON_LOG" 358 | elif [ "$current_status" = "关机" ]; then 359 | send_shutdown_warning 360 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_shutdown_warning"| tee -a "$CRON_LOG" 361 | elif [ "$current_status" = "未知" ]; then 362 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法识别当前状态,不发送通知"| tee -a "$CRON_LOG" 363 | else 364 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无需发送通知"| tee -a "$CRON_LOG" 365 | fi 366 | 367 | # 追加新状态到状态文件 368 | echo "$current_time $current_status" >> "$LAST_NOTIFICATION_FILE" 369 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已追加新状态到状态文件"| tee -a "$CRON_LOG" 370 | 371 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 流量检查完成。"| tee -a "$CRON_LOG" 372 | } 373 | 374 | # 设置定时任务 375 | setup_cron() { 376 | local correct_entry="* * * * * $SCRIPT_PATH -cron" 377 | local current_crontab=$(crontab -l 2>/dev/null) 378 | local pushplus_entries=$(echo "$current_crontab" | grep "pushplus_notifier.sh") 379 | local correct_entries_count=$(echo "$pushplus_entries" | grep -F "$correct_entry" | wc -l) 380 | 381 | if [ "$correct_entries_count" -eq 1 ]; then 382 | echo "正确的 crontab 项已存在且数量正确,无需修改。" 383 | else 384 | # 删除所有包含 pushplus_notifier.sh 的条目 385 | new_crontab=$(echo "$current_crontab" | grep -v "pushplus_notifier.sh") 386 | 387 | # 添加正确的条目 388 | new_crontab="${new_crontab} 389 | $correct_entry" 390 | 391 | # 更新 crontab 392 | echo "$new_crontab" | crontab - 393 | 394 | echo "已更新 crontab。删除了所有旧的 pushplus_notifier.sh 条目,并添加了新的条目。" 395 | fi 396 | 397 | # 显示当前的 crontab 内容 398 | echo "当前的 crontab 内容:" 399 | crontab -l 400 | } 401 | 402 | # 每日报告 403 | daily_report() { 404 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始生成每日报告"| tee -a "$CRON_LOG" 405 | echo "$(date '+%Y-%m-%d %H:%M:%S') : DAILY_REPORT_TIME=$DAILY_REPORT_TIME"| tee -a "$CRON_LOG" 406 | echo "$(date '+%Y-%m-%d %H:%M:%S') : PUSHPLUS_TOKEN=${PUSHPLUS_TOKEN:0:5}..."| tee -a "$CRON_LOG" 407 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 日志文件路径: $LOG_FILE"| tee -a "$CRON_LOG" 408 | 409 | # 反向读取日志文件,查找第一个同时包含"当前使用流量"和"限制流量"的行 410 | local usage_line=$(tac "$LOG_FILE" | grep -m 1 -E "当前使用流量:.*限制流量:") 411 | 412 | if [[ -z "$usage_line" ]]; then 413 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法在日志中找到同时包含当前使用流量和限制流量的行"| tee -a "$CRON_LOG" 414 | return 1 415 | fi 416 | 417 | local current_usage=$(echo "$usage_line" | grep -oP '当前使用流量:\s*\K[0-9.]+ [GBMKgbmk]+') 418 | local limit=$(echo "$usage_line" | grep -oP '限制流量:\s*\K[0-9.]+ [GBMKgbmk]+') 419 | 420 | if [[ -z "$current_usage" || -z "$limit" ]]; then 421 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法从行中提取流量信息"| tee -a "$CRON_LOG" 422 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 问题行: $usage_line"| tee -a "$CRON_LOG" 423 | return 1 424 | fi 425 | 426 | local title="[${MACHINE_NAME}]每日流量报告" 427 | local content="📊 每日流量报告

🖥️ 机器总流量
当前使用:$current_usage
流量限制:$limit" 428 | 429 | # 检查是否有端口流量配置 430 | local ports_config_file="$WORK_DIR/ports_traffic_config.json" 431 | local view_script="$WORK_DIR/view_port_traffic.sh" 432 | 433 | if [ -f "$ports_config_file" ]; then 434 | local port_count=$(jq -r '.ports | length' "$ports_config_file" 2>/dev/null || echo "0") 435 | 436 | if [ "$port_count" -gt 0 ]; then 437 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到 $port_count 个端口流量配置,添加端口信息"| tee -a "$CRON_LOG" 438 | 439 | # 如果有 view_port_traffic.sh 脚本,使用它 440 | if [ -f "$view_script" ]; then 441 | local port_data=$(bash "$view_script" --json 2>/dev/null) 442 | else 443 | local port_data="" 444 | fi 445 | 446 | if [ -n "$port_data" ] && echo "$port_data" | jq -e '.ports' >/dev/null 2>&1; then 447 | local actual_port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null || echo "0") 448 | 449 | if [ "$actual_port_count" -gt 0 ]; then 450 | content="${content}

🔌 端口流量详情
" 451 | 452 | # 遍历每个端口 453 | local i=0 454 | while [ $i -lt $actual_port_count ]; do 455 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 456 | local port_desc=$(echo "$port_data" | jq -r ".ports[$i].description" 2>/dev/null) 457 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 458 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 459 | 460 | if [ -n "$port" ] && [ "$port" != "null" ] && [ "$port_usage" != "null" ]; then 461 | # 格式化流量显示(保留2位小数) 462 | local port_usage_formatted=$(printf "%.2f" "$port_usage" 2>/dev/null || echo "$port_usage") 463 | local port_limit_formatted=$(printf "%.2f" "$port_limit" 2>/dev/null || echo "$port_limit") 464 | 465 | # 计算使用百分比 466 | local port_percentage=0 467 | if [ -n "$port_limit" ] && [ "$port_limit" != "null" ] && (( $(echo "$port_limit > 0" | bc -l 2>/dev/null || echo "0") )); then 468 | port_percentage=$(printf "%.2f" $(echo "scale=2; ($port_usage / $port_limit) * 100" | bc 2>/dev/null || echo "0")) 469 | fi 470 | 471 | # 根据使用率选择表情 472 | local status_icon="✅" 473 | if (( $(echo "$port_percentage >= 90" | bc -l 2>/dev/null || echo "0") )); then 474 | status_icon="🔴" 475 | elif (( $(echo "$port_percentage >= 75" | bc -l 2>/dev/null || echo "0") )); then 476 | status_icon="🟡" 477 | fi 478 | 479 | content="${content}${status_icon} 端口 ${port} (${port_desc}):${port_usage_formatted}GB / ${port_limit_formatted}GB (${port_percentage}%)
" 480 | fi 481 | 482 | i=$((i + 1)) 483 | done 484 | 485 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已添加 $actual_port_count 个端口的流量信息"| tee -a "$CRON_LOG" 486 | else 487 | echo "$(date '+%Y-%m-%d %H:%M:%S') : JSON数据中没有端口信息"| tee -a "$CRON_LOG" 488 | fi 489 | else 490 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法获取有效的端口流量JSON数据"| tee -a "$CRON_LOG" 491 | fi 492 | else 493 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有配置端口流量监控"| tee -a "$CRON_LOG" 494 | fi 495 | else 496 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 端口配置文件不存在"| tee -a "$CRON_LOG" 497 | fi 498 | 499 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 准备发送消息: $content"| tee -a "$CRON_LOG" 500 | 501 | local url="http://www.pushplus.plus/send" 502 | local response 503 | 504 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 尝试发送PushPlus消息"| tee -a "$CRON_LOG" 505 | 506 | response=$(curl -s -X POST "$url" \ 507 | -H "Content-Type: application/json" \ 508 | -d "{ 509 | \"token\": \"$PUSHPLUS_TOKEN\", 510 | \"title\": \"$title\", 511 | \"content\": \"$content\" 512 | }") 513 | 514 | if echo "$response" | grep -q '"code":200'; then 515 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功"| tee -a "$CRON_LOG" 516 | return 0 517 | else 518 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败. 响应: $response"| tee -a "$CRON_LOG" 519 | return 1 520 | fi 521 | } 522 | 523 | 524 | 525 | # 主任务 526 | main() { 527 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 进入主任务" >> "$CRON_LOG" 528 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 参数数量: $#" >> "$CRON_LOG" 529 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 所有参数: $@" >> "$CRON_LOG" 530 | 531 | check_running 532 | 533 | if [[ "$*" == *"-cron"* ]]; then 534 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到-cron参数, 进入cron模式" >> "$CRON_LOG" 535 | if read_config; then 536 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 成功读取配置文件" >> "$CRON_LOG" 537 | # 继续执行其他操作 538 | check_and_notify 539 | 540 | # 检查是否需要发送每日报告 541 | current_time=$(TZ='Asia/Shanghai' date +%H:%M) 542 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间: $current_time, 设定的报告时间: $DAILY_REPORT_TIME" >> "$CRON_LOG" 543 | if [ "$current_time" == "$DAILY_REPORT_TIME" ]; then 544 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 时间匹配,准备发送每日报告" >> "$CRON_LOG" 545 | if daily_report; then 546 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功" >> "$CRON_LOG" 547 | else 548 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败" >> "$CRON_LOG" 549 | fi 550 | else 551 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间与报告时间不匹配,不发送报告" >> "$CRON_LOG" 552 | fi 553 | else 554 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 配置文件不存在或不完整,跳过检查" >> "$CRON_LOG" 555 | exit 1 556 | fi 557 | else 558 | # 交互模式 559 | echo "进入交互模式" 560 | if ! read_config; then 561 | echo "需要进行初始化配置。" 562 | initial_config 563 | fi 564 | 565 | setup_cron 566 | 567 | # 直接显示当前配置摘要 568 | echo "当前配置摘要:" 569 | echo "机器名称: $MACHINE_NAME" 570 | echo "每日报告时间: $DAILY_REPORT_TIME" 571 | echo "PushPlus Token: ${PUSHPLUS_TOKEN:0:10}..." # 只显示前10个字符 572 | 573 | echo "脚本正在运行中。按 'q' 退出,按 'c' 检查流量,按 'd' 手动发送每日报告,按 'r' 重新加载配置,按 't' 发送测试消息,按 'm' 修改配置,按 'h' 修改每日报告时间。" 574 | while true; do 575 | read -n 1 -t 1 input 576 | if [ -n "$input" ]; then 577 | echo 578 | case $input in 579 | q|Q) 580 | echo "退出脚本。" 581 | exit 0 582 | ;; 583 | c|C) 584 | check_and_notify "true" 585 | ;; 586 | d|D) 587 | daily_report 588 | ;; 589 | r|R) 590 | read_config 591 | echo "配置已重新加载。" 592 | ;; 593 | t|T) 594 | test_pushplus_notification 595 | ;; 596 | m|M) 597 | initial_config 598 | ;; 599 | h|H) 600 | echo "请输入新的每日报告时间 (HH:MM): " 601 | read -r new_time 602 | if [[ $new_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then 603 | sed -i "s/DAILY_REPORT_TIME=.*/DAILY_REPORT_TIME=$new_time/" "$CONFIG_FILE" 604 | echo "每日报告时间已更新为 $new_time" 605 | else 606 | echo "无效的时间格式。未更改。" 607 | fi 608 | ;; 609 | *) 610 | echo "无效的输入: $input" 611 | ;; 612 | esac 613 | 614 | 615 | echo "脚本正在运行中。按 'q' 退出,按 'c' 检查流量,按 'd' 手动发送每日报告,按 'r' 重新加载配置,按 't' 发送测试消息,按 'm' 修改配置,按 'h' 修改每日报告时间。" 616 | fi 617 | done 618 | fi 619 | } 620 | 621 | 622 | # 运行主程序 623 | main "$@" 624 | -------------------------------------------------------------------------------- /trafficcop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 设置 PATH 确保 cron 环境能找到所有命令 4 | export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 5 | 6 | WORK_DIR="/root/TrafficCop" 7 | CONFIG_FILE="$WORK_DIR/traffic_monitor_config.txt" 8 | LOG_FILE="$WORK_DIR/traffic_monitor.log" 9 | SCRIPT_PATH="$WORK_DIR/trafficcop.sh" 10 | LOCK_FILE="$WORK_DIR/traffic_monitor.lock" 11 | 12 | # 设置时区为上海(东八区) 13 | export TZ='Asia/Shanghai' 14 | 15 | echo "-----------------------------------------------------"| tee -a "$LOG_FILE" 16 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前版本:1.0.84"| tee -a "$LOG_FILE" 17 | 18 | 19 | # 在脚本开始时杀死所有其他 traffic_monitor.sh 进程 20 | kill_other_instances() { 21 | local current_pid=$$ 22 | local script_name=$(basename "\$0") 23 | for pid in $(pgrep -f "$script_name"); do 24 | if [ "$pid" != "$current_pid" ]; then 25 | echo "$(date '+%Y-%m-%d %H:%M:%S') 终止其他脚本实例 (PID: $pid)" | tee -a "$LOG_FILE" 26 | kill $pid 27 | fi 28 | done 29 | } 30 | 31 | 32 | 33 | 34 | 35 | migrate_files() { 36 | # 创建新的工作目录 37 | mkdir -p "$WORK_DIR" 38 | 39 | # 迁移配置文件 40 | if [ -f "/root/traffic_monitor_config.txt" ]; then 41 | mv "/root/traffic_monitor_config.txt" "$CONFIG_FILE" 42 | fi 43 | 44 | # 迁移日志文件 45 | if [ -f "/root/traffic_monitor.log" ]; then 46 | mv "/root/traffic_monitor.log" "$LOG_FILE" 47 | fi 48 | 49 | # 删除旧的脚本文件,而不是迁移 50 | if [ -f "/root/traffic_monitor.sh" ]; then 51 | rm "/root/traffic_monitor.sh" 52 | echo "$(date '+%Y-%m-%d %H:%M:%S') 旧的脚本文件已删除" | tee -a "$LOG_FILE" 53 | fi 54 | 55 | # 创建软链接以保持向后兼容(如果crontab中仍在使用旧名称) 56 | if [ ! -e "$WORK_DIR/traffic_monitor.sh" ] && [ -f "$WORK_DIR/trafficcop.sh" ]; then 57 | ln -sf "$WORK_DIR/trafficcop.sh" "$WORK_DIR/traffic_monitor.sh" 58 | echo "$(date '+%Y-%m-%d %H:%M:%S') 已创建 traffic_monitor.sh 软链接" | tee -a "$LOG_FILE" 59 | fi 60 | 61 | # 迁移软件包安装标志文件 62 | if [ -f "/root/.traffic_monitor_packages_installed" ]; then 63 | mv "/root/.traffic_monitor_packages_installed" "$WORK_DIR/.traffic_monitor_packages_installed" 64 | fi 65 | 66 | # 迁移其他可能存在的相关文件 67 | for file in /root/traffic_monitor_*.txt /root/traffic_monitor_*.log; do 68 | if [ -f "$file" ]; then 69 | mv "$file" "$WORK_DIR/" 70 | fi 71 | done 72 | 73 | # 更新 crontab 中的脚本路径 74 | if crontab -l | grep -q "/root/traffic_monitor.sh"; then 75 | crontab -l | sed "s|/root/traffic_monitor.sh|$SCRIPT_PATH|g" | crontab - 76 | echo "$(date '+%Y-%m-%d %H:%M:%S') Crontab 已更新为新的脚本路径" | tee -a "$LOG_FILE" 77 | fi 78 | 79 | echo "$(date '+%Y-%m-%d %H:%M:%S') 文件已迁移到新的工作目录: $WORK_DIR" | tee -a "$LOG_FILE" 80 | } 81 | 82 | 83 | 84 | 85 | 86 | check_and_install_packages() { 87 | local packages=("vnstat" "jq" "bc" "iproute2" "cron") 88 | local need_install=false 89 | 90 | for package in "${packages[@]}"; do 91 | if ! dpkg -s "$package" >/dev/null 2>&1; then 92 | echo "$(date '+%Y-%m-%d %H:%M:%S') $package 未安装,将进行安装..." | tee -a "$LOG_FILE" 93 | need_install=true 94 | break 95 | fi 96 | done 97 | 98 | if $need_install; then 99 | echo "$(date '+%Y-%m-%d %H:%M:%S') 正在更新软件包列表..." | tee -a "$LOG_FILE" 100 | if ! sudo apt-get update; then 101 | echo "$(date '+%Y-%m-%d %H:%M:%S') 更新软件包列表失败,请检查网络连接和系统状态。" | tee -a "$LOG_FILE" 102 | return 1 103 | fi 104 | 105 | for package in "${packages[@]}"; do 106 | if ! dpkg -s "$package" >/dev/null 2>&1; then 107 | echo "$(date '+%Y-%m-%d %H:%M:%S') 正在安装 $package..." | tee -a "$LOG_FILE" 108 | if sudo apt-get install -y "$package"; then 109 | echo "$(date '+%Y-%m-%d %H:%M:%S') $package 安装成功" | tee -a "$LOG_FILE" 110 | else 111 | echo "$(date '+%Y-%m-%d %H:%M:%S') $package 安装失败,请手动检查并安装。" | tee -a "$LOG_FILE" 112 | return 1 113 | fi 114 | fi 115 | done 116 | else 117 | echo "$(date '+%Y-%m-%d %H:%M:%S') 所有必要的软件包已安装" | tee -a "$LOG_FILE" 118 | fi 119 | 120 | # 验证 tc 命令是否可用 121 | if ! command -v tc &> /dev/null; then 122 | echo "$(date '+%Y-%m-%d %H:%M:%S') 警告:'tc' 命令不可用,可能影响限速功能。" | tee -a "$LOG_FILE" 123 | fi 124 | 125 | # 获取 vnstat 版本 126 | local vnstat_version=$(vnstat --version 2>&1 | head -n 1) 127 | echo "$(date '+%Y-%m-%d %H:%M:%S') vnstat 版本: $vnstat_version" | tee -a "$LOG_FILE" 128 | 129 | # 获取主要网络接口 130 | local main_interface=$(ip route | grep default | sed -e 's/^.*dev \([^ ]*\).*$/\1/' | head -n 1) 131 | echo "$(date '+%Y-%m-%d %H:%M:%S') 主要网络接口: $main_interface" | tee -a "$LOG_FILE" 132 | 133 | # 获取 vnstat 统计开始时间 134 | if [ -n "$main_interface" ]; then 135 | local vnstat_json=$(vnstat -i "$main_interface" --json d) 136 | local vnstat_start_time=$(echo "$vnstat_json" | jq -r '.interfaces[0].created.date | "\(.year)-\(.month | tostring | if length == 1 then "0" + . else . end)-\(.day | tostring | if length == 1 then "0" + . else . end)"') 137 | 138 | if [ -n "$vnstat_start_time" ] && [ "$vnstat_start_time" != "null-null-null" ]; then 139 | echo "$(date '+%Y-%m-%d %H:%M:%S') vnstat 统计开始日期: $vnstat_start_time,在此之前的流量不会被纳入统计!" | tee -a "$LOG_FILE" 140 | else 141 | echo "$(date '+%Y-%m-%d %H:%M:%S') 无法获取 vnstat 统计开始时间" | tee -a "$LOG_FILE" 142 | echo "vnstat JSON 输出: $vnstat_json" | tee -a "$LOG_FILE" 143 | fi 144 | else 145 | echo "$(date '+%Y-%m-%d %H:%M:%S') 无法获取主要网络接口" | tee -a "$LOG_FILE" 146 | fi 147 | } 148 | 149 | 150 | # 检查配置和定时任务 151 | check_existing_setup() { 152 | if [ -s "$CONFIG_FILE" ]; then 153 | source "$CONFIG_FILE" 154 | echo "$(date '+%Y-%m-%d %H:%M:%S') 配置已存在"| tee -a "$LOG_FILE" 155 | if crontab -l 2>/dev/null | grep -q "$SCRIPT_PATH --run"; then 156 | echo "$(date '+%Y-%m-%d %H:%M:%S') 每分钟一次的定时任务已在执行。"| tee -a "$LOG_FILE" 157 | else 158 | echo "$(date '+%Y-%m-%d %H:%M:%S') 警告:定时任务未找到,可能需要重新设置。"| tee -a "$LOG_FILE" 159 | fi 160 | return 0 161 | else 162 | return 1 163 | fi 164 | } 165 | 166 | # 读取配置 167 | read_config() { 168 | if [ -f "$CONFIG_FILE" ]; then 169 | source "$CONFIG_FILE" 170 | return 0 171 | else 172 | return 1 173 | fi 174 | } 175 | 176 | # 写入配置 177 | write_config() { 178 | cat > "$CONFIG_FILE" << EOF 179 | TRAFFIC_MODE=$TRAFFIC_MODE 180 | TRAFFIC_PERIOD=$TRAFFIC_PERIOD 181 | TRAFFIC_LIMIT=$TRAFFIC_LIMIT 182 | TRAFFIC_TOLERANCE=$TRAFFIC_TOLERANCE 183 | PERIOD_START_DAY=${PERIOD_START_DAY:-1} 184 | LIMIT_SPEED=${LIMIT_SPEED:-20} 185 | MAIN_INTERFACE=$MAIN_INTERFACE 186 | LIMIT_MODE=$LIMIT_MODE 187 | EOF 188 | echo "$(date '+%Y-%m-%d %H:%M:%S') 配置已更新"| tee -a "$LOG_FILE" 189 | } 190 | 191 | 192 | # 显示当前配置 193 | show_current_config() { 194 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前配置:"| tee -a "$LOG_FILE" 195 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量统计模式: $TRAFFIC_MODE"| tee -a "$LOG_FILE" 196 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量统计周期: $TRAFFIC_PERIOD"| tee -a "$LOG_FILE" 197 | echo "$(date '+%Y-%m-%d %H:%M:%S') 周期起始日: ${PERIOD_START_DAY:-1}"| tee -a "$LOG_FILE" 198 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量限制: $TRAFFIC_LIMIT GB"| tee -a "$LOG_FILE" 199 | echo "$(date '+%Y-%m-%d %H:%M:%S') 容错范围: $TRAFFIC_TOLERANCE GB"| tee -a "$LOG_FILE" 200 | echo "$(date '+%Y-%m-%d %H:%M:%S') 限速: ${LIMIT_SPEED:-20} kbit/s"| tee -a "$LOG_FILE" 201 | echo "$(date '+%Y-%m-%d %H:%M:%S') 主要网络接口: $MAIN_INTERFACE"| tee -a "$LOG_FILE" 202 | echo "$(date '+%Y-%m-%d %H:%M:%S') 限制模式: $LIMIT_MODE"| tee -a "$LOG_FILE" 203 | } 204 | 205 | # 检测主要网络接口 206 | get_main_interface() { 207 | local main_interface=$(ip route | grep default | sed -n 's/^default via [0-9.]* dev \([^ ]*\).*/\1/p' | head -n1) 208 | if [ -z "$main_interface" ]; then 209 | main_interface=$(ip link | grep 'state UP' | sed -n 's/^[0-9]*: \([^:]*\):.*/\1/p' | head -n1) 210 | fi 211 | 212 | if [ -z "$main_interface" ]; then 213 | while true; do 214 | echo "$(date '+%Y-%m-%d %H:%M:%S') 无法自动检测主要网络接口。"| tee -a "$LOG_FILE" 215 | echo "$(date '+%Y-%m-%d %H:%M:%S') 可用的网络接口有:"| tee -a "$LOG_FILE" 216 | ip -o link show | sed -n 's/^[0-9]*: \([^:]*\):.*/\1/p' 217 | read -p "请从上面的列表中选择一个网络接口: " main_interface 218 | if [ -z "$main_interface" ]; then 219 | echo "$(date '+%Y-%m-%d %H:%M:%S') 请输入一个有效的接口名称。"| tee -a "$LOG_FILE" 220 | elif ip link show "$main_interface" > /dev/null 2>&1; then 221 | break 222 | else 223 | echo "$(date '+%Y-%m-%d %H:%M:%S') 无效的接口,请重新选择。"| tee -a "$LOG_FILE" 224 | fi 225 | done 226 | else 227 | read -p "检测到的主要网络接口是: $main_interface, 按Enter使用此接口,或输入新的接口名称: " new_interface 228 | if [ -n "$new_interface" ]; then 229 | if ip link show "$new_interface" > /dev/null 2>&1; then 230 | main_interface=$new_interface 231 | else 232 | echo "$(date '+%Y-%m-%d %H:%M:%S') 输入的接口无效,将使用检测到的接口: $main_interface"| tee -a "$LOG_FILE" 233 | fi 234 | fi 235 | fi 236 | 237 | echo $main_interface| tee -a "$LOG_FILE" 238 | } 239 | 240 | # 初始配置函数 241 | echo "$(date '+%Y-%m-%d %H:%M:%S') 开始初始化配置"| tee -a "$LOG_FILE" 242 | initial_config() { 243 | echo "$(date '+%Y-%m-%d %H:%M:%S') 正在检测主要网络接口..."| tee -a "$LOG_FILE" 244 | MAIN_INTERFACE=$(get_main_interface) 245 | 246 | while true; do 247 | echo "$(date '+%Y-%m-%d %H:%M:%S') 请选择流量统计模式:"| tee -a "$LOG_FILE" 248 | echo "$(date '+%Y-%m-%d %H:%M:%S') 1. 只计算出站流量"| tee -a "$LOG_FILE" 249 | echo "$(date '+%Y-%m-%d %H:%M:%S') 2. 只计算进站流量"| tee -a "$LOG_FILE" 250 | echo "$(date '+%Y-%m-%d %H:%M:%S') 3. 出进站流量都计算"| tee -a "$LOG_FILE" 251 | echo "$(date '+%Y-%m-%d %H:%M:%S') 4. 出站和进站流量只取大"| tee -a "$LOG_FILE" 252 | read -p "请输入选择 (1-4): " mode_choice 253 | case $mode_choice in 254 | 1) TRAFFIC_MODE="out"; break ;; 255 | 2) TRAFFIC_MODE="in"; break ;; 256 | 3) TRAFFIC_MODE="total"; break ;; 257 | 4) TRAFFIC_MODE="max"; break ;; 258 | *) echo "无效输入,请重新选择。" ;; 259 | esac 260 | done 261 | 262 | read -p "请选择流量统计周期 (m/q/y,默认为m): " period_choice 263 | case $period_choice in 264 | q) TRAFFIC_PERIOD="quarterly" ;; 265 | y) TRAFFIC_PERIOD="yearly" ;; 266 | m|"") TRAFFIC_PERIOD="monthly" ;; 267 | *) echo "无效输入,使用默认值:monthly"; TRAFFIC_PERIOD="monthly" ;; 268 | esac 269 | 270 | read -p "请输入周期起始日 (1-31,默认为1): " PERIOD_START_DAY 271 | if [[ -z "$PERIOD_START_DAY" ]]; then 272 | PERIOD_START_DAY=1 273 | elif ! [[ "$PERIOD_START_DAY" =~ ^[1-9]$|^[12][0-9]$|^3[01]$ ]]; then 274 | echo "无效输入,使用默认值:1" 275 | PERIOD_START_DAY=1 276 | fi 277 | 278 | while true; do 279 | read -p "请输入流量限制 (GB): " TRAFFIC_LIMIT 280 | if [[ "$TRAFFIC_LIMIT" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then 281 | break 282 | else 283 | echo "无效输入,请输入一个有效的数字。" 284 | fi 285 | done 286 | 287 | while true; do 288 | read -p "请输入容错范围 (GB): " TRAFFIC_TOLERANCE 289 | if [[ "$TRAFFIC_TOLERANCE" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then 290 | break 291 | else 292 | echo "无效输入,请输入一个有效的数字。" 293 | fi 294 | done 295 | 296 | while true; do 297 | echo "$(date '+%Y-%m-%d %H:%M:%S') 请选择限制模式:"| tee -a "$LOG_FILE" 298 | echo "$(date '+%Y-%m-%d %H:%M:%S') 1. TC 模式(更灵活)"| tee -a "$LOG_FILE" 299 | echo "$(date '+%Y-%m-%d %H:%M:%S') 2. 关机模式(更安全)"| tee -a "$LOG_FILE" 300 | read -p "请输入选择 (1-2): " limit_mode_choice 301 | case $limit_mode_choice in 302 | 1) 303 | LIMIT_MODE="tc" 304 | read -p "请输入限速 (kbit/s,默认为20): " LIMIT_SPEED 305 | LIMIT_SPEED=${LIMIT_SPEED:-20} 306 | if ! [[ "$LIMIT_SPEED" =~ ^[0-9]+$ ]]; then 307 | echo "无效输入,使用默认值:20 kbit/s" 308 | LIMIT_SPEED=20 309 | fi 310 | break 311 | ;; 312 | 2) 313 | LIMIT_MODE="shutdown" 314 | LIMIT_SPEED="" # 关机模式不需要限速 315 | break 316 | ;; 317 | *) echo "无效输入,请重新选择。" ;; 318 | esac 319 | done 320 | 321 | write_config 322 | } 323 | 324 | # 获取当前周期的起始日期 325 | get_period_start_date() { 326 | local current_date=$(date +%Y-%m-%d) 327 | local current_month=$(date +%m) 328 | local current_year=$(date +%Y) 329 | 330 | case $TRAFFIC_PERIOD in 331 | monthly) 332 | if [ $(date +%d) -lt $PERIOD_START_DAY ]; then 333 | date -d "${current_year}-${current_month}-${PERIOD_START_DAY} -1 month" +'%Y-%m-%d' 334 | else 335 | date -d "${current_year}-${current_month}-${PERIOD_START_DAY}" +%Y-%m-%d 2>/dev/null || date -d "${current_year}-${current_month}-01" +%Y-%m-%d 336 | fi 337 | ;; 338 | quarterly) 339 | local quarter_month=$(((($(date +%m) - 1) / 3) * 3 + 1)) 340 | if [ $(date +%d) -lt $PERIOD_START_DAY ] || [ $(date +%m) -eq $quarter_month ]; then 341 | date -d "${current_year}-${quarter_month}-${PERIOD_START_DAY} -3 month" +'%Y-%m-%d' 342 | else 343 | date -d "${current_year}-${quarter_month}-${PERIOD_START_DAY}" +'%Y-%m-%d' 2>/dev/null || date -d "${current_year}-${quarter_month}-01" +%Y-%m-%d 344 | fi 345 | ;; 346 | yearly) 347 | if [ $(date +%d) -lt $PERIOD_START_DAY ] || [ $(date +%m) -eq 01 ]; then 348 | date -d "${current_year}-01-${PERIOD_START_DAY} -1 year" +'%Y-%m-%d' 349 | else 350 | date -d "${current_year}-01-${PERIOD_START_DAY}" +'%Y-%m-%d' 2>/dev/null || date -d "${current_year}-01-01" +%Y-%m-%d 351 | fi 352 | ;; 353 | esac 354 | } 355 | 356 | # 获取周期结束日期 357 | get_period_end_date() { 358 | local current_date=$(date +%Y-%m-%d) 359 | local current_month=$(date +%m) 360 | local current_year=$(date +%Y) 361 | 362 | case $TRAFFIC_PERIOD in 363 | monthly) 364 | if [ $(date +%d) -lt $PERIOD_START_DAY ]; then 365 | date -d "${current_year}-${current_month}-${PERIOD_START_DAY} -1 day" +'%Y-%m-%d' 366 | else 367 | date -d "${current_year}-${current_month}-${PERIOD_START_DAY} +1 month -1 day" +'%Y-%m-%d' 368 | fi 369 | ;; 370 | quarterly) 371 | local quarter_month=$(((($(date +%m) - 1) / 3) * 3 + 1)) 372 | if [ $(date +%d) -lt $PERIOD_START_DAY ] || [ $(date +%m) -eq $quarter_month ]; then 373 | date -d "${current_year}-${quarter_month}-${PERIOD_START_DAY} +2 month -1 day" +'%Y-%m-%d' 374 | else 375 | date -d "${current_year}-${quarter_month}-${PERIOD_START_DAY} +5 month -1 day" +'%Y-%m-%d' 376 | fi 377 | ;; 378 | yearly) 379 | if [ $(date +%d) -lt $PERIOD_START_DAY ] || [ $(date +%m) -eq 01 ]; then 380 | date -d "${current_year}-12-31" +'%Y-%m-%d' 381 | else 382 | date -d "$((current_year + 1))-12-31" +'%Y-%m-%d' 383 | fi 384 | ;; 385 | esac 386 | } 387 | 388 | # 获取流量使用情况 389 | get_traffic_usage() { 390 | local start_date=$(get_period_start_date) 391 | local end_date=$(get_period_end_date) 392 | 393 | echo "$(date '+%Y-%m-%d %H:%M:%S') 周期开始日期: $start_date, 周期结束日期: $end_date" >&2 394 | 395 | # 使用 vnstat JSON API 获取每日流量数据 396 | local vnstat_json=$(vnstat -i $MAIN_INTERFACE --json 2>/dev/null) 397 | 398 | if [ -z "$vnstat_json" ]; then 399 | echo "$(date '+%Y-%m-%d %H:%M:%S') 错误: 无法获取 vnstat JSON 数据" >&2 400 | echo "0.000" 401 | return 1 402 | fi 403 | 404 | # 将日期转换为时间戳用于比较 405 | local start_ts=$(date -d "$start_date" +%s) 406 | local end_ts=$(date -d "$end_date 23:59:59" +%s) 407 | 408 | # 根据 TRAFFIC_MODE 累加对应的流量 409 | local usage_bytes 410 | case $TRAFFIC_MODE in 411 | out) 412 | usage_bytes=$(echo "$vnstat_json" | jq --argjson start_ts "$start_ts" --argjson end_ts "$end_ts" \ 413 | '[.interfaces[0].traffic.day[] | select(.timestamp >= $start_ts and .timestamp <= $end_ts) | .tx] | add // 0') 414 | ;; 415 | in) 416 | usage_bytes=$(echo "$vnstat_json" | jq --argjson start_ts "$start_ts" --argjson end_ts "$end_ts" \ 417 | '[.interfaces[0].traffic.day[] | select(.timestamp >= $start_ts and .timestamp <= $end_ts) | .rx] | add // 0') 418 | ;; 419 | total) 420 | usage_bytes=$(echo "$vnstat_json" | jq --argjson start_ts "$start_ts" --argjson end_ts "$end_ts" \ 421 | '[.interfaces[0].traffic.day[] | select(.timestamp >= $start_ts and .timestamp <= $end_ts) | (.rx + .tx)] | add // 0') 422 | ;; 423 | max) 424 | local rx_bytes=$(echo "$vnstat_json" | jq --argjson start_ts "$start_ts" --argjson end_ts "$end_ts" \ 425 | '[.interfaces[0].traffic.day[] | select(.timestamp >= $start_ts and .timestamp <= $end_ts) | .rx] | add // 0') 426 | local tx_bytes=$(echo "$vnstat_json" | jq --argjson start_ts "$start_ts" --argjson end_ts "$end_ts" \ 427 | '[.interfaces[0].traffic.day[] | select(.timestamp >= $start_ts and .timestamp <= $end_ts) | .tx] | add // 0') 428 | usage_bytes=$(printf '%s\n%s' "$rx_bytes" "$tx_bytes" | sort -rn | head -n1) 429 | ;; 430 | esac 431 | 432 | if [ -n "$usage_bytes" ] && [ "$usage_bytes" != "null" ] && [ "$usage_bytes" != "0" ]; then 433 | # 将字节转换为 GiB,使用 printf 确保格式正确 434 | local usage_gib=$(echo "scale=3; $usage_bytes/1024/1024/1024" | bc 2>/dev/null || echo "0.000") 435 | # 确保小数点前至少有一个0 436 | printf "%.3f\n" "$usage_gib" 2>/dev/null || echo "0.000" 437 | else 438 | echo "0.000" 439 | fi 440 | } 441 | 442 | 443 | # 修改 check_and_limit_traffic 函数 444 | check_and_limit_traffic() { 445 | local current_usage=$(get_traffic_usage) 446 | local limit_threshold=$(echo "$TRAFFIC_LIMIT - $TRAFFIC_TOLERANCE" | bc 2>/dev/null || echo "0") 447 | 448 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前使用流量: $current_usage GB,限制流量: $limit_threshold GB" | tee -a "$LOG_FILE" 449 | 450 | if (( $(echo "$current_usage > $limit_threshold" | bc -l 2>/dev/null || echo "0") )); then 451 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量超出限制" | tee -a "$LOG_FILE" 452 | if [ "$LIMIT_MODE" = "tc" ]; then 453 | echo "$(date '+%Y-%m-%d %H:%M:%S') 使用 TC 模式限速" | tee -a "$LOG_FILE" 454 | tc qdisc add dev $MAIN_INTERFACE root tbf rate ${LIMIT_SPEED}kbit burst 32kbit latency 400ms 455 | elif [ "$LIMIT_MODE" = "shutdown" ]; then 456 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量超出限制,系统将在 1 分钟后关机" | tee -a "$LOG_FILE" 457 | shutdown -h +1 "流量超出限制,系统将在 1 分钟后关机" 458 | fi 459 | else 460 | echo "$(date '+%Y-%m-%d %H:%M:%S') 流量正常,清除所有限制" | tee -a "$LOG_FILE" 461 | tc qdisc del dev $MAIN_INTERFACE root 2>/dev/null 462 | shutdown -c 2>/dev/null # 取消可能存在的关机计划 463 | fi 464 | } 465 | 466 | 467 | # 检查是否需要重置限制 468 | check_reset_limit() { 469 | local current_date=$(date +%Y-%m-%d) 470 | local period_start=$(get_period_start_date) 471 | 472 | if [[ "$current_date" == "$period_start" ]]; then 473 | echo "$(date '+%Y-%m-%d %H:%M:%S') 新的流量周期开始,重置限制"| tee -a "$LOG_FILE" 474 | tc qdisc del dev $MAIN_INTERFACE root 2>/dev/null 475 | fi 476 | } 477 | 478 | setup_crontab() { 479 | # 删除旧的脚本任务(如果存在) 480 | crontab -l 2>/dev/null | grep -v "$SCRIPT_PATH" | crontab - 481 | 482 | # 添加新的脚本任务 483 | (crontab -l 2>/dev/null; echo "* * * * * $SCRIPT_PATH --run") | crontab - 484 | 485 | echo "$(date '+%Y-%m-%d %H:%M:%S') Crontab 已设置,每分钟运行一次"| tee -a "$LOG_FILE" 486 | } 487 | 488 | 489 | # 主函数 490 | main() { 491 | 492 | 493 | # 调用函数来杀死其他实例 494 | kill_other_instances 495 | 496 | # 在脚本开始时调用迁移函数 497 | migrate_files 498 | 499 | # 切换到工作目录 500 | cd "$WORK_DIR" || exit 1 501 | 502 | # 创建锁文件(如果不存在) 503 | touch "${LOCK_FILE}" 504 | 505 | # 尝试获取文件锁 506 | exec 9>"${LOCK_FILE}" 507 | if ! flock -n 9; then 508 | echo "$(date '+%Y-%m-%d %H:%M:%S') 另一个脚本实例正在运行,退出。" | tee -a "$LOG_FILE" 509 | exit 1 510 | fi 511 | 512 | # 检查是否以 --run 模式运行 513 | if [ "\$1" = "--run" ]; then 514 | echo "$(date '+%Y-%m-%d %H:%M:%S') 正在以自动化模式运行" | tee -a "$LOG_FILE" 515 | if read_config; then 516 | check_reset_limit 517 | check_and_limit_traffic 518 | else 519 | echo "$(date '+%Y-%m-%d %H:%M:%S') 配置文件读取失败,请检查配置" | tee -a "$LOG_FILE" 520 | fi 521 | return 522 | fi 523 | 524 | # 非 --run 模式下的操作 525 | # 首先检查并安装必要的软件包 526 | check_and_install_packages 527 | if check_existing_setup; then 528 | read_config 529 | show_current_config 530 | 531 | echo "$(date '+%Y-%m-%d %H:%M:%S') 是否需要修改配置?(y/n): 5秒内按任意键修改配置,否则保持现有配置" | tee -a "$LOG_FILE" 532 | echo "$(date '+%Y-%m-%d %H:%M:%S') 开始等待用户输入..." | tee -a "$LOG_FILE" 533 | 534 | start_time=$(date +%s.%N) 535 | if read -t 5 -n 1 modify_config; then 536 | end_time=$(date +%s.%N) 537 | duration=$(echo "$end_time - $start_time" | bc 2>/dev/null || echo "0") 538 | echo "" # 换行 539 | echo "$(date '+%Y-%m-%d %H:%M:%S') 收到用户输入: '${modify_config}' (ASCII: $(printf '%d' "'$modify_config" 2>/dev/null || echo "N/A"))" | tee -a "$LOG_FILE" 540 | echo "$(date '+%Y-%m-%d %H:%M:%S') 等待时间: $duration 秒" | tee -a "$LOG_FILE" 541 | if [[ $duration < 0.1 ]]; then 542 | echo "$(date '+%Y-%m-%d %H:%M:%S') 警告:输入时间过短,可能是自动输入" | tee -a "$LOG_FILE" 543 | echo "$(date '+%Y-%m-%d %H:%M:%S') 忽略此输入,保持现有配置。" | tee -a "$LOG_FILE" 544 | else 545 | echo "$(date '+%Y-%m-%d %H:%M:%S') 开始修改配置..." | tee -a "$LOG_FILE" 546 | initial_config 547 | setup_crontab 548 | echo "$(date '+%Y-%m-%d %H:%M:%S') 配置已更新,脚本将每分钟自动运行一次" | tee -a "$LOG_FILE" 549 | fi 550 | else 551 | end_time=$(date +%s.%N) 552 | duration=$(echo "$end_time - $start_time" | bc 2>/dev/null || echo "0") 553 | echo "" # 换行 554 | echo "$(date '+%Y-%m-%d %H:%M:%S') 等待超时,无用户输入" | tee -a "$LOG_FILE" 555 | echo "$(date '+%Y-%m-%d %H:%M:%S') 等待时间: $duration 秒" | tee -a "$LOG_FILE" 556 | echo "$(date '+%Y-%m-%d %H:%M:%S') 保持现有配置。" | tee -a "$LOG_FILE" 557 | fi 558 | else 559 | echo "$(date '+%Y-%m-%d %H:%M:%S') 开始初始化配置..." | tee -a "$LOG_FILE" 560 | initial_config 561 | setup_crontab 562 | echo "$(date '+%Y-%m-%d %H:%M:%S') 初始配置完成,脚本将每分钟自动运行一次" | tee -a "$LOG_FILE" 563 | fi 564 | 565 | # 显示当前流量使用情况和限制状态 566 | if read_config; then 567 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前流量使用情况:" | tee -a "$LOG_FILE" 568 | local current_usage=$(get_traffic_usage) 569 | #echo "Debug: Current usage from get_traffic_usage: $current_usage" | tee -a "$LOG_FILE" 570 | if [ "$current_usage" != "0" ]; then 571 | local start_date=$(get_period_start_date) 572 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前统计周期: $TRAFFIC_PERIOD (从 $start_date 开始)" | tee -a "$LOG_FILE" 573 | echo "$(date '+%Y-%m-%d %H:%M:%S') 统计模式: $TRAFFIC_MODE" | tee -a "$LOG_FILE" 574 | echo "$(date '+%Y-%m-%d %H:%M:%S') 当前使用流量: $current_usage GB" | tee -a "$LOG_FILE" 575 | echo "$(date '+%Y-%m-%d %H:%M:%S') 检查并限制流量:" | tee -a "$LOG_FILE" 576 | check_and_limit_traffic 577 | else 578 | echo "$(date '+%Y-%m-%d %H:%M:%S') 无法获取流量数据,请检查 vnstat 配置" | tee -a "$LOG_FILE" 579 | fi 580 | else 581 | echo "$(date '+%Y-%m-%d %H:%M:%S') 配置文件读取失败,请检查配置" | tee -a "$LOG_FILE" 582 | fi 583 | 584 | # 确保脚本退出时释放锁 585 | trap 'flock -u 9; rm -f ${LOCK_FILE}' EXIT 586 | } 587 | 588 | 589 | 590 | # 执行主函数 591 | main "$@" 592 | 593 | 594 | 595 | echo "-----------------------------------------------------"| tee -a "$LOG_FILE" 596 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # TrafficCop - Smart Traffic Monitoring and Limiting Script 2 | English | [中文](README.md) 3 | 4 | [VTEXS](https://console.vtexs.com/?affid=1554) sponsored this project. 5 | 6 | ## Important Notes 7 | 8 | 1. This script relies on vnstat for traffic statistics. vnstat will only start tracking traffic after installation! 9 | 10 | 2. TC mode cannot prevent DDoS traffic consumption, and traffic can still be consumed quickly! PRs to fix this issue are welcome (if possible). 11 | 12 | 3. If you encounter GitHub API rate limiting issues, try these solutions: 13 | - Use raw content URLs to download scripts 14 | - Wait for the API limit to reset (usually 1 hour) 15 | - Use a personal access token to increase API quota 16 | - Download scripts manually and run them 17 | 18 | 4. The script runs with root privileges by default. For non-root users, ensure they have sudo privileges and prefix all commands with sudo. 19 | 20 | 5. If you encounter issues, check the log file (/root/TrafficCop/traffic_monitor.log) for more information. 21 | 22 | 6. Regularly check for script updates to get new features and bug fixes. 23 | 24 | 7. For specific VPS providers, configuration adjustments may be needed to adapt to their billing models. 25 | 26 | 8. Speed limits in TC mode may not be precise; actual speeds may vary slightly. 27 | 28 | 9. Shutdown mode completely cuts off network connections - use with caution. 29 | 30 | 10. It's recommended to regularly backup the configuration file (traffic_monitor_config.txt). 31 | 32 | ## Frequently Asked Questions 33 | 34 | Q: Why do my traffic statistics seem inaccurate? 35 | A: Ensure vnstat is properly installed and has been running for some time. Newly installed vnstat needs time to collect accurate data. 36 | 37 | Q: How do I change existing configurations? 38 | A: Run the script again, and it will prompt you to modify existing configurations. 39 | 40 | Q: What if SSH connections become slow in TC mode? 41 | A: Try increasing the speed limit value in TC mode. 42 | 43 | Q: How do I completely uninstall the script? 44 | A: Use the following commands: 45 | ``` 46 | sudo pkill -f traffic_monitor.sh 47 | sudo rm -rf /root/TrafficCop 48 | sudo tc qdisc del dev $(ip route | grep default | cut -d ' ' -f 5) root 49 | ``` 50 | 51 | ## One-Click Installation Scripts 52 | ### One-Click Interactive Installation Script 53 | ``` 54 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 55 | ``` 56 | #### Features 57 | 58 | 1. Install Traffic Monitoring - Download and install basic traffic monitoring functionality 59 | 2. Install Telegram Notifications - Add Telegram push notifications 60 | 3. Install PushPlus Notifications - Add PushPlus push notifications 61 | 4. Install ServerChan Notifications - Add ServerChan push notifications 62 | 5. Install Port Traffic Limit - Set independent traffic limits for multiple ports (Multi-Port 2.0) 63 | 6. View Port Traffic - Real-time view of all configured port traffic usage (NEW 2.0) 64 | 7. Machine Limit Management - Complete machine-level traffic limit enable/disable/restore functionality (NEW v2.4) 65 | 8. View Logs - View log files for various services 66 | 9. View Current Configuration - View configuration files for various services 67 | 10. Use Preset Configurations - Apply optimized preset configurations for different service providers 68 | 11. Stop All Services - Stop all TrafficCop-related services 69 | 12. Update All Scripts to Latest Version - One-click update for all components 70 | 71 | #### Advantages 72 | 1. One-stop management - Users only need to remember one command to manage all TrafficCop functions 73 | 2. Interactive experience - Select via numerical menu, no need to memorize complex commands 74 | 3. Visual interface - Enhanced user experience with colored output 75 | 4. Flexible operation - Return to main menu after completing an operation to continue with other options 76 | 5. User-friendly - Each operation has confirmation prompts to avoid mistakes 77 | 78 | ![image](https://github.com/user-attachments/assets/bc12c7e6-bba3-498d-a0bc-6ed8ce561e84) 79 | 80 | ### One-Click Complete Suite with Telegram Push (API call, latest version, may return 403): 81 | ``` 82 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 83 | ``` 84 | ### One-Click Complete Suite with Telegram Push (Raw content download, may be outdated): 85 | ``` 86 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 87 | ``` 88 | ### One-Click Complete Suite with PushPlus Push (API call, latest version, may return 403): 89 | ``` 90 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/pushplus_notifier.sh" | tr -d '\r' > /root/TrafficCop/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && bash /root/TrafficCop/pushplus_notifier.sh 91 | ``` 92 | ### One-Click Complete Suite with PushPlus Push (Raw content download, may be outdated): 93 | ``` 94 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh && sudo curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/pushplus_notifier.sh" | tr -d '\r' > /root/TrafficCop/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && bash /root/TrafficCop/pushplus_notifier.sh 95 | ``` 96 | ### Monitoring Only, No Notifications: 97 | ``` 98 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/trafficcop.sh" | tr -d '\r' > /root/TrafficCop/traffic_monitor.sh && chmod +x /root/TrafficCop/traffic_monitor.sh && bash /root/TrafficCop/traffic_monitor.sh 99 | ``` 100 | ## Useful Commands 101 | ### View Logs: 102 | ``` 103 | sudo tail -f -n 30 /root/TrafficCop/traffic_monitor.log 104 | ``` 105 | ### View Current Configuration: 106 | ``` 107 | sudo cat /root/TrafficCop/traffic_monitor_config.txt 108 | ``` 109 | ### Emergency Stop All traffic_monitor Processes (for when script issues occur): 110 | ``` 111 | sudo pkill -f traffic_monitor.sh 112 | ``` 113 | ### One-Click Remove Speed Limit 114 | ``` 115 | sudo curl -sSL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/remove_traffic_limit.sh | sudo bash 116 | ``` 117 | 118 | ## Script Logic 119 | - Automatically detects and selects the main network interface for traffic limiting. 120 | - Users select traffic statistics mode (four options). 121 | - Users set traffic calculation cycle (month/quarter/year) and start date. 122 | - Users input traffic limit and error tolerance range. 123 | - Users select limiting mode (TC mode or shutdown mode). 124 | - For TC mode, users can set speed limit values. 125 | - Script checks traffic consumption every minute and executes appropriate actions when limits are reached. 126 | - Automatically removes restrictions when a new traffic cycle begins. 127 | 128 | ## Script Features 129 | - Four comprehensive traffic statistics modes, adaptable to various VPS billing methods. 130 | - Custom traffic calculation cycles and start dates. 131 | - Custom traffic error tolerance range. 132 | - Interactive configuration, parameters can be modified at any time. 133 | - Real-time traffic statistics prompts. 134 | - TC mode ensures SSH connections remain available. 135 | - Shutdown mode provides stricter traffic control. 136 | - Custom bandwidth throttling (TC mode). 137 | 138 | ## Telegram Bot Integration 139 | TrafficCop now integrates Telegram Bot functionality, which can send the following notifications: 140 | 141 | - Speed limit warnings 142 | - Speed limit removal notifications 143 | - New cycle start notifications 144 | - Shutdown warnings 145 | - Daily traffic reports 146 | 147 | **Supports custom hostnames - one bot can manage all your VPS instances!** 148 | 149 | **Supports custom daily traffic report times - you can set when each VPS notifies you, or set them all to the same time to enjoy the feeling of owning multiple VPS instances at once!** 150 | 151 | To use this feature, provide your Telegram Bot Token and Chat ID during script configuration. 152 | 153 | Telegram Bot Token is displayed when you create a bot. 154 | 155 | Chat ID can be obtained via: https://api.telegram.org/bot${BOT_TOKEN}/getUpdates 156 | 157 | ${BOT_TOKEN} is your Telegram Bot Token 158 | 159 | Chat ID can also be obtained more easily through bots like [username_to_id_bot](https://t.me/username_to_id_bot) 160 | 161 | ### Related Commands 162 | One-Click Push Script (API call, latest version, may return 403): 163 | ``` 164 | sudo apt update && mkdir -p /root/TrafficCop && curl -H "Accept: application/vnd.github.v3.raw" -fsSL "https://api.github.com/repos/ypq123456789/TrafficCop/contents/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 165 | ``` 166 | One-Click Push Script (Raw content download, may be outdated): 167 | ``` 168 | sudo apt update && mkdir -p /root/TrafficCop && curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/tg_notifier.sh" | tr -d '\r' > /root/TrafficCop/tg_notifier.sh && chmod +x /root/TrafficCop/tg_notifier.sh && bash /root/TrafficCop/tg_notifier.sh 169 | ``` 170 | View TG Push Scheduled Execution Log 171 | ``` 172 | sudo tail -f -n 30 /root/TrafficCop/tg_notifier_cron.log 173 | ``` 174 | View Current Status 175 | ``` 176 | sudo tail -f -n 30 /root/TrafficCop/last_traffic_notification 177 | ``` 178 | Kill All TG Push Processes 179 | ``` 180 | sudo pkill -f tg_notifier.sh && crontab -l | grep -v "tg_notifier.sh" | crontab - 181 | ``` 182 | 183 | Push notification example: 184 | ![image](https://github.com/ypq123456789/TrafficCop/assets/114487221/7674bb25-2771-47e3-a999-8701ef160c7c) 185 | 186 | ## PushPlus Integration 187 | TrafficCop now integrates PushPlus notification functionality. 188 | 189 | Notification types are the same as above, with support for custom hostnames and custom daily traffic report times. 190 | 191 | To use this feature, provide your PushPlus token during script configuration. 192 | 193 | ### Related Commands 194 | One-Click Push Script (API call, latest version, may return 403): 195 | ``` 196 | sudo bash -c "mkdir -p /root/TrafficCop && curl -sSfL -H 'Accept: application/vnd.github.v3.raw' -o /root/TrafficCop/pushplus_notifier.sh https://api.github.com/repos/ypq123456789/TrafficCop/contents/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && /root/TrafficCop/pushplus_notifier.sh" 197 | ``` 198 | One-Click Push Script (Raw content download, may be outdated): 199 | ``` 200 | sudo mkdir -p /root/TrafficCop && curl -sSfL -o /root/TrafficCop/pushplus_notifier.sh https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/pushplus_notifier.sh && chmod +x /root/TrafficCop/pushplus_notifier.sh && /root/TrafficCop/pushplus_notifier.sh 201 | ``` 202 | View PushPlus Push Scheduled Execution Log 203 | ``` 204 | sudo tail -f -n 30 /root/TrafficCop/pushplus_notifier_cron.log 205 | ``` 206 | View Current Status 207 | ``` 208 | sudo tail -f -n 30 /root/TrafficCop/last_pushplus_notification 209 | ``` 210 | Kill All PushPlus Push Processes 211 | ``` 212 | sudo pkill -f pushplus_notifier.sh && crontab -l | grep -v "pushplus_notifier.sh" | crontab - 213 | ``` 214 | 215 | Push notification example: 216 | ![Screenshot_20240707_022328_com tencent mm](https://github.com/ypq123456789/TrafficCop/assets/114487221/c32c1ba1-1082-4f01-a26c-25608e9e3c29) 217 | 218 | ## Port Traffic Limit Feature 2.0 (NEW - Multi-Port Support) 219 | 220 | TrafficCop now supports setting independent traffic limits for **multiple ports** simultaneously! This feature is ideal for scenarios requiring fine-grained traffic management for specific services (such as web servers, proxy services, SSH, etc.). 221 | 222 | ### Features 223 | 224 | 1. **Multi-Port Traffic Management** - Monitor and limit traffic for multiple ports simultaneously, using JSON format for configuration storage 225 | 2. **Independent Port Traffic Statistics** - Use iptables to accurately track inbound and outbound traffic for each port 226 | 3. **Real-Time Traffic Viewing** - Colorful visual interface displaying all ports' traffic usage with progress bars 227 | 4. **Smart Configuration Sync** - Automatically sync machine configuration, also supports custom configuration 228 | 5. **Flexible Limit Strategies** - Supports two limit modes: 229 | - TC Mode: Throttle port traffic speed 230 | - Block Mode: Completely block port traffic when limit exceeded 231 | 6. **Configuration Validation** - Ensures port traffic limit does not exceed total machine traffic limit 232 | 7. **Push Notification Integration** - All push services (Telegram, PushPlus, ServerChan) automatically include port traffic information 233 | 8. **Automated Management** - Supports scheduled tasks for automatic monitoring and limiting 234 | 235 | ### Usage Logic 236 | 237 | #### Scenario 1: Machine Has No Traffic Limit 238 | When the machine has no configured traffic limit, setting a traffic limit for a specific port will: 239 | 1. Create port traffic configuration 240 | 2. Ask whether to sync configuration to machine traffic limit 241 | 3. If sync is selected, port configuration will automatically apply at machine level 242 | 243 | #### Scenario 2: Machine Already Has Traffic Limit 244 | When the machine already has a configured traffic limit, setting a traffic limit for a specific port will: 245 | 1. Check that port traffic limit is less than or equal to machine traffic limit 246 | 2. Default to inheriting other machine configurations (statistics mode, period, limit mode, etc.) 247 | 3. Allow custom configuration for special needs 248 | 249 | ### Installation and Configuration 250 | 251 | #### Method 1: Via Manager Script (Recommended) 252 | ```bash 253 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 254 | ``` 255 | Select "5) Install Port Traffic Limit" 256 | 257 | #### Method 2: Run Script Directly 258 | ```bash 259 | sudo mkdir -p /root/TrafficCop && \ 260 | curl -fsSL "https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/port_traffic_limit.sh" | tr -d '\r' > /root/TrafficCop/port_traffic_limit.sh && \ 261 | chmod +x /root/TrafficCop/port_traffic_limit.sh && \ 262 | bash /root/TrafficCop/port_traffic_limit.sh 263 | ``` 264 | 265 | ### Configuration Options 266 | 267 | During configuration, you need to provide: 268 | 269 | 1. **Port Number** - The port to limit traffic for (1-65535) 270 | 2. **Traffic Limit** - Maximum traffic allowed for the port (GB) 271 | 3. **Tolerance Range** - Buffer before triggering limit (GB) 272 | 4. **Configuration Method** - Choose to use machine configuration or custom configuration 273 | 274 | If choosing custom configuration, you also need to set: 275 | - Traffic statistics mode (outbound/inbound/total/max) 276 | - Statistics period (monthly/quarterly/yearly) 277 | - Period start day 278 | - Limit mode (TC throttle/block) 279 | - Throttle value (TC mode only) 280 | 281 | ### Related Commands 282 | 283 | #### View All Port Traffic (Recommended) 284 | ```bash 285 | # Normal view 286 | sudo bash /root/TrafficCop/view_port_traffic.sh 287 | 288 | # Real-time monitoring (refresh every 5 seconds) 289 | sudo bash /root/TrafficCop/view_port_traffic.sh --realtime 290 | 291 | # Export JSON report 292 | sudo bash /root/TrafficCop/view_port_traffic.sh --export 293 | ``` 294 | 295 | #### Manage Port Configuration 296 | ```bash 297 | # Open interactive configuration menu 298 | sudo bash /root/TrafficCop/port_traffic_limit.sh 299 | 300 | # Delete specific port 301 | sudo bash /root/TrafficCop/port_traffic_limit.sh --remove 80 302 | 303 | # Delete all port configurations 304 | sudo bash /root/TrafficCop/port_traffic_limit.sh --remove 305 | ``` 306 | 307 | #### Access via Manager (Recommended) 308 | ```bash 309 | bash <(curl -sL https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/trafficcop-manager.sh) 310 | # Select 12) View Port Traffic 311 | # Select 13) Manage Port Configuration 312 | ``` 313 | 314 | #### View Configuration File 315 | ```bash 316 | # View JSON configuration (multi-port) 317 | sudo cat /root/TrafficCop/ports_traffic_config.json 318 | 319 | # Pretty print 320 | sudo cat /root/TrafficCop/ports_traffic_config.json | jq 321 | ``` 322 | 323 | ### Usage Example 324 | 325 | #### Scenario: Configure Independent Traffic Limits for Multiple Services 326 | 327 | Suppose your machine has a 1TB total traffic limit, and you want to set independent traffic quotas for different services: 328 | 329 | **Configure Multiple Ports:** 330 | 1. Run port configuration script 331 | 2. Add port 80 (Web service): 200GB limit, 10GB tolerance 332 | 3. Add port 443 (HTTPS): 300GB limit, 15GB tolerance 333 | 4. Add port 22 (SSH): 50GB limit, 5GB tolerance 334 | 335 | **View All Port Traffic in Real-Time:** 336 | ```bash 337 | sudo bash /root/TrafficCop/view_port_traffic.sh 338 | ``` 339 | 340 | Example Output: 341 | ``` 342 | ════════════════════════════════════════════════════════════ 343 | Port Traffic Monitor - 2025-10-18 15:30:45 344 | ════════════════════════════════════════════════════════════ 345 | 346 | ✅ Port 80 (Web Server) 347 | Used: 150.2 GB / 200 GB (75.1%) 348 | [████████████████████████ ] Limit: 20kbit/s 349 | 350 | 🟡 Port 443 (HTTPS) 351 | Used: 280.5 GB / 300 GB (93.5%) 352 | [████████████████████████████ ] Limit: 50kbit/s 353 | 354 | ✅ Port 22 (SSH) 355 | Used: 15.3 GB / 50 GB (30.6%) 356 | [█████████ ] Limit: 10kbit/s 357 | 358 | ════════════════════════════════════════════════════════════ 359 | Total: 3 ports | Total Usage: 446.0 GB | Total Limit: 550 GB 360 | ════════════════════════════════════════════════════════════ 361 | ``` 362 | 363 | **Push Notification Example (Telegram):** 364 | ``` 365 | 📊 [MyServer] Daily Traffic Report 366 | 367 | 🖥️ Total Machine Traffic: 368 | Current Usage: 650.5 GB 369 | Traffic Limit: 1000 GB 370 | 371 | 🔌 Port Traffic Details: 372 | ✅ Port 80 (Web Server): 150.2GB / 200GB (75.1%) 373 | 🟡 Port 443 (HTTPS): 280.5GB / 300GB (93.5%) 374 | ✅ Port 22 (SSH): 15.3GB / 50GB (30.6%) 375 | ``` 376 | 377 | When a port's traffic reaches its limit: 378 | - **TC Mode**: Port speed will be limited to the set value (e.g., 20kbit/s) 379 | - **Block Mode**: Port will be completely blocked, unable to receive or send data 380 | - **Auto Notification**: All configured push services will send warning notifications 381 | 382 | ### Technical Principles 383 | 384 | Port traffic limit feature is implemented using: 385 | 386 | 1. **iptables** - Create rules to track traffic for specific ports 387 | 2. **tc (Traffic Control)** - Implement port-level traffic control and throttling 388 | 3. **HTB (Hierarchical Token Bucket)** - Hierarchical traffic control, allocating different bandwidth to different ports 389 | 4. **Packet Marking** - Use mangle table to mark packets for precise traffic classification 390 | 391 | ### Configuration File Format (JSON) 392 | 393 | ```json 394 | { 395 | "ports": [ 396 | { 397 | "port": 80, 398 | "description": "Web Server", 399 | "traffic_limit": 200, 400 | "traffic_tolerance": 10, 401 | "traffic_mode": "total", 402 | "traffic_period": "monthly", 403 | "period_start_day": 1, 404 | "limit_speed": 20, 405 | "main_interface": "eth0", 406 | "limit_mode": "tc", 407 | "created_at": "2025-10-18 12:00:00", 408 | "last_reset": "2025-10-01" 409 | } 410 | ] 411 | } 412 | ``` 413 | 414 | ### Important Notes 415 | 416 | 1. **Dependencies**: Requires jq tool (automatically installed by script) and iptables 417 | 2. **Traffic Statistics**: Port traffic statistics start from configuration time, do not include historical traffic 418 | 3. **Prerequisites**: It's recommended to run the main traffic monitor script first to ensure dependencies are installed 419 | 4. **Performance Impact**: TC mode may have slight impact on port performance 420 | 5. **Use with Caution**: Block mode completely prohibits port communication, use with caution 421 | 6. **Multi-Port Support**: ✅ Version 2.0 now supports configuring and managing multiple ports simultaneously 422 | 7. **Push Integration**: Port traffic information is integrated into all push notifications 423 | 424 | ## Preset Configurations 425 | ### Alibaba Cloud CDT 200GB: 426 | ``` 427 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-200g && cat /root/TrafficCop/traffic_monitor_config.txt 428 | ``` 429 | ### Alibaba Cloud CDT 20GB: 430 | ``` 431 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-20g && cat /root/TrafficCop/traffic_monitor_config.txt 432 | ``` 433 | ### Alibaba Cloud Lightweight 1TB: 434 | ``` 435 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/ali-1T && cat /root/TrafficCop/traffic_monitor_config.txt 436 | ``` 437 | ### Azure Student 15GB: 438 | ``` 439 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/az-15g && cat /root/TrafficCop/traffic_monitor_config.txt 440 | ``` 441 | ### Azure Student 115GB: 442 | ``` 443 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/az-115g && cat /root/TrafficCop/traffic_monitor_config.txt 444 | ``` 445 | 446 | ### GCP 625GB [High Traffic Ultimate Solution](https://www.nodeseek.com/post-115166-1): 447 | ``` 448 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/GCP-625g && cat /root/TrafficCop/traffic_monitor_config.txt 449 | ``` 450 | ### GCP 200GB (Standard route with 200GB free traffic): 451 | ``` 452 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/GCP-200g && cat /root/TrafficCop/traffic_monitor_config.txt 453 | ``` 454 | ### Alice 1500GB: 455 | ``` 456 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/alice-1500g && cat /root/TrafficCop/traffic_monitor_config.txt 457 | ``` 458 | ### Asia Cloud 300GB: 459 | ``` 460 | sudo curl -o /root/TrafficCop/traffic_monitor_config.txt https://raw.githubusercontent.com/ypq123456789/TrafficCop/main/asia-300g && cat /root/TrafficCop/traffic_monitor_config.txt 461 | ``` 462 | ## Star History 463 | 464 | [![Star History Chart](https://api.star-history.com/svg?repos=ypq123456789/TrafficCop&type=Date)](https://star-history.com/#ypq123456789/TrafficCop&Date) 465 | 466 | ## Telegram Group: 467 | https://t.me/+ydvXl1_OBBBiZWM1 468 | 469 | ## Support the Author 470 | Thank you very much for your interest in this project! Maintaining open-source projects requires a significant investment of time and energy. If you find this project valuable, please consider offering some support, even if it's just the cost of a cup of coffee. 471 | Your generous assistance will motivate me to continue improving this project and make it more practical. It will also allow me to focus more on open-source community work. If you'd like to provide sponsorship, you can do so through the following channels: 472 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 |
WeChatAlipay
WeChatAlipay
490 | -------------------------------------------------------------------------------- /tg_notifier.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 设置新的工作目录 4 | WORK_DIR="/root/TrafficCop" 5 | mkdir -p "$WORK_DIR" 6 | 7 | # 导入端口流量辅助函数 8 | if [ -f "$WORK_DIR/port_traffic_helper.sh" ]; then 9 | source "$WORK_DIR/port_traffic_helper.sh" 10 | fi 11 | 12 | # 更新文件路径 13 | CONFIG_FILE="$WORK_DIR/tg_notifier_config.txt" 14 | LOG_FILE="$WORK_DIR/traffic_monitor.log" 15 | LAST_NOTIFICATION_FILE="$WORK_DIR/last_traffic_notification" 16 | SCRIPT_PATH="$WORK_DIR/tg_notifier.sh" 17 | CRON_LOG="$WORK_DIR/tg_notifier_cron.log" 18 | 19 | # 文件迁移函数 20 | migrate_files() { 21 | # 迁移配置文件 22 | if [ -f "/root/tg_notifier_config.txt" ]; then 23 | mv "/root/tg_notifier_config.txt" "$CONFIG_FILE" 24 | fi 25 | 26 | # 迁移日志文件 27 | if [ -f "/root/traffic_monitor.log" ]; then 28 | mv "/root/traffic_monitor.log" "$LOG_FILE" 29 | fi 30 | 31 | # 迁移最后通知文件 32 | if [ -f "/tmp/last_traffic_notification" ]; then 33 | mv "/tmp/last_traffic_notification" "$LAST_NOTIFICATION_FILE" 34 | fi 35 | 36 | # 迁移脚本文件 37 | if [ -f "/root/tg_notifier.sh" ]; then 38 | mv "/root/tg_notifier.sh" "$SCRIPT_PATH" 39 | fi 40 | 41 | # 迁移 cron 日志文件 42 | if [ -f "/root/tg_notifier_cron.log" ]; then 43 | mv "/root/tg_notifier_cron.log" "$CRON_LOG" 44 | fi 45 | 46 | # 更新 crontab 中的脚本路径 47 | if crontab -l | grep -q "/root/tg_notifier.sh"; then 48 | crontab -l | sed "s|/root/tg_notifier.sh|$SCRIPT_PATH|g" | crontab - 49 | fi 50 | 51 | echo "$(date '+%Y-%m-%d %H:%M:%S') 文件已迁移到新的工作目录: $WORK_DIR" | tee -a "$CRON_LOG" 52 | } 53 | 54 | # 在脚本开始时调用迁移函数 55 | migrate_files 56 | 57 | # 切换到工作目录 58 | cd "$WORK_DIR" || exit 1 59 | 60 | # 设置时区为上海(东八区) 61 | export TZ='Asia/Shanghai' 62 | 63 | # 端口流量数据缓存文件 64 | PORT_DATA_CACHE="/tmp/port_traffic_cache.json" 65 | 66 | echo "----------------------------------------------"| tee -a "$CRON_LOG" 67 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 版本号:9.6" 68 | 69 | # 检查是否有同名的 crontab 正在执行: 70 | check_running() { 71 | # 新增:添加日志 72 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查是否有其他实例运行" >> "$CRON_LOG" 73 | if pidof -x "$(basename "\$0")" -o $$ > /dev/null; then 74 | # 新增:添加日志 75 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 另一个脚本实例正在运行,退出脚本" >> "$CRON_LOG" 76 | echo "另一个脚本实例正在运行,退出脚本" 77 | exit 1 78 | fi 79 | # 新增:添加日志 80 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有其他实例运行,继续执行" >> "$CRON_LOG" 81 | } 82 | 83 | 84 | # 函数:获取非空输入 85 | get_valid_input() { 86 | local prompt="${1:-"请输入:"}" 87 | local input="" 88 | while true; do 89 | read -p "${prompt}" input 90 | if [[ -n "${input}" ]]; then 91 | echo "${input}" 92 | return 93 | else 94 | echo "输入不能为空,请重新输入。" 95 | fi 96 | done 97 | } 98 | 99 | # 保存端口流量数据到缓存(带历史记录和详细调试) 100 | save_port_traffic_data() { 101 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 开始执行 save_port_traffic_data"| tee -a "$CRON_LOG" 102 | 103 | if [ -f "$WORK_DIR/view_port_traffic.sh" ]; then 104 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 找到 view_port_traffic.sh 文件"| tee -a "$CRON_LOG" 105 | 106 | # 详细记录执行环境 107 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] WORK_DIR=$WORK_DIR"| tee -a "$CRON_LOG" 108 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] PWD=$(pwd)"| tee -a "$CRON_LOG" 109 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 执行命令: cd $WORK_DIR && PATH='/usr/sbin:/usr/bin:/sbin:/bin:$PATH' bash view_port_traffic.sh --json"| tee -a "$CRON_LOG" 110 | 111 | local port_data 112 | port_data=$(cd "$WORK_DIR" && PATH="/usr/sbin:/usr/bin:/sbin:/bin:$PATH" bash view_port_traffic.sh --json 2>/dev/null) 113 | local exit_code=$? 114 | 115 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] view_port_traffic.sh 退出码: $exit_code"| tee -a "$CRON_LOG" 116 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 原始输出长度: ${#port_data} 字符"| tee -a "$CRON_LOG" 117 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 原始输出前200字符: $(echo "$port_data" | head -c 200)"| tee -a "$CRON_LOG" 118 | 119 | local timestamp=$(date '+%Y-%m-%d_%H:%M:%S') 120 | local caller_info="" 121 | 122 | # 识别调用来源 123 | if [[ "${BASH_SOURCE[1]}" == *"tg_notifier.sh"* ]]; then 124 | local line_num=$(caller 0 | cut -d' ' -f1) 125 | caller_info="_line${line_num}" 126 | fi 127 | 128 | if [ -n "$port_data" ]; then 129 | # 创建带时间戳的历史缓存文件 130 | local history_cache="/tmp/port_traffic_cache_${timestamp}${caller_info}.json" 131 | local tmpfile="${history_cache}.tmp.$$" 132 | 133 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 尝试解析JSON并添加元数据"| tee -a "$CRON_LOG" 134 | 135 | # 先写到临时文件并附加元数据,再验证JSON结构 136 | echo "$port_data" | jq ". + {\"timestamp\": \"$(date '+%Y-%m-%d %H:%M:%S')\", \"data_source\": \"manual\", \"caller\": \"${caller_info}\", \"exit_code\": $exit_code}" > "$tmpfile" 2>/dev/null || true 137 | 138 | if [ -s "$tmpfile" ] && jq -e '.ports' "$tmpfile" >/dev/null 2>&1; then 139 | mv -f "$tmpfile" "$history_cache" 140 | chmod 644 "$history_cache" 2>/dev/null || true 141 | 142 | # 创建/更新最新缓存的符号链接 143 | ln -sf "$history_cache" "$PORT_DATA_CACHE" 2>/dev/null || cp "$history_cache" "$PORT_DATA_CACHE" 144 | 145 | # 记录详细日志,包括数据预览 146 | local usage_summary=$(echo "$port_data" | jq -r '.ports[] | "\(.port):\(.usage)GB"' 2>/dev/null | tr '\n' ' ' || echo "无法解析端口数据") 147 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 端口流量数据已保存到缓存 $history_cache"| tee -a "$CRON_LOG" 148 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 缓存数据摘要: $usage_summary"| tee -a "$CRON_LOG" 149 | 150 | # 清理超过24小时的历史缓存文件 151 | find /tmp -name "port_traffic_cache_*" -type f -mtime +1 -delete 2>/dev/null || true 152 | else 153 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 生成缓存失败,临时文件无效或JSON解析失败"| tee -a "$CRON_LOG" 154 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 临时文件大小: $(wc -c "$tmpfile" 2>/dev/null || echo "文件不存在")"| tee -a "$CRON_LOG" 155 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 原始数据: $port_data"| tee -a "$CRON_LOG" 156 | rm -f "$tmpfile" 2>/dev/null || true 157 | fi 158 | else 159 | echo "$(date '+%Y-%m-%d %H:%M:%S') : view_port_traffic.sh返回空数据"| tee -a "$CRON_LOG" 160 | fi 161 | else 162 | echo "$(date '+%Y-%m-%d %H:%M:%S') : view_port_traffic.sh 文件不存在: $WORK_DIR/view_port_traffic.sh"| tee -a "$CRON_LOG" 163 | fi 164 | 165 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] save_port_traffic_data 执行完成"| tee -a "$CRON_LOG" 166 | } 167 | 168 | # 从缓存加载端口流量数据 169 | load_port_traffic_data() { 170 | if [ -f "$PORT_DATA_CACHE" ]; then 171 | local cache_age=$(( $(date +%s) - $(stat -c %Y "$PORT_DATA_CACHE" 2>/dev/null || echo 0) )) 172 | local cache_age_minutes=$(( cache_age / 60 )) 173 | 174 | if [ $cache_age_minutes -le 60 ]; then 175 | # 先校验缓存文件是否为有效JSON并包含ports字段 176 | if [ ! -s "$PORT_DATA_CACHE" ]; then 177 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 缓存文件存在但为空,删除并返回"| tee -a "$CRON_LOG" 178 | rm -f "$PORT_DATA_CACHE" 2>/dev/null || true 179 | return 180 | fi 181 | if ! jq -e '.ports' "$PORT_DATA_CACHE" >/dev/null 2>&1; then 182 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 缓存文件不是有效JSON或缺少ports字段,删除并返回"| tee -a "$CRON_LOG" 183 | rm -f "$PORT_DATA_CACHE" 2>/dev/null || true 184 | return 185 | fi 186 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 读取端口流量缓存,文件年龄: ${cache_age_minutes}分钟"| tee -a "$CRON_LOG" >&2 187 | cat "$PORT_DATA_CACHE" 2>/dev/null 188 | else 189 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 端口流量缓存已过期(${cache_age_minutes}分钟),删除缓存文件"| tee -a "$CRON_LOG" 190 | rm -f "$PORT_DATA_CACHE" 191 | fi 192 | fi 193 | } 194 | 195 | # 读取配置 196 | read_config() { 197 | if [ ! -f "$CONFIG_FILE" ] || [ ! -s "$CONFIG_FILE" ]; then 198 | echo "配置文件不存在或为空,需要进行初始化配置。" 199 | return 1 200 | fi 201 | 202 | # 读取配置文件 203 | source "$CONFIG_FILE" 204 | 205 | # 检查必要的配置项是否都存在 206 | if [ -z "$BOT_TOKEN" ] || [ -z "$CHAT_ID" ] || [ -z "$MACHINE_NAME" ] || [ -z "$DAILY_REPORT_TIME" ]; then 207 | echo "配置文件不完整,需要重新进行配置。" 208 | return 1 209 | fi 210 | 211 | return 0 212 | } 213 | 214 | # 写入配置 215 | write_config() { 216 | cat > "$CONFIG_FILE" << EOF 217 | BOT_TOKEN="$BOT_TOKEN" 218 | CHAT_ID="$CHAT_ID" 219 | DAILY_REPORT_TIME="$DAILY_REPORT_TIME" 220 | MACHINE_NAME="$MACHINE_NAME" 221 | EOF 222 | echo "配置已保存到 $CONFIG_FILE" 223 | } 224 | 225 | 226 | # 初始配置 227 | initial_config() { 228 | echo "======================================" 229 | echo " 修改 Telegram 通知配置" 230 | echo "======================================" 231 | echo "" 232 | echo "提示:按 Enter 保留当前配置,输入新值则更新配置" 233 | echo "" 234 | 235 | local new_token new_chat_id new_machine_name new_daily_report_time 236 | 237 | # Bot Token 238 | if [ -n "$BOT_TOKEN" ]; then 239 | # 隐藏部分Token显示 240 | local token_display="${BOT_TOKEN:0:10}...${BOT_TOKEN: -4}" 241 | echo "请输入Telegram Bot Token [当前: $token_display]: " 242 | else 243 | echo "请输入Telegram Bot Token: " 244 | fi 245 | read -r new_token 246 | # 如果输入为空且有原配置,保留原配置 247 | if [[ -z "$new_token" ]] && [[ -n "$BOT_TOKEN" ]]; then 248 | new_token="$BOT_TOKEN" 249 | echo " → 保留原配置" 250 | fi 251 | # 如果还是空(首次配置),要求必须输入 252 | while [[ -z "$new_token" ]]; do 253 | echo "Bot Token 不能为空。请重新输入: " 254 | read -r new_token 255 | done 256 | 257 | # Chat ID 258 | if [ -n "$CHAT_ID" ]; then 259 | echo "请输入Telegram Chat ID [当前: $CHAT_ID]: " 260 | else 261 | echo "请输入Telegram Chat ID: " 262 | fi 263 | read -r new_chat_id 264 | if [[ -z "$new_chat_id" ]] && [[ -n "$CHAT_ID" ]]; then 265 | new_chat_id="$CHAT_ID" 266 | echo " → 保留原配置" 267 | fi 268 | while [[ -z "$new_chat_id" ]]; do 269 | echo "Chat ID 不能为空。请重新输入: " 270 | read -r new_chat_id 271 | done 272 | 273 | # 机器名称 274 | if [ -n "$MACHINE_NAME" ]; then 275 | echo "请输入机器名称 [当前: $MACHINE_NAME]: " 276 | else 277 | echo "请输入机器名称: " 278 | fi 279 | read -r new_machine_name 280 | if [[ -z "$new_machine_name" ]] && [[ -n "$MACHINE_NAME" ]]; then 281 | new_machine_name="$MACHINE_NAME" 282 | echo " → 保留原配置" 283 | fi 284 | while [[ -z "$new_machine_name" ]]; do 285 | echo "机器名称不能为空。请重新输入: " 286 | read -r new_machine_name 287 | done 288 | 289 | # 每日报告时间 290 | if [ -n "$DAILY_REPORT_TIME" ]; then 291 | echo "请输入每日报告时间 [当前: $DAILY_REPORT_TIME,格式 HH:MM]: " 292 | else 293 | echo "请输入每日报告时间 (时区已经固定为东八区,输入格式为 HH:MM,例如 01:00): " 294 | fi 295 | read -r new_daily_report_time 296 | if [[ -z "$new_daily_report_time" ]] && [[ -n "$DAILY_REPORT_TIME" ]]; then 297 | new_daily_report_time="$DAILY_REPORT_TIME" 298 | echo " → 保留原配置" 299 | fi 300 | while [[ ! $new_daily_report_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; do 301 | echo "时间格式不正确。请重新输入 (HH:MM): " 302 | read -r new_daily_report_time 303 | done 304 | 305 | # 更新配置文件(使用引号防止空格等特殊字符问题) 306 | BOT_TOKEN="$new_token" 307 | CHAT_ID="$new_chat_id" 308 | MACHINE_NAME="$new_machine_name" 309 | DAILY_REPORT_TIME="$new_daily_report_time" 310 | 311 | write_config 312 | 313 | echo "" 314 | echo "======================================" 315 | echo "配置已更新成功!" 316 | echo "======================================" 317 | echo "" 318 | read_config 319 | } 320 | 321 | # 发送限速警告 322 | send_throttle_warning() { 323 | local url="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 324 | local port_summary=$(get_port_traffic_summary_for_tg) 325 | local message="⚠️ [${MACHINE_NAME}]限速警告:流量已达到限制,已启动 TC 模式限速。${port_summary}" 326 | curl -s -X POST "$url" -d "chat_id=$CHAT_ID" -d "text=$message" 327 | } 328 | 329 | # 获取端口流量摘要(专为Telegram格式化) 330 | get_port_traffic_summary_for_tg() { 331 | # 如果有 port_traffic_helper.sh 中的函数,优先使用 332 | if command -v get_port_traffic_summary &> /dev/null; then 333 | local summary=$(get_port_traffic_summary 5) 334 | if [ -n "$summary" ]; then 335 | # 转换换行符为URL编码格式 336 | echo "$summary" | sed 's/\n/%0A/g' 337 | return 338 | fi 339 | fi 340 | 341 | # 兼容性实现(如果port_traffic_helper.sh不可用) 342 | local ports_config_file="$WORK_DIR/ports_traffic_config.json" 343 | local summary="" 344 | 345 | if [ ! -f "$ports_config_file" ]; then 346 | return 347 | fi 348 | 349 | # 检查是否有端口配置 350 | local port_count=$(jq -r '.ports | length' "$ports_config_file" 2>/dev/null) 351 | if [ -z "$port_count" ] || [ "$port_count" -eq 0 ]; then 352 | return 353 | fi 354 | 355 | summary="%0A%0A🔌 端口流量详情:" 356 | 357 | # 使用与view_port_traffic.sh相同的方法获取流量 358 | if [ -f "$WORK_DIR/view_port_traffic.sh" ]; then 359 | local port_data=$(bash "$WORK_DIR/view_port_traffic.sh" --json 2>/dev/null) 360 | if [ -n "$port_data" ]; then 361 | local max_display=5 362 | local displayed=0 363 | 364 | for ((i=0; i/dev/null) 366 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 367 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 368 | 369 | if [ -n "$port" ] && [ "$port" != "null" ]; then 370 | local port_percentage=0 371 | if (( $(echo "$port_limit > 0" | bc -l 2>/dev/null || echo "0") )); then 372 | port_percentage=$(echo "scale=0; ($port_usage / $port_limit) * 100" | bc 2>/dev/null || echo "0") 373 | fi 374 | summary="${summary}%0A✓ 端口 ${port}: ${port_usage}GB / ${port_limit}GB (${port_percentage}%)" 375 | displayed=$((displayed + 1)) 376 | fi 377 | done 378 | 379 | if [ "$port_count" -gt "$max_display" ]; then 380 | summary="${summary}%0A...及其他 $((port_count - max_display)) 个端口" 381 | fi 382 | fi 383 | fi 384 | 385 | echo "$summary" 386 | } 387 | 388 | # 发送限速解除通知 389 | send_throttle_lifted() { 390 | local url="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 391 | local port_summary=$(get_port_traffic_summary_for_tg) 392 | local message="✅ [${MACHINE_NAME}]限速解除:流量已恢复正常,所有限制已清除。${port_summary}" 393 | curl -s -X POST "$url" -d "chat_id=$CHAT_ID" -d "text=$message" 394 | } 395 | 396 | # 发送新周期开始通知 397 | send_new_cycle_notification() { 398 | local url="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 399 | local message="🔄 [${MACHINE_NAME}]新周期开始:新的流量统计周期已开始,之前的限速(如果有)已自动解除。" 400 | curl -s -X POST "$url" -d "chat_id=$CHAT_ID" -d "text=$message" 401 | } 402 | 403 | # 发送关机警告 404 | send_shutdown_warning() { 405 | local url="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 406 | local port_summary=$(get_port_traffic_summary_for_tg) 407 | local message="🚨 [${MACHINE_NAME}]关机警告:流量已达到严重限制,系统将在 1 分钟后关机!${port_summary}" 408 | curl -s -X POST "$url" -d "chat_id=$CHAT_ID" -d "text=$message" 409 | } 410 | 411 | 412 | 413 | 414 | test_telegram_notification() { 415 | local message="🔔 [${MACHINE_NAME}]这是一条测试消息。如果您收到这条消息,说明Telegram通知功能正常工作。" 416 | local response 417 | response=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ 418 | -d "chat_id=${CHAT_ID}" \ 419 | -d "text=${message}" \ 420 | -d "disable_notification=true") 421 | 422 | if echo "$response" | grep -q '"ok":true'; then 423 | echo "✅ [${MACHINE_NAME}]测试消息已成功发送,请检查您的Telegram。" 424 | else 425 | echo "❌ [${MACHINE_NAME}]发送测试消息失败。请检查您的BOT_TOKEN和CHAT_ID设置。" 426 | fi 427 | } 428 | 429 | check_and_notify() { 430 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始检查流量状态..."| tee -a "$CRON_LOG" 431 | 432 | local current_status="未知" 433 | local current_time=$(date '+%Y-%m-%d %H:%M:%S') 434 | local relevant_log="" 435 | 436 | # 从后往前读取日志文件,找到第一个包含相关信息的行 437 | relevant_log=$(tac "$LOG_FILE" | grep -m 1 -E "流量超出限制|使用 TC 模式限速|新的流量周期开始|流量正常,清除所有限制") 438 | 439 | # 记录相关的日志内容 440 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 相关的日志内容: $relevant_log"| tee -a "$CRON_LOG" 441 | 442 | # 确定当前状态 443 | if echo "$relevant_log" | grep -q "流量超出限制,系统将在 1 分钟后关机"; then 444 | current_status="关机" 445 | elif echo "$relevant_log" | grep -q "流量超出限制"; then 446 | current_status="限速" 447 | elif echo "$relevant_log" | grep -q "新的流量周期开始,重置限制"; then 448 | current_status="新周期" 449 | elif echo "$relevant_log" | grep -q "流量正常,清除所有限制"; then 450 | current_status="正常" 451 | fi 452 | 453 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前检测到的状态: $current_status"| tee -a "$CRON_LOG" 454 | 455 | local last_status="" 456 | if [ -f "$LAST_NOTIFICATION_FILE" ]; then 457 | last_status=$(tail -n 1 "$LAST_NOTIFICATION_FILE" | cut -d' ' -f3-) 458 | fi 459 | 460 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 上次记录的状态: $last_status"| tee -a "$CRON_LOG" 461 | 462 | # 根据状态调用相应的通知函数 463 | if [ "$current_status" = "限速" ] && [ "$last_status" != "限速" ]; then 464 | send_throttle_warning 465 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_warning"| tee -a "$CRON_LOG" 466 | elif [ "$current_status" = "正常" ] && [ "$last_status" = "限速" ]; then 467 | send_throttle_lifted 468 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_throttle_lifted"| tee -a "$CRON_LOG" 469 | elif [ "$current_status" = "新周期" ]; then 470 | send_new_cycle_notification 471 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_new_cycle_notification"| tee -a "$CRON_LOG" 472 | elif [ "$current_status" = "关机" ]; then 473 | send_shutdown_warning 474 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已调用 send_shutdown_warning"| tee -a "$CRON_LOG" 475 | elif [ "$current_status" = "未知" ]; then 476 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法识别当前状态,不发送通知"| tee -a "$CRON_LOG" 477 | else 478 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无需发送通知"| tee -a "$CRON_LOG" 479 | fi 480 | 481 | # 追加新状态到状态文件 482 | echo "$current_time $current_status" >> "$LAST_NOTIFICATION_FILE" 483 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已追加新状态到状态文件"| tee -a "$CRON_LOG" 484 | 485 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 流量检查完成。"| tee -a "$CRON_LOG" 486 | } 487 | 488 | 489 | 490 | 491 | # 设置定时任务 492 | setup_cron() { 493 | local correct_entry="* * * * * $SCRIPT_PATH -cron" 494 | local current_crontab=$(crontab -l 2>/dev/null) 495 | local tg_notifier_entries=$(echo "$current_crontab" | grep "tg_notifier.sh") 496 | local correct_entries_count=$(echo "$tg_notifier_entries" | grep -F "$correct_entry" | wc -l) 497 | 498 | if [ "$correct_entries_count" -eq 1 ]; then 499 | echo "正确的 crontab 项已存在且只有一个,无需修改。" 500 | else 501 | # 删除所有包含 tg_notifier.sh 的条目 502 | new_crontab=$(echo "$current_crontab" | grep -v "tg_notifier.sh") 503 | 504 | # 添加一个正确的条目 505 | new_crontab="${new_crontab} 506 | $correct_entry" 507 | 508 | # 更新 crontab 509 | echo "$new_crontab" | crontab - 510 | 511 | echo "已更新 crontab。删除了所有旧的 tg_notifier.sh 条目,并添加了一个每分钟执行的条目。" 512 | fi 513 | 514 | # 显示当前的 crontab 内容 515 | echo "当前的 crontab 内容:" 516 | crontab -l 517 | } 518 | 519 | # 更新cron任务中的时间(当修改每日报告时间时调用) 520 | update_cron_time() { 521 | local new_time="$1" 522 | echo "正在更新cron任务时间为: $new_time" 523 | 524 | # 重新读取配置以获取最新时间 525 | read_config 526 | 527 | # 重新设置cron任务 528 | setup_cron 529 | 530 | echo "cron任务时间已更新" 531 | } 532 | 533 | # 每日报告 534 | daily_report() { 535 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 开始生成每日报告"| tee -a "$CRON_LOG" 536 | echo "$(date '+%Y-%m-%d %H:%M:%S') : DAILY_REPORT_TIME=$DAILY_REPORT_TIME"| tee -a "$CRON_LOG" 537 | echo "$(date '+%Y-%m-%d %H:%M:%S') : BOT_TOKEN=${BOT_TOKEN:0:5}... CHAT_ID=$CHAT_ID"| tee -a "$CRON_LOG" 538 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 日志文件路径: $LOG_FILE"| tee -a "$CRON_LOG" 539 | 540 | # 反向读取日志文件,查找第一个同时包含"当前使用流量"和"限制流量"的行 541 | local usage_line=$(tac "$LOG_FILE" | grep -m 1 -E "当前使用流量:.*限制流量:") 542 | 543 | if [[ -z "$usage_line" ]]; then 544 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法在日志中找到同时包含当前使用流量和限制流量的行"| tee -a "$CRON_LOG" 545 | return 1 546 | fi 547 | 548 | local current_usage=$(echo "$usage_line" | grep -oP '当前使用流量:\s*\K[0-9.]+ [GBMKgbmk]+') 549 | local limit=$(echo "$usage_line" | grep -oP '限制流量:\s*\K[0-9.]+ [GBMKgbmk]+') 550 | 551 | if [[ -z "$current_usage" || -z "$limit" ]]; then 552 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法从行中提取流量信息"| tee -a "$CRON_LOG" 553 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 问题行: $usage_line"| tee -a "$CRON_LOG" 554 | return 1 555 | fi 556 | 557 | # 构建基础消息 558 | local message="📊 [${MACHINE_NAME}]每日流量报告%0A%0A🖥️ 机器总流量:%0A当前使用:$current_usage%0A流量限制:$limit" 559 | 560 | # 检查是否有端口流量配置 561 | local ports_config_file="$WORK_DIR/ports_traffic_config.json" 562 | local view_script="$WORK_DIR/view_port_traffic.sh" 563 | 564 | if [ -f "$ports_config_file" ]; then 565 | local port_count=$(jq -r '.ports | length' "$ports_config_file" 2>/dev/null || echo "0") 566 | 567 | if [ "$port_count" -gt 0 ]; then 568 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到 $port_count 个端口流量配置,添加端口信息"| tee -a "$CRON_LOG" 569 | 570 | # 尝试从缓存加载准确的端口数据 571 | local port_data=$(load_port_traffic_data) 572 | 573 | # 调试:显示获取到的端口数据(只显示前100个字符避免日志过长) 574 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 获取到的端口数据长度: ${#port_data}字符"| tee -a "$CRON_LOG" 575 | if [ -n "$port_data" ]; then 576 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 数据预览: $(echo "$port_data" | head -c 100)..."| tee -a "$CRON_LOG" 577 | fi 578 | 579 | if [ -n "$port_data" ] && echo "$port_data" | jq -e '.ports' >/dev/null 2>&1; then 580 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 使用缓存的端口流量数据"| tee -a "$CRON_LOG" 581 | local actual_port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null || echo "0") 582 | 583 | if [ "$actual_port_count" -gt 0 ]; then 584 | message="${message}%0A%0A🔌 端口流量详情:" 585 | 586 | # 遍历每个端口 587 | local i=0 588 | while [ $i -lt $actual_port_count ]; do 589 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 590 | local port_desc=$(echo "$port_data" | jq -r ".ports[$i].description" 2>/dev/null) 591 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 592 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 593 | 594 | # 调试:显示每个端口的原始数据 595 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 端口[$i] port=$port, desc=$port_desc, usage=$port_usage, limit=$port_limit"| tee -a "$CRON_LOG" 596 | 597 | if [ -n "$port" ] && [ "$port" != "null" ] && [ "$port_usage" != "null" ]; then 598 | # 格式化流量显示(保留2位小数) 599 | local port_usage_formatted=$(printf "%.2f" "$port_usage" 2>/dev/null || echo "$port_usage") 600 | local port_limit_formatted=$(printf "%.2f" "$port_limit" 2>/dev/null || echo "$port_limit") 601 | 602 | # 根据使用率选择表情 603 | local port_percentage=0 604 | if [ -n "$port_limit" ] && [ "$port_limit" != "null" ] && (( $(echo "$port_limit > 0" | bc -l 2>/dev/null || echo "0") )); then 605 | port_percentage=$(printf "%.2f" $(echo "scale=2; ($port_usage / $port_limit) * 100" | bc 2>/dev/null || echo "0")) 606 | fi 607 | 608 | local status_icon="✅" 609 | if (( $(echo "$port_percentage >= 90" | bc -l 2>/dev/null || echo "0") )); then 610 | status_icon="🔴" 611 | elif (( $(echo "$port_percentage >= 75" | bc -l 2>/dev/null || echo "0") )); then 612 | status_icon="🟡" 613 | fi 614 | 615 | message="${message}%0A${status_icon} 端口 ${port} (${port_desc}):${port_usage_formatted}GB / ${port_limit_formatted}GB" 616 | fi 617 | 618 | i=$((i + 1)) 619 | done 620 | 621 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已添加 $actual_port_count 个端口的流量信息"| tee -a "$CRON_LOG" 622 | else 623 | echo "$(date '+%Y-%m-%d %H:%M:%S') : JSON数据中没有端口信息"| tee -a "$CRON_LOG" 624 | fi 625 | else 626 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 无法获取缓存的端口流量数据,尝试实时获取"| tee -a "$CRON_LOG" 627 | 628 | # 备用方案:尝试实时获取数据 629 | if [ -f "$view_script" ]; then 630 | local fallback_data=$(bash "$view_script" --json 2>/dev/null) 631 | if [ -n "$fallback_data" ] && echo "$fallback_data" | jq -e '.ports' >/dev/null 2>&1; then 632 | port_data="$fallback_data" 633 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 使用实时端口流量数据作为备用"| tee -a "$CRON_LOG" 634 | # 重新处理端口数据 635 | local actual_port_count=$(echo "$port_data" | jq -r '.ports | length' 2>/dev/null || echo "0") 636 | if [ "$actual_port_count" -gt 0 ]; then 637 | message="${message}%0A%0A🔌 端口流量详情:" 638 | local i=0 639 | while [ $i -lt $actual_port_count ]; do 640 | local port=$(echo "$port_data" | jq -r ".ports[$i].port" 2>/dev/null) 641 | local port_desc=$(echo "$port_data" | jq -r ".ports[$i].description" 2>/dev/null) 642 | local port_usage=$(echo "$port_data" | jq -r ".ports[$i].usage" 2>/dev/null) 643 | local port_limit=$(echo "$port_data" | jq -r ".ports[$i].limit" 2>/dev/null) 644 | 645 | if [ -n "$port" ] && [ "$port" != "null" ] && [ "$port_usage" != "null" ]; then 646 | local port_usage_formatted=$(printf "%.2f" "$port_usage" 2>/dev/null || echo "$port_usage") 647 | local port_limit_formatted=$(printf "%.2f" "$port_limit" 2>/dev/null || echo "$port_limit") 648 | 649 | local port_percentage=0 650 | if [ -n "$port_limit" ] && [ "$port_limit" != "null" ] && (( $(echo "$port_limit > 0" | bc -l 2>/dev/null || echo "0") )); then 651 | port_percentage=$(printf "%.2f" $(echo "scale=2; ($port_usage / $port_limit) * 100" | bc 2>/dev/null || echo "0")) 652 | fi 653 | 654 | local status_icon="✅" 655 | if (( $(echo "$port_percentage >= 90" | bc -l 2>/dev/null || echo "0") )); then 656 | status_icon="🔴" 657 | elif (( $(echo "$port_percentage >= 75" | bc -l 2>/dev/null || echo "0") )); then 658 | status_icon="🟡" 659 | fi 660 | 661 | message="${message}%0A${status_icon} 端口 ${port} (${port_desc}):${port_usage_formatted}GB / ${port_limit_formatted}GB" 662 | fi 663 | i=$((i + 1)) 664 | done 665 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 已添加 $actual_port_count 个端口的流量信息(备用数据)"| tee -a "$CRON_LOG" 666 | fi 667 | else 668 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 实时数据获取也失败,跳过端口流量显示"| tee -a "$CRON_LOG" 669 | fi 670 | else 671 | echo "$(date '+%Y-%m-%d %H:%M:%S') : view_port_traffic.sh脚本不存在,跳过端口流量显示"| tee -a "$CRON_LOG" 672 | fi 673 | fi 674 | else 675 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 没有配置端口流量监控"| tee -a "$CRON_LOG" 676 | fi 677 | else 678 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 端口配置文件不存在"| tee -a "$CRON_LOG" 679 | fi 680 | 681 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 准备发送消息"| tee -a "$CRON_LOG" 682 | 683 | # 调试:显示即将发送的消息内容 684 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] 发送到TG的消息内容:"| tee -a "$CRON_LOG" 685 | echo "$(date '+%Y-%m-%d %H:%M:%S') : [调试] $message"| tee -a "$CRON_LOG" 686 | 687 | local url="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 688 | local response 689 | 690 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 尝试发送Telegram消息"| tee -a "$CRON_LOG" 691 | 692 | response=$(curl -s -X POST "$url" -d "chat_id=$CHAT_ID" -d "text=$message") 693 | 694 | if echo "$response" | grep -q '"ok":true'; then 695 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功"| tee -a "$CRON_LOG" 696 | return 0 697 | else 698 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败. 响应: $response"| tee -a "$CRON_LOG" 699 | return 1 700 | fi 701 | } 702 | 703 | 704 | 705 | # 主任务 706 | main() { 707 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 进入主任务" >> "$CRON_LOG" 708 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 参数数量: $#" >> "$CRON_LOG" 709 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 所有参数: $@" >> "$CRON_LOG" 710 | 711 | check_running 712 | 713 | if [[ "$*" == *"-cron"* ]]; then 714 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 检测到-cron参数, 进入cron模式" >> "$CRON_LOG" 715 | if read_config; then 716 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 成功读取配置文件" >> "$CRON_LOG" 717 | # 继续执行其他操作 718 | check_and_notify "false" 719 | 720 | # 检查是否需要发送每日报告 721 | # 先刷新缓存,保证定时发送时有最新的端口数据(在cron环境下主动生成缓存) 722 | save_port_traffic_data 2>/dev/null || true 723 | current_time=$(TZ='Asia/Shanghai' date +%H:%M) 724 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间: $current_time, 设定的报告时间: $DAILY_REPORT_TIME" >> "$CRON_LOG" 725 | if [ "$current_time" == "$DAILY_REPORT_TIME" ]; then 726 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 时间匹配,准备发送每日报告" >> "$CRON_LOG" 727 | if daily_report; then 728 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送成功" >> "$CRON_LOG" 729 | else 730 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 每日报告发送失败" >> "$CRON_LOG" 731 | fi 732 | else 733 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 当前时间与报告时间不匹配,不发送报告" >> "$CRON_LOG" 734 | fi 735 | else 736 | echo "$(date '+%Y-%m-%d %H:%M:%S') : 配置文件不存在或不完整,跳过检查" >> "$CRON_LOG" 737 | exit 1 738 | fi 739 | 740 | else 741 | # 菜单模式 (替换原来的交互模式) 742 | if ! read_config; then 743 | echo "需要进行初始化配置。" 744 | initial_config 745 | fi 746 | 747 | setup_cron 748 | 749 | # 显示菜单 750 | while true; do 751 | clear 752 | echo "======================================" 753 | echo " Telegram 通知脚本管理菜单" 754 | echo "======================================" 755 | echo "当前配置摘要:" 756 | echo "机器名称: $MACHINE_NAME" 757 | echo "每日报告时间: $DAILY_REPORT_TIME" 758 | echo "Bot Token: ${BOT_TOKEN:0:10}..." # 只显示前10个字符 759 | echo "Chat ID: $CHAT_ID" 760 | echo "======================================" 761 | echo "1. 检查流量并推送" 762 | echo "2. 手动发送每日报告" 763 | echo "3. 发送测试消息" 764 | echo "4. 重新加载配置" 765 | echo "5. 修改配置" 766 | echo "6. 修改每日报告时间" 767 | echo "7. 查看缓存调试信息" 768 | echo "0. 退出" 769 | echo "======================================" 770 | echo -n "请选择操作 [0-7]: " 771 | 772 | read choice 773 | echo 774 | 775 | case $choice in 776 | 0) 777 | echo "退出脚本。" 778 | exit 0 779 | ;; 780 | 1) 781 | echo "正在检查流量并推送..." 782 | # 检查流量时保存当前准确的端口数据 783 | save_port_traffic_data 784 | check_and_notify 785 | ;; 786 | 2) 787 | echo "正在发送每日报告..." 788 | # 手动发送每日报告前保存当前准确的端口数据 789 | save_port_traffic_data 790 | daily_report 791 | ;; 792 | 3) 793 | echo "正在发送测试消息..." 794 | test_telegram_notification 795 | ;; 796 | 4) 797 | echo "正在重新加载配置..." 798 | read_config 799 | echo "配置已重新加载。" 800 | ;; 801 | 5) 802 | echo "进入配置修改模式..." 803 | initial_config 804 | ;; 805 | 6) 806 | echo "修改每日报告时间" 807 | echo -n "请输入新的每日报告时间 (HH:MM): " 808 | read -r new_time 809 | if [[ $new_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then 810 | # 直接使用命令行工具修改配置,避免交互环境问题 811 | cp "$CONFIG_FILE" "$CONFIG_FILE.backup" 812 | awk -v new_time="$new_time" ' 813 | /^DAILY_REPORT_TIME=/ { print "DAILY_REPORT_TIME=" new_time; next } 814 | { print } 815 | ' "$CONFIG_FILE.backup" > "$CONFIG_FILE" 816 | 817 | echo "每日报告时间已更新为 $new_time" 818 | # 更新 cron 任务 819 | update_cron_time "$new_time" 820 | # 修改时间后立即刷新缓存 821 | echo "正在刷新端口流量缓存..." 822 | save_port_traffic_data 823 | echo "缓存已刷新,定时推送将使用最新数据。" 824 | else 825 | echo "无效的时间格式。请使用 HH:MM 格式 (如: 09:30)" 826 | fi 827 | ;; 828 | 7) 829 | echo "查看最近的缓存调试信息..." 830 | echo "最近5个缓存文件:" 831 | ls -lt /tmp/port_traffic_cache_*.json 2>/dev/null | head -5 832 | echo 833 | echo "最新缓存内容:" 834 | latest_cache=$(ls -t /tmp/port_traffic_cache_*.json 2>/dev/null | head -1) 835 | if [ -n "$latest_cache" ]; then 836 | echo "文件: $latest_cache" 837 | cat "$latest_cache" | jq '.' 2>/dev/null || cat "$latest_cache" 838 | else 839 | echo "未找到缓存文件" 840 | fi 841 | ;; 842 | *) 843 | echo "无效的选择,请输入 0-7" 844 | ;; 845 | esac 846 | 847 | if [ "$choice" != "0" ]; then 848 | echo 849 | echo "按 Enter 键继续..." 850 | read 851 | fi 852 | done 853 | fi 854 | } 855 | 856 | 857 | # 执行主函数 858 | main "$@" 859 | echo "----------------------------------------------"| tee -a "$CRON_LOG" 860 | --------------------------------------------------------------------------------