diff --git a/extractor/structs/parsed.go b/extractor/structs/parsed.go index cae8757..535e789 100644 --- a/extractor/structs/parsed.go +++ b/extractor/structs/parsed.go @@ -66,3 +66,11 @@ type CategoryData struct { Streams []CategoryMinifiedStream `json:"streams"` Cover string `json:"cover"` } + +type Badge struct { + Id string `json:"id"` + Title string `json:"title"` + SetId string `json:"setId"` + Version string `json:"version"` + Images map[string]string `json:"images"` +} diff --git a/extractor/twitch/parser.go b/extractor/twitch/parser.go index 7269074..3c24929 100644 --- a/extractor/twitch/parser.go +++ b/extractor/twitch/parser.go @@ -1,6 +1,7 @@ package twitch import ( + "encoding/base64" "errors" "safetwitch-backend/extractor" "safetwitch-backend/extractor/structs" @@ -79,7 +80,7 @@ func ParseCategory(data gjson.Result) (structs.CategoryPreview, error) { func ParseMinifiedStream(data gjson.Result) (structs.CategoryMinifiedStream, error) { var tags []string - tagArea := data.Get("node.freeformTags").Array() + tagArea := data.Get("node.freeformTacgs").Array() for _, tag := range tagArea { tags = append(tags, tag.Get("name").String()) } @@ -108,3 +109,29 @@ func ParseMinifiedStream(data gjson.Result) (structs.CategoryMinifiedStream, err return parsedStream, nil } + +func ParseBadges(data gjson.Result) ([]structs.Badge, error) { + var formattedBadges = []structs.Badge{} + + for _, badge := range data.Array() { + id := badge.Get("id").String() + decodedId, err := base64.StdEncoding.DecodeString(id) + if err != nil { + return []structs.Badge{}, nil + } + + formattedBadges = append(formattedBadges, structs.Badge{ + Id: string(decodedId), + SetId: badge.Get("setID").String(), + Title: badge.Get("title").String(), + Version: badge.Get("version").String(), + Images: map[string]string{ + "image1x": extractor.ProxyUrl(badge.Get("image1x").String()), + "image2x": extractor.ProxyUrl(badge.Get("image2x").String()), + "image4x": extractor.ProxyUrl(badge.Get("image4x").String()), + }, + }) + } + + return formattedBadges, nil +} diff --git a/extractor/twitch/twitchExtractor.go b/extractor/twitch/twitchExtractor.go index 30126cb..8570f2f 100644 --- a/extractor/twitch/twitchExtractor.go +++ b/extractor/twitch/twitchExtractor.go @@ -295,3 +295,62 @@ func GetDiscoveryItem(name string, streamLimit int, cursor string) (structs.Cate return parsedCategory, err } + +func GetTwitchBadges() ([]structs.Badge, error) { + payload := []TwitchPayload{ + { + "operationName": "ChannelPointsPredictionBadges", + "variables": map[string]interface{}{}, + "extensions": map[string]interface{}{ + "persistedQuery": map[string]interface{}{ + "sha256Hash": "36995b30b22c31d1cd0aa329987ac9b5368bb7e6e1ab1df42808bdaa80a6dbf9", + "version": 1, + }, + }, + }, + } + + _, body, err := parseResponse(payload) + if err != nil { + return []structs.Badge{}, err + } + + rawBadges := gjson.Get(string(body), "0.data.badges") + formattedBadges, err := ParseBadges(rawBadges) + if err != nil { + return []structs.Badge{}, err + } + + return formattedBadges, nil +} + +func GetStreamerBadges(streamerName string) ([]structs.Badge, error) { + payload := []TwitchPayload{ + { + "extensions": map[string]interface{}{ + "persistedQuery": map[string]interface{}{ + "sha256Hash": "86f43113c04606e6476e39dcd432dee47c994d77a83e54b732e11d4935f0cd08", + "version": 1, + }, + }, + "operationName": "ChatList_Badges", + "variables": map[string]interface{}{ + "channelLogin": streamerName, + }, + }, + } + + _, body, err := parseResponse(payload) + if err != nil { + return []structs.Badge{}, err + } + + rawBadges := gjson.Get(string(body), "0.data.user.broadcastBadges") + + formattedBadges, err := ParseBadges(rawBadges) + if err != nil { + return []structs.Badge{}, err + } + + return formattedBadges, nil +} diff --git a/routes/api/badges/badges.go b/routes/api/badges/badges.go new file mode 100644 index 0000000..4c23016 --- /dev/null +++ b/routes/api/badges/badges.go @@ -0,0 +1,33 @@ +package badges + +import ( + "github.com/gin-gonic/gin" + + "safetwitch-backend/extractor" + "safetwitch-backend/extractor/twitch" +) + +func Routes(route *gin.Engine) { + auth := route.Group("/api/badges") + + auth.GET("/", func(context *gin.Context) { + query := context.Query("streamerName") + if query != "" { + data, err := twitch.GetStreamerBadges(query) + if err != nil { + context.Error(err) + return + } + + context.JSON(200, extractor.FormatMessage(data, true)) + } else { + data, err := twitch.GetTwitchBadges() + if err != nil { + context.Error(err) + return + } + + context.JSON(200, extractor.FormatMessage(data, true)) + } + }) +} diff --git a/routes/routes.go b/routes/routes.go index 849c5b7..dce6d4d 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -1,6 +1,7 @@ package routes import ( + "safetwitch-backend/routes/api/badges" "safetwitch-backend/routes/api/discover" "safetwitch-backend/routes/api/users" "safetwitch-backend/routes/proxy" @@ -12,6 +13,7 @@ import ( func SetRoutes(router *gin.Engine) { users.Routes(router) discover.Routes(router) + badges.Routes(router) proxy.Routes(router) root.Routes(router)