client.go (5767B)
1 package main 2 3 import ( 4 "fmt" 5 "github.com/jroimartin/gocui" 6 "log" 7 "net" 8 "strings" 9 ) 10 11 type Command struct { 12 Name string 13 Description string 14 Signature string 15 Function func(*gocui.View, []string) error 16 } 17 18 const commandCnt = 5 19 20 var commands [commandCnt]Command 21 var gui *gocui.Gui 22 var serverConn net.Conn 23 24 const serverAddr = "127.0.0.1:6969" 25 const initMsg = `This is a client for connecting to GoTel chat server. 26 =======================================================` 27 28 func main() { 29 initCommands() 30 gui, _ = gocui.NewGui(gocui.OutputNormal) 31 // if err != nil { 32 // log.Panicln(err) 33 // } 34 defer gui.Close() 35 36 gui.Cursor = true 37 gui.Mouse = true 38 gui.SetManagerFunc(layout) 39 40 if err := gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { 41 log.Panicln(err) 42 } 43 44 if err := gui.SetKeybinding("prompt", gocui.KeyEnter, gocui.ModNone, getInput); err != nil { 45 log.Panicln(err) 46 } 47 48 if err := gui.MainLoop(); err != nil && err != gocui.ErrQuit { 49 log.Panicln(err) 50 } 51 } 52 53 func layout(g *gocui.Gui) error { 54 maxX, maxY := g.Size() 55 chatLogW := maxX - 2 56 chatLogH := maxY - 10 57 promptX1 := 1 58 promptY1 := chatLogH + 1 59 promptX2 := maxX - 2 60 promptY2 := maxY - 2 61 if chatLog, err := g.SetView("chatLog", 1, 1, chatLogW, chatLogH); err != nil { 62 if err != gocui.ErrUnknownView { 63 return err 64 } 65 chatLog.Wrap = true 66 chatLog.Autoscroll = true 67 fmt.Fprintln(chatLog, initMsg) 68 } 69 if prompt, promptErr := g.SetView("prompt", promptX1, promptY1, promptX2, promptY2); promptErr != nil { 70 if promptErr != gocui.ErrUnknownView { 71 return promptErr 72 } 73 prompt.Editable = true 74 prompt.Wrap = true 75 if _, err := g.SetCurrentView("prompt"); err != nil { 76 return err 77 } 78 } 79 return nil 80 } 81 82 func getInput(g *gocui.Gui, v *gocui.View) error { 83 input := strings.TrimRight(v.Buffer(), "\r\n") 84 items := strings.Split(input, " ") 85 v.Clear() 86 v.SetCursor(0, 0) 87 chatLog, _ := g.View("chatLog") 88 if strings.HasPrefix(items[0], "/") { 89 for _, cmd := range commands { 90 if strings.HasPrefix(cmd.Signature, items[0]) { 91 cmd.Function(chatLog, items) 92 return nil 93 } 94 } 95 fmt.Fprintf(chatLog, "Invalid command: %s\n", items[0]) 96 return nil 97 } 98 if serverConn != nil { 99 serverConn.Write([]byte(input)) 100 } 101 return nil 102 } 103 104 func initCommands() { 105 commands = [commandCnt]Command{ 106 107 Command{ 108 Name: "help", 109 Description: "Print help menu", 110 Signature: "/help [command]", 111 Function: printHelp, 112 }, 113 114 Command{ 115 Name: "join", 116 Description: "Join the chat", 117 Signature: "/join <ipv4>:<port> <TOKEN>", 118 Function: sendJoin, 119 }, 120 121 Command{ 122 Name: "signup", 123 Description: "Sign up to server", 124 Signature: "/signup <username> <password>", 125 Function: sendSignup, 126 }, 127 128 Command{ 129 Name: "login", 130 Description: "Login to server", 131 Signature: "/login <username> <password>", 132 Function: sendLogin, 133 }, 134 135 Command{ 136 Name: "exit", 137 Description: "Logout from server", 138 Signature: "/exit", 139 Function: sendQuit, 140 }, 141 } 142 } 143 144 func printHelp(v *gocui.View, input []string) error { 145 if len(input) == 1 { 146 fmt.Fprintf(v, "Commands:\n") 147 for _, cmd := range commands { 148 fmt.Fprintf(v, "%s - %s\n", cmd.Signature, cmd.Description) 149 } 150 return nil 151 } 152 if len(input) == 2 { 153 for _, cmd := range commands { 154 if strings.HasPrefix(cmd.Signature, input[1]) { 155 fmt.Fprintf(v, "%s - %s\n", cmd.Signature, cmd.Description) 156 return nil 157 } 158 } 159 fmt.Fprintf(v, "%s is not a valid command. Type /help to see the full list of commands.\n", input[1]) 160 return nil 161 } 162 fmt.Fprintln(v, "Too many arguments for /help command.") 163 fmt.Fprintf(v, "%s - %s\n", commands[0].Signature, commands[0].Description) 164 return nil 165 } 166 167 func sendJoin(v *gocui.View, input []string) error { 168 var err error 169 if len(input) != 3 { 170 fmt.Fprintf(v, "Invalid join command.\n") 171 input := []string{"/help", "/join"} 172 printHelp(v, input) 173 return nil 174 } 175 176 if len(input[2]) != 32 { 177 fmt.Fprintf(v, "Invalid token: %s\nThe token should be a 32-character string.\n", input[1]) 178 return nil 179 } 180 181 serverConn, err = net.Dial("tcp", input[1]) 182 if err != nil { 183 fmt.Fprintf(v, "Server is not responding.\n") 184 return nil 185 } 186 _, err = serverConn.Write([]byte(input[0] + " " + input[2])) 187 if err != nil { 188 fmt.Fprintf(v, "Could not send join request to the server: %s\n", err) 189 return nil 190 } 191 go getMsg(serverConn) 192 return nil 193 } 194 195 func sendSignup(v *gocui.View, input []string) error { 196 if len(input) != 3 { 197 fmt.Fprintf(v, "Invalid signup command.\n") 198 input := []string{"/help", "/signup"} 199 printHelp(v, input) 200 return nil 201 } 202 203 _, err := serverConn.Write([]byte(input[0] + " " + input[1] + " " + input[2])) 204 if err != nil { 205 fmt.Fprintf(v, "Could not send signup request to the server: %s\nTry join command first.", err) 206 } 207 return nil 208 } 209 210 func sendLogin(v *gocui.View, input []string) error { 211 if len(input) != 3 { 212 fmt.Fprintf(v, "Invalid login command.\n") 213 input := []string{"/help", "/login"} 214 printHelp(v, input) 215 return nil 216 } 217 218 _, err := serverConn.Write([]byte(input[0] + " " + input[1] + " " + input[2])) 219 if err != nil { 220 fmt.Fprintf(v, "Could not send login request to the server: %s\nTry /join command first.", err) 221 } 222 return nil 223 } 224 225 func sendQuit(v *gocui.View, input []string) error { 226 // fmt.Fprintln(v, "Quiting from server") 227 serverConn.Close() 228 return nil 229 } 230 231 func getMsg(conn net.Conn) { 232 readBuf := make([]byte, 512) 233 for { 234 n, readErr := conn.Read(readBuf) 235 236 if readErr != nil { 237 return 238 } 239 gui.Update(func(g *gocui.Gui) error { 240 v, err := gui.View("chatLog") 241 if err != nil { 242 return err 243 } 244 fmt.Fprintln(v, string(readBuf[:n])) 245 return nil 246 }) 247 } 248 } 249 250 func quit(g *gocui.Gui, v *gocui.View) error { 251 sendQuit(nil, nil ) 252 return gocui.ErrQuit 253 }