Last active
April 3, 2025 06:32
-
-
Save zwzheng45/0dd38456489b987f217b2e1eb45bb2ed to your computer and use it in GitHub Desktop.
定时网速监测:使用ookia的speedtest服务进行网速测试,并通过飞书Webhook推送结果
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # scheduled_speedtest.sh | |
| # | |
| # 定时网速监测 | |
| # - 使用ookia的speedtest服务进行网速测试 | |
| # - 如果大于设置的速度合格阈值,则直接退出 | |
| # - 如果未达到测速阈值,调用飞书Webhook发送警告,并进行3次补测,再将补测结果一并推送到飞书 | |
| # - 如果Webhook调用失败,会将待发送信息先保存在本地,每次运行时都会检查并尝试发送之前未成功发送的信息 | |
| # - 使用crontab等程序定时运行此脚本即可实现定时监测,如: | |
| # - */15 * * * * /home/zzw/scheduled_speedtest.sh >> /home/zzw/scheduled_speedtest.log 2>&1 | |
| ######################################## | |
| # 配置区域 | |
| ######################################## | |
| # speedtest 可执行文件路径,例如: | |
| # SPEEDTEST_CLI="/home/zzw/ookla-speedtest-1.2.0-linux-aarch64/speedtest" | |
| SPEEDTEST_CLI= | |
| # 飞书 Webhook 地址,例如: | |
| # FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/abcdefghijklmn-opqrst-uvwxyz" | |
| FEISHU_WEBHOOK= | |
| # 离线消息储存路径,例如: | |
| # OFFLINE_MSG_FILE="/home/zzw/offline_messages.txt" | |
| OFFLINE_MSG_FILE= | |
| # 日志文件路径,例如: | |
| # LOG_FILE="/home/zzw/speedtest_log" | |
| LOG_FILE= | |
| # 速度合格阈值(单位 Mbps) | |
| THRESHOLD=600 | |
| ######################################## | |
| # 函数 | |
| ######################################## | |
| log_speedtest_result() { | |
| local timestamp="$1" | |
| local speed="$2" | |
| local date_part | |
| date_part=$(date -d "$timestamp" "+%Y-%m-%d") | |
| local daily_log_file="${LOG_FILE}/${date_part}.txt" | |
| mkdir -p "${LOG_FILE}" | |
| echo "测试时间:$timestamp,下载速度:$speed Mbps" >> "$daily_log_file" | |
| } | |
| send_to_feishu_api() { | |
| local content="$1" | |
| local payload | |
| payload=$(cat <<EOF | |
| { | |
| "msg_type": "post", | |
| "content": { | |
| "post": { | |
| "zh_cn": { | |
| "title": "网速未达标", | |
| "content": [ | |
| [ | |
| { | |
| "tag": "text", | |
| "text": "$content" | |
| } | |
| ] | |
| ] | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| ) | |
| local http_code | |
| http_code=$(curl -s -S -X POST \ | |
| -H "Content-Type: application/json" \ | |
| -d "$payload" \ | |
| -o /dev/null \ | |
| -w "%{http_code}" \ | |
| "$FEISHU_WEBHOOK" ) | |
| if [[ "$http_code" == "200" ]]; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| store_offline_message() { | |
| local msg="$1" | |
| echo "$msg" >> "$OFFLINE_MSG_FILE" | |
| } | |
| flush_offline_messages() { | |
| if [[ ! -f "$OFFLINE_MSG_FILE" || ! -s "$OFFLINE_MSG_FILE" ]]; then | |
| return | |
| fi | |
| local temp_file | |
| temp_file=$(mktemp) | |
| send_to_feishu_api "以下为离线时未能发送的日志记录:" | |
| while IFS= read -r line; do | |
| if [[ -z "$line" ]]; then | |
| continue | |
| fi | |
| if send_to_feishu_api "$line"; then | |
| echo "Flushed offline message: $line" | |
| else | |
| echo "$line" >> "$temp_file" | |
| fi | |
| done < "$OFFLINE_MSG_FILE" | |
| mv "$temp_file" "$OFFLINE_MSG_FILE" | |
| } | |
| run_speedtest_once() { | |
| # -f json 输出多段 JSON,用 jq 把它们组合成数组后选出 type=="result" 的项 | |
| local raw_json | |
| raw_json="$("$SPEEDTEST_CLI" -f json 2>/dev/null | jq -n '[inputs] | map(select(.type=="result")) | .[0]' )" | |
| if [[ "$raw_json" == "null" || -z "$raw_json" ]]; then | |
| echo "0" | |
| return | |
| fi | |
| # 提取 download.bandwidth | |
| local bandwidth | |
| bandwidth=$(jq '.download.bandwidth' <<< "$raw_json" 2>/dev/null) | |
| if [[ -z "$bandwidth" || "$bandwidth" == "null" ]]; then | |
| echo "0" | |
| return | |
| fi | |
| # 换算 Mbps | |
| local speed_mbps | |
| speed_mbps=$(awk -v b="$bandwidth" 'BEGIN { printf "%.2f", b*8/1000000 }') | |
| echo "$speed_mbps" | |
| } | |
| send_to_feishu() { | |
| local msg="$1" | |
| if ! send_to_feishu_api "$msg"; then | |
| # 如果发送失败,则存储到离线文件下次重试 | |
| store_offline_message "$msg" | |
| fi | |
| } | |
| ######################################## | |
| # 程序入口 | |
| ######################################## | |
| flush_offline_messages | |
| RESULT_1=$(run_speedtest_once) | |
| SPEED_1=$RESULT_1 # 下载速度,单位 Mbps | |
| # RAW_JSON_1=$(cut -d'|' -f2- <<< "$RESULT_1") # 完整 JSON | |
| is_ge_threshold=$(awk -v speed="$SPEED_1" -v thr="$THRESHOLD" 'BEGIN { if (speed>=thr) print 1; else print 0 }') | |
| if [[ "$is_ge_threshold" -eq 1 ]]; then | |
| CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S") | |
| log_speedtest_result "$CURRENT_TIME" "$SPEED_1" | |
| exit 0 | |
| fi | |
| CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S") | |
| first_fail_msg="测试时间:$CURRENT_TIME\n下载速度:$SPEED_1 Mbps\n(开始进行补测...)" | |
| send_to_feishu "$first_fail_msg" | |
| declare -a speeds=() | |
| for i in {1..3}; do | |
| RESULT=$(run_speedtest_once) | |
| speed=$(cut -d'|' -f1 <<< "$RESULT") | |
| speeds+=("$speed") | |
| sleep 5 | |
| done | |
| pass_flag=0 | |
| for s in "${speeds[@]}"; do | |
| is_ge_thr=$(awk -v ss="$s" -v thr="$THRESHOLD" 'BEGIN { if (ss>=thr) print 1; else print 0 }') | |
| if [[ "$is_ge_thr" -eq 1 ]]; then | |
| pass_flag=1 | |
| break | |
| fi | |
| done | |
| msg_detail="补测结果:\n" | |
| for idx in "${!speeds[@]}"; do | |
| msg_detail+="第 $((idx+1)) 轮:${speeds[$idx]} Mbps\n" | |
| done | |
| if [[ "$pass_flag" -eq 1 ]]; then | |
| msg_detail+="\n疑似正常网络波动" | |
| else | |
| msg_detail+="\n速率均未合格,网络出现异常!" | |
| fi | |
| send_to_feishu "$msg_detail" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment