mirror of
https://codeberg.org/SafeTwitch/safetwitch-backend.git
synced 2024-12-22 05:02:58 -05:00
Add emote support
This commit is contained in:
parent
76ac6571be
commit
8e572b9bbd
5 changed files with 241 additions and 4 deletions
53
extractor/structs/emote.go
Normal file
53
extractor/structs/emote.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package structs
|
||||
|
||||
type BttvGlobalEmoteData struct {
|
||||
Id string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
ImageType string `json:"imageType"`
|
||||
Animated bool `json:"animated"`
|
||||
UserId string `json:"userId"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
|
||||
type BttvEmoteData struct {
|
||||
Id string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
ImageType string `json:"imageType"`
|
||||
Animated bool `json:"animated"`
|
||||
User BttvEmoteUser `json:"user"`
|
||||
}
|
||||
|
||||
type BttvEmoteUser struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ProviderId string `json:"providerId"`
|
||||
}
|
||||
|
||||
type FfzData struct {
|
||||
Room Room `json:"room"`
|
||||
Sets map[string]Set `json:"sets"`
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
Id string `json:"id"`
|
||||
Is_group bool `json:"is_group"`
|
||||
Display_name string `json:"display_name"`
|
||||
Set int `json:"set"`
|
||||
}
|
||||
|
||||
type Set struct {
|
||||
Id int `json:"id"`
|
||||
Icon string `json:"icon"`
|
||||
Title string `json:"title"`
|
||||
Emoticons []FfzEmoteData `json:"emoticons"`
|
||||
}
|
||||
|
||||
type FfzEmoteData struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
Urls map[string]string `json:"urls"`
|
||||
}
|
61
extractor/twitch/emoteHandler.go
Normal file
61
extractor/twitch/emoteHandler.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"safetwitch-backend/extractor/structs"
|
||||
)
|
||||
|
||||
type Emote struct {
|
||||
Name string `json:"name"`
|
||||
Urls map[string]string `json:"urls"`
|
||||
}
|
||||
|
||||
func ParseFfzEmotes(emotes structs.FfzData) []Emote {
|
||||
setId := emotes.Room.Set
|
||||
sets := emotes.Sets[fmt.Sprint(setId)]
|
||||
parsedEmotes := []Emote{}
|
||||
|
||||
for _, emote := range sets.Emoticons {
|
||||
parsedEmotes = append(parsedEmotes, Emote{
|
||||
Name: emote.Name,
|
||||
Urls: emote.Urls,
|
||||
})
|
||||
}
|
||||
|
||||
return parsedEmotes
|
||||
}
|
||||
|
||||
func generateBttvEmotesUrls(id string) map[string]string {
|
||||
url := "https://cdn.betterttv.net/emote/"
|
||||
createdUrls := map[string]string{}
|
||||
|
||||
for i := 1; i < 4; i++ {
|
||||
createdUrls[fmt.Sprint(i)] = url + id + "/" + fmt.Sprint(i) + "x"
|
||||
}
|
||||
|
||||
return createdUrls
|
||||
}
|
||||
|
||||
func ParseBttvEmotes(emoteData []structs.BttvEmoteData) []Emote {
|
||||
parsedEmotes := []Emote{}
|
||||
for _, emote := range emoteData {
|
||||
parsedEmotes = append(parsedEmotes, Emote{
|
||||
Name: emote.Code,
|
||||
Urls: generateBttvEmotesUrls(emote.Id),
|
||||
})
|
||||
}
|
||||
|
||||
return parsedEmotes
|
||||
}
|
||||
|
||||
func ParseBttvGlobalEmotes(emoteData []structs.BttvGlobalEmoteData) []Emote {
|
||||
parsedEmotes := []Emote{}
|
||||
for _, emote := range emoteData {
|
||||
parsedEmotes = append(parsedEmotes, Emote{
|
||||
Name: emote.Code,
|
||||
Urls: generateBttvEmotesUrls(emote.Id),
|
||||
})
|
||||
}
|
||||
|
||||
return parsedEmotes
|
||||
}
|
|
@ -27,27 +27,41 @@ func call(url, method string, body io.Reader) (*http.Response, error) {
|
|||
return resp, err
|
||||
}
|
||||
|
||||
func getResponse(url string) ([]byte, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func parseResponse(payload []TwitchPayload) (response *[]structs.TwitchApiResponse, body []byte, err error) {
|
||||
json_data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := call(twitchUrl, "POST", bytes.NewBuffer(json_data))
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
rawBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var twitchResponse []structs.TwitchApiResponse
|
||||
err = json.Unmarshal(rawBody, &twitchResponse)
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &twitchResponse, rawBody, nil
|
||||
|
@ -515,4 +529,88 @@ func GetSearchResult(query string) (structs.SearchResult, error) {
|
|||
}
|
||||
|
||||
return final, nil
|
||||
|
||||
}
|
||||
|
||||
func GetStreamerId(channelName string) (string, error) {
|
||||
payload := []TwitchPayload{
|
||||
{
|
||||
"operationName": "ChannelRoot_AboutPanel",
|
||||
"variables": map[string]interface{}{
|
||||
"channelLogin": channelName,
|
||||
"skipSchedule": true,
|
||||
},
|
||||
"extensions": map[string]interface{}{
|
||||
"persistedQuery": map[string]interface{}{
|
||||
"version": 1,
|
||||
"sha256Hash": "6089531acef6c09ece01b440c41978f4c8dc60cb4fa0124c9a9d3f896709b6c6",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, body, err := parseResponse(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
id := gjson.Get(string(body), "0.data.user.id").String()
|
||||
|
||||
if id != "" {
|
||||
return id, nil
|
||||
} else {
|
||||
return "", errors.New("Could not find user")
|
||||
}
|
||||
}
|
||||
|
||||
func GetEmotes(channelName string) ([]Emote, error) {
|
||||
ffzUrl := "https://api.frankerfacez.com/v1/room/" + channelName
|
||||
|
||||
body, err := getResponse(ffzUrl)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
|
||||
ffzEmotes := &structs.FfzData{}
|
||||
err = json.Unmarshal(body, ffzEmotes)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
|
||||
emotes := ParseFfzEmotes(*ffzEmotes)
|
||||
|
||||
id, err := GetStreamerId(channelName)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
|
||||
bttvGlobalUrl := "https://api.betterttv.net/3/cached/emotes/global"
|
||||
bttvChannelUrl := "https://api.betterttv.net/3/cached/users/twitch/" + id
|
||||
|
||||
body, err = getResponse(bttvGlobalUrl)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
|
||||
bttvGlobalEmotes := &[]structs.BttvGlobalEmoteData{}
|
||||
err = json.Unmarshal(body, bttvGlobalEmotes)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
emotes = append(emotes, ParseBttvGlobalEmotes(*bttvGlobalEmotes)...)
|
||||
|
||||
body, err = getResponse(bttvChannelUrl)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
|
||||
bttvChannelEmotes := &[]structs.BttvEmoteData{}
|
||||
emoteArray := gjson.Get(string(body), "channelEmotes").String()
|
||||
err = json.Unmarshal([]byte(emoteArray), bttvChannelEmotes)
|
||||
if err != nil {
|
||||
return []Emote{}, err
|
||||
}
|
||||
emotes = append(emotes, ParseBttvEmotes(*bttvChannelEmotes)...)
|
||||
return emotes, nil
|
||||
|
||||
}
|
||||
|
|
23
routes/api/emotes/emotes.go
Normal file
23
routes/api/emotes/emotes.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package emotes
|
||||
|
||||
import (
|
||||
"safetwitch-backend/extractor"
|
||||
"safetwitch-backend/extractor/twitch"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Routes(route *gin.Engine) {
|
||||
auth := route.Group("/api/emotes")
|
||||
|
||||
auth.GET("/:streamerName", func(context *gin.Context) {
|
||||
streamer := context.Param("streamerName")
|
||||
emotes, err := twitch.GetEmotes(streamer)
|
||||
if err != nil {
|
||||
context.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
context.JSON(200, extractor.FormatMessage(emotes, true))
|
||||
})
|
||||
}
|
|
@ -3,6 +3,7 @@ package routes
|
|||
import (
|
||||
"safetwitch-backend/routes/api/badges"
|
||||
"safetwitch-backend/routes/api/discover"
|
||||
"safetwitch-backend/routes/api/emotes"
|
||||
"safetwitch-backend/routes/api/search"
|
||||
"safetwitch-backend/routes/api/users"
|
||||
"safetwitch-backend/routes/proxy"
|
||||
|
@ -16,6 +17,7 @@ func SetRoutes(router *gin.Engine) {
|
|||
discover.Routes(router)
|
||||
badges.Routes(router)
|
||||
search.Routes(router)
|
||||
emotes.Routes(router)
|
||||
|
||||
proxy.Routes(router)
|
||||
root.Routes(router)
|
||||
|
|
Loading…
Reference in a new issue