package main import "os" import "fmt" import "time" import "bufio" import "context" import "crypto/tls" import "git.tebibyte.media/sashakoshka/hopp" import "git.tebibyte.media/sashakoshka/hopp/examples/chat" func main() { name := os.Args[0] if len(os.Args) != 3 && len(os.Args) != 4 { fmt.Fprintf(os.Stderr, "Usage: %s HOST:PORT ROOM [NICKNAME]\n", name) os.Exit(2) } address := os.Args[1] room := os.Args[2] var nickname hopp.Option[string]; if len(os.Args) >= 4 { nickname = hopp.O(os.Args[3]) } trans, err := join(address, room, nickname) handleErr(1, err) go func() { reader := bufio.NewReader(os.Stdin) for { line, err := reader.ReadString('\n') if err != nil { break } send(trans, line) } }() for { message, err := chat.Receive(trans) handleErr(1, err) switch message := message.(type) { case *chat.MessageChat: nickname := "Anonymous" if value, ok := message.Nickname.Get(); ok { nickname = value } fmt.Fprintf(os.Stdout, "%s: %s\n", nickname, message.Content) case *chat.MessageJoinNotify: fmt.Fprintf(os.Stdout, "(i) %s joined the room\n", message.Nickname) case *chat.MessageLeaveNotify: fmt.Fprintf(os.Stdout, "(i) %s left the room\n", message.Nickname) } } } func join(address string, room string, nickname hopp.Option[string]) (hopp.Trans, error) { ctx, done := context.WithTimeout(context.Background(), 16 * time.Second) defer done() dialer := hopp.Dialer { TLSConfig: &tls.Config { // don't actually do this in real life InsecureSkipVerify: true, }, } conn, err := dialer.Dial(ctx, "quic", address) if err != nil { return nil, err } err = updateProfile(conn, nickname) if err != nil { return nil, err } transRoom, err := conn.OpenTrans() if err != nil { return nil, err } err = chat.Send(transRoom, &chat.MessageJoin { Room: room, }) if err != nil { return nil, err } return transRoom, nil } func send(trans hopp.Trans, content string) error { return chat.Send(trans, &chat.MessageChat { Content: content, }) } func updateProfile(conn hopp.Conn, nickname hopp.Option[string]) error { trans, err := conn.OpenTrans() if err != nil { return err } defer trans.Close() err = chat.Send(trans, &chat.MessageUpdateProfile { Nickname: nickname, }) if err != nil { return err } message, err := chat.Receive(trans) if err != nil { return err } switch message := message.(type) { case *chat.MessageError: return message default: return nil } } func handleErr(code int, err error) { if err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err) os.Exit(code) } }