gotel

simple terminal chat program
git clone git://git.mdnr.space/gotel
Log | Files | Refs | README | LICENSE

commit f74b68a36be462737b8e07e47b7287d341dd8687
parent 6f59b78e2f686dd95a96076ed7444c18ec810ba9
Author: mdnrz <mehdeenoroozi@gmail.com>
Date:   Tue,  8 Apr 2025 01:07:04 +0330

add README.md and rename module

Diffstat:
M.gitignore | 2+-
ALICENSE | 19+++++++++++++++++++
AREADME.md | 5+++++
Mgo.mod | 2+-
Agotel.go | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dhello.go | 112-------------------------------------------------------------------------------
6 files changed, 138 insertions(+), 114 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1 @@ -hello +gotel diff --git a/LICENSE b/LICENSE @@ -0,0 +1,19 @@ +Copyright 2025 Mehdi Noroozi <mehdeenoroozi@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md @@ -0,0 +1,5 @@ +# GoTel + +Simple telnet multi-user chat program. + +My attempt at learning golang. diff --git a/go.mod b/go.mod @@ -1,3 +1,3 @@ -module hello +module gotel go 1.24.0 diff --git a/gotel.go b/gotel.go @@ -0,0 +1,112 @@ +package main + +import ( + "log" + "net" + "time" + "strings" +) + +type msgType int + +const ( + msgConnect msgType = iota + 1 + msgLogin + msgText + msgQuit +) + +type Client struct { + UserName string + Request msgType + Strike int + Banned bool + BannedAt time.Time + Conn net.Conn + Text string +} + +const Port = "6969" + +func addClient(conn net.Conn, Client_q chan Client) { + loginPrompt := "Who are you?\n> " + _, err := conn.Write([]byte(loginPrompt)) + if err != nil { + log.Printf("[ERROR] Could not send login prompt to user %s: %s\n", + conn.RemoteAddr().String(), err) + } + readBuf := make([]byte, 512) + for { + n, err := conn.Read(readBuf) + if err != nil { + log.Printf("Could not read message from client %s: %s\n", conn.RemoteAddr().String(), err) + conn.Close() + return + } + Client_q <- Client { + Request: msgText, + Conn: conn, + Text: string(readBuf[:n]), + } + } +} + +func server(Client_q chan Client) { + clientsOnline := make(map[string]Client) + clientsOffline := make(map[string]Client) + for { + client := <-Client_q + keyString := client.Conn.RemoteAddr().String(); + switch client.Request { + case msgConnect: + log.Printf("Got login request from %s\n", keyString); + clientsOffline[keyString] = client; + case msgText: + clientOffline, ok := clientsOffline[keyString]; + if ok { + clientOffline.UserName = strings.TrimRight(client.Text, "\r\n"); + log.Printf("logging in %s\n", clientOffline.UserName); + clientsOnline[keyString] = clientOffline; + delete(clientsOffline, keyString); + _, err := clientsOnline[keyString].Conn.Write([]byte("Welcome " + clientsOnline[keyString].UserName + "\n\n")); + if err != nil { + log.Printf("Could not send message to client %s\n", clientsOnline[keyString].UserName) + } + break; + } + author, _ := clientsOnline[keyString]; + author.Text = client.Text; + for _, value := range clientsOnline { + if value.Conn == author.Conn { + continue + } + _, err := value.Conn.Write([]byte(author.UserName + ": " + author.Text)) + if err != nil { + log.Printf("Could not send message to client %s\n", value.UserName) + } + } + } + } +} + +func main() { + ln, err := net.Listen("tcp", ":"+Port) + if err != nil { + log.Fatalf("Could not listen to port %s: %s\n", Port, err) + } + Client_q := make(chan Client) + go server(Client_q) + for { + conn, err := ln.Accept() + if err != nil { + log.Printf("Could not accept the connection: %s\n", err) + continue + } + log.Printf("Accepted connection from %s", conn.RemoteAddr()) + Client_q <- Client { + Request: msgConnect, + Conn: conn, + } + go addClient(conn, Client_q) + } +} diff --git a/hello.go b/hello.go @@ -1,112 +0,0 @@ -package main - -import ( - "log" - "net" - "time" - "strings" -) - -type msgType int - -const ( - msgConnect msgType = iota + 1 - msgLogin - msgText - msgQuit -) - -type Client struct { - UserName string - Request msgType - Strike int - Banned bool - BannedAt time.Time - Conn net.Conn - Text string -} - -const Port = "6969" - -func client(conn net.Conn, Client_q chan Client) { - loginPrompt := "Who are you?\n> " - _, err := conn.Write([]byte(loginPrompt)) - if err != nil { - log.Printf("[ERROR] Could not send login prompt to user %s: %s\n", - conn.RemoteAddr().String(), err) - } - readBuf := make([]byte, 512) - for { - n, err := conn.Read(readBuf) - if err != nil { - log.Printf("Could not read message from client %s: %s\n", conn.RemoteAddr().String(), err) - conn.Close() - return - } - Client_q <- Client { - Request: msgText, - Conn: conn, - Text: string(readBuf[:n]), - } - } -} - -func server(Client_q chan Client) { - clientsOnline := make(map[string]Client) - clientsOffline := make(map[string]Client) - for { - client := <-Client_q - keyString := client.Conn.RemoteAddr().String(); - switch client.Request { - case msgConnect: - log.Printf("Got login request from %s\n", keyString); - clientsOffline[keyString] = client; - case msgText: - clientOffline, ok := clientsOffline[keyString]; - if ok { - clientOffline.UserName = strings.TrimRight(client.Text, "\r\n"); - log.Printf("logging in %s\n", clientOffline.UserName); - clientsOnline[keyString] = clientOffline; - delete(clientsOffline, keyString); - _, err := clientsOnline[keyString].Conn.Write([]byte("Welcome " + clientsOnline[keyString].UserName + "\n\n")); - if err != nil { - log.Printf("Could not send message to client %s\n", clientsOnline[keyString].UserName) - } - break; - } - author, _ := clientsOnline[keyString]; - author.Text = client.Text; - for _, value := range clientsOnline { - if value.Conn == author.Conn { - continue - } - _, err := value.Conn.Write([]byte(author.UserName + ": " + author.Text)) - if err != nil { - log.Printf("Could not send message to client %s\n", value.UserName) - } - } - } - } -} - -func main() { - ln, err := net.Listen("tcp", ":"+Port) - if err != nil { - log.Fatalf("Could not listen to port %s: %s\n", Port, err) - } - Client_q := make(chan Client) - go server(Client_q) - for { - conn, err := ln.Accept() - if err != nil { - log.Printf("Could not accept the connection: %s\n", err) - continue - } - log.Printf("Accepted connection from %s", conn.RemoteAddr()) - Client_q <- Client { - Request: msgConnect, - Conn: conn, - } - go client(conn, Client_q) - } -}