aboutsummaryrefslogtreecommitdiff
path: root/main.go
blob: c9dc76b52d349a7fef0778141b6965f1df070776 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package main

import (
    "flag"
    "fmt"
    "math/rand"
    "strings"
    "time"
    "unicode"
)

var (
    syllables = []string{
        "ka", "lo", "mi", "te", "sa", "ri", "zu", "ve", "po", "ni",
        "fa", "go", "bi", "du", "ze", "wi", "la", "mo", "si", "ta",

        "chi", "so", "ki", "ma", "ha", "yu", "tsu", "ko", "na", "fu",
        "mi", "ya", "to", "hi", "su", "no", "ra", "se", "ku", "me",

        "el", "ar", "is", "on", "vi", "lu", "ce", "ra", "di", "vo",
        "ti", "re", "ca", "li", "pe", "co", "mu", "ne", "si", "la",

        "skal", "vorn", "dre", "thor", "sig", "fre", "bjorn", "ulf", "ragn", "skol",
        "grim", "tor", "val", "hild", "sven", "lok", "frig", "od", "run", "skir",

        "bri", "cen", "lor", "ael", "wyn", "fion", "dea", "mor", "cai", "lyn",
        "glen", "row", "sev", "bran", "cer", "dru", "fin", "gwen", "lir", "mae",

        "za", "nur", "sal", "jam", "far", "lay", "ami", "rah", "sah", "nad",
        "mal", "yas", "zin", "bah", "qas", "hil", "ram", "sul", "zar", "mun",

        "vla", "mir", "sto", "dre", "bor", "slav", "rad", "mil", "ziv", "gor",
        "dan", "luk", "pet", "nik", "sov", "yar", "zol", "kras", "vit", "bog",

        "nde", "kwe", "zuri", "oba", "chi", "tum", "san", "yem", "fol", "dji",
        "bwe", "kud", "mal", "ngo", "sia", "tan", "waz", "yol", "zim", "kwa",

        "dev", "sri", "jai", "anu", "raj", "pri", "san", "vid", "kir", "ran",
        "ash", "vik", "dip", "man", "sur", "tar", "neh", "par", "jit", "sam",

        "theo", "nyx", "eos", "zir", "phi", "rho", "kai", "zel", "ion", "pyr",
        "hel", "tha", "cri", "lys", "dor", "xan", "nym", "pol", "ath", "zen",

        "moa", "lua", "kai", "nalu", "hina", "mana", "tane", "rua", "vai", "lea",

        "wald", "grim", "hart", "stein", "burg", "feld", "wolf", "brand", "helm", "sieg",

        "ay", "gul", "tur", "khan", "sel", "yur", "tem", "bar", "oz", "alp",
    }
    words = []string{
        "sky", "leaf", "stone", "cloud", "river", "moon", "wind", "tree", "hill", "dawn",
        "mist", "vale", "ridge", "bloom", "shade", "wave", "pine", "oak", "fern", "lake",
        "dune", "brook", "frost", "glen", "tide", "cove", "reef", "snow", "breeze", "field",
        "sand", "peak", "wood", "stream", "bay", "crag", "pond", "marsh", "cliff", "mead",
        "spruce", "cedar", "vine", "moss", "thorn", "birch", "ash", "elm", "heath", "gorge",

        "dream", "echo", "spark", "flow", "haze", "gleam", "pulse", "vibe", "dusk", "hope",
        "calm", "glow", "whisper", "drift", "flicker", "muse", "soul", "void", "quest", "fate",
        "bliss", "surge", "glee", "trust", "peace", "spirit", "charm", "vision", "awe", "grace",
        "clarity", "essence", "radiance", "serenity", "mirth", "zest", "bloom", "luster", "shade",
        "gleam", "sparkle", "wisp", "nexus", "vapor", "glee", "mood", "scent", "breeze", "tide",

        "key", "flame", "path", "crown", "forge", "veil", "spire", "gem", "lock", "chain",
        "lamp", "coin", "blade", "ring", "scroll", "torch", "bell", "horn", "vase", "cloak",
        "shield", "spear", "flag", "book", "gate", "mirror", "staff", "wand", "chest", "map",
        "drum", "quill", "rope", "wheel", "hook", "lens", "knot", "pipe", "cup", "bowl",
        "plate", "knife", "rod", "candle", "locket", "pendant", "banner", "beacon", "forge", "loom",

        "nova", "orbit", "luna", "comet", "star", "galaxy", "nebula", "cosmo", "pulse", "ray",
        "quasar", "meteor", "sun", "void", "eclipse", "astro", "light", "dawn", "dusk", "sky",
        "flare", "crater", "dust", "ring", "belt", "moon", "tide", "glint", "beam", "spark",
        "cluster", "void", "halo", "arc", "zenith", "nadir", "sky", "luster", "gleam", "vapor",
        "corona", "umbra", "penumbra", "aurora", "cosmos", "spire", "vortex", "nebula", "pulsar", "orbit",

        "nyx", "zephyr", "aura", "titan", "phoenix", "siren", "griffin", "hydra", "minos", "atlas",
        "hera", "zeus", "odin", "thor", "loki", "freya", "cronos", "ares", "muse", "fury",
        "apollo", "artemis", "prometheus", "helios", "selene", "pan", "echo", "nereus", "psyche", "eros",
        "orion", "hermes", "hades", "persephone", "dionysus", "athena", "hector", "achilles", "triton", "nymph",
        "centaur", "satyr", "medusa", "cerberus", "chimera", "pegasus", "icarus", "daedalus", "midgard", "valhalla",

        "joy", "hope", "fear", "love", "rage", "calm", "glee", "sorrow", "trust", "pride",
        "bliss", "awe", "grief", "zeal", "peace", "lust", "wrath", "cheer", "dread", "gleam",
        "mirth", "gloom", "fervor", "ease", "fury", "spark", "zest", "muse", "thrill", "calm",
        "ardor", "glee", "woe", "rapture", "vibe", "pang", "blaze", "whisper", "pulse", "dream",
        "delight", "sigh", "yearn", "mood", "spirit", "surge", "charm", "elation", "serenity", "vigor",

        "azure", "crimson", "emerald", "amber", "violet", "ruby", "sapphire", "onyx", "jade", "gold",
        "silver", "ivory", "ebony", "coral", "topaz", "pearl", "garnet", "opal", "turquoise", "amethyst",
        "bronze", "copper", "sienna", "indigo", "vermilion", "ochre", "saffron", "mauve", "lavender", "teal",
        "maroon", "cerulean", "cobalt", "sepia", "magenta", "cyan", "olive", "taupe", "rose", "lilac",
        "umber", "sable", "chartreuse", "vermilion", "puce", "fuchsia", "aqua", "bistre", "sage", "mint",

        "fire", "water", "earth", "air", "stone", "ice", "storm", "flame", "tide", "mist",
        "wind", "soil", "sky", "snow", "ash", "clay", "rock", "wave", "dust", "fog",
        "blaze", "stream", "breeze", "frost", "sand", "magma", "vapor", "spark", "glint", "surge",
        "quartz", "ember", "gust", "silt", "rubble", "dew", "spray", "rift", "smoke", "foam",
        "gale", "cinder", "ore", "salt", "soot", "gravel", "slag", "haze", "ridge", "crust",
    }
    modifiers = []string{
        "neo", "ix", "al", "en", "ora", "is", "yn", "io", "za", "qu",
        "ex", "um", "an", "er", "os", "ad", "il", "or", "es", "ys",
        "ax", "on", "et", "im", "us", "id", "ox", "em", "ar", "un",
        "ol", "az", "ir", "ov", "ut", "av", "ez", "om", "in", "ep",
        "as", "iz", "ed", "up", "at", "it", "ef", "ag", "am", "oc",

        "byte", "code", "link", "sys", "net", "tech", "data", "grid", "bit", "wave",
        "hack", "node", "port", "chip", "flux", "core", "sync", "pulse", "lan", "web",
        "byte", "scan", "loop", "disk", "ram", "mod", "algo", "hash", "ping", "boot",
        "cache", "link", "wire", "bolt", "spark", "flow", "beam", "drive", "glitch", "frame",
        "pixel", "voxel", "quantum", "circuit", "relay", "signal", "stream", "cloud", "cyber", "nano",

        "zen", "vox", "lux", "syn", "arc", "haze", "gleam", "flow", "dusk", "mist",
        "void", "spark", "echo", "drift", "aura", "soul", "whisper", "flicker", "bliss", "calm",
        "pulse", "vibe", "shade", "bloom", "surge", "glee", "trust", "peace", "spirit", "charm",
        "vision", "awe", "grace", "clarity", "essence", "radiance", "serenity", "mirth", "zest", "luster",
        "dream", "hope", "muse", "wisp", "vapor", "gleam", "sparkle", "mood", "scent", "breeze",

        "kai", "ren", "sol", "mir", "xu", "yul", "zin", "rah", "sah", "jam",
        "far", "lay", "ami", "nad", "bah", "qas", "hil", "ram", "sul", "zar",
        "tal", "nur", "jem", "sal", "yaz", "khal", "zara", "nuri", "sami", "hana",
        "ravi", "sana", "tari", "zain", "lila", "maya", "raj", "sri", "dev", "jai",
        "anu", "kir", "vid", "ash", "vik", "dip", "man", "sur", "tar", "neh",

        "cryo", "plex", "nova", "zeta", "omni", "cyon", "lith", "quark", "ion", "nexus",
        "volt", "pulse", "beam", "holo", "grid", "flux", "core", "spire", "link", "wave",
        "chrono", "synth", "neo", "arc", "vox", "lume", "zoid", "micron", "photon", "plasma",
        "cyber", "nano", "omega", "sigma", "delta", "theta", "gamma", "epsilon", "lambda", "mu",
        "rho", "tau", "phi", "psi", "chi", "iota", "kappa", "beta", "alpha", "omicron",

        "verse", "tale", "saga", "epic", "rune", "myth", "fable", "ode", "lyric", "prose",
        "sonnet", "ballad", "poem", "stanza", "rhyme", "epos", "lay", "saga", "yarn", "lore",
        "canticle", "elegy", "psalm", "hymn", "chant", "fable", "mythos", "legend", "parable", "story",
        "novella", "epic", "ode", "verse", "rune", "tale", "saga", "lyric", "prose", "ballad",
        "sonnet", "poem", "stanza", "rhyme", "epos", "lay", "yarn", "lore", "canticle", "elegy",

        "note", "chord", "tune", "beat", "rhythm", "mel", "song", "vibe", "harmony", "tempo",
        "pitch", "scale", "key", "tone", "cadence", "lilt", "hum", "riff", "melody", "verse",
        "chorus", "bridge", "hook", "strain", "bar", "meter", "pulse", "flow", "swing", "jazz",
        "blues", "rock", "pop", "folk", "soul", "funk", "reggae", "samba", "tango", "waltz",
        "sonata", "fugue", "prelude", "coda", "aria", "rondo", "symphony", "ballad", "chant", "hymn",
    }
    vowels = []string{
        "a", "e", "i", "o", "u", "ai", "ou", "ae", "ei", "oa", "au", "ie",
        "ue", "oi", "ui", "ea", "eo", "io", "ua", "ay", "ao", "eu", "iu", "oy",
        "ya", "ye", "yo", "yu",
    }
    consonants = []string{
        "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "r", "s", "t",
        "v", "w", "y", "z", "sh", "ch", "th", "ph", "kr", "sl", "br", "tr", "kl",
        "gr", "fl", "pr", "dr", "sk", "sm", "sn", "sp", "st", "sw", "bl", "cl",
        "fr", "gl", "pl", "sc", "tw", "wh", "zh", "qu",
    }
)

func GenerateUsername(minLength, maxLength int, noNumbers, noCamelCase bool) string {
    rand.Seed(time.Now().UnixNano() ^ int64(rand.Int()))
    var username strings.Builder

    separators := []string{"-", "_"}
    separator := separators[rand.Intn(len(separators))]

    strategy := rand.Intn(5)
    switch strategy {
    case 0:
        numSyllables := rand.Intn(3) + 2
        for i := 0; i < numSyllables; i++ {
            syllable := syllables[rand.Intn(len(syllables))]
            if rand.Float32() < 0.4 {
                syllable = strings.Title(syllable)
            }
            username.WriteString(syllable)
            if i < numSyllables-1 && rand.Float32() < 0.2 {
                username.WriteString(separator)
            }
        }

    case 1:
        word := words[rand.Intn(len(words))]
        if rand.Float32() < 0.5 {
            word = strings.Title(word)
        }
        username.WriteString(word)
        if rand.Float32() < 0.6 {
            modifier := modifiers[rand.Intn(len(modifiers))]
            if rand.Float32() < 0.5 {
                modifier = strings.Title(modifier)
            }
            username.WriteString(modifier)
        } else {
            syllable := syllables[rand.Intn(len(syllables))]
            if rand.Float32() < 0.5 {
                syllable = strings.Title(syllable)
            }
            username.WriteString(syllable)
        }

    case 2:
        length := rand.Intn(4) + 3
        for i := 0; i < length; i++ {
            if i%2 == 0 && rand.Float32() < 0.7 {
                username.WriteString(consonants[rand.Intn(len(consonants))])
            } else {
                username.WriteString(vowels[rand.Intn(len(vowels))])
            }
        }
        if rand.Float32() < 0.4 {
            result := username.String()
            if len(result) > 0 {
                runes := []rune(result)
                runes[0] = unicode.ToUpper(runes[0])
                username = strings.Builder{}
                username.WriteString(string(runes))
            }
        }

    case 3:
        components := []string{
            syllables[rand.Intn(len(syllables))],
            words[rand.Intn(len(words))],
            modifiers[rand.Intn(len(modifiers))],
        }
        rand.Shuffle(len(components), func(i, j int) {
            components[i], components[j] = components[j], components[i]
        })
        numComponents := rand.Intn(2) + 2
        for i := 0; i < numComponents; i++ {
            comp := components[i]
            if rand.Float32() < 0.5 {
                comp = strings.Title(comp)
            }
            username.WriteString(comp)
            if i < numComponents-1 && rand.Float32() < 0.2 {
                username.WriteString(separator)
            }
        }

    case 4:
        length := rand.Intn(5) + 4
        pattern := rand.Intn(3)
        for i := 0; i < length; i++ {
            if pattern == 0 {
                if i%2 == 0 {
                    username.WriteString(consonants[rand.Intn(len(consonants))])
                } else {
                    username.WriteString(vowels[rand.Intn(len(vowels))])
                }
            } else if pattern == 1 {
                if rand.Float32() < 0.6 {
                    username.WriteString(vowels[rand.Intn(len(vowels))])
                } else {
                    username.WriteString(consonants[rand.Intn(len(consonants))])
                }
            } else {
                if rand.Float32() < 0.6 {
                    username.WriteString(consonants[rand.Intn(len(consonants))])
                } else {
                    username.WriteString(vowels[rand.Intn(len(vowels))])
                }
            }
        }
        if !noCamelCase && rand.Float32() < 0.3 {
            result := username.String()
            words := strings.Split(result, "")
            for i := range words {
                if rand.Float32() < 0.2 {
                    words[i] = strings.Title(words[i])
                }
            }
            username = strings.Builder{}
            username.WriteString(strings.Join(words, ""))
        }
    }

    if !noNumbers && rand.Float32() < 0.05 {
        num := rand.Intn(100)
        username.WriteString(fmt.Sprintf("%02d", num))
    }

    if rand.Float32() < 0.05 {
        result := username.String()
        if strings.Contains(result, "a") {
            result = strings.Replace(result, "a", "ae", 1)
            username = strings.Builder{}
            username.WriteString(result)
        }
    }

    result := username.String()

    if len(result) < minLength || len(result) > maxLength {
        return GenerateUsername(minLength, maxLength, noNumbers, noCamelCase)
    }

    lowerResult := strings.ToLower(result)
    for _, word := range words {
        if lowerResult == word {
            return GenerateUsername(minLength, maxLength, noNumbers, noCamelCase)
        }
    }

    if rand.Float32() < 0.1 {
        result = strings.Replace(result, "-", ".", 1)
        result = strings.Replace(result, "_", ".", 1)
    }

    return result
}

func main() {
    minLength := flag.Int("min-length", 8, "minimum username length")
    maxLength := flag.Int("max-length", 12, "maximum username length")
    noNumbers := flag.Bool("no-numbers", false, "disable appending numbers")
    noCamelCase := flag.Bool("no-camelcase", false, "disable camelCase styling")
    count := flag.Int("count", 10, "number of usernames to generate")

    flag.Parse()

    if *minLength > *maxLength {
        fmt.Println("[err] min-length cannot be greater than max-length")
        return
    }
    if *count <= 0 {
        fmt.Println("[err] count must be positive")
        return
    }

    for i := 0; i < *count; i++ {
        fmt.Println(GenerateUsername(*minLength, *maxLength, *noNumbers, *noCamelCase))
    }
}