Files
blog/bot-go/internal/helpers/helpers.go
T
2026-06-01 17:57:22 +08:00

160 lines
3.2 KiB
Go

package helpers
import (
"fmt"
"net"
"strings"
"time"
)
var lanIPCache struct {
ip string
ts time.Time
}
func GetLanIP(preferIface string) string {
if lanIPCache.ip != "" && time.Since(lanIPCache.ts) < 30*time.Second {
return lanIPCache.ip
}
ip := "0.0.0.0"
ifaces, err := net.Interfaces()
if err != nil {
lanIPCache.ip = ip
lanIPCache.ts = time.Now()
return ip
}
type candidate struct {
name string
address string
}
var candidates []candidate
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
ip4 := ipNet.IP.To4()
if ip4 == nil {
continue
}
if ip4.IsLoopback() {
continue
}
ipStr := ip4.String()
if strings.HasPrefix(ipStr, "169.254.") {
continue
}
candidates = append(candidates, candidate{name: iface.Name, address: ipStr})
}
}
prefLower := strings.ToLower(preferIface)
for _, c := range candidates {
if strings.Contains(strings.ToLower(c.name), prefLower) {
ip = c.address
lanIPCache.ip = ip
lanIPCache.ts = time.Now()
return ip
}
}
for _, c := range candidates {
n := strings.ToLower(c.name)
if !strings.Contains(n, "vmware") && !strings.Contains(n, "virtual") && !strings.Contains(n, "vethernet") {
ip = c.address
lanIPCache.ip = ip
lanIPCache.ts = time.Now()
return ip
}
}
if len(candidates) > 0 {
ip = candidates[0].address
}
lanIPCache.ip = ip
lanIPCache.ts = time.Now()
return ip
}
func CheckPort(port int) bool {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 1500*time.Millisecond)
if err != nil {
return false
}
conn.Close()
return true
}
func WaitForReady(port int, timeoutMs int) bool {
if timeoutMs <= 0 {
timeoutMs = 30000
}
deadline := time.Now().Add(time.Duration(timeoutMs) * time.Millisecond)
for time.Now().Before(deadline) {
if CheckPort(port) {
return true
}
time.Sleep(1500 * time.Millisecond)
}
return false
}
func MakeSlug(title string) string {
dateStr := time.Now().Format("2006-01-02")
slugPart := title
// Replace non-word, non-CJK chars with -
slugPart = strings.Map(func(r rune) rune {
if (r >= 0x4e00 && r <= 0x9fff) || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' {
return r
}
return '-'
}, slugPart)
slugPart = strings.Trim(slugPart, "-")
slugPart = strings.ToLower(slugPart)
if slugPart == "" {
slugPart = "post"
}
return dateStr + "-" + slugPart
}
func SessionSummary(title string, categories, tags []string, content string) string {
lines := 0
chars := len([]rune(content))
for _, ch := range content {
if ch == '\n' {
lines++
}
}
if content != "" {
lines++
}
displayTitle := title
if displayTitle == "" {
displayTitle = "未命名"
}
parts := []string{
fmt.Sprintf("📝 <b>%s</b>", displayTitle),
}
if len(categories) > 0 {
parts = append(parts, "📂 "+strings.Join(categories, "、"))
}
if len(tags) > 0 {
parts = append(parts, "🏷 "+strings.Join(tags, "、"))
}
if chars > 0 {
parts = append(parts, fmt.Sprintf("✍ %d 行 %d 字", lines, chars))
}
return strings.Join(parts, " · ")
}