mirror of
https://github.com/naoufalzerai/simple-chat.git
synced 2025-11-08 14:25:53 +00:00
136 lines
2.9 KiB
Go
136 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/go-redis/redis/v8"
|
|
)
|
|
|
|
var (
|
|
ctx = context.Background()
|
|
TTL = time.Minute * 20
|
|
conn = redis.NewClient(&redis.Options{
|
|
Addr: "localhost:6379",
|
|
Password: "", // no password set
|
|
DB: 0, // use default DB
|
|
})
|
|
)
|
|
|
|
func main() {
|
|
// The user name is the only argument, so we'll grab that here
|
|
if len(os.Args) != 2 {
|
|
fmt.Println("Usage: goredchat username")
|
|
os.Exit(1)
|
|
}
|
|
username := os.Args[1]
|
|
|
|
defer conn.Close()
|
|
|
|
// We make a key and set it with the username as a value and a time to live
|
|
// this will be the lock on the username and if we can't set it, its a name clash
|
|
|
|
userkey := "online." + username
|
|
val, err := conn.Set(ctx, userkey, username, TTL).Result()
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if val == "" {
|
|
fmt.Println("User already online")
|
|
os.Exit(1)
|
|
}
|
|
|
|
valint, err := conn.SAdd(ctx, "users", username).Result()
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if valint == 0 {
|
|
fmt.Println("User still in online set")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// A ticker will let us update our presence on the Redis server
|
|
tickerChan := time.NewTicker(time.Second * 60).C
|
|
|
|
// Now we create a channel and go routine that'll subscribe to our published messages
|
|
// We'll give it its own connection because subscribes like to have their own connection
|
|
subChan := make(chan string)
|
|
go func() {
|
|
|
|
psc := conn.Subscribe(ctx, "messages")
|
|
_, err := psc.Receive(ctx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// Go channel which receives messages.
|
|
ch := psc.Channel()
|
|
for v := range ch {
|
|
subChan <- string(v.Payload)
|
|
}
|
|
}()
|
|
|
|
// Now we'll make a simple channel and go routine that listens for complete lines from the user
|
|
// When a complete line is entered, it'll be delivered to the channel.
|
|
sayChan := make(chan string)
|
|
go func() {
|
|
prompt := username + ">"
|
|
bio := bufio.NewReader(os.Stdin)
|
|
for {
|
|
fmt.Print(prompt)
|
|
line, _, err := bio.ReadLine()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
sayChan <- "/exit"
|
|
return
|
|
}
|
|
sayChan <- string(line)
|
|
}
|
|
}()
|
|
|
|
conn.Publish(ctx, "messages", username+" has joined")
|
|
|
|
chatExit := false
|
|
|
|
for !chatExit {
|
|
select {
|
|
case msg := <-subChan:
|
|
fmt.Println(msg)
|
|
case <-tickerChan:
|
|
val, err = conn.Set(ctx, userkey, username, TTL).Result()
|
|
if err != nil || val == "" {
|
|
fmt.Println("Heartbeat set failed")
|
|
chatExit = true
|
|
}
|
|
case line := <-sayChan:
|
|
if line == "/exit" {
|
|
chatExit = true
|
|
} else if line == "/who" {
|
|
names, _ := conn.SMembers(ctx, "users").Result()
|
|
|
|
for _, name := range names {
|
|
fmt.Println(name)
|
|
}
|
|
} else {
|
|
conn.Publish(ctx, "messages", username+":"+line)
|
|
}
|
|
default:
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
// We're leaving so let's delete the userkey and remove the username from the online set
|
|
conn.Del(ctx, userkey)
|
|
conn.SRem(ctx, "users", username)
|
|
conn.Publish(ctx, "messages", username+" has left")
|
|
|
|
}
|