mirror of
https://codeberg.org/SafeTwitch/safetwitch-backend.git
synced 2024-12-22 13:13:00 -05:00
Complete users endpoint, parsers, structs, and all
This commit is contained in:
parent
547bbbd214
commit
2bb911111c
6 changed files with 121 additions and 19 deletions
|
@ -3,6 +3,7 @@ package extractor
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"safetwitch-backend/extractor/structs"
|
"safetwitch-backend/extractor/structs"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
@ -19,8 +20,38 @@ func ParseSocials(data string) ([]structs.Social, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !result.Exists() {
|
if !result.Exists() {
|
||||||
return parsedSocials, errors.New("Error while parsing socials, path does not exist.")
|
return parsedSocials, errors.New("error while parsing socials, path does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedSocials, nil
|
return parsedSocials, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseStream(data string) (*structs.Stream, error) {
|
||||||
|
// check if live
|
||||||
|
stream := gjson.Get(data, "1.data.user.stream")
|
||||||
|
if !stream.IsObject() {
|
||||||
|
return nil, errors.New("streamer is not live")
|
||||||
|
}
|
||||||
|
|
||||||
|
var tags []string
|
||||||
|
tagArea := gjson.Get(data, "2.data.user.stream.freeformTags").Array()
|
||||||
|
for _, tag := range tagArea {
|
||||||
|
tags = append(tags, tag.Get("name").String())
|
||||||
|
}
|
||||||
|
|
||||||
|
time, err := time.Parse(time.RFC3339, stream.Get("createdAt").String())
|
||||||
|
if err != nil {
|
||||||
|
return &structs.Stream{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedStream := structs.Stream{
|
||||||
|
Title: gjson.Get(data, "1.data.user.lastBroadcast.title").String(),
|
||||||
|
Topic: stream.Get("game.name").String(),
|
||||||
|
StartedAt: time,
|
||||||
|
Tags: tags,
|
||||||
|
Viewers: int(gjson.Get(data, "4.data.user.stream.viewersCount").Int()),
|
||||||
|
Preview: ProxyUrl(gjson.Get(data, "3.data.user.stream.previewImageURL").String()),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &parsedStream, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,32 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Social struct {
|
type Social struct {
|
||||||
Type string
|
Type string `json:"type"`
|
||||||
Title string
|
Title string `json:"title"`
|
||||||
Url string
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stream struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Topic string `json:"topic"`
|
||||||
|
StartedAt time.Time `json:"startedAt"`
|
||||||
|
Viewers int `json:"viewers"`
|
||||||
|
Preview string `json:"preview"`
|
||||||
|
Cursor string `json:"cursor,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Streamer struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
About string `json:"about"`
|
||||||
|
Pfp string `json:"pfp"`
|
||||||
|
Followers int `json:"followers"`
|
||||||
|
Socials []Social `json:"socials"`
|
||||||
|
IsLive bool `json:"isLive"`
|
||||||
|
IsPartner bool `json:"isPartner"`
|
||||||
|
ColorHex string `json:"colorHex"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
Stream *Stream `json:"stream,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"safetwitch-backend/extractor/structs"
|
"safetwitch-backend/extractor/structs"
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ func call(url, method string, body io.Reader) (*http.Response, error) {
|
||||||
|
|
||||||
type TwitchPayload = map[string]interface{}
|
type TwitchPayload = map[string]interface{}
|
||||||
|
|
||||||
func GetStreamerInfo(streamerName string) (structs.TwitchApiResponse, error) {
|
func GetStreamerInfo(streamerName string) (structs.Streamer, error) {
|
||||||
payload := []TwitchPayload{
|
payload := []TwitchPayload{
|
||||||
{
|
{
|
||||||
"operationName": "ChannelRoot_AboutPanel",
|
"operationName": "ChannelRoot_AboutPanel",
|
||||||
|
@ -93,40 +92,65 @@ func GetStreamerInfo(streamerName string) (structs.TwitchApiResponse, error) {
|
||||||
}
|
}
|
||||||
json_data, err := json.Marshal(payload)
|
json_data, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return structs.TwitchApiResponse{}, nil
|
return structs.Streamer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := call(twitchUrl, "POST", bytes.NewBuffer(json_data))
|
resp, err := call(twitchUrl, "POST", bytes.NewBuffer(json_data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return structs.TwitchApiResponse{}, err
|
return structs.Streamer{}, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
RespStatusHandler(resp.StatusCode)
|
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return structs.TwitchApiResponse{}, err
|
return structs.Streamer{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var twitchResponse []structs.TwitchApiResponse
|
var twitchResponse []structs.TwitchApiResponse
|
||||||
err = json.Unmarshal(body, &twitchResponse)
|
err = json.Unmarshal(body, &twitchResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return structs.TwitchApiResponse{}, err
|
return structs.Streamer{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin parsing response
|
// begin parsing response
|
||||||
streamerFound := gjson.Get(string(body), "0.data.user")
|
streamerFound := gjson.Get(string(body), "0.data.user")
|
||||||
if streamerFound.String() == "" {
|
if streamerFound.String() == "" {
|
||||||
return twitchResponse[0], errors.New("streamer not found")
|
return structs.Streamer{}, errors.New("streamer not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
streamerData := gjson.Get(string(body), "0.data")
|
streamerData := gjson.Get(string(body), "0.data")
|
||||||
parsedSocials, err := ParseSocials(streamerData.String())
|
parsedSocials, err := ParseSocials(streamerData.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return twitchResponse[0], nil
|
return structs.Streamer{}, err
|
||||||
}
|
}
|
||||||
log.Println(parsedSocials)
|
|
||||||
|
|
||||||
return twitchResponse[0], nil
|
parsedStream, err := ParseStream(string(body))
|
||||||
|
var isLive bool
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "streamer is not live" {
|
||||||
|
parsedStream = nil
|
||||||
|
} else {
|
||||||
|
return structs.Streamer{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parsedStream != nil {
|
||||||
|
isLive = true
|
||||||
|
} else {
|
||||||
|
isLive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedStreamer := structs.Streamer{
|
||||||
|
Username: streamerData.Get("user.displayName").String(),
|
||||||
|
About: streamerData.Get("user.description").String(),
|
||||||
|
Pfp: ProxyUrl(streamerData.Get("user.profileImageURL").String()),
|
||||||
|
Followers: int(streamerData.Get("user.followers.totalCount").Int()),
|
||||||
|
Socials: parsedSocials,
|
||||||
|
IsLive: isLive,
|
||||||
|
IsPartner: streamerData.Get("user.isPartner").Bool(),
|
||||||
|
ColorHex: streamerData.Get("user.primaryColorHex").String(),
|
||||||
|
Id: streamerData.Get("user.id").String(),
|
||||||
|
Stream: parsedStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedStreamer, nil
|
||||||
}
|
}
|
||||||
|
|
13
extractor/utils.go
Normal file
13
extractor/utils.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package extractor
|
||||||
|
|
||||||
|
import (
|
||||||
|
b64 "encoding/base64"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProxyUrl(url string) string {
|
||||||
|
encodedUrl := b64.StdEncoding.EncodeToString([]byte(url))
|
||||||
|
backendUrl := os.Getenv("URL")
|
||||||
|
|
||||||
|
return backendUrl + "/img/" + encodedUrl
|
||||||
|
}
|
7
go.mod
7
go.mod
|
@ -2,10 +2,12 @@ module safetwitch-backend
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/gin-gonic/gin v1.9.0
|
require (
|
||||||
|
github.com/gin-gonic/gin v1.9.0
|
||||||
|
github.com/tidwall/gjson v1.14.4
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/buger/jsonparser v1.1.1 // indirect
|
|
||||||
github.com/bytedance/sonic v1.8.8 // indirect
|
github.com/bytedance/sonic v1.8.8 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
@ -20,7 +22,6 @@ require (
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||||
github.com/tidwall/gjson v1.14.4 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -1,3 +1,4 @@
|
||||||
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
|
github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
|
||||||
github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
@ -52,6 +53,13 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
|
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
|
Loading…
Reference in a new issue