Skip to content

Commit

Permalink
Merge pull request #10 from ZeusWPI/tap
Browse files Browse the repository at this point in the history
Tap
  • Loading branch information
Topvennie authored Aug 9, 2024
2 parents 9bec2f4 + 3f80038 commit c10b709
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 101 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@
# Go workspace file
go.work

tmp.go
tmp.go

config.yaml

scc
10 changes: 7 additions & 3 deletions api/main.go → api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ func Start(screenApp *screen.ScreenApp) {
// Routes

// Cammie chat routes
r.GET("/message", handlerWrapper(screenApp, getMessage))
r.POST("/message", handlerWrapper(screenApp, postMessage))
r.GET("/message", handlerWrapper(screenApp, cammieGetMessage))
r.POST("/message", handlerWrapper(screenApp, cammiePostMessage))

// Spotify routes
r.POST("/spotify", spotifyHandlerWrapper(screenApp))
r.POST("/spotify", handlerWrapper(screenApp, spotifyGetMessage))

// Start Tap
go runTapRequests(screenApp)

// Start API
r.Run()
}
39 changes: 21 additions & 18 deletions api/cammie.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,39 @@ package api
import (
"fmt"
"net/http"
"scc/config"
"scc/screen"
"slices"

gin "github.com/gin-gonic/gin"
)

// messageCammie struct
type messageCammie struct {
// cammieMessage struct
type cammieMessage struct {
Message string `form:"message" json:"message" xml:"message" binding:"required"`
}

// headerCammie struct
type headerCammie struct {
// cammieHeader struct
type cammieHeader struct {
Name string `header:"X-Username"`
IP string `header:"X-Real-IP"`
}

var messages uint64 = 0
var blockedNames = []string{"Paul-Henri Spaak"} // Blocekd names
var blockedIps = []string{} // Blocked IPs
var maxMessageLength = 200 // Maximum message length
var (
cammieMessages uint64 = 0
cammieBlockedNames = config.GetConfig().Cammie.BlockedNames // Blocked names
cammieBlockedIps = config.GetConfig().Cammie.BlockedIps // Blocked IPs
cammieMaxMessageLength = config.GetConfig().Cammie.MaxMessageLength // Maximum message length
)

func getMessage(app *screen.ScreenApp, c *gin.Context) {
c.JSON(200, gin.H{"messages": messages})
func cammieGetMessage(app *screen.ScreenApp, c *gin.Context) {
c.JSON(200, gin.H{"messages": cammieMessages})
}

func postMessage(app *screen.ScreenApp, c *gin.Context) {
func cammiePostMessage(app *screen.ScreenApp, c *gin.Context) {
// Get structs
header := &headerCammie{}
message := &messageCammie{}
header := &cammieHeader{}
message := &cammieMessage{}

// Check Header
if err := c.ShouldBindHeader(header); err != nil {
Expand All @@ -47,21 +50,21 @@ func postMessage(app *screen.ScreenApp, c *gin.Context) {
}

// Max message length
if len(message.Message) > maxMessageLength {
c.JSON(http.StatusBadRequest, gin.H{"error": "Message too long, maximum " + fmt.Sprint(maxMessageLength)})
if len(message.Message) > cammieMaxMessageLength {
c.JSON(http.StatusBadRequest, gin.H{"error": "Message too long, maximum " + fmt.Sprint(cammieMaxMessageLength)})
return
}

// Check if sender is blocked and construct message
var newMessage string
if header.Name != "" {
if slices.Contains(blockedNames, header.Name) {
if slices.Contains(cammieBlockedNames, header.Name) {
c.JSON(http.StatusOK, gin.H{"message": "Message received"})
return
}
newMessage = fmt.Sprintf("[%s[] %s", header.Name, message.Message)
} else if header.IP != "" {
if slices.Contains(blockedIps, header.IP) {
if slices.Contains(cammieBlockedIps, header.IP) {
c.JSON(http.StatusOK, gin.H{"message": "Message received"})
return
}
Expand All @@ -71,7 +74,7 @@ func postMessage(app *screen.ScreenApp, c *gin.Context) {
}

// Increment messages
messages++
cammieMessages++

app.Cammie.Update(newMessage)

Expand Down
45 changes: 21 additions & 24 deletions api/spotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package api
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"scc/config"
"scc/screen"
"strings"
"time"
Expand All @@ -32,18 +33,14 @@ type spotifyTrackResponse struct {
Artists []spotifyArtist `json:"artists"`
}

var accessToken = ""
var expiresOn int64 = 0
var clientID = "d385173507a54bca93cc3327c0c2f5d9"
var clientSecret = "8e78977c1ba54b90b17f9dcd6b301c37"

func spotifyHandlerWrapper(app *screen.ScreenApp) func(*gin.Context) {
return func(ctx *gin.Context) {
spotifyHandler(app, ctx)
}
}
var (
spotifyAccessToken = ""
spotifyExpiresOn int64 = 0
spotifyClientID = config.GetConfig().Spotify.ClientID
spotifyClientSecret = config.GetConfig().Spotify.ClientSecret
)

func spotifyHandler(app *screen.ScreenApp, ctx *gin.Context) {
func spotifyGetMessage(app *screen.ScreenApp, ctx *gin.Context) {
message := &spotifyMessage{}

if err := ctx.ShouldBindJSON(message); err != nil {
Expand All @@ -53,26 +50,26 @@ func spotifyHandler(app *screen.ScreenApp, ctx *gin.Context) {

ctx.JSON(http.StatusOK, gin.H{"track_id": "Track ID received"})

if expiresOn < time.Now().Unix() {
if err := setAccessToken(); err != nil {
fmt.Fprintf(os.Stderr, "Error: Unable to refresh spotify token: %s", err)
if spotifyExpiresOn < time.Now().Unix() {
if err := spotifySetAccessToken(); err != nil {
log.Printf("Error: Unable to refresh spotify token: %s\n", err)
}
}

track, err := getTrackTitle(message.TrackID)
track, err := spotifyGetTrackTitle(message.TrackID)

if err != nil {
fmt.Fprintf(os.Stderr, "Error: Unable to get track information: %s", err)
log.Printf("Error: Unable to get track information: %s\n", err)
}

app.Spotify.Update(track)
}

func setAccessToken() error {
func spotifySetAccessToken() error {
data := url.Values{}
data.Set("grant_type", "client_credentials")
data.Set("client_id", clientID)
data.Set("client_secret", clientSecret)
data.Set("client_id", spotifyClientID)
data.Set("client_secret", spotifyClientSecret)

// Send the POST request
resp, err := http.PostForm("https://accounts.spotify.com/api/token", data)
Expand All @@ -91,20 +88,20 @@ func setAccessToken() error {
return err
}

accessToken = message.AccessToken
expiresOn = time.Now().Unix() + message.ExpiresIn
spotifyAccessToken = message.AccessToken
spotifyExpiresOn = time.Now().Unix() + message.ExpiresIn

return nil
}

func getTrackTitle(trackID string) (string, error) {
func spotifyGetTrackTitle(trackID string) (string, error) {
url := fmt.Sprintf("https://api.spotify.com/v1/tracks/%s", trackID)

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Authorization", "Bearer "+spotifyAccessToken)

client := &http.Client{}
resp, err := client.Do(req)
Expand Down
64 changes: 64 additions & 0 deletions api/tap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package api

import (
"encoding/json"
"log"
"net/http"
"scc/config"
"scc/screen"
"time"
)

type tapReponse struct {
Orders []screen.TapOrder `json:"orders"`
}

var (
tapURL = config.GetConfig().Tap.URL
timestampLayout = config.GetConfig().Tap.TimestampLayout
lastOrderTimestamp = time.Time{}
)

func runTapRequests(app *screen.ScreenApp) {
for true {
recentOrders, err := tapGetRecentOrders()
if err != nil {
log.Printf("Error: Unable to get recent order: %s\n", err)
}
for _, order := range recentOrders.Orders {
timestamp, err := time.Parse(timestampLayout, order.OrderCreatedAt)
if err != nil {
log.Printf("Error: Unable to parse timestamp: %s\n", err)
}

if order.ProductCategory == "beverages" && timestamp.After(lastOrderTimestamp) {
app.Tap.Update(&order)
lastOrderTimestamp = timestamp
}
}

time.Sleep(1 * time.Minute)
}
}

func tapGetRecentOrders() (*tapReponse, error) {
req, err := http.NewRequest("GET", tapURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

response := &tapReponse{}
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
return nil, err
}

return response, nil
}
7 changes: 7 additions & 0 deletions config.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cammie:
blocked_names: []
blocked_ips: []
max_message_length:
spotify:
client_id:
client_secret:
51 changes: 51 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package config

import (
"log"
"os"
"sync"

"gopkg.in/yaml.v3"
)

type cammieConfig struct {
BlockedNames []string `yaml:"blocked_names"`
BlockedIps []string `yaml:"blocked_ips"`
MaxMessageLength int `yaml:"max_message_length"`
}

type spotifyConfig struct {
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
}

type tapConfig struct {
URL string `yaml:"url"`
TimestampLayout string `yaml:"timestamp_layout"`
Beers []string `yaml:"beer"`
}

type Config struct {
Cammie cammieConfig `yaml:"cammie"`
Spotify spotifyConfig `yaml:"spotify"`
Tap tapConfig `yaml:"tap"`
}

var (
configInstance *Config
once sync.Once
)

func GetConfig() *Config {
once.Do(func() {
configInstance = &Config{}
data, err := os.ReadFile("config.yaml")
if err != nil {
log.Fatalf("Failed to read config file: %v", err)
}
if err := yaml.Unmarshal(data, configInstance); err != nil {
log.Fatalf("Failed to unmarshal config: %v", err)
}
})
return configInstance
}
17 changes: 9 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ module scc
go 1.22.0

require (
github.com/gdamore/tcell/v2 v2.7.1
github.com/gdamore/tcell/v2 v2.7.4
github.com/gin-gonic/gin v1.9.1
github.com/rivo/tview v0.0.0-20240225120200-5605142ca62e
github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand All @@ -26,16 +27,16 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/navidys/tvxwidgets v0.7.0
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit c10b709

Please sign in to comment.