This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / state / userutil / userutil.go
2.9 kB 106 lines
1package userutil 2 3import ( 4 "bufio" 5 "log/slog" 6 "os" 7 "regexp" 8 "strings" 9) 10 11var ( 12 handleRegex = regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`) 13 didRegex = regexp.MustCompile(`^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`) 14) 15 16func IsHandle(s string) bool { 17 // ref: https://atproto.com/specs/handle 18 return handleRegex.MatchString(s) 19} 20 21// IsDid checks if the given string is a standard DID. 22func IsDid(s string) bool { 23 return didRegex.MatchString(s) 24} 25 26func UnflattenDid(s string) string { 27 if !IsFlattenedDid(s) { 28 return s 29 } 30 31 return strings.Replace(s, "-", ":", 2) 32} 33 34// IsFlattenedDid checks if the given string is a flattened DID. 35func IsFlattenedDid(s string) bool { 36 // Check if the string starts with "did-" 37 if !strings.HasPrefix(s, "did-") { 38 return false 39 } 40 41 // Reconstruct as a standard DID format using Replace 42 // Example: "did-plc-xyz-abc" becomes "did:plc:xyz-abc" 43 reconstructed := strings.Replace(s, "-", ":", 2) 44 45 return didRegex.MatchString(reconstructed) 46} 47 48// FlattenDid converts a DID to a flattened format. 49// A flattened DID is a DID with the :s swapped to -s to satisfy certain 50// application requirements, such as Go module naming conventions. 51func FlattenDid(s string) string { 52 if IsDid(s) { 53 return strings.Replace(s, ":", "-", 2) 54 } 55 return s 56} 57 58var subdomainRegex = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{2,61}[a-z0-9])?$`) 59 60func IsValidSubdomain(name string) bool { 61 return len(name) >= 4 && len(name) <= 63 && subdomainRegex.MatchString(name) 62} 63 64// LoadDisallowedNicknames reads a newline-separated list of reserved nicknames 65// from filepath (blank lines and #-comments ignored). An empty filepath or a 66// read error yields an empty set. Invalid entries are logged and skipped. 67func LoadDisallowedNicknames(filepath string, logger *slog.Logger) map[string]bool { 68 disallowed := make(map[string]bool) 69 70 if filepath == "" { 71 logger.Warn("no disallowed nicknames file configured") 72 return disallowed 73 } 74 75 file, err := os.Open(filepath) 76 if err != nil { 77 logger.Warn("failed to open disallowed nicknames file", "file", filepath, "error", err) 78 return disallowed 79 } 80 defer file.Close() 81 82 scanner := bufio.NewScanner(file) 83 lineNum := 0 84 for scanner.Scan() { 85 lineNum++ 86 line := strings.TrimSpace(scanner.Text()) 87 if line == "" || strings.HasPrefix(line, "#") { 88 continue 89 } 90 91 nickname := strings.ToLower(line) 92 if IsValidSubdomain(nickname) { 93 disallowed[nickname] = true 94 } else { 95 logger.Warn("invalid nickname format in disallowed nicknames file", 96 "file", filepath, "line", lineNum, "nickname", nickname) 97 } 98 } 99 100 if err := scanner.Err(); err != nil { 101 logger.Error("error reading disallowed nicknames file", "file", filepath, "error", err) 102 } 103 104 logger.Info("loaded disallowed nicknames", "count", len(disallowed), "file", filepath) 105 return disallowed 106}