Documentation ¶
Index ¶
- Constants
- Variables
- func ValidateStatus(opts ClientRequestOpts, resp *http.Response) error
- type APICache
- type AuthType
- type BadgeCollection
- type Category
- type ChannelStreamSchedule
- type ChannelStreamScheduleSegment
- type Client
- func (c *Client) APICache() *APICache
- func (c *Client) AddChannelVIP(ctx context.Context, broadcasterName, userName string) error
- func (c *Client) BanUser(channel, username string, duration time.Duration, reason string) error
- func (c *Client) CreateClip(ctx context.Context, channel string, addDelay bool) (ccr CreateClipResponse, err error)
- func (c *Client) DeleteMessage(channel, messageID string) error
- func (c *Client) GetAuthorizedUser() (userID string, userName string, err error)
- func (c *Client) GetBroadcasterSubscriptionCount(ctx context.Context, broadcasterName string) (subCount, subPoints int64, err error)
- func (c *Client) GetChannelStreamSchedule(ctx context.Context, channel string) (*ChannelStreamSchedule, error)
- func (c *Client) GetClipByID(ctx context.Context, clipID string) (ClipInfo, error)
- func (c *Client) GetCurrentStreamInfo(username string) (*StreamInfo, error)
- func (c *Client) GetDisplayNameForUser(username string) (string, error)
- func (c *Client) GetFollowDate(from, to string) (time.Time, error)
- func (c *Client) GetIDForUsername(username string) (string, error)
- func (c *Client) GetLatestPoll(ctx context.Context, username string) (*PollInfo, error)
- func (c *Client) GetRecentStreamInfo(username string) (string, string, error)
- func (c *Client) GetToken() (string, error)
- func (c *Client) GetTokenInfo(ctx context.Context) (user string, scopes []string, expiresAt time.Time, err error)
- func (c *Client) GetTwitchAppAccessToken() (string, error)
- func (c *Client) GetUserInformation(user string) (*User, error)
- func (c *Client) GetUsernameForID(ctx context.Context, id string) (string, error)
- func (c *Client) HasLiveStream(username string) (bool, error)
- func (c *Client) ModifyChannelInformation(ctx context.Context, broadcasterName string, game, title *string) error
- func (c *Client) RefreshToken() error
- func (c *Client) RemoveChannelVIP(ctx context.Context, broadcasterName, userName string) error
- func (c *Client) Request(opts ClientRequestOpts) error
- func (c *Client) RunCommercial(ctx context.Context, channel string, duration int64) error
- func (c *Client) SearchCategories(ctx context.Context, name string) ([]Category, error)
- func (c *Client) SendChatAnnouncement(channel, color, message string) error
- func (c *Client) SendShoutout(channel, user string) error
- func (c *Client) SendWhisper(toUser, message string) error
- func (c *Client) SetTokenUpdateHook(f func(string, string) error)
- func (c *Client) UnbanUser(channel, username string) error
- func (c *Client) UpdateShieldMode(ctx context.Context, channel string, enable bool) error
- func (c *Client) UpdateToken(accessToken, refreshToken string)
- func (c *Client) ValidateToken(ctx context.Context, force bool) error
- type ClientRequestOpts
- type ClipInfo
- type CreateClipResponse
- type ErrorResponse
- type EventSubClientdeprecated
- type EventSubCondition
- type EventSubEventAdBreakBegin
- type EventSubEventChannelPointCustomRewardRedemptionAdd
- type EventSubEventChannelUpdate
- type EventSubEventFollow
- type EventSubEventPoll
- type EventSubEventRaid
- type EventSubEventShoutoutCreated
- type EventSubEventShoutoutReceived
- type EventSubEventStreamOffline
- type EventSubEventStreamOnline
- type EventSubEventUserAuthorizationRevoke
- type EventSubSocketClient
- type EventSubSocketClientOpt
- func WithLogger(logger *logrus.Entry) EventSubSocketClientOpt
- func WithMustSubscribe(event, version string, condition EventSubCondition, ...) EventSubSocketClientOpt
- func WithRetryBackgroundSubscribe(event, version string, condition EventSubCondition, ...) EventSubSocketClientOpt
- func WithSocketURL(url string) EventSubSocketClientOpt
- func WithTwitchClient(c *Client) EventSubSocketClientOpt
- type HTTPError
- type OAuthTokenResponse
- type OAuthTokenValidationResponse
- type PollInfo
- type StreamInfo
- type User
Constants ¶
const ( BadgeBroadcaster = "broadcaster" BadgeFounder = "founder" BadgeModerator = "moderator" BadgeSubscriber = "subscriber" BadgeVIP = "vip" )
const ( EventSubEventTypeChannelAdBreakBegin = "channel.ad_break.begin" EventSubEventTypeChannelFollow = "channel.follow" EventSubEventTypeChannelPointCustomRewardRedemptionAdd = "channel.channel_points_custom_reward_redemption.add" EventSubEventTypeChannelRaid = "channel.raid" EventSubEventTypeChannelShoutoutCreate = "channel.shoutout.create" EventSubEventTypeChannelShoutoutReceive = "channel.shoutout.receive" EventSubEventTypeChannelUpdate = "channel.update" EventSubEventTypeChannelPollBegin = "channel.poll.begin" EventSubEventTypeChannelPollEnd = "channel.poll.end" EventSubEventTypeChannelPollProgress = "channel.poll.progress" EventSubEventTypeStreamOffline = "stream.offline" EventSubEventTypeStreamOnline = "stream.online" EventSubEventTypeUserAuthorizationRevoke = "user.authorization.revoke" EventSubTopicVersion1 = "1" EventSubTopicVersion2 = "2" EventSubTopicVersionBeta = "beta" )
const ( // API Scopes ScopeChannelBot = "channel:bot" ScopeChannelEditCommercial = "channel:edit:commercial" ScopeChannelManageAds = "channel:manage:ads" ScopeChannelManageBroadcast = "channel:manage:broadcast" ScopeChannelManageModerators = "channel:manage:moderators" ScopeChannelManagePolls = "channel:manage:polls" ScopeChannelManagePredictions = "channel:manage:predictions" ScopeChannelManageRaids = "channel:manage:raids" ScopeChannelManageRedemptions = "channel:manage:redemptions" ScopeChannelManageVIPS = "channel:manage:vips" ScopeChannelManageWhispers = "user:manage:whispers" ScopeChannelReadAds = "channel:read:ads" ScopeChannelReadPolls = "channel:read:polls" ScopeChannelReadRedemptions = "channel:read:redemptions" ScopeChannelReadSubscriptions = "channel:read:subscriptions" ScopeClipsEdit = "clips:edit" ScopeModeratorManageAnnoucements = "moderator:manage:announcements" ScopeModeratorManageBannedUsers = "moderator:manage:banned_users" ScopeModeratorManageChatMessages = "moderator:manage:chat_messages" ScopeModeratorManageChatSettings = "moderator:manage:chat_settings" ScopeModeratorManageShieldMode = "moderator:manage:shield_mode" ScopeModeratorManageShoutouts = "moderator:manage:shoutouts" ScopeModeratorReadFollowers = "moderator:read:followers" ScopeModeratorReadShoutouts = "moderator:read:shoutouts" ScopeUserBot = "user:bot" ScopeUserManageChatColor = "user:manage:chat_color" ScopeUserManageWhispers = "user:manage:whispers" ScopeUserReadChat = "user:read:chat" // Deprecated v5 scope but used in chat ScopeV5ChannelEditor = "channel_editor" // Chat Scopes ScopeChatEdit = "chat:edit" // Send live stream chat and rooms messages. ScopeChatRead = "chat:read" // View live stream chat and rooms messages. ScopeWhisperRead = "whispers:read" // View your whisper messages. )
Variables ¶
var ErrAnyHTTPError = newHTTPError(0, nil, nil)
ErrAnyHTTPError can be used in errors.Is() to match an HTTPError with any status code
var ErrNoStreamsFound = errors.New("no streams found")
ErrNoStreamsFound allows to differntiate between an HTTP error and the fact there just is no stream found
var ErrUserDoesNotFollow = errors.New("no follow-relation found")
var KnownBadges = []string{ BadgeBroadcaster, BadgeFounder, BadgeModerator, BadgeSubscriber, BadgeVIP, }
Functions ¶
func ValidateStatus ¶ added in v3.15.0
func ValidateStatus(opts ClientRequestOpts, resp *http.Response) error
ValidateStatus is the default validation function used when no ValidateFunc is given in the ClientRequestOpts and checks for the returned HTTP status is equal to the OKStatus.
When the status is http.StatusTooManyRequests the function will return an error terminating any retries as retrying would not make sense (the error returned from Request will still be an HTTPError with status 429).
When wrapping this function the body should not have been read before in order to have the response body available in the returned HTTPError
Types ¶
type AuthType ¶ added in v3.14.0
type AuthType uint8
const ( AuthTypeAppAccessToken AuthTypeBearerToken )
type BadgeCollection ¶
func ParseBadgeLevels ¶
func ParseBadgeLevels(m *irc.Message) BadgeCollection
func (BadgeCollection) Add ¶
func (b BadgeCollection) Add(badge string, level int)
func (BadgeCollection) Get ¶
func (b BadgeCollection) Get(badge string) int
func (BadgeCollection) Has ¶
func (b BadgeCollection) Has(badge string) bool
type ChannelStreamSchedule ¶ added in v3.15.0
type ChannelStreamSchedule struct { Segments []ChannelStreamScheduleSegment `json:"segments"` BroadcasterID string `json:"broadcaster_id"` BroadcasterName string `json:"broadcaster_name"` BroadcasterLogin string `json:"broadcaster_login"` Vacation struct { StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` } `json:"vacation"` }
ChannelStreamSchedule represents the schedule of a channels with its segments represening single planned streams
type ChannelStreamScheduleSegment ¶ added in v3.20.0
type ChannelStreamScheduleSegment struct { ID string `json:"id"` StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` Title string `json:"title"` CanceledUntil *time.Time `json:"canceled_until"` Category struct { ID string `json:"id"` Name string `json:"name"` } `json:"category"` IsRecurring bool `json:"is_recurring"` }
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
func (*Client) AddChannelVIP ¶ added in v3.2.0
func (*Client) BanUser ¶
BanUser bans or timeouts a user in the given channel. Setting the duration to 0 will result in a ban, setting if greater than 0 will result in a timeout. The timeout is automatically converted to full seconds. The timeout duration must be less than 1209600s.
func (*Client) CreateClip ¶ added in v3.16.0
func (c *Client) CreateClip(ctx context.Context, channel string, addDelay bool) (ccr CreateClipResponse, err error)
CreateClip triggers the creation of a clip in the given channel. If addDelay is true an artificial delay will be added (for broadcasters who trigger this function already knowing something will happen but not yet visible in stream).
func (*Client) DeleteMessage ¶
DeleteMessage deletes one or all messages from the specified chat. If no messageID is given all messages are deleted. If a message ID is given the message must be no older than 6 hours and it must not be posted by broadcaster or moderator.
func (*Client) GetAuthorizedUser ¶
func (*Client) GetBroadcasterSubscriptionCount ¶ added in v3.9.0
func (c *Client) GetBroadcasterSubscriptionCount(ctx context.Context, broadcasterName string) (subCount, subPoints int64, err error)
GetBroadcasterSubscriptionCount gets a list of users that subscribe to the specified broadcaster.
func (*Client) GetChannelStreamSchedule ¶ added in v3.15.0
func (c *Client) GetChannelStreamSchedule(ctx context.Context, channel string) (*ChannelStreamSchedule, error)
GetChannelStreamSchedule gets the broadcaster’s streaming schedule
func (*Client) GetClipByID ¶ added in v3.8.0
GetClipByID gets a video clip that were captured from streams by its ID (slug in the URL)
func (*Client) GetCurrentStreamInfo ¶
func (c *Client) GetCurrentStreamInfo(username string) (*StreamInfo, error)
func (*Client) GetDisplayNameForUser ¶
func (*Client) GetIDForUsername ¶
func (*Client) GetLatestPoll ¶ added in v3.11.0
func (*Client) GetRecentStreamInfo ¶
func (*Client) GetTokenInfo ¶ added in v3.20.0
func (c *Client) GetTokenInfo(ctx context.Context) (user string, scopes []string, expiresAt time.Time, err error)
GetTokenInfo requests a validation for the token set within the client and returns the authorized user, their granted scopes on this token and an error in case something went wrong.
func (*Client) GetTwitchAppAccessToken ¶ added in v3.19.0
func (*Client) GetUsernameForID ¶ added in v3.18.0
GetUsernameForID retrieves the login name (not the display name) for the given user ID
func (*Client) ModifyChannelInformation ¶
func (*Client) RefreshToken ¶
func (*Client) RemoveChannelVIP ¶ added in v3.2.0
func (*Client) Request ¶ added in v3.14.0
func (c *Client) Request(opts ClientRequestOpts) error
func (*Client) RunCommercial ¶ added in v3.7.0
RunCommercial starts a commercial on the specified channel
func (*Client) SearchCategories ¶
func (*Client) SendChatAnnouncement ¶
SendChatAnnouncement sends an announcement in the specified channel with the given message. Colors must be blue, green, orange, purple or primary (empty color = primary)
func (*Client) SendShoutout ¶ added in v3.4.0
SendShoutout creates a Twitch-native shoutout in the given channel for the given user. This equals `/shoutout <user>` in the channel.
func (*Client) SendWhisper ¶
SendWhisper sends a whisper from the bot to the specified user.
For details about message limits see the official documentation: https://dev.twitch.tv/docs/api/reference#send-whisper
func (*Client) SetTokenUpdateHook ¶
func (*Client) UpdateShieldMode ¶ added in v3.5.0
UpdateShieldMode activates or deactivates the Shield Mode in the given channel
func (*Client) UpdateToken ¶
type ClientRequestOpts ¶ added in v3.14.0
type ClipInfo ¶ added in v3.8.0
type ClipInfo struct { ID string `json:"id"` URL string `json:"url"` EmbedURL string `json:"embed_url"` BroadcasterID string `json:"broadcaster_id"` BroadcasterName string `json:"broadcaster_name"` CreatorID string `json:"creator_id"` CreatorName string `json:"creator_name"` VideoID string `json:"video_id"` GameID string `json:"game_id"` Language string `json:"language"` Title string `json:"title"` ViewCount int64 `json:"view_count"` CreatedAt time.Time `json:"created_at"` ThumbnailURL string `json:"thumbnail_url"` Duration float64 `json:"duration"` VodOffset int64 `json:"vod_offset"` }
type CreateClipResponse ¶ added in v3.16.0
type ErrorResponse ¶ added in v3.15.0
type EventSubClient
deprecated
type EventSubClient struct {
// contains filtered or unexported fields
}
Deprecated: This client should no longer be used and will not be maintained afterwards. Replace with EventSubSocketClient.
func NewEventSubClient
deprecated
func NewEventSubClient(twitchClient *Client, apiURL, secret, secretHandle string) (*EventSubClient, error)
Deprecated: See deprecation notice of EventSubClient
func (*EventSubClient) HandleEventsubPush ¶
func (e *EventSubClient) HandleEventsubPush(w http.ResponseWriter, r *http.Request)
func (*EventSubClient) PreFetchSubscriptions ¶
func (e *EventSubClient) PreFetchSubscriptions(ctx context.Context) error
func (*EventSubClient) RegisterEventSubHooks ¶
func (e *EventSubClient) RegisterEventSubHooks(event, version string, condition EventSubCondition, callback func(json.RawMessage) error) (func(), error)
type EventSubCondition ¶
type EventSubCondition struct { BroadcasterUserID string `json:"broadcaster_user_id,omitempty"` CampaignID string `json:"campaign_id,omitempty"` CategoryID string `json:"category_id,omitempty"` ClientID string `json:"client_id,omitempty"` ExtensionClientID string `json:"extension_client_id,omitempty"` FromBroadcasterUserID string `json:"from_broadcaster_user_id,omitempty"` OrganizationID string `json:"organization_id,omitempty"` RewardID string `json:"reward_id,omitempty"` ToBroadcasterUserID string `json:"to_broadcaster_user_id,omitempty"` UserID string `json:"user_id,omitempty"` ModeratorUserID string `json:"moderator_user_id,omitempty"` }
func (EventSubCondition) Hash ¶
func (e EventSubCondition) Hash() (string, error)
type EventSubEventAdBreakBegin ¶ added in v3.19.0
type EventSubEventAdBreakBegin struct { Duration int64 `json:"duration_seconds"` Timestamp time.Time `json:"timestamp"` IsAutomatic bool `json:"is_automatic"` BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` RequesterUserID string `json:"requester_user_id"` RequesterUserLogin string `json:"requester_user_login"` RequesterUserName string `json:"requester_user_name"` }
type EventSubEventChannelPointCustomRewardRedemptionAdd ¶
type EventSubEventChannelPointCustomRewardRedemptionAdd struct { ID string `json:"id"` BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` UserID string `json:"user_id"` UserLogin string `json:"user_login"` UserName string `json:"user_name"` UserInput string `json:"user_input"` Status string `json:"status"` Reward struct { ID string `json:"id"` Title string `json:"title"` Cost int64 `json:"cost"` Prompt string `json:"prompt"` } `json:"reward"` RedeemedAt time.Time `json:"redeemed_at"` }
type EventSubEventChannelUpdate ¶
type EventSubEventChannelUpdate struct { BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` Title string `json:"title"` Language string `json:"language"` CategoryID string `json:"category_id"` CategoryName string `json:"category_name"` ContentClassificationLabels []string `json:"content_classification_labels"` }
type EventSubEventFollow ¶
type EventSubEventFollow struct { UserID string `json:"user_id"` UserLogin string `json:"user_login"` UserName string `json:"user_name"` BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` FollowedAt time.Time `json:"followed_at"` }
type EventSubEventPoll ¶ added in v3.11.0
type EventSubEventPoll struct { ID string `json:"id"` BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` Title string `json:"title"` Choices []struct { ID string `json:"id"` Title string `json:"title"` ChannelPointsVotes int `json:"channel_points_votes"` Votes int `json:"votes"` } `json:"choices"` ChannelPointsVoting struct { IsEnabled bool `json:"is_enabled"` AmountPerVote int `json:"amount_per_vote"` } `json:"channel_points_voting"` StartedAt time.Time `json:"started_at"` // begin, progress, end EndsAt time.Time `json:"ends_at,omitempty"` // begin, progress Status string `json:"status,omitempty"` // end -- enum(completed, archived, terminated) EndedAt time.Time `json:"ended_at,omitempty"` // end }
type EventSubEventRaid ¶
type EventSubEventRaid struct { FromBroadcasterUserID string `json:"from_broadcaster_user_id"` FromBroadcasterUserLogin string `json:"from_broadcaster_user_login"` FromBroadcasterUserName string `json:"from_broadcaster_user_name"` ToBroadcasterUserID string `json:"to_broadcaster_user_id"` ToBroadcasterUserLogin string `json:"to_broadcaster_user_login"` ToBroadcasterUserName string `json:"to_broadcaster_user_name"` Viewers int64 `json:"viewers"` }
type EventSubEventShoutoutCreated ¶ added in v3.7.0
type EventSubEventShoutoutCreated struct { BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` ModeratorUserID string `json:"moderator_user_id"` ModeratorUserLogin string `json:"moderator_user_login"` ModeratorUserName string `json:"moderator_user_name"` ToBroadcasterUserID string `json:"to_broadcaster_user_id"` ToBroadcasterUserLogin string `json:"to_broadcaster_user_login"` ToBroadcasterUserName string `json:"to_broadcaster_user_name"` ViewerCount int64 `json:"viewer_count"` StartedAt time.Time `json:"started_at"` CooldownEndsAt time.Time `json:"cooldown_ends_at"` TargetCooldownEndsAt time.Time `json:"target_cooldown_ends_at"` }
type EventSubEventShoutoutReceived ¶ added in v3.6.0
type EventSubEventShoutoutReceived struct { BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` FromBroadcasterUserID string `json:"from_broadcaster_user_id"` FromBroadcasterUserLogin string `json:"from_broadcaster_user_login"` FromBroadcasterUserName string `json:"from_broadcaster_user_name"` ViewerCount int64 `json:"viewer_count"` StartedAt time.Time `json:"started_at"` }
type EventSubEventStreamOnline ¶
type EventSubEventStreamOnline struct { ID string `json:"id"` BroadcasterUserID string `json:"broadcaster_user_id"` BroadcasterUserLogin string `json:"broadcaster_user_login"` BroadcasterUserName string `json:"broadcaster_user_name"` Type string `json:"type"` StartedAt time.Time `json:"started_at"` }
type EventSubSocketClient ¶ added in v3.10.0
type EventSubSocketClient struct {
// contains filtered or unexported fields
}
func NewEventSubSocketClient ¶ added in v3.10.0
func NewEventSubSocketClient(opts ...EventSubSocketClientOpt) (*EventSubSocketClient, error)
func (*EventSubSocketClient) Close ¶ added in v3.10.0
func (e *EventSubSocketClient) Close()
func (*EventSubSocketClient) Run ¶ added in v3.10.0
func (e *EventSubSocketClient) Run() error
type EventSubSocketClientOpt ¶ added in v3.10.0
type EventSubSocketClientOpt func(*EventSubSocketClient)
func WithLogger ¶ added in v3.10.0
func WithLogger(logger *logrus.Entry) EventSubSocketClientOpt
func WithMustSubscribe ¶ added in v3.22.0
func WithMustSubscribe(event, version string, condition EventSubCondition, callback func(json.RawMessage) error) EventSubSocketClientOpt
func WithRetryBackgroundSubscribe ¶ added in v3.22.0
func WithRetryBackgroundSubscribe(event, version string, condition EventSubCondition, callback func(json.RawMessage) error) EventSubSocketClientOpt
func WithSocketURL ¶ added in v3.10.0
func WithSocketURL(url string) EventSubSocketClientOpt
func WithTwitchClient ¶ added in v3.10.0
func WithTwitchClient(c *Client) EventSubSocketClientOpt
type HTTPError ¶ added in v3.15.0
HTTPError represents an HTTP error containing the response body (or the wrapped error occurred while readiny the body) and the status code returned by the server
func (HTTPError) Error ¶ added in v3.15.0
Error implements the error interface and returns a formatted version of the error including the body, might therefore leak confidential information when included in the response body
type OAuthTokenResponse ¶
type PollInfo ¶ added in v3.11.0
type PollInfo struct { ID string `json:"id"` BroadcasterID string `json:"broadcaster_id"` BroadcasterName string `json:"broadcaster_name"` BroadcasterLogin string `json:"broadcaster_login"` Title string `json:"title"` Choices []struct { ID string `json:"id"` Title string `json:"title"` Votes int `json:"votes"` ChannelPointsVotes int `json:"channel_points_votes"` } `json:"choices"` ChannelPointsVotingEnabled bool `json:"channel_points_voting_enabled"` ChannelPointsPerVote int `json:"channel_points_per_vote"` Status string `json:"status"` Duration int `json:"duration"` StartedAt time.Time `json:"started_at"` EndedAt *time.Time `json:"ended_at"` }
type StreamInfo ¶
type StreamInfo struct { ID string `json:"id"` UserID string `json:"user_id"` UserLogin string `json:"user_login"` UserName string `json:"user_name"` GameID string `json:"game_id"` GameName string `json:"game_name"` Type string `json:"type"` Title string `json:"title"` ViewerCount int64 `json:"viewer_count"` StartedAt time.Time `json:"started_at"` Language string `json:"language"` ThumbnailURL string `json:"thumbnail_url"` TagIds []string `json:"tag_ids"` IsMature bool `json:"is_mature"` }