Skip to content

Instantly share code, notes, and snippets.

@zwzheng45
Last active April 3, 2025 06:32
Show Gist options
  • Save zwzheng45/0dd38456489b987f217b2e1eb45bb2ed to your computer and use it in GitHub Desktop.
Save zwzheng45/0dd38456489b987f217b2e1eb45bb2ed to your computer and use it in GitHub Desktop.
定时网速监测:使用ookia的speedtest服务进行网速测试,并通过飞书Webhook推送结果
#!/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