模块介绍 / 今日规划 / 下班日报 / 团队摘要 生成成功后没有上报,也几乎不写「AI」日志。
流结束时的 usage 用了非阻塞发送,通道一满就被丢掉;很多服务商默认还不在流里带 usage。 现在每次真正打到模型的调用都会: 在本地日志(分类 AI,消息 AI 调用完成)写下 scene / prompt / completion / cached 登录后同步上报到后台日汇总
This commit is contained in:
276
network_info.go
Normal file
276
network_info.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package main
|
||||
|
||||
// network_info.go:采集本机网卡信息(Windows 跑 ipconfig /all 并结构化解析,其它平台用 Go net)。
|
||||
// 自动识别主局域网 IPv4,供启动台一键复制。
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NetAdapter 单块网卡的摘要信息。
|
||||
type NetAdapter struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
MAC string `json:"mac,omitempty"`
|
||||
IPv4 []string `json:"ipv4,omitempty"`
|
||||
IPv6 []string `json:"ipv6,omitempty"`
|
||||
Gateway []string `json:"gateway,omitempty"`
|
||||
DNS []string `json:"dns,omitempty"`
|
||||
DHCP bool `json:"dhcp"`
|
||||
Primary bool `json:"primary"`
|
||||
Virtual bool `json:"virtual"`
|
||||
}
|
||||
|
||||
// NetworkInfo 本机网络总览。
|
||||
type NetworkInfo struct {
|
||||
Hostname string `json:"hostname"`
|
||||
PrimaryIPv4 string `json:"primaryIPv4"`
|
||||
Adapters []NetAdapter `json:"adapters"`
|
||||
Raw string `json:"raw"`
|
||||
}
|
||||
|
||||
// GetNetworkInfo 一键采集本机网卡信息。
|
||||
func (a *App) GetNetworkInfo() (NetworkInfo, error) {
|
||||
out := NetworkInfo{Adapters: []NetAdapter{}}
|
||||
out.Hostname, _ = os.Hostname()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
raw, err := exec.Command("powershell", "-NoProfile", "-Command",
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ipconfig /all").CombinedOutput()
|
||||
if err != nil || len(raw) == 0 {
|
||||
raw, err = exec.Command("ipconfig", "/all").CombinedOutput()
|
||||
}
|
||||
out.Raw = string(raw)
|
||||
if err == nil && len(raw) > 0 {
|
||||
out.Adapters = parseIPConfig(out.Raw)
|
||||
}
|
||||
}
|
||||
if len(out.Adapters) == 0 {
|
||||
out.Adapters = listAdaptersGo()
|
||||
if out.Raw == "" {
|
||||
out.Raw = formatAdaptersRaw(out.Hostname, out.Adapters)
|
||||
}
|
||||
}
|
||||
markPrimary(&out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func isVirtualName(s string) bool {
|
||||
low := strings.ToLower(s)
|
||||
for _, k := range []string{
|
||||
"virtual", "vmware", "vbox", "virtualbox", "hyper-v", "vethernet",
|
||||
"wsl", "docker", "vpn", "tap-windows", "zerotier", "tailscale",
|
||||
"loopback", "蓝牙", "bluetooth", "isatap", "teredo", "microsoft wi-fi direct",
|
||||
} {
|
||||
if strings.Contains(low, k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPrivateIPv4(ip string) bool {
|
||||
p := net.ParseIP(strings.Split(ip, "/")[0])
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
v4 := p.To4()
|
||||
if v4 == nil {
|
||||
return false
|
||||
}
|
||||
return v4[0] == 10 ||
|
||||
(v4[0] == 172 && v4[1] >= 16 && v4[1] <= 31) ||
|
||||
(v4[0] == 192 && v4[1] == 168)
|
||||
}
|
||||
|
||||
func markPrimary(info *NetworkInfo) {
|
||||
best := -1
|
||||
bestScore := -1
|
||||
for i := range info.Adapters {
|
||||
info.Adapters[i].Virtual = isVirtualName(info.Adapters[i].Name + " " + info.Adapters[i].Description)
|
||||
score := 0
|
||||
if info.Adapters[i].Virtual {
|
||||
score -= 50
|
||||
}
|
||||
for _, ip := range info.Adapters[i].IPv4 {
|
||||
if strings.HasPrefix(ip, "127.") {
|
||||
continue
|
||||
}
|
||||
score += 10
|
||||
if isPrivateIPv4(ip) {
|
||||
score += 30
|
||||
}
|
||||
if len(info.Adapters[i].Gateway) > 0 {
|
||||
score += 20
|
||||
}
|
||||
break
|
||||
}
|
||||
if score > bestScore {
|
||||
bestScore = score
|
||||
best = i
|
||||
}
|
||||
}
|
||||
if best >= 0 && bestScore > 0 {
|
||||
info.Adapters[best].Primary = true
|
||||
for _, ip := range info.Adapters[best].IPv4 {
|
||||
if !strings.HasPrefix(ip, "127.") {
|
||||
info.PrimaryIPv4 = strings.Split(ip, "/")[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseIPConfig 解析 Windows `ipconfig /all` 文本(兼容中英文关键字)。
|
||||
func parseIPConfig(raw string) []NetAdapter {
|
||||
lines := strings.Split(strings.ReplaceAll(raw, "\r\n", "\n"), "\n")
|
||||
var list []NetAdapter
|
||||
var cur *NetAdapter
|
||||
|
||||
flush := func() {
|
||||
if cur == nil {
|
||||
return
|
||||
}
|
||||
if cur.Name != "" || len(cur.IPv4) > 0 || len(cur.IPv6) > 0 {
|
||||
list = append(list, *cur)
|
||||
}
|
||||
cur = nil
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
trim := strings.TrimSpace(line)
|
||||
if trim == "" {
|
||||
continue
|
||||
}
|
||||
// 适配器标题行:不以空格开头,且以冒号结束或含“适配器/adapter”
|
||||
if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
|
||||
flush()
|
||||
name := strings.TrimRight(trim, ":")
|
||||
cur = &NetAdapter{Name: name}
|
||||
continue
|
||||
}
|
||||
if cur == nil {
|
||||
continue
|
||||
}
|
||||
key, val, ok := splitKV(trim)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
lk := strings.ToLower(key)
|
||||
switch {
|
||||
case strings.Contains(lk, "description") || strings.Contains(key, "描述"):
|
||||
cur.Description = val
|
||||
case strings.Contains(lk, "physical address") || strings.Contains(key, "物理地址"):
|
||||
cur.MAC = val
|
||||
case strings.Contains(lk, "dhcp enabled") || strings.Contains(key, "dhcp 已启用") || strings.Contains(key, "DHCP 已启用"):
|
||||
cur.DHCP = strings.EqualFold(val, "yes") || val == "是"
|
||||
case strings.Contains(lk, "ipv4") || strings.Contains(key, "IPv4"):
|
||||
cur.IPv4 = append(cur.IPv4, cleanIP(val))
|
||||
case strings.Contains(lk, "ipv6") || strings.Contains(key, "IPv6"):
|
||||
if !strings.HasPrefix(strings.ToLower(val), "fe80") {
|
||||
cur.IPv6 = append(cur.IPv6, cleanIP(val))
|
||||
}
|
||||
case strings.Contains(lk, "default gateway") || strings.Contains(key, "默认网关"):
|
||||
if v := cleanIP(val); v != "" {
|
||||
cur.Gateway = append(cur.Gateway, v)
|
||||
}
|
||||
case strings.Contains(lk, "dns servers") || strings.Contains(key, "dns 服务器") || strings.Contains(key, "DNS 服务器"):
|
||||
if v := cleanIP(val); v != "" {
|
||||
cur.DNS = append(cur.DNS, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return list
|
||||
}
|
||||
|
||||
func splitKV(line string) (key, val string, ok bool) {
|
||||
// ipconfig 用 ". . . . :" 或直接 ":" 分隔
|
||||
idx := strings.LastIndex(line, ":")
|
||||
if idx <= 0 {
|
||||
return "", "", false
|
||||
}
|
||||
key = strings.TrimSpace(strings.TrimRight(line[:idx], ". \t"))
|
||||
val = strings.TrimSpace(line[idx+1:])
|
||||
if key == "" {
|
||||
return "", "", false
|
||||
}
|
||||
return key, val, true
|
||||
}
|
||||
|
||||
func cleanIP(v string) string {
|
||||
v = strings.TrimSpace(v)
|
||||
// 去掉 "(首选)" / "(Preferred)" 等后缀
|
||||
if i := strings.IndexAny(v, " ("); i > 0 {
|
||||
v = strings.TrimSpace(v[:i])
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func listAdaptersGo() []NetAdapter {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var list []NetAdapter
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
addrs, _ := iface.Addrs()
|
||||
a := NetAdapter{Name: iface.Name, MAC: iface.HardwareAddr.String()}
|
||||
for _, addr := range addrs {
|
||||
s := addr.String()
|
||||
ip, _, _ := net.ParseCIDR(s)
|
||||
if ip == nil {
|
||||
ip = net.ParseIP(s)
|
||||
}
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
if v4 := ip.To4(); v4 != nil {
|
||||
a.IPv4 = append(a.IPv4, v4.String())
|
||||
} else if !ip.IsLinkLocalUnicast() {
|
||||
a.IPv6 = append(a.IPv6, ip.String())
|
||||
}
|
||||
}
|
||||
if len(a.IPv4) == 0 && len(a.IPv6) == 0 {
|
||||
continue
|
||||
}
|
||||
list = append(list, a)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func formatAdaptersRaw(host string, adapters []NetAdapter) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("Hostname: " + host + "\n\n")
|
||||
for _, a := range adapters {
|
||||
b.WriteString(a.Name)
|
||||
if a.Description != "" {
|
||||
b.WriteString(" (" + a.Description + ")")
|
||||
}
|
||||
b.WriteString(":\n")
|
||||
if a.MAC != "" {
|
||||
b.WriteString(" MAC: " + a.MAC + "\n")
|
||||
}
|
||||
for _, ip := range a.IPv4 {
|
||||
b.WriteString(" IPv4: " + ip + "\n")
|
||||
}
|
||||
for _, ip := range a.IPv6 {
|
||||
b.WriteString(" IPv6: " + ip + "\n")
|
||||
}
|
||||
for _, g := range a.Gateway {
|
||||
b.WriteString(" Gateway: " + g + "\n")
|
||||
}
|
||||
for _, d := range a.DNS {
|
||||
b.WriteString(" DNS: " + d + "\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user