mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 11:51:17 +00:00
[templating] Add currentVOD
function
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
740a71a173
commit
19038dbc6e
4 changed files with 215 additions and 2 deletions
|
@ -165,6 +165,19 @@ Example:
|
|||
* 1 6
|
||||
```
|
||||
|
||||
### `currentVOD`
|
||||
|
||||
Returns the VOD of the currently running stream in the given channel (causes an error if no current stream / VOD is found)
|
||||
|
||||
Syntax: `currentVOD <username>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ currentVOD .channel }}
|
||||
* https://www.twitch.tv/videos/123456789
|
||||
```
|
||||
|
||||
### `displayName`
|
||||
|
||||
Returns the display name the specified user set for themselves
|
||||
|
@ -467,7 +480,7 @@ Example:
|
|||
|
||||
```
|
||||
# Your int this hour: {{ printf "%.0f" (mulf (seededRandom (list "int" .username (now | date "2006-01-02 15") | join ":")) 100) }}%
|
||||
< Your int this hour: 41%
|
||||
< Your int this hour: 23%
|
||||
```
|
||||
|
||||
### `spotifyCurrentPlaying`
|
||||
|
|
|
@ -116,7 +116,7 @@ SocketMessage received for every event and passed to the new `(eventObj) => { ..
|
|||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [event_id] | <code>Number</code> | UID of the event used to re-trigger an event |
|
||||
| [event_id] | <code>String</code> | UID of the event used to re-trigger an event |
|
||||
| [is_live] | <code>Boolean</code> | Whether the event was sent through a replay (false) or occurred live (true) |
|
||||
| [reason] | <code>String</code> | Reason of this message (one of `bulk-replay`, `live-event`, `single-replay`) |
|
||||
| [time] | <code>String</code> | RFC3339 timestamp of the event |
|
||||
|
|
50
internal/template/twitch/videos.go
Normal file
50
internal/template/twitch/videos.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
||||
"github.com/Luzifer/twitch-bot/v3/plugins"
|
||||
)
|
||||
|
||||
func init() {
|
||||
regFn = append(
|
||||
regFn,
|
||||
tplTwitchCurrentVOD,
|
||||
)
|
||||
}
|
||||
|
||||
func tplTwitchCurrentVOD(args plugins.RegistrationArguments) {
|
||||
args.RegisterTemplateFunction("currentVOD", plugins.GenericTemplateFunctionGetter(func(username string) (string, error) {
|
||||
si, err := args.GetTwitchClient().GetCurrentStreamInfo(context.Background(), strings.TrimLeft(username, "#"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting stream info: %w", err)
|
||||
}
|
||||
|
||||
vids, err := args.GetTwitchClient().GetVideos(context.TODO(), twitch.GetVideoOpts{
|
||||
UserID: si.UserID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting videos: %w", err)
|
||||
}
|
||||
|
||||
for _, v := range vids {
|
||||
if v.StreamID == nil || *v.StreamID != si.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
return v.URL, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no matching VOD found")
|
||||
}), plugins.TemplateFuncDocumentation{
|
||||
Description: "Returns the VOD of the currently running stream in the given channel (causes an error if no current stream / VOD is found)",
|
||||
Syntax: "currentVOD <username>",
|
||||
Example: &plugins.TemplateFuncDocumentationExample{
|
||||
Template: `{{ currentVOD .channel }}`,
|
||||
FakedOutput: "https://www.twitch.tv/videos/123456789",
|
||||
},
|
||||
})
|
||||
}
|
150
pkg/twitch/videos.go
Normal file
150
pkg/twitch/videos.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
)
|
||||
|
||||
type (
|
||||
// GetVideoOpts contain the query parameter for the GetVideos query
|
||||
//
|
||||
// See https://dev.twitch.tv/docs/api/reference/#get-videos for details
|
||||
GetVideoOpts struct {
|
||||
ID string // Required: Exactly one of ID, UserID, GameID
|
||||
UserID string // Required: Exactly one of ID, UserID, GameID
|
||||
GameID string // Required: Exactly one of ID, UserID, GameID
|
||||
Language string // Optional: Use only with GameID
|
||||
Period GetVideoOptsPeriod // Optional: Use only with GameID or UserID
|
||||
Sort GetVideoOptsSort // Optional: Use only with GameID or UserID
|
||||
Type GetVideoOptsType // Optional: Use only with GameID or UserID
|
||||
First int64 // Optional: Use only with GameID or UserID
|
||||
After string // Optional: Use only with UserID
|
||||
Before string // Optional: Use only with UserID
|
||||
}
|
||||
|
||||
// GetVideoOptsPeriod represents a filter used to filter the list of
|
||||
// videos by when they were published
|
||||
GetVideoOptsPeriod string
|
||||
// GetVideoOptsSort represents the order to sort the returned videos in
|
||||
GetVideoOptsSort string
|
||||
// GetVideoOptsType represents a filter used to filter the list of
|
||||
// videos by the video's type
|
||||
GetVideoOptsType string
|
||||
|
||||
// Video contains information about a published video
|
||||
Video struct {
|
||||
ID string `json:"id"`
|
||||
StreamID *string `json:"stream_id"`
|
||||
UserID string `json:"user_id"`
|
||||
UserLogin string `json:"user_login"`
|
||||
UserName string `json:"user_name"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
URL string `json:"url"`
|
||||
ThumbnailURL string `json:"thumbnail_url"`
|
||||
Viewable string `json:"viewable"`
|
||||
ViewCount int64 `json:"view_count"`
|
||||
Language string `json:"language"`
|
||||
Type string `json:"type"`
|
||||
Duration string `json:"duration"`
|
||||
MutedSegments []struct {
|
||||
Duration int64 `json:"duration"`
|
||||
Offset int64 `json:"offset"`
|
||||
} `json:"muted_segments"`
|
||||
}
|
||||
)
|
||||
|
||||
// List of filters for GetVideoOpts.Period
|
||||
const (
|
||||
GetVideoOptsPeriodAll GetVideoOptsPeriod = "all"
|
||||
GetVideoOptsPeriodDay GetVideoOptsPeriod = "day"
|
||||
GetVideoOptsPeriodMonth GetVideoOptsPeriod = "month"
|
||||
GetVideoOptsPeriodWeek GetVideoOptsPeriod = "week"
|
||||
)
|
||||
|
||||
// List of sort options for GetVideoOpts.Sort
|
||||
const (
|
||||
GetVideoOptsSortTime GetVideoOptsSort = "time"
|
||||
GetVideoOptsSortTrending GetVideoOptsSort = "trending"
|
||||
GetVideoOptsSortViews GetVideoOptsSort = "views"
|
||||
)
|
||||
|
||||
// List of types for GetVideoOpts.Type
|
||||
const (
|
||||
GetVideoOptsTypeAll GetVideoOptsType = "all"
|
||||
GetVideoOptsTypeArchive GetVideoOptsType = "archive"
|
||||
GetVideoOptsTypeHighlight GetVideoOptsType = "highlight"
|
||||
GetVideoOptsTypeUpload GetVideoOptsType = "upload"
|
||||
)
|
||||
|
||||
// GetVideos fetches information about one or more published videos
|
||||
func (c *Client) GetVideos(ctx context.Context, opts GetVideoOpts) (videos []Video, err error) {
|
||||
optsCacheKey, err := opts.cacheKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting opts cache key: %w", err)
|
||||
}
|
||||
|
||||
cacheKey := []string{"currentVideos", optsCacheKey}
|
||||
if vids := c.apiCache.Get(cacheKey); vids != nil {
|
||||
return vids.([]Video), nil
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Data []Video `json:"data"`
|
||||
}
|
||||
|
||||
if err := c.Request(ctx, ClientRequestOpts{
|
||||
AuthType: AuthTypeAppAccessToken,
|
||||
Method: http.MethodGet,
|
||||
OKStatus: http.StatusOK,
|
||||
Out: &payload,
|
||||
URL: fmt.Sprintf("https://api.twitch.tv/helix/videos?%s", opts.queryParams()),
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("requesting videos: %w", err)
|
||||
}
|
||||
|
||||
// Videos can be changed at any moment, cache for a short period of time
|
||||
c.apiCache.Set(cacheKey, twitchMinCacheTime, payload.Data)
|
||||
|
||||
return payload.Data, nil
|
||||
}
|
||||
|
||||
func (g GetVideoOpts) cacheKey() (string, error) {
|
||||
h, err := hashstructure.Hash(g, hashstructure.FormatV2, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("hashing opts: %w", err)
|
||||
}
|
||||
|
||||
return strconv.FormatUint(h, 10), nil
|
||||
}
|
||||
|
||||
func (g GetVideoOpts) queryParams() string {
|
||||
params := url.Values{}
|
||||
|
||||
for k, v := range map[string]string{
|
||||
"id": g.ID,
|
||||
"user_id": g.UserID,
|
||||
"game_id": g.GameID,
|
||||
"language": g.Language,
|
||||
"period": string(g.Period),
|
||||
"sort": string(g.Sort),
|
||||
"type": string(g.Type),
|
||||
"first": strconv.FormatInt(g.First, 10),
|
||||
"after": g.After,
|
||||
"before": g.Before,
|
||||
} {
|
||||
if v != "" && v != "0" {
|
||||
params.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return params.Encode()
|
||||
}
|
Loading…
Reference in a new issue