diff --git a/colors.go b/colors.go index 949f5db3..297c6fd1 100644 --- a/colors.go +++ b/colors.go @@ -16,7 +16,7 @@ import ( func makeFlag(colors []string) func(a string) string { flag := make([]*gchalk.Builder, len(colors)) for i := range colors { - flag[i] = chalk.WithHex(colors[i]) + flag[i] = Chalk.WithHex(colors[i]) } return func(a string) string { return applyRainbow(flag, a) @@ -34,23 +34,23 @@ func applyRainbow(rainbow []*gchalk.Builder, a string) string { } var ( - chalk = gchalk.New(gchalk.ForceLevel(gchalk.LevelAnsi256)) - green = ansi256(1, 5, 1) - red = ansi256(5, 1, 1) - cyan = ansi256(1, 5, 5) - magenta = ansi256(5, 1, 5) - yellow = ansi256(5, 5, 1) - orange = ansi256(5, 3, 0) - blue = ansi256(0, 3, 5) - white = ansi256(5, 5, 5) - styles = []*style{ - {"white", buildStyle(white)}, - {"red", buildStyle(red)}, + Chalk = gchalk.New(gchalk.ForceLevel(gchalk.LevelAnsi256)) + Green = ansi256(1, 5, 1) + Red = ansi256(5, 1, 1) + Cyan = ansi256(1, 5, 5) + Magenta = ansi256(5, 1, 5) + Yellow = ansi256(5, 5, 1) + Orange = ansi256(5, 3, 0) + Blue = ansi256(0, 3, 5) + White = ansi256(5, 5, 5) + Styles = []*Style{ + {"white", buildStyle(White)}, + {"red", buildStyle(Red)}, {"coral", buildStyle(ansi256(5, 2, 2))}, - {"green", buildStyle(green)}, + {"green", buildStyle(Green)}, {"sky", buildStyle(ansi256(3, 5, 5))}, - {"cyan", buildStyle(cyan)}, - {"magenta", buildStyle(magenta)}, + {"cyan", buildStyle(Cyan)}, + {"magenta", buildStyle(Magenta)}, {"pink", buildStyle(ansi256(5, 3, 4))}, {"rose", buildStyle(ansi256(5, 0, 2))}, {"cranberry", buildStyle(ansi256(3, 0, 1))}, @@ -58,16 +58,16 @@ var ( {"fire", buildStyle(ansi256(5, 2, 0))}, {"pastel green", buildStyle(ansi256(0, 5, 3))}, {"olive", buildStyle(ansi256(4, 5, 1))}, - {"yellow", buildStyle(yellow)}, - {"orange", buildStyle(orange)}, - {"blue", buildStyle(blue)}} - secretStyles = []*style{ - {"ukraine", buildStyle(chalk.WithHex("#005bbb").WithBgHex("#ffd500"))}, - {"easter", buildStyle(chalk.WithRGB(255, 51, 255).WithBgRGB(255, 255, 0))}, - {"baby", buildStyle(chalk.WithRGB(255, 51, 255).WithBgRGB(102, 102, 255))}, - {"hacker", buildStyle(chalk.WithRGB(0, 255, 0).WithBgRGB(0, 0, 0))}, - {"l33t", buildStyleNoStrip(chalk.WithBgBrightBlack())}, - {"whiten", buildStyleNoStrip(chalk.WithBgWhite())}, + {"yellow", buildStyle(Yellow)}, + {"orange", buildStyle(Orange)}, + {"blue", buildStyle(Blue)}} + SecretStyles = []*Style{ + {"ukraine", buildStyle(Chalk.WithHex("#005bbb").WithBgHex("#ffd500"))}, + {"easter", buildStyle(Chalk.WithRGB(255, 51, 255).WithBgRGB(255, 255, 0))}, + {"baby", buildStyle(Chalk.WithRGB(255, 51, 255).WithBgRGB(102, 102, 255))}, + {"hacker", buildStyle(Chalk.WithRGB(0, 255, 0).WithBgRGB(0, 0, 0))}, + {"l33t", buildStyleNoStrip(Chalk.WithBgBrightBlack())}, + {"whiten", buildStyleNoStrip(Chalk.WithBgWhite())}, {"trans", makeFlag([]string{"#55CDFC", "#F7A8B8", "#FFFFFF", "#F7A8B8", "#55CDFC"})}, {"gay", makeFlag([]string{"#FF0018", "#FFA52C", "#FFFF41", "#008018", "#0000F9", "#86007D"})}, {"lesbian", makeFlag([]string{"#D62E02", "#FD9855", "#FFFFFF", "#D161A2", "#A20160"})}, @@ -79,7 +79,7 @@ var ( {"genderfluid", makeFlag([]string{"#FE75A1", "#FFFFFF", "#BE18D6", "#333333", "#333EBC"})}, {"agender", makeFlag([]string{"#333333", "#BCC5C6", "#FFFFFF", "#B5F582", "#FFFFFF", "#BCC5C6", "#333333"})}, {"rainbow", func(a string) string { - rainbow := []*gchalk.Builder{red, orange, yellow, green, cyan, blue, ansi256(2, 2, 5), magenta} + rainbow := []*gchalk.Builder{Red, Orange, Yellow, Green, Cyan, Blue, ansi256(2, 2, 5), Magenta} return applyRainbow(rainbow, a) }}} ) @@ -88,7 +88,7 @@ func init() { markdown.CurrentTheme = chromastyles.ParaisoDark } -type style struct { +type Style struct { name string apply func(string) string } @@ -107,15 +107,15 @@ func buildStyleNoStrip(c *gchalk.Builder) func(string) string { // with r, g and b values from 0 to 5 func ansi256(r, g, b uint8) *gchalk.Builder { - return chalk.WithRGB(255/5*r, 255/5*g, 255/5*b) + return Chalk.WithRGB(255/5*r, 255/5*g, 255/5*b) } func bgAnsi256(r, g, b uint8) *gchalk.Builder { - return chalk.WithBgRGB(255/5*r, 255/5*g, 255/5*b) + return Chalk.WithBgRGB(255/5*r, 255/5*g, 255/5*b) } // Applies color from name -func (u *user) changeColor(colorName string) error { +func (u *User) changeColor(colorName string) error { style, err := getStyle(colorName) if err != nil { return err @@ -151,7 +151,7 @@ func applyColorToData(data string, color string, colorBG string) (string, error) // Sets either the foreground or the background with a random color if the // given name is correct. -func getRandomColor(name string) *style { +func getRandomColor(name string) *Style { var foreground bool if name == "random" { foreground = true @@ -164,32 +164,32 @@ func getRandomColor(name string) *style { g := rand.Intn(6) b := rand.Intn(6) if foreground { - return &style{fmt.Sprintf("%03d", r*100+g*10+b), buildStyle(ansi256(uint8(r), uint8(g), uint8(b)))} + return &Style{fmt.Sprintf("%03d", r*100+g*10+b), buildStyle(ansi256(uint8(r), uint8(g), uint8(b)))} } - return &style{fmt.Sprintf("bg-%03d", r*100+g*10+b), buildStyleNoStrip(bgAnsi256(uint8(r), uint8(g), uint8(b)))} + return &Style{fmt.Sprintf("bg-%03d", r*100+g*10+b), buildStyleNoStrip(bgAnsi256(uint8(r), uint8(g), uint8(b)))} } // If the input is a named style, returns it. Otherwise, returns nil. -func getNamedColor(name string) *style { - for i := range styles { - if styles[i].name == name { - return styles[i] +func getNamedColor(name string) *Style { + for i := range Styles { + if Styles[i].name == name { + return Styles[i] } } - for i := range secretStyles { - if secretStyles[i].name == name { - return secretStyles[i] + for i := range SecretStyles { + if SecretStyles[i].name == name { + return SecretStyles[i] } } return nil } -func getCustomColor(name string) (*style, error) { +func getCustomColor(name string) (*Style, error) { if strings.HasPrefix(name, "#") { - return &style{name, buildStyle(chalk.WithHex(name))}, nil + return &Style{name, buildStyle(Chalk.WithHex(name))}, nil } if strings.HasPrefix(name, "bg-#") { - return &style{name, buildStyleNoStrip(chalk.WithBgHex(strings.TrimPrefix(name, "bg-")))}, nil + return &Style{name, buildStyleNoStrip(Chalk.WithBgHex(strings.TrimPrefix(name, "bg-")))}, nil } if len(name) == 3 || len(name) == 6 { rgbCode := name @@ -205,9 +205,9 @@ func getCustomColor(name string) (*style, error) { return nil, errors.New("custom colors have values from 0 to 5 smh") } if strings.HasPrefix(name, "bg-") { - return &style{name, buildStyleNoStrip(bgAnsi256(uint8(r), uint8(g), uint8(b)))}, nil + return &Style{name, buildStyleNoStrip(bgAnsi256(uint8(r), uint8(g), uint8(b)))}, nil } - return &style{name, buildStyle(ansi256(uint8(r), uint8(g), uint8(b)))}, nil + return &Style{name, buildStyle(ansi256(uint8(r), uint8(g), uint8(b)))}, nil } return nil, err } @@ -215,20 +215,20 @@ func getCustomColor(name string) (*style, error) { } // Turns name into a style (defaults to nil) -func getStyle(name string) (*style, error) { +func getStyle(name string) (*Style, error) { randomColor := getRandomColor(name) if randomColor != nil { return randomColor, nil } if name == "bg-off" { - return &style{"bg-off", func(a string) string { return a }}, nil // Used to remove one's background + return &Style{"bg-off", func(a string) string { return a }}, nil // Used to remove one's background } namedColor := getNamedColor(name) if namedColor != nil { return namedColor, nil } if strings.HasPrefix(name, "#") { - return &style{name, buildStyle(chalk.WithHex(name))}, nil + return &Style{name, buildStyle(Chalk.WithHex(name))}, nil } customColor, err := getCustomColor(name) if err != nil { @@ -237,15 +237,15 @@ func getStyle(name string) (*style, error) { if customColor != nil { return customColor, nil } - //s, err := chalk.WithStyle(strings.Split(name, "-")...) + //s, err := Chalk.WithStyle(strings.Split(name, "-")...) //if err == nil { // return &style{name, buildStyle(s)}, nil //} return nil, errors.New("Which color? Choose from random, " + strings.Join(func() []string { - colors := make([]string, 0, len(styles)) - for i := range styles { - colors = append(colors, styles[i].name) + colors := make([]string, 0, len(Styles)) + for i := range Styles { + colors = append(colors, Styles[i].name) } return colors }(), ", ") + " \nMake your own colors using hex (#A0FFFF, etc) or RGB values from 0 to 5 (for example, `color 530`, a pretty nice orange). Set bg color like this: color bg-530; remove bg color with color bg-off.\nThere's also a few secret colors :)") diff --git a/commands.go b/commands.go index 40e5993c..9bde52a8 100644 --- a/commands.go +++ b/commands.go @@ -20,16 +20,16 @@ import ( "github.com/shurcooL/tictactoe" ) -type cmd struct { +type CMD struct { name string - run func(line string, u *user) + run func(line string, u *User) argsInfo string info string } var ( - allcmds = make([]cmd, 0, 30) - cmds = []cmd{ + CMDs = make([]CMD, 0, 30) + MainCMDs = []CMD{ {"=", dmCMD, "", "DM with "}, // won't actually run, here just to show in docs {"users", usersCMD, "", "List users"}, {"color", colorCMD, "", "Change your name's color"}, @@ -48,7 +48,7 @@ var ( {"pronouns", pronounsCMD, "@user|pronouns", "Set your pronouns or get another user's"}, {"theme", themeCMD, "|list", "Change the syntax highlighting theme"}, {"rest", commandsRestCMD, "", "Uncommon commands list"}} - cmdsRest = []cmd{ + RestCMDs = []CMD{ {"people", peopleCMD, "", "See info about nice people who joined"}, {"id", idCMD, "", "Get a unique ID for a user (hashed key)"}, {"admins", adminsCMD, "", "Print the ID (hashed key) for all admins"}, @@ -64,7 +64,7 @@ var ( {"autoload", autoloadCMD, "on|off", "enable autoloading of preferences"}, {"save", saveCMD, "", "save"}, {"load", loadCMD, "", "load"}} - secretCMDs = []cmd{ + SecretCMDs = []CMD{ {"ls", lsCMD, "???", "???"}, {"cat", catCMD, "???", "???"}, {"rm", rmCMD, "???", "???"}, @@ -73,20 +73,20 @@ var ( ) const ( - maxLengthRoomName = 30 + MaxRoomNameLen = 30 ) func init() { - cmds = append(cmds, cmd{"cmds", commandsCMD, "", "Show this message"}) // avoid initialization loop - allcmds = append(append(append(allcmds, - cmds...), cmdsRest...), secretCMDs...) + MainCMDs = append(MainCMDs, CMD{"cmds", commandsCMD, "", "Show this message"}) // avoid initialization loop + CMDs = append(append(append(CMDs, + MainCMDs...), RestCMDs...), SecretCMDs...) } -// runCommands parses a line of raw input from a user and sends a message as -// required, running any commands the user may have called. +// runCommands parses a line of raw input from a User and sends a message as +// required, running any commands the User may have called. // It also accepts a boolean indicating if the line of input is from slack, in // which case some commands will not be run (such as ./tz and ./exit) -func runCommands(line string, u *user) { +func runCommands(line string, u *User) { if detectBadWords(line) { banUser("devbot [grow up]", u) return @@ -97,7 +97,7 @@ func runCommands(line string, u *user) { } defer func() { // crash protection if i := recover(); i != nil { - mainRoom.broadcast(devbot, "Slap the developers in the face for me, the server almost crashed, also tell them this: "+fmt.Sprint(i, "\n"+string(debug.Stack()))) + MainRoom.broadcast(Devbot, "Slap the developers in the face for me, the server almost crashed, also tell them this: "+fmt.Sprint(i, "\n"+string(debug.Stack()))) } }() currCmd := strings.Fields(line)[0] @@ -130,7 +130,7 @@ func runCommands(line string, u *user) { devbotChat(u.room, line) - for _, c := range allcmds { + for _, c := range CMDs { if c.name == currCmd { c.run(strings.TrimSpace(strings.TrimPrefix(line, c.name)), u) return @@ -156,15 +156,15 @@ func detectBadWords(text string) bool { return false } -func dmCMD(rest string, u *user) { +func dmCMD(rest string, u *User) { restSplit := strings.Fields(rest) if len(restSplit) < 2 { - u.writeln(devbot, "You gotta have a message, mate") + u.writeln(Devbot, "You gotta have a message, mate") return } peer, ok := findUserByName(u.room, restSplit[0]) if !ok { - u.writeln(devbot, "No such person lol, who you wanna dm? (you might be in the wrong room)") + u.writeln(Devbot, "No such person lol, who you wanna dm? (you might be in the wrong room)") return } msg := strings.TrimSpace(strings.TrimPrefix(rest, restSplit[0])) @@ -179,34 +179,34 @@ func dmCMD(rest string, u *user) { peer.writeln(u.Name+" -> ", msg) } -func hangCMD(rest string, u *user) { +func hangCMD(rest string, u *User) { if len(rest) > 1 { if !u.isSlack { u.writeln(u.Name, "hang "+rest) - u.writeln(devbot, "(that word won't show dw)") + u.writeln(Devbot, "(that word won't show dw)") } hangGame = &hangman{rest, 15, " "} // default value of guesses so empty space is given away - u.room.broadcast(devbot, u.Name+" has started a new game of Hangman! Guess letters with hang ") - u.room.broadcast(devbot, "```\n"+hangPrint(hangGame)+"\nTries: "+strconv.Itoa(hangGame.triesLeft)+"\n```") + u.room.broadcast(Devbot, u.Name+" has started a new game of Hangman! Guess letters with hang ") + u.room.broadcast(Devbot, "```\n"+hangPrint(hangGame)+"\nTries: "+strconv.Itoa(hangGame.triesLeft)+"\n```") return } if !u.isSlack { u.room.broadcast(u.Name, "hang "+rest) } if strings.Trim(hangGame.word, hangGame.guesses) == "" { - u.room.broadcast(devbot, "The game has ended. Start a new game with hang ") + u.room.broadcast(Devbot, "The game has ended. Start a new game with hang ") return } if len(rest) == 0 { - u.room.broadcast(devbot, "Start a new game with hang or guess with hang ") + u.room.broadcast(Devbot, "Start a new game with hang or guess with hang ") return } if hangGame.triesLeft == 0 { - u.room.broadcast(devbot, "No more tries! The word was "+hangGame.word) + u.room.broadcast(Devbot, "No more tries! The word was "+hangGame.word) return } if strings.Contains(hangGame.guesses, rest) { - u.room.broadcast(devbot, "You already guessed "+rest) + u.room.broadcast(Devbot, "You already guessed "+rest) return } hangGame.guesses += rest @@ -214,23 +214,23 @@ func hangCMD(rest string, u *user) { hangGame.triesLeft-- } display := hangPrint(hangGame) - u.room.broadcast(devbot, "```\n"+display+"\nTries: "+strconv.Itoa(hangGame.triesLeft)+"\n```") + u.room.broadcast(Devbot, "```\n"+display+"\nTries: "+strconv.Itoa(hangGame.triesLeft)+"\n```") if strings.Trim(hangGame.word, hangGame.guesses) == "" { - u.room.broadcast(devbot, "You got it! The word was "+hangGame.word) + u.room.broadcast(Devbot, "You got it! The word was "+hangGame.word) } else if hangGame.triesLeft == 0 { - u.room.broadcast(devbot, "No more tries! The word was "+hangGame.word) + u.room.broadcast(Devbot, "No more tries! The word was "+hangGame.word) } } -func clearCMD(_ string, u *user) { +func clearCMD(_ string, u *User) { u.term.Write([]byte("\033[H\033[2J")) } -func usersCMD(_ string, u *user) { +func usersCMD(_ string, u *User) { u.room.broadcast("", printUsersInRoom(u.room)) } -func dmRoomCMD(line string, u *user) { +func dmRoomCMD(line string, u *User) { u.writeln(u.messaging.Name+" <- ", line) if u == u.messaging { devbotRespond(u.room, []string{"You must be really lonely, DMing yourself.", @@ -243,16 +243,16 @@ func dmRoomCMD(line string, u *user) { } // named devmonk at the request of a certain ced -func devmonkCMD(_ string, u *user) { +func devmonkCMD(_ string, u *User) { sentences := []string{"I really want to go to work, but I am too sick to drive.", "The fence was confused about whether it was supposed to keep things in or keep things out.", "He found the end of the rainbow and was surprised at what he found there.", "He had concluded that pigs must be able to fly in Hog Heaven.", "I just wanted to tell you I could see the love you have for your child by the way you look at her.", "We will not allow you to bring your pet armadillo along.", "The father died during childbirth.", "I covered my friend in baby oil.", "Cursive writing is the best way to build a race track.", "My Mum tries to be cool by saying that she likes all the same things that I do.", "The sky is clear; the stars are twinkling.", "Flash photography is best used in full sunlight.", "The rusty nail stood erect, angled at a 45-degree angle, just waiting for the perfect barefoot to come along.", "People keep telling me \"orange\" but I still prefer \"pink\".", "Peanut butter and jelly caused the elderly lady to think about her past.", "She always had an interesting perspective on why the world must be flat.", "People who insist on picking their teeth with their elbows are so annoying!", "Joe discovered that traffic cones make excellent megaphones.", "They say people remember important moments in their life well, yet no one even remembers their own birth.", "Purple is the best city in the forest.", "The book is in front of the table.", "Everyone was curious about the large white blimp that appeared overnight.", "He wondered if she would appreciate his toenail collection.", "Situps are a terrible way to end your day.", "He barked orders at his daughters but they just stared back with amusement.", "She couldn't decide of the glass was half empty or half full so she drank it.", "It caught him off guard that space smelled of seared steak.", "There are few things better in life than a slice of pie.", "After exploring the abandoned building, he started to believe in ghosts.", "This is a Japanese doll.", "I've never seen a more beautiful brandy glass filled with wine.", "Don't piss in my garden and tell me you're trying to help my plants grow.", "She looked at the masterpiece hanging in the museum but all she could think is that her five-year-old could do better.", "Nobody loves a pig wearing lipstick.", "She always speaks to him in a loud voice.", "The teens wondered what was kept in the red shed on the far edge of the school grounds.", "I'll have you know I've written over fifty novels", "He didn't understand why the bird wanted to ride the bicycle.", "Potato wedges probably are not best for relationships.", "Baby wipes are made of chocolate stardust.", "Lucifer was surprised at the amount of life at Death Valley.", "She was too busy always talking about what she wanted to do to actually do any of it.", "The sudden rainstorm washed crocodiles into the ocean.", "I used to live in my neighbor's fishpond, but the aesthetic wasn't to my taste.", "He kept telling himself that one day it would all somehow make sense.", "The random sentence generator generated a random sentence about a random sentence.", "The reservoir water level continued to lower while we enjoyed our long shower.", "A song can make or ruin a person’s day if they let it get to them.", "He stomped on his fruit loops and thus became a cereal killer.", "I know many children ask for a pony, but I wanted a bicycle with rockets strapped to it."} text := sentences[rand.Intn(len(sentences))] - u.writeln(devbot, "Okay type this text: \n\n> "+text) + u.writeln(Devbot, "Okay type this text: \n\n> "+text) u.term.SetPrompt("> ") defer u.term.SetPrompt(u.Name + ": ") start := time.Now() line, err := u.term.ReadLine() if err == term.ErrPasteIndicator { // TODO: doesn't work for some reason? - u.room.broadcast(devbot, "SMH did you know that "+u.Name+" tried to cheat in a typing game?") + u.room.broadcast(Devbot, "SMH did you know that "+u.Name+" tried to cheat in a typing game?") return } dur := time.Since(start) @@ -281,54 +281,54 @@ func devmonkCMD(_ string, u *user) { } } - u.room.broadcast(devbot, "Okay "+u.Name+", you typed that in "+dur.Truncate(time.Second/10).String()+" so your speed is "+ + u.room.broadcast(Devbot, "Okay "+u.Name+", you typed that in "+dur.Truncate(time.Second/10).String()+" so your speed is "+ strconv.FormatFloat( float64(len(strings.Fields(text)))/dur.Minutes(), 'f', 1, 64, )+" wpm"+" with accuracy "+strconv.FormatFloat(accuracy, 'f', 1, 64)+"%", ) } -func ticCMD(rest string, u *user) { +func ticCMD(rest string, u *User) { if rest == "" { - u.room.broadcast(devbot, "Starting a new game of Tic Tac Toe! The first player is always X.") - u.room.broadcast(devbot, "Play using tic ") + u.room.broadcast(Devbot, "Starting a new game of Tic Tac Toe! The first player is always X.") + u.room.broadcast(Devbot, "Play using tic ") currentPlayer = tictactoe.X tttGame = new(tictactoe.Board) - u.room.broadcast(devbot, "```\n"+" 1 │ 2 │ 3\n───┼───┼───\n 4 │ 5 │ 6\n───┼───┼───\n 7 │ 8 │ 9\n"+"\n```") + u.room.broadcast(Devbot, "```\n"+" 1 │ 2 │ 3\n───┼───┼───\n 4 │ 5 │ 6\n───┼───┼───\n 7 │ 8 │ 9\n"+"\n```") return } m, err := strconv.Atoi(rest) if err != nil { - u.room.broadcast(devbot, "Make sure you're using a number lol") + u.room.broadcast(Devbot, "Make sure you're using a number lol") return } if m < 1 || m > 9 { - u.room.broadcast(devbot, "Moves are numbers between 1 and 9!") + u.room.broadcast(Devbot, "Moves are numbers between 1 and 9!") return } err = tttGame.Apply(tictactoe.Move(m-1), currentPlayer) if err != nil { - u.room.broadcast(devbot, err.Error()) + u.room.broadcast(Devbot, err.Error()) return } - u.room.broadcast(devbot, "```\n"+tttPrint(tttGame.Cells)+"\n```") + u.room.broadcast(Devbot, "```\n"+tttPrint(tttGame.Cells)+"\n```") if currentPlayer == tictactoe.X { currentPlayer = tictactoe.O } else { currentPlayer = tictactoe.X } if !(tttGame.Condition() == tictactoe.NotEnd) { - u.room.broadcast(devbot, tttGame.Condition().String()) + u.room.broadcast(Devbot, tttGame.Condition().String()) currentPlayer = tictactoe.X tttGame = new(tictactoe.Board) } } -func exitCMD(_ string, u *user) { - u.close(u.Name + red.Paint(" has left the chat")) +func exitCMD(_ string, u *User) { + u.close(u.Name + Red.Paint(" has left the chat")) } -func bellCMD(rest string, u *user) { +func bellCMD(rest string, u *User) { switch rest { case "off": u.Bell = false @@ -350,36 +350,36 @@ func bellCMD(rest string, u *user) { u.room.broadcast("", "bell off (never)") } default: - u.room.broadcast(devbot, "your options are off, on and all") + u.room.broadcast(Devbot, "your options are off, on and all") } } -func cdCMD(rest string, u *user) { +func cdCMD(rest string, u *User) { if u.messaging != nil { u.messaging = nil - u.writeln(devbot, "Left private chat") + u.writeln(Devbot, "Left private chat") if rest == "" || rest == ".." { return } } if rest == ".." { // cd back into the main room u.room.broadcast(u.Name, "cd "+rest) - if u.room != mainRoom { - u.changeRoom(mainRoom) + if u.room != MainRoom { + u.changeRoom(MainRoom) } return } if strings.HasPrefix(rest, "#") { u.room.broadcast(u.Name, "cd "+rest) - if len(rest) > maxLengthRoomName { - rest = rest[0:maxLengthRoomName] - u.room.broadcast(devbot, "Room name lengths are limited, so I'm shortening it to "+rest+".") + if len(rest) > MaxRoomNameLen { + rest = rest[0:MaxRoomNameLen] + u.room.broadcast(Devbot, "Room name lengths are limited, so I'm shortening it to "+rest+".") } - if v, ok := rooms[rest]; ok { + if v, ok := Rooms[rest]; ok { u.changeRoom(v) } else { - rooms[rest] = &room{rest, make([]*user, 0, 10), sync.Mutex{}} - u.changeRoom(rooms[rest]) + Rooms[rest] = &Room{rest, make([]*User, 0, 10), sync.Mutex{}} + u.changeRoom(Rooms[rest]) } return } @@ -390,7 +390,7 @@ func cdCMD(rest string, u *user) { numOfUsers int } var ss []kv - for k, v := range rooms { + for k, v := range Rooms { ss = append(ss, kv{k, len(v.users)}) } sort.Slice(ss, func(i, j int) bool { @@ -398,26 +398,26 @@ func cdCMD(rest string, u *user) { }) roomsInfo := "" for _, kv := range ss { - roomsInfo += blue.Paint(kv.roomName) + ": " + printUsersInRoom(rooms[kv.roomName]) + " \n" + roomsInfo += Blue.Paint(kv.roomName) + ": " + printUsersInRoom(Rooms[kv.roomName]) + " \n" } u.room.broadcast("", "Rooms and users \n"+strings.TrimSpace(roomsInfo)) return } name := strings.Fields(rest)[0] if len(name) == 0 { - u.writeln(devbot, "You think people have empty names?") + u.writeln(Devbot, "You think people have empty names?") return } peer, ok := findUserByName(u.room, name) if !ok { - u.writeln(devbot, "No such person lol, who do you want to dm? (you might be in the wrong room)") + u.writeln(Devbot, "No such person lol, who do you want to dm? (you might be in the wrong room)") return } u.messaging = peer - u.writeln(devbot, "Now in DMs with "+peer.Name+". To leave use cd ..") + u.writeln(Devbot, "Now in DMs with "+peer.Name+". To leave use cd ..") } -func tzCMD(tzArg string, u *user) { +func tzCMD(tzArg string, u *User) { var err error if tzArg == "" { u.Timezone = nil @@ -436,9 +436,9 @@ func tzCMD(tzArg string, u *user) { tz = "America/Phoenix" } loc, err := time.LoadLocation(tz) - u.Timezone = (*timeLocation)(loc) + u.Timezone = (*TimeLocation)(loc) if err != nil { - u.room.broadcast(devbot, "Weird timezone you have there, use the format Continent/City, the usual US timezones (PST, PDT, EST, EDT...) or check nodatime.org/TimeZones!") + u.room.broadcast(Devbot, "Weird timezone you have there, use the format Continent/City, the usual US timezones (PST, PDT, EST, EDT...) or check nodatime.org/TimeZones!") return } if len(tzArgList) == 2 { @@ -446,10 +446,10 @@ func tzCMD(tzArg string, u *user) { } else { u.FormatTime24 = false } - u.room.broadcast(devbot, "Changed your timezone!") + u.room.broadcast(Devbot, "Changed your timezone!") } -func idCMD(line string, u *user) { +func idCMD(line string, u *User) { victim, ok := findUserByName(u.room, line) if !ok { u.room.broadcast("", "User not found") @@ -458,53 +458,53 @@ func idCMD(line string, u *user) { u.room.broadcast("", victim.id) } -func nickCMD(line string, u *user) { +func nickCMD(line string, u *User) { u.pickUsername(line) //nolint:errcheck // if reading input fails, the next repl will err out } -func listBansCMD(_ string, u *user) { +func listBansCMD(_ string, u *User) { msg := "Printing bans by ID: \n" - for i := 0; i < len(bans); i++ { - msg += cyan.Cyan(strconv.Itoa(i+1)) + ". " + bans[i].ID + " \n" + for i := 0; i < len(Bans); i++ { + msg += Cyan.Cyan(strconv.Itoa(i+1)) + ". " + Bans[i].ID + " \n" } - u.room.broadcast(devbot, msg) + u.room.broadcast(Devbot, msg) } -func unbanCMD(toUnban string, u *user) { +func unbanCMD(toUnban string, u *User) { if !auth(u) { - u.room.broadcast(devbot, "Not authorized") + u.room.broadcast(Devbot, "Not authorized") return } if unbanIDorIP(toUnban) { - u.room.broadcast(devbot, "Unbanned person: "+toUnban) + u.room.broadcast(Devbot, "Unbanned person: "+toUnban) saveBans() } else { - u.room.broadcast(devbot, "I couldn't find that person") + u.room.broadcast(Devbot, "I couldn't find that person") } } // unbanIDorIP unbans an ID or an IP, but does NOT save bans to the bans file. // It returns whether the person was found, and so, whether the bans slice was modified. func unbanIDorIP(toUnban string) bool { - for i := 0; i < len(bans); i++ { - if bans[i].ID == toUnban || bans[i].Addr == toUnban { // allow unbanning by either ID or IP + for i := 0; i < len(Bans); i++ { + if Bans[i].ID == toUnban || Bans[i].Addr == toUnban { // allow unbanning by either ID or IP // remove this ban - bans = append(bans[:i], bans[i+1:]...) + Bans = append(Bans[:i], Bans[i+1:]...) return true } } return false } -func banCMD(line string, u *user) { +func banCMD(line string, u *User) { if !auth(u) { - u.room.broadcast(devbot, "Not authorized") + u.room.broadcast(Devbot, "Not authorized") return } split := strings.Split(line, " ") if len(split) == 0 { - u.room.broadcast(devbot, "Which user do you want to ban?") + u.room.broadcast(Devbot, "Which user do you want to ban?") return } victim, ok := findUserByName(u.room, split[0]) @@ -516,10 +516,10 @@ func banCMD(line string, u *user) { if len(split) > 1 { dur, err := time.ParseDuration(split[1]) if err != nil { - u.room.broadcast(devbot, "I couldn't parse that as a duration") + u.room.broadcast(Devbot, "I couldn't parse that as a duration") return } - bans = append(bans, ban{victim.addr, victim.id}) + Bans = append(Bans, Ban{victim.addr, victim.id}) victim.close(victim.Name + " has been banned by " + u.Name + " for " + dur.String()) go func(id string) { time.Sleep(dur) @@ -530,42 +530,42 @@ func banCMD(line string, u *user) { banUser(u.Name, victim) } -func banUser(banner string, victim *user) { - bans = append(bans, ban{victim.addr, victim.id}) +func banUser(banner string, victim *User) { + Bans = append(Bans, Ban{victim.addr, victim.id}) saveBans() victim.close(victim.Name + " has been banned by " + banner) } -func kickCMD(line string, u *user) { +func kickCMD(line string, u *User) { victim, ok := findUserByName(u.room, line) if !ok { u.room.broadcast("", "User not found") return } if !auth(u) { - u.room.broadcast(devbot, "Not authorized") + u.room.broadcast(Devbot, "Not authorized") return } - victim.close(victim.Name + red.Paint(" has been kicked by ") + u.Name) + victim.close(victim.Name + Red.Paint(" has been kicked by ") + u.Name) } -func colorCMD(rest string, u *user) { +func colorCMD(rest string, u *User) { if rest == "which" { - u.room.broadcast(devbot, "fg: "+u.Color+" & bg: "+u.ColorBG) + u.room.broadcast(Devbot, "fg: "+u.Color+" & bg: "+u.ColorBG) } else if err := u.changeColor(rest); err != nil { - u.room.broadcast(devbot, err.Error()) + u.room.broadcast(Devbot, err.Error()) } } -func adminsCMD(_ string, u *user) { +func adminsCMD(_ string, u *User) { msg := "Admins: \n" for i := range Config.Admins { msg += Config.Admins[i] + ": " + i + " \n" } - u.room.broadcast(devbot, msg) + u.room.broadcast(Devbot, msg) } -func peopleCMD(_ string, u *user) { +func peopleCMD(_ string, u *User) { u.room.broadcast("", ` **Hack Club members** Zach Latta - Founder of Hack Club @@ -603,7 +603,7 @@ Harsh @harshb__ **And many more have joined!**`) } -func helpCMD(_ string, u *user) { +func helpCMD(_ string, u *User) { u.room.broadcast("", `Welcome to Devzat! Devzat is chat over SSH: github.com/quackduck/devzat Because there's SSH apps on all platforms, even on mobile, you can join from anywhere. @@ -626,7 +626,7 @@ Made by Ishan Goel with feature ideas from friends. Thanks to Caleb Denio for lending his server!`) } -func catCMD(line string, u *user) { +func catCMD(line string, u *User) { if line == "" { u.room.broadcast("", "usage: cat [-benstuv] [file ...]") } else if line == "README.md" { @@ -636,7 +636,7 @@ func catCMD(line string, u *user) { } } -func rmCMD(line string, u *user) { +func rmCMD(line string, u *User) { if line == "" { u.room.broadcast("", `usage: rm [-f | -i] [-dPRrvW] file ... unlink file`) @@ -645,12 +645,12 @@ unlink file`) } } -func exampleCodeCMD(line string, u *user) { +func exampleCodeCMD(line string, u *User) { if line == "big" { - u.room.broadcast(devbot, "```go\npackage main\n\nimport \"fmt\"\n\nfunc sum(nums ...int) {\n fmt.Print(nums, \" \")\n total := 0\n for _, num := range nums {\n total += num\n }\n fmt.Println(total)\n}\n\nfunc main() {\n\n sum(1, 2)\n sum(1, 2, 3)\n\n nums := []int{1, 2, 3, 4}\n sum(nums...)\n}\n```") + u.room.broadcast(Devbot, "```go\npackage main\n\nimport \"fmt\"\n\nfunc sum(nums ...int) {\n fmt.Print(nums, \" \")\n total := 0\n for _, num := range nums {\n total += num\n }\n fmt.Println(total)\n}\n\nfunc main() {\n\n sum(1, 2)\n sum(1, 2, 3)\n\n nums := []int{1, 2, 3, 4}\n sum(nums...)\n}\n```") return } - u.room.broadcast(devbot, "\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n fmt.Println(\"Example!\")\n}\n```") + u.room.broadcast(Devbot, "\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n fmt.Println(\"Example!\")\n}\n```") } func init() { // add Matt Gleich's blackbird theme from https://github.com/blackbirdtheme/vscode/blob/master/themes/blackbird-midnight-color-theme.json#L175 @@ -696,26 +696,26 @@ func init() { // add Matt Gleich's blackbird theme from https://github.com/black })) } -func themeCMD(line string, u *user) { +func themeCMD(line string, u *User) { if line == "list" { - u.room.broadcast(devbot, "Available themes: "+strings.Join(chromastyles.Names(), ", ")) + u.room.broadcast(Devbot, "Available themes: "+strings.Join(chromastyles.Names(), ", ")) return } for _, name := range chromastyles.Names() { if name == line { markdown.CurrentTheme = chromastyles.Get(name) - u.room.broadcast(devbot, "Theme set to "+name) + u.room.broadcast(Devbot, "Theme set to "+name) return } } - u.room.broadcast(devbot, "What theme is that? Use theme list to see what's available.") + u.room.broadcast(Devbot, "What theme is that? Use theme list to see what's available.") } -func asciiArtCMD(_ string, u *user) { - u.room.broadcast("", art) +func asciiArtCMD(_ string, u *User) { + u.room.broadcast("", Art) } -func pwdCMD(_ string, u *user) { +func pwdCMD(_ string, u *User) { if u.messaging != nil { u.writeln("", u.messaging.Name) u.messaging.writeln("", u.messaging.Name) @@ -724,62 +724,62 @@ func pwdCMD(_ string, u *user) { } } -func shrugCMD(line string, u *user) { +func shrugCMD(line string, u *User) { u.room.broadcast(u.Name, line+` ¯\\_(ツ)_/¯`) } -func pronounsCMD(line string, u *user) { +func pronounsCMD(line string, u *User) { args := strings.Fields(line) if line == "" { - u.room.broadcast(devbot, "Set pronouns by providing em or query a user's pronouns!") + u.room.broadcast(Devbot, "Set pronouns by providing em or query a user's pronouns!") return } if len(args) == 1 && strings.HasPrefix(args[0], "@") { victim, ok := findUserByName(u.room, args[0][1:]) if !ok { - u.room.broadcast(devbot, "Who's that?") + u.room.broadcast(Devbot, "Who's that?") return } - u.room.broadcast(devbot, victim.Name+"'s pronouns are "+victim.displayPronouns()) + u.room.broadcast(Devbot, victim.Name+"'s pronouns are "+victim.displayPronouns()) return } u.Pronouns = strings.Fields(strings.ReplaceAll(strings.ToLower(line), "\n", "")) //u.changeColor(u.Color) // refresh pronouns - u.room.broadcast(devbot, u.Name+" now goes by "+u.displayPronouns()) + u.room.broadcast(Devbot, u.Name+" now goes by "+u.displayPronouns()) } -func emojisCMD(_ string, u *user) { - u.room.broadcast(devbot, "Check out https\\://github.com/ikatyang/emoji-cheat-sheet") +func emojisCMD(_ string, u *User) { + u.room.broadcast(Devbot, "Check out https\\://github.com/ikatyang/emoji-cheat-sheet") } -func commandsRestCMD(_ string, u *user) { - u.room.broadcast("", "The rest \n"+autogenCommands(cmdsRest)) +func commandsRestCMD(_ string, u *User) { + u.room.broadcast("", "The rest \n"+autogenCommands(RestCMDs)) } -func manCMD(rest string, u *user) { +func manCMD(rest string, u *User) { if rest == "" { - u.room.broadcast(devbot, "What command do you want help with?") + u.room.broadcast(Devbot, "What command do you want help with?") return } - for _, c := range allcmds { + for _, c := range CMDs { if c.name == rest { - u.room.broadcast(devbot, "Usage: "+c.name+" "+c.argsInfo+" \n"+c.info) + u.room.broadcast(Devbot, "Usage: "+c.name+" "+c.argsInfo+" \n"+c.info) return } } u.room.broadcast("", "This system has been minimized by removing packages and content that are not required on a system that users do not log into.\n\nTo restore this content, including manpages, you can run the 'unminimize' command. You will still need to ensure the 'man-db' package is installed.") } -func lsCMD(rest string, u *user) { +func lsCMD(rest string, u *User) { if len(rest) > 0 && rest[0] == '#' { - if r, ok := rooms[rest]; ok { + if r, ok := Rooms[rest]; ok { usersList := "" for _, us := range r.users { - usersList += us.Name + blue.Paint("/ ") + usersList += us.Name + Blue.Paint("/ ") } u.room.broadcast("", usersList) return @@ -790,21 +790,21 @@ func lsCMD(rest string, u *user) { return } roomList := "" - for _, r := range rooms { - roomList += blue.Paint(r.name + "/ ") + for _, r := range Rooms { + roomList += Blue.Paint(r.name + "/ ") } usersList := "" for _, us := range u.room.users { - usersList += us.Name + blue.Paint("/ ") + usersList += us.Name + Blue.Paint("/ ") } u.room.broadcast("", "README.md "+usersList+roomList) } -func commandsCMD(_ string, u *user) { - u.room.broadcast("", "Commands \n"+autogenCommands(cmds)) +func commandsCMD(_ string, u *User) { + u.room.broadcast("", "Commands \n"+autogenCommands(MainCMDs)) } -func autoloadCMD(arg string, u *user) { +func autoloadCMD(arg string, u *User) { if arg == "on" { u.Autoload = true } else if arg == "off" { @@ -818,21 +818,21 @@ func autoloadCMD(arg string, u *user) { } u.room.broadcast("", "autoload "+status) } else { - u.room.broadcast(devbot, "your options are off or on") + u.room.broadcast(Devbot, "your options are off or on") } } -func saveCMD(_ string, u *user) { +func saveCMD(_ string, u *User) { err := u.savePrefs() if err != nil { - u.room.broadcast(devbot, "An error occurred while saving: "+err.Error()) - l.Println("error while saving user data:", err.Error()) + u.room.broadcast(Devbot, "An error occurred while saving: "+err.Error()) + Log.Println("error while saving user data:", err.Error()) } } -func loadCMD(_ string, u *user) { +func loadCMD(_ string, u *User) { err := u.loadPrefs() if err != nil { - u.room.broadcast(devbot, "An error occurred while loading: "+err.Error()) - l.Println("error while loading user data:", err.Error()) + u.room.broadcast(Devbot, "An error occurred while loading: "+err.Error()) + Log.Println("error while loading user data:", err.Error()) } } diff --git a/config.go b/config.go index e7d28935..2446366a 100644 --- a/config.go +++ b/config.go @@ -7,7 +7,7 @@ import ( "strconv" ) -type config struct { +type ConfigType struct { Port int `yaml:"port"` AltPort int `yaml:"alt_port"` ProfilePort int `yaml:"profile_port"` @@ -18,25 +18,25 @@ type config struct { IntegrationConfig string `yaml:"integration_config"` } -// integrations stores information needed by integrations. +// IntegrationsType stores information needed by integrations. // Code that uses this should check if fields are nil. -type integrations struct { +type IntegrationsType struct { // Twitter stores the information needed for the Twitter integration. // Check if it is enabled by checking if Twitter is nil. - Twitter *twitterInfo `yaml:"twitter"` + Twitter *TwitterInfo `yaml:"twitter"` // Slack stores the information needed for the Slack integration. // Check if it is enabled by checking if Slack is nil. - Slack *slackInfo `yaml:"slack"` + Slack *SlackInfo `yaml:"slack"` } -type twitterInfo struct { +type TwitterInfo struct { ConsumerKey string `yaml:"consumer_key"` ConsumerSecret string `yaml:"consumer_secret"` AccessToken string `yaml:"access_token"` AccessTokenSecret string `yaml:"access_token_secret"` } -type slackInfo struct { +type SlackInfo struct { // Token is the Slack API token Token string `yaml:"token"` // Channel is the Slack channel to post to @@ -46,7 +46,7 @@ type slackInfo struct { } var ( - Config = config{ // first stores default config + Config = ConfigType{ // first stores default config Port: 2221, AltPort: 8080, ProfilePort: 5555, @@ -56,7 +56,7 @@ var ( IntegrationConfig: "", } - Integrations = integrations{ + Integrations = IntegrationsType{ Twitter: nil, Slack: nil, } diff --git a/devchat.go b/devchat.go index 4f4538b1..5f062b0a 100644 --- a/devchat.go +++ b/devchat.go @@ -28,43 +28,42 @@ import ( ) var ( - scrollback = 16 - - mainRoom = &room{"#main", make([]*user, 0, 10), sync.Mutex{}} - rooms = map[string]*room{mainRoom.name: mainRoom} - backlog = make([]backlogMessage, 0, scrollback) - bans = make([]ban, 0, 10) - idsInMinToTimes = make(map[string]int, 10) // TODO: maybe add some IP-based factor to disallow rapid key-gen attempts - antispamMessages = make(map[string]int) - - l *log.Logger - devbot = "" // initialized in main - startupTime = time.Now() + Scrollback = 16 + + MainRoom = &Room{"#main", make([]*User, 0, 10), sync.Mutex{}} + Rooms = map[string]*Room{MainRoom.name: MainRoom} + Backlog = make([]backlogMessage, 0, Scrollback) + Bans = make([]Ban, 0, 10) + IDsInMinToTimes = make(map[string]int, 10) // TODO: maybe add some IP-based factor to disallow rapid key-gen attempts + AntispamMessages = make(map[string]int) + + Log *log.Logger + Devbot = "" // initialized in main ) const ( maxMsgLen = 5120 ) -type ban struct { +type Ban struct { Addr string ID string } -type room struct { +type Room struct { name string - users []*user + users []*User usersMutex sync.Mutex } -type user struct { +type User struct { Name string Pronouns []string session ssh.Session term *terminal.Terminal - room *room - messaging *user // currently messaging this user in a DM + room *Room + messaging *User // currently messaging this User in a DM Bell bool PingEverytime bool @@ -81,18 +80,18 @@ type user struct { closeOnce sync.Once lastTimestamp time.Time joinTime time.Time - Timezone *timeLocation + Timezone *TimeLocation } -// timeLocation is an alias of time.Location and is used to properly implement MarshalJSON and UnmarshalJSON -type timeLocation time.Location +// TimeLocation is an alias of time.Location and is used to properly implement MarshalJSON and UnmarshalJSON +type TimeLocation time.Location -func (t *timeLocation) MarshalJSON() ([]byte, error) { +func (t *TimeLocation) MarshalJSON() ([]byte, error) { loc := (*time.Location)(t) return json.Marshal(loc.String()) } -func (t *timeLocation) UnmarshalJSON(data []byte) error { +func (t *TimeLocation) UnmarshalJSON(data []byte) error { name := "" err := json.Unmarshal(data, &name) if err != nil { @@ -102,7 +101,7 @@ func (t *timeLocation) UnmarshalJSON(data []byte) error { if err != nil { return err } - *t = timeLocation(*loc) + *t = TimeLocation(*loc) return nil } @@ -116,17 +115,17 @@ type backlogMessage struct { func main() { logfile, err := os.OpenFile(Config.DataDir+string(os.PathSeparator)+"log.txt", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { - l.Println(err) + Log.Println(err) return } - l = log.New(io.MultiWriter(logfile, os.Stdout), "", log.Ldate|log.Ltime|log.Lshortfile) + Log = log.New(io.MultiWriter(logfile, os.Stdout), "", log.Ldate|log.Ltime|log.Lshortfile) go func() { err := http.ListenAndServe(fmt.Sprintf(":%d", Config.ProfilePort), nil) if err != nil { - l.Println(err) + Log.Println(err) } }() - devbot = green.Paint("devbot") + Devbot = Green.Paint("devbot") rand.Seed(time.Now().Unix()) readBans() c := make(chan os.Signal, 2) @@ -137,11 +136,11 @@ func main() { saveBans() logfile.Close() time.AfterFunc(time.Second, func() { - l.Println("Broadcast taking too long, exiting server early.") + Log.Println("Broadcast taking too long, exiting server early.") os.Exit(4) }) - for _, r := range rooms { - r.broadcast(devbot, "Server going down! This is probably because it is being updated. Try joining back immediately. \n"+ + for _, r := range Rooms { + r.broadcast(Devbot, "Server going down! This is probably because it is being updated. Try joining back immediately. \n"+ "If you still can't join, try joining back in 2 minutes. If you _still_ can't join, make an issue at github.com/quackduck/devzat/issues") } os.Exit(0) @@ -154,7 +153,7 @@ func main() { } defer func() { // crash protection if i := recover(); i != nil { - mainRoom.broadcast(devbot, "Slap the developers in the face for me, the server almost crashed, also tell them this: "+fmt.Sprint(i)+", stack: "+string(debug.Stack())) + MainRoom.broadcast(Devbot, "Slap the developers in the face for me, the server almost crashed, also tell them this: "+fmt.Sprint(i)+", stack: "+string(debug.Stack())) } }() u.repl() @@ -177,23 +176,23 @@ func main() { } } -func (r *room) broadcast(senderName, msg string) { +func (r *Room) broadcast(senderName, msg string) { if msg == "" { return } if senderName != "" { - slackChan <- "[" + r.name + "] " + senderName + ": " + msg + SlackChan <- "[" + r.name + "] " + senderName + ": " + msg } else { - slackChan <- "[" + r.name + "] " + msg + SlackChan <- "[" + r.name + "] " + msg } r.broadcastNoSlack(senderName, msg) } -func (r *room) broadcastNoSlack(senderName, msg string) { +func (r *Room) broadcastNoSlack(senderName, msg string) { if msg == "" { return } - msg = strings.ReplaceAll(msg, "@everyone", green.Paint("everyone\a")) + msg = strings.ReplaceAll(msg, "@everyone", Green.Paint("everyone\a")) r.usersMutex.Lock() for i := range r.users { msg = strings.ReplaceAll(msg, "@"+stripansi.Strip(r.users[i].Name), r.users[i].Name) @@ -203,15 +202,15 @@ func (r *room) broadcastNoSlack(senderName, msg string) { r.users[i].writeln(senderName, msg) } r.usersMutex.Unlock() - if r == mainRoom { - backlog = append(backlog, backlogMessage{time.Now(), senderName, msg + "\n"}) - if len(backlog) > scrollback { - backlog = backlog[len(backlog)-scrollback:] + if r == MainRoom { + Backlog = append(Backlog, backlogMessage{time.Now(), senderName, msg + "\n"}) + if len(Backlog) > Scrollback { + Backlog = Backlog[len(Backlog)-Scrollback:] } } } -func autocompleteCallback(u *user, line string, pos int, key rune) (string, int, bool) { +func autocompleteCallback(u *User, line string, pos int, key rune) (string, int, bool) { if key == '\t' { // Autocomplete a username @@ -230,11 +229,11 @@ func autocompleteCallback(u *user, line string, pos int, key rune) (string, int, return "", pos, false } -func userMentionAutocomplete(u *user, words []string) string { +func userMentionAutocomplete(u *User, words []string) string { if len(words) < 1 { return "" } - // Check the last word and see if it's trying to refer to a user + // Check the last word and see if it's trying to refer to a User if words[len(words)-1][0] == '@' || (len(words)-1 == 0 && words[0][0] == '=') { // mentioning someone or dm-ing someone inputWord := words[len(words)-1][1:] // slice the @ or = off for i := range u.room.users { @@ -248,11 +247,11 @@ func userMentionAutocomplete(u *user, words []string) string { return "" } -func roomAutocomplete(_ *user, words []string) string { +func roomAutocomplete(_ *User, words []string) string { // trying to refer to a room? if len(words) > 0 && words[len(words)-1][0] == '#' { // don't slice the # off, since the room name includes it - for name := range rooms { + for name := range Rooms { toAdd := strings.TrimPrefix(name, words[len(words)-1]) if toAdd != name { // there was a match, and some text got trimmed! return toAdd + " " @@ -262,7 +261,7 @@ func roomAutocomplete(_ *user, words []string) string { return "" } -func newUser(s ssh.Session) *user { +func newUser(s ssh.Session) *User { term := terminal.NewTerminal(s, "> ") _ = term.SetSize(10000, 10000) // disable any formatting done by term pty, winChan, _ := s.Pty() @@ -278,7 +277,7 @@ func newUser(s ssh.Session) *user { toHash = host } - u := &user{ + u := &User{ Name: s.User(), Pronouns: []string{"unset"}, session: s, @@ -291,79 +290,79 @@ func newUser(s ssh.Session) *user { win: w, lastTimestamp: time.Now(), joinTime: time.Now(), - room: mainRoom} + room: MainRoom} go func() { for u.win = range winChan { } }() - l.Println("Connected " + u.Name + " [" + u.id + "]") + Log.Println("Connected " + u.Name + " [" + u.id + "]") - if bansContains(bans, u.addr, u.id) { - l.Println("Rejected " + u.Name + " [" + host + "]") - u.writeln(devbot, "**You are banned**. If you feel this was a mistake, please reach out at github.com/quackduck/devzat/issues or email igoel.mail@gmail.com. Please include the following information: [ID "+u.id+"]") + if bansContains(Bans, u.addr, u.id) { + Log.Println("Rejected " + u.Name + " [" + host + "]") + u.writeln(Devbot, "**You are banned**. If you feel this was a mistake, please reach out at github.com/quackduck/devzat/issues or email igoel.mail@gmail.com. Please include the following information: [ID "+u.id+"]") u.closeQuietly() return nil } - idsInMinToTimes[u.id]++ + IDsInMinToTimes[u.id]++ time.AfterFunc(60*time.Second, func() { - idsInMinToTimes[u.id]-- + IDsInMinToTimes[u.id]-- }) - if idsInMinToTimes[u.id] > 6 { - bans = append(bans, ban{u.addr, u.id}) - mainRoom.broadcast(devbot, "`"+s.User()+"` has been banned automatically. ID: "+u.id) + if IDsInMinToTimes[u.id] > 6 { + Bans = append(Bans, Ban{u.addr, u.id}) + MainRoom.broadcast(Devbot, "`"+s.User()+"` has been banned automatically. ID: "+u.id) return nil } clearCMD("", u) // always clear the screen on connect valentines(u) - if len(backlog) > 0 { - lastStamp := backlog[0].timestamp + if len(Backlog) > 0 { + lastStamp := Backlog[0].timestamp u.rWriteln(printPrettyDuration(u.joinTime.Sub(lastStamp)) + " earlier") - for i := range backlog { - if backlog[i].timestamp.Sub(lastStamp) > time.Minute { - lastStamp = backlog[i].timestamp + for i := range Backlog { + if Backlog[i].timestamp.Sub(lastStamp) > time.Minute { + lastStamp = Backlog[i].timestamp u.rWriteln(printPrettyDuration(u.joinTime.Sub(lastStamp)) + " earlier") } - u.writeln(backlog[i].senderName, backlog[i].text) + u.writeln(Backlog[i].senderName, Backlog[i].text) } } - if err := u.pickUsernameQuietly(s.User()); err != nil { // user exited or had some error - l.Println(err) + if err := u.pickUsernameQuietly(s.User()); err != nil { // User exited or had some error + Log.Println(err) s.Close() return nil } err := u.loadPrefs() // since we are loading for the first time, respect the saved value if err != nil { - l.Println("Could not load user:", err) + Log.Println("Could not load user:", err) } - mainRoom.usersMutex.Lock() - mainRoom.users = append(mainRoom.users, u) + MainRoom.usersMutex.Lock() + MainRoom.users = append(MainRoom.users, u) go sendCurrentUsersTwitterMessage() - mainRoom.usersMutex.Unlock() + MainRoom.usersMutex.Unlock() u.term.SetBracketedPasteMode(true) // experimental paste bracketing support term.AutoCompleteCallback = func(line string, pos int, key rune) (string, int, bool) { return autocompleteCallback(u, line, pos, key) } - switch len(mainRoom.users) - 1 { + switch len(MainRoom.users) - 1 { case 0: - u.writeln("", blue.Paint("Welcome to the chat. There are no more users")) + u.writeln("", Blue.Paint("Welcome to the chat. There are no more users")) case 1: - u.writeln("", yellow.Paint("Welcome to the chat. There is one more user")) + u.writeln("", Yellow.Paint("Welcome to the chat. There is one more user")) default: - u.writeln("", green.Paint("Welcome to the chat. There are", strconv.Itoa(len(mainRoom.users)-1), "more users")) + u.writeln("", Green.Paint("Welcome to the chat. There are", strconv.Itoa(len(MainRoom.users)-1), "more users")) } - mainRoom.broadcast(devbot, u.Name+" has joined the chat") + MainRoom.broadcast(Devbot, u.Name+" has joined the chat") return u } -func valentines(u *user) { +func valentines(u *User) { if time.Now().Month() == time.February && (time.Now().Day() == 14 || time.Now().Day() == 15 || time.Now().Day() == 13) { // TODO: add a few more random images u.writeln("", "![❤️](https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/81/heavy-black-heart_2764.png)") @@ -375,39 +374,39 @@ func valentines(u *user) { } // cleanupRoom deletes a room if it's empty and isn't the main room -func cleanupRoom(r *room) { - if r != mainRoom && len(r.users) == 0 { - delete(rooms, r.name) +func cleanupRoom(r *Room) { + if r != MainRoom && len(r.users) == 0 { + delete(Rooms, r.name) } } -// Removes a user and prints Twitter and chat message -func (u *user) close(msg string) { +// Removes a User and prints Twitter and chat message +func (u *User) close(msg string) { u.closeOnce.Do(func() { u.closeQuietly() err := u.savePrefs() if err != nil { - l.Println(err) // not much else we can do + Log.Println(err) // not much else we can do } go sendCurrentUsersTwitterMessage() if time.Since(u.joinTime) > time.Minute/2 { msg += ". They were online for " + printPrettyDuration(time.Since(u.joinTime)) } - u.room.broadcast(devbot, msg) + u.room.broadcast(Devbot, msg) u.room.users = remove(u.room.users, u) cleanupRoom(u.room) }) } -// Removes a user silently, used to close banned users -func (u *user) closeQuietly() { +// Removes a User silently, used to close banned users +func (u *User) closeQuietly() { u.room.usersMutex.Lock() u.room.users = remove(u.room.users, u) u.room.usersMutex.Unlock() u.session.Close() } -func (u *user) writeln(senderName string, msg string) { +func (u *User) writeln(senderName string, msg string) { if strings.Contains(msg, u.Name) { // is a ping msg += "\a" } @@ -448,8 +447,8 @@ func (u *user) writeln(senderName string, msg string) { } } -// Write to the right of the user's window -func (u *user) rWriteln(msg string) { +// Write to the right of the User's window +func (u *User) rWriteln(msg string) { if u.win.Width-lenString(msg) > 0 { u.term.Write([]byte(strings.Repeat(" ", u.win.Width-lenString(msg)) + msg + "\n")) } else { @@ -457,22 +456,22 @@ func (u *user) rWriteln(msg string) { } } -// pickUsernameQuietly changes the user's username, broadcasting a name change notification if needed. +// pickUsernameQuietly changes the User's username, broadcasting a name change notification if needed. // An error is returned if the username entered had a bad word or reading input failed. -func (u *user) pickUsername(possibleName string) error { +func (u *User) pickUsername(possibleName string) error { oldName := u.Name err := u.pickUsernameQuietly(possibleName) if err != nil { return err } - if stripansi.Strip(u.Name) != stripansi.Strip(oldName) && stripansi.Strip(u.Name) != possibleName { // did the name change, and is it not what the user entered? - u.room.broadcast(devbot, oldName+" is now called "+u.Name) + if stripansi.Strip(u.Name) != stripansi.Strip(oldName) && stripansi.Strip(u.Name) != possibleName { // did the name change, and is it not what the User entered? + u.room.broadcast(Devbot, oldName+" is now called "+u.Name) } return nil } // pickUsernameQuietly is like pickUsername but does not broadcast a name change notification. -func (u *user) pickUsernameQuietly(possibleName string) error { +func (u *User) pickUsernameQuietly(possibleName string) error { possibleName = cleanName(possibleName) var err error for { @@ -512,11 +511,11 @@ func (u *user) pickUsernameQuietly(possibleName string) error { u.changeColor("random") //nolint:errcheck // we know "random" is a valid color return nil } - u.changeColor(styles[rand.Intn(len(styles))].name) //nolint:errcheck // we know this is a valid color + u.changeColor(Styles[rand.Intn(len(Styles))].name) //nolint:errcheck // we know this is a valid color return nil } -func (u *user) displayPronouns() string { +func (u *User) displayPronouns() string { result := "" for i := 0; i < len(u.Pronouns); i++ { str, _ := applyColorToData(u.Pronouns[i], u.Color, u.ColorBG) @@ -528,7 +527,7 @@ func (u *user) displayPronouns() string { return result[1:] } -func (u *user) savePrefs() error { +func (u *User) savePrefs() error { oldname := u.Name u.Name = stripansi.Strip(u.Name) data, err := json.Marshal(u) @@ -546,7 +545,7 @@ func (u *user) savePrefs() error { return err } -func (u *user) loadPrefs() error { +func (u *User) loadPrefs() error { save := filepath.Join(Config.DataDir, "user-prefs", u.id+".json") data, err := os.ReadFile(save) @@ -556,7 +555,7 @@ func (u *user) loadPrefs() error { oldUser := *u //nolint:govet // complains because of a lock copy. We may need that exact lock value later on - // temp := user{} + // temp := User{} err = json.Unmarshal(data, u) // won't overwrite private fields if err != nil { return err @@ -588,22 +587,22 @@ func (u *user) loadPrefs() error { return nil } -func (u *user) changeRoom(r *room) { +func (u *User) changeRoom(r *Room) { if u.room == r { return } u.room.users = remove(u.room.users, u) - u.room.broadcast("", u.Name+" is joining "+blue.Paint(r.name)) // tell the old room + u.room.broadcast("", u.Name+" is joining "+Blue.Paint(r.name)) // tell the old room cleanupRoom(u.room) u.room = r if _, dup := userDuplicate(u.room, u.Name); dup { u.pickUsername("") //nolint:errcheck // if reading input failed the next repl will err out } u.room.users = append(u.room.users, u) - u.room.broadcast(devbot, u.Name+" has joined "+blue.Paint(u.room.name)) + u.room.broadcast(Devbot, u.Name+" has joined "+Blue.Paint(u.room.name)) } -func (u *user) repl() { +func (u *User) repl() { for { line, err := u.term.ReadLine() if err == io.EOF { @@ -624,7 +623,7 @@ func (u *user) repl() { line += additionalLine + "\n" } if err != nil { - l.Println(u.Name, err) + Log.Println(u.Name, err) u.close(u.Name + " has left the chat due to an error: " + err.Error()) return } @@ -646,20 +645,20 @@ func (u *user) repl() { continue } - antispamMessages[u.id]++ + AntispamMessages[u.id]++ time.AfterFunc(5*time.Second, func() { - antispamMessages[u.id]-- + AntispamMessages[u.id]-- }) - if antispamMessages[u.id] >= 30 { - u.room.broadcast(devbot, u.Name+", stop spamming or you could get banned.") + if AntispamMessages[u.id] >= 30 { + u.room.broadcast(Devbot, u.Name+", stop spamming or you could get banned.") } - if antispamMessages[u.id] >= 50 { - if !bansContains(bans, u.addr, u.id) { - bans = append(bans, ban{u.addr, u.id}) + if AntispamMessages[u.id] >= 50 { + if !bansContains(Bans, u.addr, u.id) { + Bans = append(Bans, Ban{u.addr, u.id}) saveBans() } - u.writeln(devbot, "anti-spam triggered") - u.close(red.Paint(u.Name + " has been banned for spamming")) + u.writeln(Devbot, "anti-spam triggered") + u.close(Red.Paint(u.Name + " has been banned for spamming")) return } line = replaceSlackEmoji(line) @@ -722,7 +721,7 @@ func fetchEmojiSingle(name string) string { } // may contain a bug ("may" because it could be the terminal's fault) -func calculateLinesTaken(u *user, s string, width int) { +func calculateLinesTaken(u *User, s string, width int) { s = stripansi.Strip(s) //fmt.Println("`"+s+"`", "width", width) pos := 0 @@ -746,7 +745,7 @@ func calculateLinesTaken(u *user, s string, width int) { } // bansContains reports if the addr or id is found in the bans list -func bansContains(b []ban, addr string, id string) bool { +func bansContains(b []Ban, addr string, id string) bool { for i := 0; i < len(b); i++ { if b[i].Addr == addr || b[i].ID == id { return true diff --git a/games.go b/games.go index bcfcd9d9..10684fe3 100644 --- a/games.go +++ b/games.go @@ -42,6 +42,6 @@ func tttPrint(cells [9]tictactoe.State) string { cells[3], cells[4], cells[5], cells[6], cells[7], cells[8]), - tictactoe.X.String(), chalk.BrightYellow(tictactoe.X.String())), // add some coloring - tictactoe.O.String(), chalk.BrightGreen(tictactoe.O.String())) + tictactoe.X.String(), Chalk.BrightYellow(tictactoe.X.String())), // add some coloring + tictactoe.O.String(), Chalk.BrightGreen(tictactoe.O.String())) } diff --git a/slack.go b/slack.go index 893b39e7..660ea0ae 100644 --- a/slack.go +++ b/slack.go @@ -12,9 +12,9 @@ import ( ) var ( - slackChan chan string - api *slack.Client - rtm *slack.RTM + SlackChan chan string + API *slack.Client + RTM *slack.RTM ) func getMsgsFromSlack() { @@ -22,10 +22,10 @@ func getMsgsFromSlack() { return } - go rtm.ManageConnection() - uslack := new(user) - uslack.room = mainRoom - for msg := range rtm.IncomingEvents { + go RTM.ManageConnection() + uslack := new(User) + uslack.room = MainRoom + for msg := range RTM.IncomingEvents { switch ev := msg.Data.(type) { case *slack.MessageEvent: msg := ev.Msg @@ -33,18 +33,18 @@ func getMsgsFromSlack() { if msg.SubType != "" { break // We're only handling normal messages. } - u, _ := api.GetUserInfo(msg.User) + u, _ := API.GetUserInfo(msg.User) if !strings.HasPrefix(text, "./hide") { h := sha1.Sum([]byte(u.ID)) i, _ := strconv.ParseInt(hex.EncodeToString(h[:2]), 16, 0) // two bytes as an int - uslack.Name = yellow.Paint(Integrations.Slack.Prefix+" ") + (styles[int(i)%len(styles)]).apply(strings.Fields(u.RealName)[0]) + uslack.Name = Yellow.Paint(Integrations.Slack.Prefix+" ") + (Styles[int(i)%len(Styles)]).apply(strings.Fields(u.RealName)[0]) uslack.isSlack = true runCommands(text, uslack) } case *slack.ConnectedEvent: - l.Println("Connected to Slack") + Log.Println("Connected to Slack") case *slack.InvalidAuthEvent: - l.Println("Invalid token") + Log.Println("Invalid token") return } } @@ -52,21 +52,21 @@ func getMsgsFromSlack() { func slackInit() { // called by init() in config.go if Integrations.Slack == nil { - slackChan = make(chan string, 2) + SlackChan = make(chan string, 2) go func() { - for range slackChan { + for range SlackChan { } }() return } - api = slack.New(Integrations.Slack.Token) - rtm = api.NewRTM() - slackChan = make(chan string, 100) + API = slack.New(Integrations.Slack.Token) + RTM = API.NewRTM() + SlackChan = make(chan string, 100) go func() { - for msg := range slackChan { + for msg := range SlackChan { msg = strings.ReplaceAll(stripansi.Strip(msg), `\n`, "\n") - rtm.SendMessage(rtm.NewOutgoingMessage(msg, Integrations.Slack.ChannelID)) + RTM.SendMessage(RTM.NewOutgoingMessage(msg, Integrations.Slack.ChannelID)) } }() } diff --git a/twitter.go b/twitter.go index d21ec0a7..25259290 100644 --- a/twitter.go +++ b/twitter.go @@ -11,8 +11,9 @@ import ( ) var ( - client *twitter.Client - allowTweet = true + StartupTime = time.Now() + Client *twitter.Client + AllowTweet = true ) func sendCurrentUsersTwitterMessage() { @@ -20,15 +21,15 @@ func sendCurrentUsersTwitterMessage() { return } // TODO: count all users in all rooms - if len(mainRoom.users) == 0 { + if len(MainRoom.users) == 0 { return } - if !allowTweet { + if !AllowTweet { return } - allowTweet = false - usersSnapshot := append(make([]*user, 0, len(mainRoom.users)), mainRoom.users...) - areUsersEqual := func(a []*user, b []*user) bool { + AllowTweet = false + usersSnapshot := append(make([]*User, 0, len(MainRoom.users)), MainRoom.users...) + areUsersEqual := func(a []*User, b []*User) bool { if len(a) != len(b) { return false } @@ -41,24 +42,24 @@ func sendCurrentUsersTwitterMessage() { } go func() { time.Sleep(time.Second * 60) - allowTweet = true - if !areUsersEqual(mainRoom.users, usersSnapshot) { + AllowTweet = true + if !areUsersEqual(MainRoom.users, usersSnapshot) { return } - l.Println("Sending twitter update") - names := make([]string, 0, len(mainRoom.users)) - for _, us := range mainRoom.users { + Log.Println("Sending twitter update") + names := make([]string, 0, len(MainRoom.users)) + for _, us := range MainRoom.users { names = append(names, us.Name) } - t, _, err := client.Statuses.Update("People on Devzat rn: "+stripansi.Strip(fmt.Sprint(names))+"\nJoin em with \"ssh devzat.hackclub.com\"\nUptime: "+printPrettyDuration(time.Since(startupTime)), nil) + t, _, err := Client.Statuses.Update("People on Devzat rn: "+stripansi.Strip(fmt.Sprint(names))+"\nJoin em with \"ssh devzat.hackclub.com\"\nUptime: "+printPrettyDuration(time.Since(StartupTime)), nil) if err != nil { if !strings.Contains(err.Error(), "twitter: 187 Status is a duplicate.") { - mainRoom.broadcast(devbot, "err: "+err.Error()) + MainRoom.broadcast(Devbot, "err: "+err.Error()) } - l.Println("Got twitter err", err) + Log.Println("Got twitter err", err) return } - mainRoom.broadcast(devbot, "https\\://twitter.com/"+t.User.ScreenName+"/status/"+t.IDStr) + MainRoom.broadcast(Devbot, "https\\://twitter.com/"+t.User.ScreenName+"/status/"+t.IDStr) }() } @@ -70,5 +71,5 @@ func twitterInit() { // called by init() in config.go config := oauth1.NewConfig(Integrations.Twitter.ConsumerKey, Integrations.Twitter.ConsumerSecret) token := oauth1.NewToken(Integrations.Twitter.AccessToken, Integrations.Twitter.AccessTokenSecret) httpClient := config.Client(oauth1.NoContext, token) - client = twitter.NewClient(httpClient) + Client = twitter.NewClient(httpClient) } diff --git a/util.go b/util.go index dbf090df..127df2ae 100644 --- a/util.go +++ b/util.go @@ -18,7 +18,7 @@ import ( ) var ( - art = getASCIIArt() + Art = getASCIIArt() ) func getASCIIArt() string { @@ -30,7 +30,7 @@ func getASCIIArt() string { return string(b) } -func printUsersInRoom(r *room) string { +func printUsersInRoom(r *Room) string { names := "" admins := "" for _, us := range r.users { @@ -55,7 +55,7 @@ func lenString(a string) int { return len([]rune(stripansi.Strip(a))) } -func autogenCommands(cmds []cmd) string { +func autogenCommands(cmds []CMD) string { b := new(bytes.Buffer) w := tabwriter.NewWriter(b, 0, 0, 2, ' ', 0) for _, cmd := range cmds { @@ -65,8 +65,8 @@ func autogenCommands(cmds []cmd) string { return b.String() } -// check if a user is an admin -func auth(u *user) bool { +// check if a User is an admin +func auth(u *User) bool { _, ok := Config.Admins[u.id] return ok } @@ -117,8 +117,8 @@ func mdRender(a string, beforeMessageLen int, lineWidth int) string { return strings.Join(split, "\n") } -// Returns true and the user with the same name if the username is taken, false and nil otherwise -func userDuplicate(r *room, a string) (*user, bool) { +// Returns true and the User with the same name if the username is taken, false and nil otherwise +func userDuplicate(r *Room, a string) (*User, bool) { for i := range r.users { if stripansi.Strip(r.users[i].Name) == stripansi.Strip(a) { return r.users[i], true @@ -130,16 +130,16 @@ func userDuplicate(r *room, a string) (*user, bool) { func saveBans() { f, err := os.Create(Config.DataDir + string(os.PathSeparator) + "bans.json") if err != nil { - l.Println(err) + Log.Println(err) return } defer f.Close() j := json.NewEncoder(f) j.SetIndent("", " ") - err = j.Encode(bans) + err = j.Encode(Bans) if err != nil { - mainRoom.broadcast(devbot, "error saving bans: "+err.Error()) - l.Println(err) + MainRoom.broadcast(Devbot, "error saving bans: "+err.Error()) + Log.Println(err) return } } @@ -147,19 +147,19 @@ func saveBans() { func readBans() { f, err := os.Open(Config.DataDir + string(os.PathSeparator) + "bans.json") if err != nil && !os.IsNotExist(err) { // if there is an error and it is not a "file does not exist" error - l.Println(err) + Log.Println(err) return } defer f.Close() - err = json.NewDecoder(f).Decode(&bans) + err = json.NewDecoder(f).Decode(&Bans) if err != nil { - mainRoom.broadcast(devbot, "error reading bans: "+err.Error()) - l.Println(err) + MainRoom.broadcast(Devbot, "error reading bans: "+err.Error()) + Log.Println(err) return } } -func findUserByName(r *room, name string) (*user, bool) { +func findUserByName(r *Room, name string) (*User, bool) { r.usersMutex.Lock() defer r.usersMutex.Unlock() for _, u := range r.users { @@ -170,7 +170,7 @@ func findUserByName(r *room, name string) (*user, bool) { return nil, false } -func remove(s []*user, a *user) []*user { +func remove(s []*User, a *User) []*User { for j := range s { if s[j] == a { return append(s[:j], s[j+1:]...) @@ -179,7 +179,7 @@ func remove(s []*user, a *user) []*user { return s } -func devbotChat(room *room, line string) { +func devbotChat(room *Room, line string) { if strings.Contains(line, "devbot") { if strings.Contains(line, "how are you") || strings.Contains(line, "how you") { devbotRespond(room, []string{"How are _you_", @@ -256,12 +256,12 @@ func devbotChat(room *room, line string) { } } -func devbotRespond(room *room, messages []string, chance int) { +func devbotRespond(room *Room, messages []string, chance int) { if chance == 100 || chance > rand.Intn(100) { go func() { time.Sleep(time.Second / 2) pick := messages[rand.Intn(len(messages))] - room.broadcast(devbot, pick) + room.broadcast(Devbot, pick) }() } }