0
Fork 0
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:
dragongoose 2023-06-17 21:14:28 -04:00
parent 76ac6571be
commit 8e572b9bbd
No known key found for this signature in database
GPG key ID: 50DB99B921579009
5 changed files with 241 additions and 4 deletions

View 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"`
}

View 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
}

View file

@ -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
}

View 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))
})
}

View file

@ -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)