mirror of
https://github.com/glanceapp/glance.git
synced 2025-07-24 01:15:40 +02:00
126 lines
3.3 KiB
Go
126 lines
3.3 KiB
Go
package glance
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var twitchGamesWidgetTemplate = mustParseTemplate("twitch-games-list.html", "widget-base.html")
|
|
|
|
type twitchGamesWidget struct {
|
|
widgetBase `yaml:",inline"`
|
|
Categories []twitchCategory `yaml:"-"`
|
|
Exclude []string `yaml:"exclude"`
|
|
Limit int `yaml:"limit"`
|
|
CollapseAfter int `yaml:"collapse-after"`
|
|
}
|
|
|
|
func (widget *twitchGamesWidget) initialize() error {
|
|
widget.
|
|
withTitle("Top games on Twitch").
|
|
withTitleURL("https://www.twitch.tv/directory?sort=VIEWER_COUNT").
|
|
withCacheDuration(time.Minute * 10)
|
|
|
|
if widget.Limit <= 0 {
|
|
widget.Limit = 10
|
|
}
|
|
|
|
if widget.CollapseAfter == 0 || widget.CollapseAfter < -1 {
|
|
widget.CollapseAfter = 5
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (widget *twitchGamesWidget) update(ctx context.Context) {
|
|
categories, err := fetchTopGamesFromTwitch(widget.Exclude, widget.Limit)
|
|
|
|
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
|
return
|
|
}
|
|
|
|
widget.Categories = categories
|
|
}
|
|
|
|
func (widget *twitchGamesWidget) Render() template.HTML {
|
|
return widget.renderTemplate(widget, twitchGamesWidgetTemplate)
|
|
}
|
|
|
|
type twitchCategory struct {
|
|
Slug string `json:"slug"`
|
|
Name string `json:"name"`
|
|
AvatarUrl string `json:"avatarURL"`
|
|
ViewersCount int `json:"viewersCount"`
|
|
Tags []struct {
|
|
Name string `json:"tagName"`
|
|
} `json:"tags"`
|
|
GameReleaseDate string `json:"originalReleaseDate"`
|
|
IsNew bool `json:"-"`
|
|
}
|
|
|
|
type twitchDirectoriesOperationResponse struct {
|
|
Data struct {
|
|
DirectoriesWithTags struct {
|
|
Edges []struct {
|
|
Node twitchCategory `json:"node"`
|
|
} `json:"edges"`
|
|
} `json:"directoriesWithTags"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
const twitchDirectoriesOperationRequestBody = `[
|
|
{"operationName": "BrowsePage_AllDirectories","variables": {"limit": %d,"options": {"sort": "VIEWER_COUNT","tags": []}},"extensions": {"persistedQuery": {"version": 1,"sha256Hash": "2f67f71ba89f3c0ed26a141ec00da1defecb2303595f5cda4298169549783d9e"}}}
|
|
]`
|
|
|
|
func fetchTopGamesFromTwitch(exclude []string, limit int) ([]twitchCategory, error) {
|
|
reader := strings.NewReader(fmt.Sprintf(twitchDirectoriesOperationRequestBody, len(exclude)+limit))
|
|
request, _ := http.NewRequest("POST", twitchGqlEndpoint, reader)
|
|
request.Header.Add("Client-ID", twitchGqlClientId)
|
|
response, err := decodeJsonFromRequest[[]twitchDirectoriesOperationResponse](defaultHTTPClient, request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(response) == 0 {
|
|
return nil, errors.New("no categories could be retrieved")
|
|
}
|
|
|
|
edges := (response)[0].Data.DirectoriesWithTags.Edges
|
|
categories := make([]twitchCategory, 0, len(edges))
|
|
|
|
for i := range edges {
|
|
if slices.Contains(exclude, edges[i].Node.Slug) {
|
|
continue
|
|
}
|
|
|
|
category := &edges[i].Node
|
|
category.AvatarUrl = strings.Replace(category.AvatarUrl, "285x380", "144x192", 1)
|
|
|
|
if len(category.Tags) > 2 {
|
|
category.Tags = category.Tags[:2]
|
|
}
|
|
|
|
gameReleasedDate, err := time.Parse("2006-01-02T15:04:05Z", category.GameReleaseDate)
|
|
|
|
if err == nil {
|
|
if time.Since(gameReleasedDate) < 14*24*time.Hour {
|
|
category.IsNew = true
|
|
}
|
|
}
|
|
|
|
categories = append(categories, *category)
|
|
}
|
|
|
|
if len(categories) > limit {
|
|
categories = categories[:limit]
|
|
}
|
|
|
|
return categories, nil
|
|
}
|