13 func randomLine(f *os.File) (string, error) {
18 offset := rand.Int63n(fi.Size())
19 offset, err = f.Seek(offset, io.SeekStart)
24 br := bufio.NewReader(f)
26 b, err := br.ReadByte()
34 line, err := br.ReadString('\n')
35 if errors.Is(err, io.EOF) {
36 // the file ends without a newline - no problem
37 } else if err != nil {
41 line = strings.TrimSpace(line)
43 // empty line. we're either at the end or hit a blank line. try again
49 // /usr/share/dict/words contains derogatory words
50 // which we don't think should be used in public usernames.
51 func derogatory(s string) bool {
55 if strings.HasPrefix(s, "nigger") {
61 // RandomUsername generates a random username from the dictionary file at dictpath.
62 // The dictionary file should be a newline delimited text file, one word per line.
63 // On Unix systems, the file /usr/share/dic/words is a suitable file.
64 func RandomUsername(dictpath string) (string, error) {
65 f, err := os.Open(dictpath)
67 return "", fmt.Errorf("open dictionary: %w", err)
71 var first, second string
73 first, err = randomLine(f)
75 return "", fmt.Errorf("first random word: %w", err)
77 first = strings.ToLower(first)
78 if !derogatory(first) {
83 second, err = randomLine(f)
85 return "", fmt.Errorf("second random word: %w", err)
87 second = strings.ToLower(second)
88 if !derogatory(second) {
92 return fmt.Sprintf("%s_%s%02d", first, second, rand.Intn(99)), nil