经过和chatGPT多次友好耐心的沟通,终于把这个脚本写好了。
使用的是ChatGPT-o1-preview,还不能全信它,还是要看看代码的,否则它会一个问题里打转,永远出不来。

# 定义要监测的接口名称
:local interfaceName "wireguard1-usa5-ros2"
:log info "脚本开始执行,监测接口:$interfaceName"
# 定义 Kuma ID 变量
:local kumaID "3333333"
# 处理接口名称,移除文件名中不支持的字符(如空格、斜杠等)
:local sanitizedInterfaceName ""
:for i from=0 to=([:len $interfaceName] - 1) do={
:local char [:pick $interfaceName $i]
:if ($char ~ "[a-zA-Z0-9_-]") do={
:set sanitizedInterfaceName ($sanitizedInterfaceName . $char)
} else={
:set sanitizedInterfaceName ($sanitizedInterfaceName . "_")
}
}
:log info "处理后的接口名称:$sanitizedInterfaceName"
# 定义输出文件名,使用接口名称区分
:local outputFileName ("ping_output_" . $sanitizedInterfaceName . ".txt")
:local ipID [/ip address find interface=$interfaceName]
:if ($ipID != "") do={
# 获取 IP 地址并去掉子网掩码部分
:local ipAddress [/ip address get $ipID address]
:set ipAddress [:pick $ipAddress 0 [:find $ipAddress "/"]]
:log info "获取到 IP 地址:$ipAddress"
# 解析 IP 地址的各个部分
:local pos1 [:find $ipAddress "."]
:local pos2 [:find $ipAddress "." ($pos1 + 1)]
:local pos3 [:find $ipAddress "." ($pos2 + 1)]
# 检查是否成功找到三个点的位置
:if (($pos1 != "") && ($pos2 != "") && ($pos3 != "")) do={
# 提取每个八位组
:local octet1 [:pick $ipAddress 0 $pos1]
:local octet2 [:pick $ipAddress ($pos1 + 1) $pos2]
:local octet3 [:pick $ipAddress ($pos2 + 1) $pos3]
# 将对端 IP 的最后一个八位组设置为 1
:local octet4 "1"
# 构建对端 IP 地址
:local peerIP ($octet1 . "." . $octet2 . "." . $octet3 . "." . $octet4)
:log info "构建对端 IP 地址:$peerIP"
:log info "正在 Ping 对端 IP $peerIP"
# 定义 Ping 参数
:local pingCount 10
:local maxLossPercent 50
# 执行 Ping 命令并将输出保存到唯一的文件
:log info "开始执行 Ping 命令,输出文件:$outputFileName"
:execute "/ping $peerIP count=$pingCount" file=$outputFileName
:log info "Ping 命令已启动"
# 等待 Ping 命令完成
:delay ($pingCount * 1s)
:log info "等待 Ping 命令完成"
# 检查是否生成了输出文件
:if ([/file find name=$outputFileName] != "") do={
:log info "成功生成 $outputFileName 文件"
# 读取 Ping 输出文件的内容
:local pingOutput [/file get [find name=$outputFileName] contents]
:log info "读取 $outputFileName 文件内容"
# 删除临时文件
/file remove $outputFileName
:log info "删除临时文件 $outputFileName"
# 输出 pingOutput 内容到日志,供调试使用
#:log info "Ping Output:\n$pingOutput"
# 初始化变量
:local sent ""
:local received ""
:local packetLoss ""
:local avgRtt 0
# 将 pingOutput 按行分割为数组
:local lines [:toarray ""]
:local line ""
:local lineEnd 0
:while ([:len $pingOutput] > 0) do={
:set lineEnd [:find $pingOutput "\r\n"]
:if ($lineEnd = -1) do={
:set line $pingOutput
:set pingOutput ""
} else={
:set line [:pick $pingOutput 0 $lineEnd]
:set pingOutput [:pick $pingOutput ($lineEnd + 2) [:len $pingOutput]]
}
:if ([:len $line] > 0) do={
:set lines ($lines, $line)
}
}
# 检查是否包含 "max-rtt"
:local containsMaxRtt false
:foreach l in=$lines do={
:if ([:find $l "max-rtt="] != -1) do={
:set containsMaxRtt true
}
}
:log info "Ping 输出是否包含 'max-rtt=':$containsMaxRtt"
# 获取统计信息行
:local summaryLine ""
:if ($containsMaxRtt) do={
# 取最后两行合并
:local lineCount [:len $lines]
:if ($lineCount >= 2) do={
:set summaryLine (($lines->($lineCount - 2)) . " " . ($lines->($lineCount - 1)))
:log info "合并最后两行为统计信息行:$summaryLine"
} else={
:log error "Ping 输出行数不足,无法获取统计信息"
}
} else={
# 取最后一行
:local lineCount [:len $lines]
:if ($lineCount >= 1) do={
:set summaryLine ($lines->($lineCount - 1))
:log info "使用最后一行为统计信息行:$summaryLine"
} else={
:log error "Ping 输出行数不足,无法获取统计信息"
}
}
# 检查是否获取到统计信息行
:if ([:len $summaryLine] > 0) do={
# 提取 sent
:local sentPos [:find $summaryLine "sent="]
:local sentEnd [:find $summaryLine " " ($sentPos + 5)]
:if ($sentEnd = -1) do={
:set sentEnd [:len $summaryLine]
}
:local sentStr [:pick $summaryLine ($sentPos + 5) $sentEnd]
:set sent [:tonum $sentStr]
:log info "解析到 sent=$sent"
# 提取 received
:local recvPos [:find $summaryLine "received="]
:local recvEnd [:find $summaryLine " " ($recvPos + 9)]
:if ($recvEnd = -1) do={
:set recvEnd [:len $summaryLine]
}
:local recvStr [:pick $summaryLine ($recvPos + 9) $recvEnd]
:set received [:tonum $recvStr]
:log info "解析到 received=$received"
# 提取 packet-loss
:local lossPos [:find $summaryLine "packet-loss="]
:local lossEnd [:find $summaryLine "%" ($lossPos + 12)]
:if ($lossEnd = -1) do={
:set lossEnd [:len $summaryLine]
}
:local lossStr [:pick $summaryLine ($lossPos + 12) $lossEnd]
:set packetLoss [:tonum $lossStr]
:log info "解析到 packetLoss=$packetLoss"
# 提取 avg-rtt
:local avgPos [:find $summaryLine "avg-rtt="]
:if ($avgPos != -1) do={
:local avgEnd [:find $summaryLine " " ($avgPos + 8)]
:if ($avgEnd = -1) do={
:set avgEnd [:len $summaryLine]
}
:local avgRttStr [:pick $summaryLine ($avgPos + 8) $avgEnd]
:log info "解析到 avgRttStr=$avgRttStr"
# 将 avgRttStr 转换为毫秒数
# 处理类似于 "166ms277us" 的字符串
:local msPos [:find $avgRttStr "ms"]
:local usPos [:find $avgRttStr "us"]
:local msValue 0
:local usValue 0
:if ($msPos != -1) do={
:set msValue [:tonum [:pick $avgRttStr 0 $msPos]]
}
:if ($usPos != -1) do={
:set usValue [:tonum [:pick $avgRttStr ($msPos + 2) $usPos]]
}
:set avgRtt ($msValue + ($usValue / 1000))
:log info "解析到 avgRtt=$avgRtt ms"
} else={
:set avgRtt 0
:log warning "未能解析到 avg-rtt"
}
# 判断并提交到 Kuma
:if ($packetLoss < $maxLossPercent) do={
:log info "Ping 成功,丢包率=$packetLoss%,平均 RTT: $avgRtt ms"
:local msg ("PingOK_Loss" . $packetLoss . "%")
:log info "提交结果到 Kuma:状态=up,消息=$msg,ping=$avgRtt"
/tool fetch url=("http://kuma2.ffffff.com:3001/api/push/" . $kumaID . "?status=up&msg=" . $msg . "&ping=" . $avgRtt) keep-result=no
} else={
:log error "Ping 失败,丢包率=$packetLoss%"
:log info "状态为 down,不向 Kuma 提交结果"
}
} else={
:log error "未获取到统计信息行,Ping 失败"
:log info "状态为 down,不向 Kuma 提交结果"
}
} else={
:log error "未生成 $outputFileName 文件,Ping 命令可能失败"
}
} else={
:log error "无法解析 IP 地址 $ipAddress"
}
} else={
:log error "接口 $interfaceName 未分配 IP 地址"
}
:log info "脚本执行完毕"