Skip to content

Commit

Permalink
base layout for chat
Browse files Browse the repository at this point in the history
  • Loading branch information
qnkhuat committed Jul 12, 2021
1 parent f972648 commit 00ee692
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 43 deletions.
107 changes: 65 additions & 42 deletions tstream/cmd/tstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func main() {
var server = flag.String("server", "https://server.tstream.club", "Server endpoint")
var client = flag.String("client", "https://tstream.club", "TStream client url")
var version = flag.Bool("version", false, fmt.Sprintf("TStream version: %s", cfg.STREAMER_VERSION))
var chat = flag.Bool("chat", false, "Open chat client: %s")

flag.Parse()

Expand All @@ -54,20 +55,6 @@ func main() {
os.Exit(0)
return
}

user, err := user.Current()
if err != nil {
log.Fatalf(err.Error())
}

var username string
cfg, err := streamer.ReadCfg(streamer.CONFIG_PATH)
if err != nil {
username = user.Username
} else {
username = cfg.Username
}

validateUsername := func(input string) error {
var validUsername = regexp.MustCompile(`^[a-z][a-z0-9]*[._-]?[a-z0-9]+$`)
if validUsername.MatchString(input) && len(input) > 3 && len(input) < 20 {
Expand All @@ -85,6 +72,19 @@ func main() {
}
}

u, err := user.Current()
if err != nil {
log.Fatalf(err.Error())
}

var username string
cfg, err := streamer.ReadCfg(streamer.CONFIG_PATH)
if err != nil {
username = u.Username
} else {
username = cfg.Username
}

promptUsername := promptui.Prompt{
Label: "Username",
Default: username,
Expand All @@ -96,38 +96,61 @@ func main() {
Validate: validateTitle,
}

username, err = promptUsername.Run()
if err != nil {
os.Exit(1)
}
title, err := promptTitle.Run()
if err != nil {
os.Exit(1)
}
if !*chat {
// Start Streaming session
username, err = promptUsername.Run()
if err != nil {
os.Exit(1)
}
title, err := promptTitle.Run()
if err != nil {
os.Exit(1)
}

s := streamer.New(*client, *server, username, title)
s := streamer.New(*client, *server, username, title)

statusCode := s.RequestAddRoom()
log.Printf("Got status code: %d", statusCode)
if statusCode == 400 {
fmt.Printf("Detected a session is streaming with the same username\nProceed to stream from this terminal? (y/n): ")
confirm, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if confirm[0] != 'y' {
os.Exit(1)
}
} else if statusCode == 401 {
fmt.Printf("Username: %s is currently used by other streamer. Please use a different username!\n", username)
os.Exit(1)
} else if statusCode == 426 {
fmt.Printf("Please update Tstream to continue streaming\nFind the latest version at: https://github.com/qnkhuat/tstream/releases\n")
os.Exit(1)
}
// Update config before start
cfg.Username = username
streamer.UpdateCfg(streamer.CONFIG_PATH, "Username", username)

err = s.Start()
if err != nil {
log.Printf("Failed to start tstream : %s", err)
}
} else {
var username = "" // also is sessionID
cfg, err := streamer.ReadCfg(streamer.CONFIG_PATH)

statusCode := s.RequestAddRoom()
log.Printf("Got status code: %d", statusCode)
if statusCode == 400 {
fmt.Printf("Detected a session is streaming with the same username\nProceed to stream from this terminal? (y/n): ")
confirm, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if confirm[0] != 'y' {
if err != nil {
fmt.Printf("No stream session detected\n")
os.Exit(1)
} else {
username = cfg.Username
}
} else if statusCode == 401 {
fmt.Printf("Username: %s is currently used by other streamer. Please use a different username!\n", username)
os.Exit(1)
} else if statusCode == 426 {
fmt.Printf("Please update Tstream to continue streaming\nFind the latest version at: https://github.com/qnkhuat/tstream/releases\n")
os.Exit(1)
}
// Update config before start
cfg.Username = username
streamer.UpdateCfg(streamer.CONFIG_PATH, "Username", username)

err = s.Start()
if err != nil {
log.Printf("Failed to start tstream : %s", err)
if username == "" {
username, err = promptUsername.Run()
if err != nil {
os.Exit(1)
}
}

c := streamer.NewChat(username, *server, username)
c.Start()
}
}
2 changes: 2 additions & 0 deletions tstream/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ go 1.16
require (
github.com/boltdb/bolt v1.3.1
github.com/creack/pty v1.1.13
github.com/gdamore/tcell/v2 v2.3.3
github.com/google/uuid v1.2.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/gorilla/websocket v1.4.2
github.com/manifoldco/promptui v0.8.0
github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2
github.com/rs/cors v1.8.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
Expand Down
20 changes: 20 additions & 0 deletions tstream/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us=
github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.3.3 h1:RKoI6OcqYrr/Do8yHZklecdGzDTJH9ACKdfECbRdw3M=
github.com/gdamore/tcell/v2 v2.3.3/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
Expand All @@ -33,6 +37,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo=
Expand All @@ -42,9 +49,16 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2 h1:I5N0WNMgPSq5NKUFspB4jMJ6n2P0ipz5FlOlB4BXviQ=
github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2/go.mod h1:IxQujbYMAh4trWr0Dwa8jfciForjVmxyHpskZX6aydQ=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rs/cors v1.8.0 h1:P2KMzcFwrPoSjkF1WLRPsp3UMLyql8L4v9hQpVeK5so=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -58,12 +72,18 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
Expand Down
1 change: 1 addition & 0 deletions tstream/pkg/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Chat struct {
Content string
Color string
Time string
Role string
}

type StreamerConnect struct {
Expand Down
25 changes: 24 additions & 1 deletion tstream/pkg/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (s *Server) handleAddRoom(w http.ResponseWriter, r *http.Request) {
}
}

// Websocket connetion from streamer
// Websocket connetion from streamer to stream terminal
func (s *Server) handleWSViewer(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
roomName := vars["roomName"]
Expand All @@ -146,6 +146,29 @@ func (s *Server) handleWSViewer(w http.ResponseWriter, r *http.Request) {
room.ReadAndHandleViewerMessage(viewerID) // Blocking call
}

// Websocket connection for streamer to chat
func (s *Server) s.handleWSSChat(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
roomName := vars["roomName"]
log.Printf("Client %s entered room: %s", r.RemoteAddr, roomName)
room, ok := s.rooms[roomName]
if !ok {
fmt.Fprintf(w, "Room not existed")
log.Printf("Room :%s not existed", roomName)
return
}
conn, err := httpUpgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Failed to upgrade to websocket: %s", err)
}

viewerID := uuid.New().String()
room.AddViewer(viewerID, conn)

// Handle incoming request from user
room.ReadAndHandleViewerMessage(viewerID) // Blocking call
}

// Websocket connection from streamer
// TODO: Add key checking to make sure only streamer can stream via this endpoint
func (s *Server) handleWSStreamer(w http.ResponseWriter, r *http.Request) {
Expand Down
1 change: 1 addition & 0 deletions tstream/pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (s *Server) Start() {
// Add room
router.HandleFunc("/api/room", s.handleAddRoom).Queries("streamerID", "{streamerID}", "title", "{title}").Methods("POST", "OPTIONS")
router.HandleFunc("/ws/{roomName}/streamer", s.handleWSStreamer) // for streamers
router.HandleFunc("/ws/{roomName}/schat", s.handleWSSChat) // chat for streamers
router.HandleFunc("/ws/{roomName}/viewer", s.handleWSViewer) // for viewers
handler := cors.Default().Handler(router)

Expand Down
83 changes: 83 additions & 0 deletions tstream/pkg/streamer/chat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* Chat client on terminal */
package streamer

import (
"github.com/gdamore/tcell/v2"
"github.com/gorilla/websocket"
"github.com/rivo/tview"
)

type Chat struct {
username string
sessionId string
serverAddr string
color string
conn *websocket.Conn
app *tview.Application
}

func NewChat(sessionId, serverAddr, username string) *Chat {
return &Chat{
username: username,
sessionId: sessionId,
serverAddr: serverAddr,
color: "red",
app: tview.NewApplication(),
}
}

func (c *Chat) Start() error {
c.InitUI()

if err := c.app.EnableMouse(true).Run(); err != nil {
panic(err)
}
return nil
}

func (c *Chat) InitUI() error {
layout := tview.NewGrid().
SetRows(3, 0, 1).
SetColumns(0).
SetBorders(true)

newPrimitive := func(text string) tview.Primitive {
return tview.NewTextView().
SetTextAlign(tview.AlignCenter).
SetText(text)
}
menu := newPrimitive("Menu")
ChatTextView := tview.NewTextView().
SetScrollable(true).
SetDynamicColors(true).
SetWordWrap(true).SetText("a\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\na\nb\nc\nd\ne\nf\ng\nh\n").
ScrollToEnd()
//sideBar := newPrimitive("Side Bar")
messageInput := tview.NewInputField()
messageInput.SetLabel("[red]>[red] ").
SetDoneFunc(func(key tcell.Key) {
messageInput.SetText("")
})

layout.AddItem(menu, 0, 0, 1, 1, 0, 0, false).
AddItem(ChatTextView, 1, 0, 1, 1, 0, 0, false).
AddItem(messageInput, 2, 0, 1, 1, 0, 0, true)

c.app.SetRoot(layout, true)
return nil
}

//func main() {
// app := tview.NewApplication()
// inputField := tview.NewInputField().
// SetLabel("Enter a number: ").
// SetPlaceholder("E.g. 1234").
// SetFieldWidth(10).
// SetAcceptanceFunc(tview.InputFieldInteger).
// SetDoneFunc(func(key tcell.Key) {
// app.Stop()
// })
// if err := app.SetRoot(inputField, true).EnableMouse(true).Run(); err != nil {
// panic(err)
// }
//}
1 change: 1 addition & 0 deletions tstream/pkg/streamer/streamer.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* Streamer package to stream terminal to server */
package streamer

import (
Expand Down

0 comments on commit 00ee692

Please # to comment.