diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..f526442 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +golang 1.22.0 diff --git a/api/endpoints.go b/api/cammie.go similarity index 75% rename from api/endpoints.go rename to api/cammie.go index 86efc71..7e73d24 100644 --- a/api/endpoints.go +++ b/api/cammie.go @@ -3,6 +3,7 @@ package api import ( "fmt" "net/http" + "scc/screen" "slices" gin "github.com/gin-gonic/gin" @@ -22,58 +23,55 @@ var blockedNames = []string{"Paul-Henri Spaak"} var blockedIps = []string{} var maxMessageLength = 200 -func getMessage(c *gin.Context) { +func getMessage(app *screen.ScreenApp, c *gin.Context) { c.JSON(200, gin.H{"messages": messages}) } -func postMessage(c *gin.Context) { +func postMessage(app *screen.ScreenApp, c *gin.Context) { // Get structs header := &header{} message := &message{} // Check Header if err := c.ShouldBindHeader(header); err != nil { - fmt.Println("Here") c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Check Data if err := c.ShouldBindJSON(message); err != nil { - fmt.Println("Other") c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Max message length if len(message.Message) > maxMessageLength { - c.JSON(http.StatusBadRequest, gin.H{"error": "Message too long, maximum " + string(maxMessageLength)}) + c.JSON(http.StatusBadRequest, gin.H{"error": "Message too long, maximum " + fmt.Sprint(maxMessageLength)}) return } - // Check if sender is blocked - sender := "" + // Check if sender is blocked and construct message + var newMessage string if header.Name != "" { if slices.Contains(blockedNames, header.Name) { c.JSON(http.StatusOK, gin.H{"message": "Message received"}) return } - sender = header.Name + newMessage = fmt.Sprintf("[%s] %s", header.Name, message.Message) } else if header.Ip != "" { if slices.Contains(blockedIps, header.Ip) { c.JSON(http.StatusOK, gin.H{"message": "Message received"}) return } - sender = header.Ip - + newMessage = fmt.Sprintf("<%s> %s", header.Ip, message.Message) + } else { + newMessage = message.Message } - // Send message to tty - - fmt.Println("Message received from", sender, ":", message.Message) - // Increment messages messages++ + app.Cammie.Update(newMessage) + c.JSON(http.StatusOK, gin.H{"message": "Message received"}) } diff --git a/api/main.go b/api/main.go index b895bc1..2a5b833 100644 --- a/api/main.go +++ b/api/main.go @@ -2,18 +2,27 @@ package api import ( "io" + "scc/screen" "github.com/gin-gonic/gin" ) -func Start() { +func handlerWrapper(app *screen.ScreenApp, callback func(*screen.ScreenApp, *gin.Context)) func(*gin.Context) { + return func(ctx *gin.Context) { + callback(app, ctx) + } +} + +func Start(screenApp *screen.ScreenApp) { gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard r := gin.Default() - r.GET("/message", getMessage) - r.POST("/message", postMessage) + r.GET("/message", handlerWrapper(screenApp, getMessage)) + r.POST("/message", handlerWrapper(screenApp, postMessage)) + + r.POST("/spotify", spotifyHandlerWrapper(screenApp)) r.Run() } diff --git a/api/spotify.go b/api/spotify.go new file mode 100644 index 0000000..89c0190 --- /dev/null +++ b/api/spotify.go @@ -0,0 +1,17 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "scc/screen" +) + +func spotifyHandlerWrapper(app *screen.ScreenApp) func(*gin.Context) { + return func(ctx *gin.Context) { + spotifyHandler(app, ctx) + } +} + +func spotifyHandler(app *screen.ScreenApp, ctx *gin.Context) { + b, _ := ctx.GetRawData() + app.Spotify.Update(string(b)) +} diff --git a/main.go b/main.go index 07f29b2..6649361 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,9 @@ import ( ) func main() { - go api.Start() + screenApp := screen.NewScreenApp() - screen.InitApp() + go api.Start(screenApp) + + screen.Start(screenApp) } diff --git a/screen/cammie.go b/screen/cammie.go new file mode 100644 index 0000000..fd35e17 --- /dev/null +++ b/screen/cammie.go @@ -0,0 +1,59 @@ +package screen + +import ( + "scc/utils" + "time" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +// Initial value, gets adjusted once it's known how much space is available +var maxMessages = 20 + +type Cammie struct { + screenApp *ScreenApp + view *tview.TextView + + queue *utils.Queue[string] + text string + buffer string +} + +func NewCammie(screenApp *ScreenApp) *Cammie { + cammie := Cammie{ + screenApp: screenApp, + view: tview.NewTextView().SetWrap(true).SetWordWrap(true).SetText("pls"), + + queue: utils.NewQueue[string](maxMessages), + } + + cammie.view.SetTitle(" Cammie ") + cammie.view.SetBorder(true) + cammie.view.SetTextColor(tcell.ColorOrange) + cammie.view.SetBorderColor(tcell.ColorOrange) + cammie.view.SetTitleColor(tcell.ColorOrange) + + return &cammie +} + +func (cammie *Cammie) Run() { + time.Sleep(5 * time.Second) + + _, _, _, h := cammie.view.GetInnerRect() + cammie.queue.SetMaxSize(h) +} + +func (cammie *Cammie) Update(message string) { + cammie.queue.Enqueue(message) + + cammie.screenApp.execute(func() { + cammie.screenApp.app.QueueUpdateDraw(func() { + cammie.view.Clear() + + for _, message := range cammie.queue.Get() { + cammie.view.Write([]byte(message + "\n")) + } + }) + }) +} diff --git a/screen/graph1.go b/screen/graph1.go new file mode 100644 index 0000000..c6fb98c --- /dev/null +++ b/screen/graph1.go @@ -0,0 +1,23 @@ +package screen + +import "github.com/rivo/tview" + +type Graph1 struct { + ScreenApp *ScreenApp + view *tview.Box +} + +func NewGraph1(screenApp *ScreenApp) *Graph1 { + graph1 := Graph1{ + ScreenApp: screenApp, + view: tview.NewBox().SetBorder(true).SetTitle("Graph 1"), + } + + return &graph1 +} + +func (graph1 *Graph1) Run() { +} + +func (graph1 *Graph1) Update(text string) { +} diff --git a/screen/graph2.go b/screen/graph2.go new file mode 100644 index 0000000..5471e03 --- /dev/null +++ b/screen/graph2.go @@ -0,0 +1,23 @@ +package screen + +import "github.com/rivo/tview" + +type Graph2 struct { + ScreenApp *ScreenApp + view *tview.Box +} + +func NewGraph2(screenApp *ScreenApp) *Graph2 { + graph2 := Graph2{ + ScreenApp: screenApp, + view: tview.NewBox().SetBorder(true).SetTitle("Graph 1"), + } + + return &graph2 +} + +func (graph1 *Graph2) Run() { +} + +func (graph1 *Graph2) Update(text string) { +} diff --git a/screen/main.go b/screen/main.go new file mode 100644 index 0000000..1bf0336 --- /dev/null +++ b/screen/main.go @@ -0,0 +1,61 @@ +package screen + +import ( + "sync" + + "github.com/rivo/tview" +) + +type ScreenApp struct { + mu sync.Mutex + app *tview.Application + + Spotify *Spotify + Cammie *Cammie + Graph1 *Graph1 + Graph2 *Graph2 +} + +type s_cammie struct { + cammie *tview.TextView +} + +func (screenApp *ScreenApp) execute(f func()) { + screenApp.mu.Lock() + defer screenApp.mu.Unlock() + f() +} + +func NewScreenApp() *ScreenApp { + screen := ScreenApp{ + app: tview.NewApplication(), + } + + screen.Spotify = NewSpotify(&screen) + screen.Cammie = NewCammie(&screen) + screen.Graph1 = NewGraph1(&screen) + screen.Graph2 = NewGraph2(&screen) + + screen.app.SetRoot(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(screen.Spotify.view, 3, 2, false). + AddItem(tview.NewFlex(). + AddItem(screen.Cammie.view, 0, 5, false). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(screen.Graph1.view, 0, 1, false). + AddItem(screen.Graph2.view, 0, 1, false), 0, 4, false), 0, 13, false), true). + EnableMouse(true) + + return &screen +} + +func Start(screen *ScreenApp) { + + go screen.Spotify.Run() + go screen.Cammie.Run() + go screen.Graph1.Run() + go screen.Graph2.Run() + + if err := screen.app.Run(); err != nil { + panic(err) + } +} diff --git a/screen/screen.go b/screen/screen.go deleted file mode 100644 index 3d0a88f..0000000 --- a/screen/screen.go +++ /dev/null @@ -1,19 +0,0 @@ -package screen - -import "github.com/rivo/tview" - -func InitApp() { - app := tview.NewApplication() - - flex := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(tview.NewBox().SetBorder(true).SetTitle("Spotify"), 0, 2, false). - AddItem(tview.NewFlex(). - AddItem(tview.NewBox().SetBorder(true).SetTitle("Cammie"), 0, 5, false). - AddItem(tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(tview.NewBox().SetBorder(true).SetTitle("Graph 1"), 0, 1, false). - AddItem(tview.NewBox().SetBorder(true).SetTitle("Graph 2"), 0, 1, false), 0, 4, false), 0, 13, false) - - if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil { - panic(err) - } -} \ No newline at end of file diff --git a/screen/spotify.go b/screen/spotify.go new file mode 100644 index 0000000..4c00875 --- /dev/null +++ b/screen/spotify.go @@ -0,0 +1,74 @@ +package screen + +import ( + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + "strings" + "sync" + "time" +) + +type Spotify struct { + screenApp *ScreenApp + view *tview.TextView + + mu sync.Mutex + text string + buffer string +} + +func NewSpotify(screenApp *ScreenApp) *Spotify { + spotify := Spotify{ + screenApp: screenApp, + view: tview.NewTextView(), + + text: "VERY COOL SONG - Le Artist", + buffer: "", + } + + spotify.view.SetTitle(" Spotify ") + spotify.view.SetBorder(true) + spotify.view.SetTextColor(tcell.ColorLimeGreen) + spotify.view.SetBorderColor(tcell.ColorLimeGreen) + spotify.view.SetTitleColor(tcell.ColorLimeGreen) + + return &spotify +} + +func (spotify *Spotify) Run() { + time.Sleep(1 * time.Second) + + for { + _, _, w, _ := spotify.view.GetInnerRect() + + if w != 0 { + + spotify.mu.Lock() + + if len(spotify.buffer) != w { + if len(spotify.text) > w { + spotify.text = spotify.text[0 : w-4] + spotify.text += "..." + } + spotify.buffer = spotify.text + strings.Repeat(" ", w-len(spotify.text)) + } + + spotify.buffer = spotify.buffer[1:] + string(spotify.buffer[0]) + + spotify.mu.Unlock() + + spotify.screenApp.app.QueueUpdateDraw(func() { + spotify.view.SetText(spotify.buffer) + }) + } + time.Sleep(50 * time.Millisecond) + } +} + +func (spotify *Spotify) Update(text string) { + spotify.mu.Lock() + defer spotify.mu.Unlock() + + spotify.text = text + spotify.buffer = "" +} diff --git a/utils/queue.go b/utils/queue.go new file mode 100644 index 0000000..9a0ef81 --- /dev/null +++ b/utils/queue.go @@ -0,0 +1,52 @@ +package utils + +type Queue[T any] struct { + maxSize int + Items []T +} + +func NewQueue[T any](maxSize int) *Queue[T] { + return &Queue[T]{ + maxSize: maxSize, + Items: make([]T, 0, maxSize), + } +} + +func (q *Queue[T]) Enqueue(item T) { + if len(q.Items) >= q.maxSize { + q.Items = q.Items[1:] + } + q.Items = append(q.Items, item) +} + +func (q *Queue[T]) Dequeue() (T, bool) { + if len(q.Items) == 0 { + var zero T + return zero, false + } + + item := q.Items[0] + q.Items = q.Items[1:] + return item, true +} + +func (q *Queue[T]) Peek() (T, bool) { + if len(q.Items) == 0 { + var zero T + return zero, false + } + + return q.Items[0], true +} + +func (q *Queue[T]) Get() []T { + return q.Items +} + +func (q *Queue[T]) SetMaxSize(maxSize int) { + q.maxSize = maxSize + + if len(q.Items) > maxSize { + q.Items = q.Items[:maxSize] + } +}