diff --git a/cmd/client.go b/cmd/client.go new file mode 100644 index 0000000..05cce97 --- /dev/null +++ b/cmd/client.go @@ -0,0 +1,94 @@ +/* +Copyright © 2024 Raul +*/ + +package cmd + +import ( + "bufio" + "fmt" + "github.com/jroimartin/gocui" + "github.com/spf13/cobra" + "log" + "net" + "os" +) + +// clientCmd represents the client command +var clientCmd = &cobra.Command{ + Use: "client", + Short: "Connect to a mini-chat server", + Long: `Connect to a mini-chat server. + Example: + ./mini-chat client --ip 192.168.0.50 --port 1337`, + Run: func(cmd *cobra.Command, args []string) { + client(cmd) + }, +} + +var err error +var conn net.Conn + +func init() { + rootCmd.AddCommand(clientCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // clientCmd.PersistentFlags().String("foo", "", "A help for foo") + clientCmd.Flags().String("ip", "", "Server to connect to") + clientCmd.Flags().String("port", "1302", "Port to connect to") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // clientCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +func client(cmd *cobra.Command) { + port, _ := cmd.Flags().GetString("port") + ip, _ := cmd.Flags().GetString("ip") + if ip == "" { + fmt.Println("Not enough arguments, run \"-h\" parameter to see all the parameters!") + os.Exit(0) + } + if port == "" { + port = "1302" + } + socket := ip + ":" + port + conn, err = net.Dial("tcp", socket) + cobra.CheckErr(err) + defer conn.Close() + + reply := make([]byte, 1024) + conn.Read(reply) + fmt.Print(string(reply)) + + msg, err := bufio.NewReader(os.Stdin).ReadString('\n') + cobra.CheckErr(err) + conn.Write([]byte(msg)) + + ui(conn) + +} + +func sendmsg(g *gocui.Gui, v *gocui.View) error { + textarea, err := g.View("textarea") + if err != nil { + log.Panicln(err) + } + message := textarea.Buffer() + msg := string(message) + if msg == "" { + return nil + } + + if msg == "" { + return nil + } + + fmt.Fprint(conn, msg) + textarea.Clear() + textarea.SetCursor(0, 0) + return nil +} diff --git a/cmd/server.go b/cmd/server.go index 72da336..823b414 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -90,25 +90,11 @@ func masterReceiver(slaveChannel chan<- string, masterChannel <-chan string) { } } -// func receiver(conn net.Conn, ch chan string) { -// fmt.Println("THE RECEIVER HAS BEEN STARTED, YOU CAN ONLY SEE THIS MESSAGE ONCE") -// receiverIsStarted = false -// for { -// select { -// case otherMessage := <-ch: -// conn.Write([]byte(otherMessage)) -// default: -// } -// } -// } - func handleConn(conn net.Conn, slaveChannel <-chan string, masterChannel chan<- string) { defer conn.Close() - //var otherMessage string go func() { for { - //select { message := <-slaveChannel conn.Write([]byte(message)) } diff --git a/cmd/ui.go b/cmd/ui.go new file mode 100644 index 0000000..9f75b2f --- /dev/null +++ b/cmd/ui.go @@ -0,0 +1,108 @@ +package cmd + +import ( + "fmt" + "github.com/jroimartin/gocui" + "github.com/nsf/termbox-go" + "log" + "net" + "time" +) + +func ui(conn net.Conn) { + g, err := gocui.NewGui(gocui.OutputNormal) + if err != nil { + log.Panicln(err) + } + defer g.Close() + g.SetManagerFunc(layout) + g.Mouse = true + g.Cursor = true + initKeybindings(g) + + go listener(g, conn) + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + log.Panicln(err) + } +} + +func listener(g *gocui.Gui, conn net.Conn) { + time.Sleep(time.Second) + chatbox, err := g.View("chatbox") + if err != nil { + log.Panicln(err) + } + for { + reply := make([]byte, 2048) + _, err := conn.Read(reply) + if err != nil { + g.Close() + log.Fatalf("Server closed connection\n") + } + + fmt.Fprintln(chatbox, string(reply)) + termbox.Interrupt() + } +} + +func quit(*gocui.Gui, *gocui.View) error { + return gocui.ErrQuit +} + +func initKeybindings(g *gocui.Gui) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("button", gocui.MouseLeft, gocui.ModNone, sendmsg); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("textarea", gocui.KeyEnter, gocui.ModNone, sendmsg); err != nil { + log.Panicln(err) + } + return nil +} + +func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + + if chatbox, err := g.SetView("chatbox", 2, 1, maxX/2+40, maxY-6); err != nil { + if err != gocui.ErrUnknownView { + return err + } + chatbox.Title = "Chat Box" + } + + if button, err := g.SetView("button", maxX/2+32, maxY-4, maxX/2+40, maxY-2); err != nil { + if err != gocui.ErrUnknownView { + return err + } + //button.BgColor = gocui.ColorRed + button.Wrap = true + button.Frame = true + fmt.Fprintln(button, "Send it") + } + + if textarea, err := g.SetView("textarea", 2, maxY-4, maxX/2+28, maxY-2); err != nil { + if err != gocui.ErrUnknownView { + return err + } + if _, err := g.SetCurrentView("textarea"); err != nil { + log.Panicln(err) + } + textarea.Title = "Send message" + textarea.Wrap = true + textarea.Editable = true + } + + // if currentUsers, err := g.SetView("currentUsers", maxX/2+42, 1, maxX-6, maxY-6); err != nil { + // if err != gocui.ErrUnknownView { + // return err + // } + // currentUsers.Title = "Connected users" + // } + + return nil +} diff --git a/go.mod b/go.mod index 6e4e6a2..815fe61 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,15 @@ module mini-chat go 1.22.1 -require github.com/spf13/cobra v1.8.0 +require ( + github.com/jroimartin/gocui v0.5.0 + github.com/nsf/termbox-go v1.1.1 + github.com/spf13/cobra v1.8.0 +) require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index d0e8c2c..27bb663 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,16 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jroimartin/gocui v0.5.0 h1:DCZc97zY9dMnHXJSJLLmx9VqiEnAj0yh0eTNpuEtG/4= +github.com/jroimartin/gocui v0.5.0/go.mod h1:l7Hz8DoYoL6NoYnlnaX6XCNR62G7J5FfSW5jEogzaxE= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=