···1414 "os"
1515 "path/filepath"
1616 "sort"
1717- "strconv"
1817 "strings"
1918 "time"
2019···10651064 return strings.TrimSpace(v)
10661065 }
10671066 return ""
10681068-}
10691069-10701070-func sanitizeSkillDirName(name string) string {
10711071- name = strings.TrimSpace(name)
10721072- if name == "" {
10731073- return ""
10741074- }
10751075- // Prefer stable, simple directory names.
10761076- name = strings.ToLower(name)
10771077- var b strings.Builder
10781078- for _, r := range name {
10791079- switch {
10801080- case r >= 'a' && r <= 'z':
10811081- b.WriteRune(r)
10821082- case r >= '0' && r <= '9':
10831083- b.WriteRune(r)
10841084- case r == '-' || r == '_' || r == '.':
10851085- b.WriteRune(r)
10861086- case r == ' ':
10871087- b.WriteByte('-')
10881088- default:
10891089- // drop
10901090- }
10911091- }
10921092- out := strings.Trim(b.String(), "-")
10931093- if out == "" {
10941094- return ""
10951095- }
10961096- // Cap length to something reasonable for a directory name.
10971097- if len(out) > 80 {
10981098- out = out[:80]
10991099- out = strings.TrimRight(out, "-")
11001100- }
11011101- // Avoid windows reserved device names (defensive).
11021102- switch out {
11031103- case "con", "prn", "aux", "nul":
11041104- out = out + "-" + strconv.FormatInt(time.Now().Unix(), 10)
11051105- }
11061106- return out
11071067}
1108106811091069func validateSkillDirName(name string) (string, error) {
-10
contacts/file_store.go
···11561156 return out
11571157}
1158115811591159-func clamp(v float64, min float64, max float64) float64 {
11601160- if v < min {
11611161- return min
11621162- }
11631163- if v > max {
11641164- return max
11651165- }
11661166- return v
11671167-}
11681168-11691159func busInboxRecordKey(channel string, platformMessageID string) (string, error) {
11701160 normalizedChannel, err := normalizeBusChannel(channel)
11711161 if err != nil {