Add mock clients to streaming platforms
Some checks failed
rolling-release / build (push) Has been cancelled
rolling-release / rolling-release (push) Has been cancelled

This commit is contained in:
Dmitrii Okunev
2025-07-20 02:24:23 +01:00
parent bc97d5d6b8
commit 733ddd1f00
19 changed files with 474 additions and 123 deletions

View File

@@ -12,14 +12,6 @@ type ChatHandler struct {
onClose func(context.Context)
}
func (k *Kick) newChatHandler(
ctx context.Context,
broadcasterUserID *int,
onClose func(context.Context),
) (*ChatHandler, error) {
return NewChatHandler(ctx, k.Client, broadcasterUserID, onClose)
}
func NewChatHandler(
ctx context.Context,
client *gokick.Client,

View File

@@ -0,0 +1,46 @@
package kick
import (
"context"
"github.com/scorfly/gokick"
)
type Client interface {
GetAuthorize(redirectURI, state, codeChallenge string, scope []gokick.Scope) (string, error)
GetToken(ctx context.Context, redirectURI, code, codeVerifier string) (gokick.TokenResponse, error)
OnUserAccessTokenRefreshed(callback func(accessToken, refreshToken string))
UpdateStreamTitle(ctx context.Context, title string) (gokick.EmptyResponse, error)
UpdateStreamCategory(ctx context.Context, categoryID int) (gokick.EmptyResponse, error)
GetChannels(ctx context.Context, filter gokick.ChannelListFilter) (gokick.ChannelsResponseWrapper, error)
SendChatMessage(
ctx context.Context,
broadcasterUserID *int,
content string,
replyToMessageID *string,
messageType gokick.MessageType,
) (gokick.ChatResponseWrapper, error)
BanUser(
ctx context.Context,
broadcasterUserID int,
userID int,
duration *int,
reason *string,
) (gokick.BanUserResponseWrapper, error)
}
type clientScorfly struct {
*gokick.Client
}
func newClient(options *gokick.ClientOptions) (*clientScorfly, error) {
client, err := gokick.NewClient(options)
if err != nil {
return nil, err
}
return &clientScorfly{Client: client}, nil
}
func (c *clientScorfly) OnUserAccessTokenRefreshed(callback func(accessToken, refreshToken string)) {
c.Client.OnUserAccessTokenRefreshed(callback)
}

View File

@@ -0,0 +1,87 @@
package kick
import (
"context"
"github.com/scorfly/gokick"
)
type clientMock struct{}
var _ Client = (*clientMock)(nil)
func newClientMock() *clientMock {
return &clientMock{}
}
func (clientMock) GetAuthorize(redirectURI, state, codeChallenge string, scope []gokick.Scope) (string, error) {
return "", nil
}
func (clientMock) GetToken(ctx context.Context, redirectURI, code, codeVerifier string) (gokick.TokenResponse, error) {
return gokick.TokenResponse{}, nil
}
func (clientMock) OnUserAccessTokenRefreshed(callback func(accessToken, refreshToken string)) {}
func (clientMock) UpdateStreamTitle(ctx context.Context, title string) (gokick.EmptyResponse, error) {
return gokick.EmptyResponse{}, nil
}
func (clientMock) UpdateStreamCategory(ctx context.Context, categoryID int) (gokick.EmptyResponse, error) {
return gokick.EmptyResponse{}, nil
}
func (clientMock) GetChannels(ctx context.Context, filter gokick.ChannelListFilter) (gokick.ChannelsResponseWrapper, error) {
return gokick.ChannelsResponseWrapper{
Result: []gokick.ChannelResponse{{
BannerPicture: "BannerPicture",
BroadcasterUserID: 1,
Category: gokick.CategoryResponse{
ID: 2,
Name: "Name",
Thumbnail: "Thumbnail",
},
ChannelDescription: "ChannelDescription",
Slug: "Slug",
Stream: gokick.StreamResponse{
Key: "Key",
URL: "URL",
IsLive: true,
IsMature: false,
Language: "Language",
StartTime: "StartTime",
Thumbnail: "Thumbnail",
ViewerCount: 3,
},
StreamTitle: "StreamTitle",
}},
}, nil
}
func (clientMock) SendChatMessage(
ctx context.Context,
broadcasterUserID *int,
content string,
replyToMessageID *string,
messageType gokick.MessageType,
) (gokick.ChatResponseWrapper, error) {
return gokick.ChatResponseWrapper{
Result: gokick.ChatResponse{
IsSent: true,
MessageID: "MessageID",
},
}, nil
}
func (clientMock) BanUser(
ctx context.Context,
broadcasterUserID int,
userID int,
duration *int,
reason *string,
) (gokick.BanUserResponseWrapper, error) {
return gokick.BanUserResponseWrapper{
Result: gokick.BanUserResponse{},
}, nil
}

View File

@@ -23,6 +23,10 @@ import (
"github.com/xaionaro-go/xsync"
)
const (
debugUseMockClient = false
)
type ReverseEngClient interface {
ChatClientOBSOLETE
}
@@ -31,7 +35,7 @@ type Kick struct {
CloseCtx context.Context
CloseFn context.CancelFunc
Channel *kickcom.ChannelV1
Client *gokick.Client
Client *Client
ClientOBSOLETE *kickcom.Kick
ChatHandler *ChatHandlerOBSOLETE
ChatHandlerLocker xsync.CtxLocker
@@ -57,36 +61,50 @@ func New(
return nil, fmt.Errorf("channel is not set")
}
options := &gokick.ClientOptions{
UserAccessToken: cfg.Config.UserAccessToken.Get(),
UserRefreshToken: cfg.Config.RefreshToken.Get(),
ClientID: cfg.Config.ClientID,
ClientSecret: cfg.Config.ClientSecret.Get(),
}
client, err := gokick.NewClient(options)
if err != nil {
return nil, fmt.Errorf("unable to initialize a client to Kick: %w", err)
}
clientOld, err := kickcom.New()
if err != nil {
return nil, fmt.Errorf("unable to initialize the old client: %w", err)
}
client, err := getClient(cfg.Config)
if err != nil {
return nil, fmt.Errorf("unable to initialize the client: %w", err)
}
ctx, closeFn := context.WithCancel(ctx)
k := &Kick{
CloseCtx: ctx,
CloseFn: closeFn,
ChatHandlerLocker: make(xsync.CtxLocker, 1),
CurrentConfig: cfg,
Client: client,
ClientOBSOLETE: clientOld,
SaveCfgFn: saveCfgFn,
}
k.SetClient(client)
client.OnUserAccessTokenRefreshed(k.onUserAccessTokenRefreshed)
return k, nil
}
func getClient(
cfg PlatformSpecificConfig,
) (Client, error) {
if debugUseMockClient {
return newClientMock(), nil
}
client, err := newClient(&gokick.ClientOptions{
UserAccessToken: cfg.UserAccessToken.Get(),
UserRefreshToken: cfg.RefreshToken.Get(),
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret.Get(),
})
if err != nil {
return nil, err
}
return client, nil
}
func (k *Kick) onUserAccessTokenRefreshed(
userAccessToken string,
refreshToken string,
@@ -230,7 +248,7 @@ func (k *Kick) getAccessTokenNoLock(
gokick.ScopeModerationBan,
}
logger.Debugf(ctx, "scopes: %v", scopes)
authURL, err := k.getClient().GetAuthorize(
authURL, err := k.GetClient().GetAuthorize(
redirectURL,
"EMPTY",
codeChallenge,
@@ -248,7 +266,7 @@ func (k *Kick) getAccessTokenNoLock(
code string,
) error {
now := time.Now()
token, err := k.getClient().GetToken(ctx, redirectURL, code, codeVerifier)
token, err := k.GetClient().GetToken(ctx, redirectURL, code, codeVerifier)
if err != nil {
return fmt.Errorf("unable to get an access token: %w", err)
}
@@ -297,7 +315,7 @@ func (k *Kick) SetTitle(ctx context.Context, title string) (err error) {
return fmt.Errorf("unable to get a prepared client: %w", err)
}
_, err = k.getClient().UpdateStreamTitle(ctx, title)
_, err = k.GetClient().UpdateStreamTitle(ctx, title)
return
}
@@ -391,7 +409,7 @@ func (k *Kick) getStreamStatusUsingNormalClient(
logger.Debugf(ctx, "getStreamStatusUsingNormalClient")
defer func() { logger.Debugf(ctx, "/getStreamStatusUsingNormalClient: %v %v", _ret, _err) }()
resp, err := k.getClient().GetChannels(
resp, err := k.GetClient().GetChannels(
ctx,
gokick.NewChannelListFilter().SetBroadcasterUserIDs([]int{int(k.Channel.UserID)}),
)
@@ -500,7 +518,7 @@ func (k *Kick) GetChatMessagesChan(
func (k *Kick) SendChatMessage(ctx context.Context, message string) (_err error) {
logger.Debugf(ctx, "SendChatMessage(ctx, '%s')", message)
defer func() { logger.Debugf(ctx, "/SendChatMessage(ctx, '%s'): %v", message, _err) }()
resp, err := k.Client.SendChatMessage(ctx, ptr(int(k.Channel.UserID)), message, nil, gokick.MessageTypeUser)
resp, err := k.GetClient().SendChatMessage(ctx, ptr(int(k.Channel.UserID)), message, nil, gokick.MessageTypeUser)
logger.Debugf(ctx, "SendChatMessage(ctx, '%s'): %#+v", message, resp)
return err
}
@@ -535,7 +553,7 @@ func (k *Kick) BanUser(
return nil
}
}
resp, err := k.Client.BanUser(ctx, int(k.Channel.UserID), int(userIDInt), duration, reasonPtr)
resp, err := k.GetClient().BanUser(ctx, int(k.Channel.UserID), int(userIDInt), duration, reasonPtr)
logger.Debugf(ctx, "BanUser(ctx, %d, '%s', %v): %#+v", userID, reason, deadline, resp)
return err
}
@@ -556,7 +574,7 @@ func (k *Kick) ApplyProfile(
if profile.CategoryID != nil {
logger.Debugf(ctx, "has a CategoryID")
_, err := k.getClient().UpdateStreamCategory(ctx, int(*profile.CategoryID))
_, err := k.GetClient().UpdateStreamCategory(ctx, int(*profile.CategoryID))
if err != nil {
result = append(result, fmt.Errorf("unable to update the category: %w", err))
}

View File

@@ -2,13 +2,12 @@ package kick
import (
"github.com/go-ng/xatomic"
"github.com/scorfly/gokick"
)
func (k *Kick) getClient() *gokick.Client {
return xatomic.LoadPointer(&k.Client)
func (k *Kick) GetClient() Client {
return *xatomic.LoadPointer(&k.Client)
}
func (k *Kick) setClient(client *gokick.Client) {
xatomic.StorePointer(&k.Client, client)
func (k *Kick) SetClient(client Client) {
xatomic.StorePointer(&k.Client, &client)
}

View File

@@ -5,13 +5,13 @@ import (
"fmt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/nicklaw5/helix/v2"
"github.com/xaionaro-go/streamctl/pkg/secret"
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
)
func NewTokenByApp(
ctx context.Context,
client *helix.Client,
client twitch.Client,
) (secret.String, error) {
logger.Debugf(ctx, "getNewTokenByApp")
defer func() { logger.Debugf(ctx, "/getNewTokenByApp") }()

View File

@@ -5,14 +5,14 @@ import (
"fmt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/nicklaw5/helix/v2"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/secret"
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
)
func NewTokenByUser(
ctx context.Context,
client *helix.Client,
client twitch.Client,
clientCode secret.String,
) (secret.String, secret.String, error) {
logger.Debugf(ctx, "getNewTokenByUser")

View File

@@ -16,9 +16,10 @@ import (
"github.com/nicklaw5/helix/v2"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
)
type OAuthHandler func(context.Context, oauthhandler.OAuthHandlerArgument) error
type OAuthHandler = twitch.OAuthHandler
func getScopes() []string {
scopes := map[string]struct{}{

View File

@@ -0,0 +1,12 @@
package twitch
import (
"github.com/nicklaw5/helix/v2"
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
)
type client = twitch.Client
type clientNicklaw5 struct {
*helix.Client
}

View File

@@ -0,0 +1,155 @@
package twitch
import (
"time"
"github.com/nicklaw5/helix/v2"
)
type clientMock struct{}
func newClientMock() *clientMock {
return &clientMock{}
}
var _ client = (*clientMock)(nil)
func (c *clientMock) GetAppAccessToken() string {
return ""
}
func (c *clientMock) GetUserAccessToken() string {
return ""
}
func (c *clientMock) GetRefreshToken() string {
return ""
}
func (c *clientMock) SetAppAccessToken(accessToken string) {}
func (c *clientMock) SetUserAccessToken(accessToken string) {}
func (c *clientMock) SetRefreshToken(refreshToken string) {}
func (c *clientMock) OnUserAccessTokenRefreshed(f func(newAccessToken, newRefreshToken string)) {}
func (c *clientMock) GetChannelInformation(params *helix.GetChannelInformationParams) (*helix.GetChannelInformationResponse, error) {
return &helix.GetChannelInformationResponse{
Data: helix.ManyChannelInformation{
Channels: []helix.ChannelInformation{{
BroadcasterID: "BroadcasterID",
BroadcasterName: "BroadcasterName",
BroadcasterLanguage: "BroadcasterLanguage",
GameID: "GameID",
GameName: "GameName",
Title: "Title",
Delay: 1,
Tags: []string{"Tag"},
}},
},
}, nil
}
func (c *clientMock) RequestUserAccessToken(code string) (*helix.UserAccessTokenResponse, error) {
return &helix.UserAccessTokenResponse{}, nil
}
func (c *clientMock) RequestAppAccessToken(scopes []string) (*helix.AppAccessTokenResponse, error) {
return &helix.AppAccessTokenResponse{}, nil
}
func (c *clientMock) EditChannelInformation(params *helix.EditChannelInformationParams) (*helix.EditChannelInformationResponse, error) {
return &helix.EditChannelInformationResponse{}, nil
}
func (c *clientMock) GetGames(params *helix.GamesParams) (*helix.GamesResponse, error) {
return &helix.GamesResponse{
Data: helix.ManyGames{
Games: []helix.Game{{
ID: "ID",
Name: "Name",
BoxArtURL: "BoxArtURL",
}},
},
}, nil
}
func (c *clientMock) GetStreams(params *helix.StreamsParams) (*helix.StreamsResponse, error) {
return &helix.StreamsResponse{
Data: helix.ManyStreams{
Streams: []helix.Stream{{
ID: "ID",
UserID: "UserID",
UserLogin: "UserLogin",
UserName: "UserName",
GameID: "GameID",
GameName: "GameName",
TagIDs: []string{"TagID"},
Tags: []string{"Tag"},
IsMature: false,
Type: "Type",
Title: "Title",
ViewerCount: 1,
StartedAt: time.Now(),
Language: "Language",
ThumbnailURL: "ThumbnailURL",
}},
},
}, nil
}
func (c *clientMock) GetTopGames(params *helix.TopGamesParams) (*helix.TopGamesResponse, error) {
return &helix.TopGamesResponse{
Data: helix.ManyGamesWithPagination{
ManyGames: helix.ManyGames{
Games: []helix.Game{{
ID: "ID",
Name: "Name",
BoxArtURL: "BoxArtURL",
}},
},
},
}, nil
}
func (c *clientMock) SendChatMessage(params *helix.SendChatMessageParams) (*helix.ChatMessageResponse, error) {
return &helix.ChatMessageResponse{}, nil
}
func (c *clientMock) DeleteChatMessage(params *helix.DeleteChatMessageParams) (*helix.DeleteChatMessageResponse, error) {
return &helix.DeleteChatMessageResponse{}, nil
}
func (c *clientMock) BanUser(params *helix.BanUserParams) (*helix.BanUserResponse, error) {
return &helix.BanUserResponse{}, nil
}
func (c *clientMock) StartRaid(params *helix.StartRaidParams) (*helix.RaidResponse, error) {
return &helix.RaidResponse{}, nil
}
func (c *clientMock) SendShoutout(params *helix.SendShoutoutParams) (*helix.SendShoutoutResponse, error) {
return &helix.SendShoutoutResponse{}, nil
}
func (c *clientMock) CreateEventSubSubscription(payload *helix.EventSubSubscription) (*helix.EventSubSubscriptionsResponse, error) {
return &helix.EventSubSubscriptionsResponse{}, nil
}
func (c *clientMock) GetUsers(params *helix.UsersParams) (*helix.UsersResponse, error) {
return &helix.UsersResponse{
Data: helix.ManyUsers{
Users: []helix.User{{
ID: "ID",
Login: "Login",
DisplayName: "DisplayName",
Type: "Type",
BroadcasterType: "BroadcasterType",
Description: "Description",
ProfileImageURL: "ProfileImageURL",
OfflineImageURL: "OfflineImageURL",
ViewCount: 1,
Email: "Email",
CreatedAt: helix.Time{Time: time.Now()},
}},
},
}, nil
}

View File

@@ -28,7 +28,7 @@ type Twitch struct {
closeFn context.CancelFunc
chatHandlerSub *ChatHandlerSub
chatHandlerIRC *ChatHandlerIRC
client *helix.Client
client client
config Config
broadcasterID string
lazyInitOnce sync.Once
@@ -39,7 +39,10 @@ type Twitch struct {
clientSecret secret.String
}
const twitchDebug = false
const (
twitchDebug = false
debugUseMockClient = false
)
var _ streamcontrol.StreamController[StreamProfile] = (*Twitch)(nil)
@@ -141,7 +144,7 @@ func New(
func GetUserID(
_ context.Context,
client *helix.Client,
client client,
login string,
) (string, error) {
resp, err := client.GetUsers(&helix.UsersParams{
@@ -613,10 +616,13 @@ func (t *Twitch) getNewTokenByApp(
func (t *Twitch) getClient(
ctx context.Context,
oauthListenPort uint16,
) (*helix.Client, error) {
) (client, error) {
logger.Debugf(ctx, "getClient(ctx, %#+v, %v)", t.config, oauthListenPort)
defer func() { logger.Debugf(ctx, "/getClient") }()
if debugUseMockClient {
return newClientMock(), nil
}
options := &helix.Options{
ClientID: t.clientID,
ClientSecret: t.clientSecret.Get(),

View File

@@ -0,0 +1,29 @@
package twitch
import (
"github.com/nicklaw5/helix/v2"
)
type Client interface {
GetAppAccessToken() string
GetUserAccessToken() string
GetRefreshToken() string
SetAppAccessToken(accessToken string)
SetUserAccessToken(accessToken string)
SetRefreshToken(refreshToken string)
OnUserAccessTokenRefreshed(f func(newAccessToken, newRefreshToken string))
GetChannelInformation(params *helix.GetChannelInformationParams) (*helix.GetChannelInformationResponse, error)
RequestUserAccessToken(code string) (*helix.UserAccessTokenResponse, error)
RequestAppAccessToken(scopes []string) (*helix.AppAccessTokenResponse, error)
EditChannelInformation(params *helix.EditChannelInformationParams) (*helix.EditChannelInformationResponse, error)
GetGames(params *helix.GamesParams) (*helix.GamesResponse, error)
GetStreams(params *helix.StreamsParams) (*helix.StreamsResponse, error)
GetTopGames(params *helix.TopGamesParams) (*helix.TopGamesResponse, error)
SendChatMessage(params *helix.SendChatMessageParams) (*helix.ChatMessageResponse, error)
DeleteChatMessage(params *helix.DeleteChatMessageParams) (*helix.DeleteChatMessageResponse, error)
BanUser(params *helix.BanUserParams) (*helix.BanUserResponse, error)
StartRaid(params *helix.StartRaidParams) (*helix.RaidResponse, error)
SendShoutout(params *helix.SendShoutoutParams) (*helix.SendShoutoutResponse, error)
CreateEventSubSubscription(payload *helix.EventSubSubscription) (*helix.EventSubSubscriptionsResponse, error)
GetUsers(params *helix.UsersParams) (*helix.UsersResponse, error)
}

View File

@@ -4,13 +4,10 @@ import (
"github.com/xaionaro-go/streamctl/pkg/buildvars"
"github.com/xaionaro-go/streamctl/pkg/secret"
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/auth"
)
const ID = streamctl.PlatformName("twitch")
type OAuthHandler = auth.OAuthHandler
type PlatformSpecificConfig struct {
Channel string
ClientID string

View File

@@ -0,0 +1,9 @@
package twitch
import (
"context"
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
)
type OAuthHandler func(context.Context, oauthhandler.OAuthHandlerArgument) error

View File

@@ -17,20 +17,20 @@ import (
type ChatListener struct {
videoID string
liveChatID string
client YouTubeChatClient
client ChatClient
wg sync.WaitGroup
cancelFunc context.CancelFunc
messagesOutChan chan streamcontrol.ChatMessage
}
type YouTubeChatClient interface {
type ChatClient interface {
GetLiveChatMessages(ctx context.Context, chatID string, pageToken string, parts []string) (*youtube.LiveChatMessageListResponse, error)
}
func NewChatListener(
ctx context.Context,
ytClient YouTubeChatClient,
ytClient ChatClient,
videoID string,
liveChatID string,
) (*ChatListener, error) {

View File

@@ -36,8 +36,8 @@ func (t BroadcastType) String() string {
return fmt.Sprintf("unexpected_value_%d", int(t))
}
type YouTubeClient interface {
YouTubeChatClient
type client interface {
ChatClient
Ping(context.Context) error
GetBroadcasts(ctx context.Context, t BroadcastType, ids []string, parts []string, pageToken string) (*youtube.LiveBroadcastListResponse, error)
UpdateBroadcast(context.Context, *youtube.LiveBroadcast, []string) error
@@ -65,7 +65,7 @@ const (
EventTypeUpcoming = EventType("upcoming")
)
type YouTubeClientV3 struct {
type clientV3 struct {
*youtube.Service
RequestWrapper func(context.Context, func(context.Context) error) error
}
@@ -106,24 +106,24 @@ func wrapRequestR[T any](
return
}
var _ YouTubeClient = (*YouTubeClientV3)(nil)
var _ client = (*clientV3)(nil)
func NewYouTubeClientV3(
func newClientV3(
ctx context.Context,
requestWrapper func(context.Context, func(context.Context) error) error,
opts ...option.ClientOption,
) (*YouTubeClientV3, error) {
) (*clientV3, error) {
srv, err := youtube.NewService(ctx, opts...)
if err != nil {
return nil, err
}
return &YouTubeClientV3{
return &clientV3{
Service: srv,
RequestWrapper: requestWrapper,
}, nil
}
func (c *YouTubeClientV3) Ping(
func (c *clientV3) Ping(
ctx context.Context,
) (_err error) {
logger.Tracef(ctx, "Ping")
@@ -141,7 +141,7 @@ func (c *YouTubeClientV3) Ping(
return err
}
func (c *YouTubeClientV3) GetBroadcasts(
func (c *clientV3) GetBroadcasts(
ctx context.Context,
t BroadcastType,
ids []string,
@@ -171,7 +171,7 @@ func (c *YouTubeClientV3) GetBroadcasts(
return wrapRequestR(ctx, c.RequestWrapper, r.Do, googleapi.QueryParameter("order", "date"))
}
func (c *YouTubeClientV3) UpdateBroadcast(
func (c *clientV3) UpdateBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -182,7 +182,7 @@ func (c *YouTubeClientV3) UpdateBroadcast(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) InsertBroadcast(
func (c *clientV3) InsertBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -192,7 +192,7 @@ func (c *YouTubeClientV3) InsertBroadcast(
return c.Service.LiveBroadcasts.Insert(parts, broadcast).Context(ctx).Do()
}
func (c *YouTubeClientV3) DeleteBroadcast(
func (c *clientV3) DeleteBroadcast(
ctx context.Context,
broadcastID string,
) (_err error) {
@@ -202,7 +202,7 @@ func (c *YouTubeClientV3) DeleteBroadcast(
return wrapRequest(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) InsertCuepoint(
func (c *clientV3) InsertCuepoint(
ctx context.Context,
p *youtube.Cuepoint,
) (_err error) {
@@ -212,7 +212,7 @@ func (c *YouTubeClientV3) InsertCuepoint(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) GetVideos(
func (c *clientV3) GetVideos(
ctx context.Context,
broadcastIDs []string,
parts []string,
@@ -223,7 +223,7 @@ func (c *YouTubeClientV3) GetVideos(
return wrapRequestR(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) UpdateVideo(
func (c *clientV3) UpdateVideo(
ctx context.Context,
video *youtube.Video,
parts []string,
@@ -234,7 +234,7 @@ func (c *YouTubeClientV3) UpdateVideo(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) GetPlaylists(
func (c *clientV3) GetPlaylists(
ctx context.Context,
parts []string,
) (_ret *youtube.PlaylistListResponse, _err error) {
@@ -244,7 +244,7 @@ func (c *YouTubeClientV3) GetPlaylists(
return wrapRequestR(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) GetPlaylistItems(
func (c *clientV3) GetPlaylistItems(
ctx context.Context,
playlistID string,
videoID string,
@@ -256,7 +256,7 @@ func (c *YouTubeClientV3) GetPlaylistItems(
return wrapRequestR(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) InsertPlaylistItem(
func (c *clientV3) InsertPlaylistItem(
ctx context.Context,
item *youtube.PlaylistItem,
parts []string,
@@ -267,7 +267,7 @@ func (c *YouTubeClientV3) InsertPlaylistItem(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) SetThumbnail(
func (c *clientV3) SetThumbnail(
ctx context.Context,
broadcastID string,
thumbnail io.Reader,
@@ -278,7 +278,7 @@ func (c *YouTubeClientV3) SetThumbnail(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) GetStreams(
func (c *clientV3) GetStreams(
ctx context.Context,
parts []string,
) (_ret *youtube.LiveStreamListResponse, _err error) {
@@ -288,7 +288,7 @@ func (c *YouTubeClientV3) GetStreams(
return wrapRequestR(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) InsertCommentThread(
func (c *clientV3) InsertCommentThread(
ctx context.Context,
t *youtube.CommentThread,
parts []string,
@@ -299,7 +299,7 @@ func (c *YouTubeClientV3) InsertCommentThread(
return wrapRequestS(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) ListChatMessages(
func (c *clientV3) ListChatMessages(
ctx context.Context,
chatID string,
parts []string,
@@ -310,7 +310,7 @@ func (c *YouTubeClientV3) ListChatMessages(
return wrapRequestR(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) DeleteChatMessage(
func (c *clientV3) DeleteChatMessage(
ctx context.Context,
messageID string,
) (_err error) {
@@ -320,7 +320,7 @@ func (c *YouTubeClientV3) DeleteChatMessage(
return wrapRequest(ctx, c.RequestWrapper, do)
}
func (c *YouTubeClientV3) GetLiveChatMessages(
func (c *clientV3) GetLiveChatMessages(
ctx context.Context,
chatID string,
pageToken string,
@@ -335,7 +335,7 @@ func (c *YouTubeClientV3) GetLiveChatMessages(
return q.Do()
}
func (c *YouTubeClientV3) Search(
func (c *clientV3) Search(
ctx context.Context,
chanID string,
eventType EventType,

View File

@@ -26,17 +26,17 @@ func init() {
}
// see also: https://developers.google.com/youtube/v3/determine_quota_cost
type YouTubeClientCalcPoints struct {
Client YouTubeClient
type ClientCalcPoints struct {
Client client
UsedPoints atomic.Uint64
CheckMutex sync.Mutex
PreviousCheckAt time.Time
}
var _ YouTubeClient = (*YouTubeClientCalcPoints)(nil)
var _ client = (*ClientCalcPoints)(nil)
func NewYouTubeClientCalcPoints(client YouTubeClient) *YouTubeClientCalcPoints {
return &YouTubeClientCalcPoints{
func NewYouTubeClientCalcPoints(client client) *ClientCalcPoints {
return &ClientCalcPoints{
Client: client,
}
}
@@ -45,7 +45,7 @@ func getQuotaCutoffDate(t time.Time) string {
return t.In(tzLosAngeles).Format("2006-01-02")
}
func (c *YouTubeClientCalcPoints) addUsedPointsIfNoError(
func (c *ClientCalcPoints) addUsedPointsIfNoError(
ctx context.Context,
points uint,
err error,
@@ -71,12 +71,12 @@ func (c *YouTubeClientCalcPoints) addUsedPointsIfNoError(
}
}
func (c *YouTubeClientCalcPoints) Ping(ctx context.Context) (_err error) {
func (c *ClientCalcPoints) Ping(ctx context.Context) (_err error) {
defer func() { c.addUsedPointsIfNoError(ctx, 1, _err) }()
return c.Client.Ping(ctx)
}
func (c *YouTubeClientCalcPoints) GetBroadcasts(
func (c *ClientCalcPoints) GetBroadcasts(
ctx context.Context,
t BroadcastType,
ids []string,
@@ -87,7 +87,7 @@ func (c *YouTubeClientCalcPoints) GetBroadcasts(
return c.Client.GetBroadcasts(ctx, t, ids, parts, pageToken)
}
func (c *YouTubeClientCalcPoints) UpdateBroadcast(
func (c *ClientCalcPoints) UpdateBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -96,7 +96,7 @@ func (c *YouTubeClientCalcPoints) UpdateBroadcast(
return c.Client.UpdateBroadcast(ctx, broadcast, parts)
}
func (c *YouTubeClientCalcPoints) InsertBroadcast(
func (c *ClientCalcPoints) InsertBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -105,7 +105,7 @@ func (c *YouTubeClientCalcPoints) InsertBroadcast(
return c.Client.InsertBroadcast(ctx, broadcast, parts)
}
func (c *YouTubeClientCalcPoints) DeleteBroadcast(
func (c *ClientCalcPoints) DeleteBroadcast(
ctx context.Context,
broadcastID string,
) (_err error) {
@@ -113,7 +113,7 @@ func (c *YouTubeClientCalcPoints) DeleteBroadcast(
return c.Client.DeleteBroadcast(ctx, broadcastID)
}
func (c *YouTubeClientCalcPoints) GetStreams(
func (c *ClientCalcPoints) GetStreams(
ctx context.Context,
parts []string,
) (_ret *youtube.LiveStreamListResponse, _err error) {
@@ -121,7 +121,7 @@ func (c *YouTubeClientCalcPoints) GetStreams(
return c.Client.GetStreams(ctx, parts)
}
func (c *YouTubeClientCalcPoints) GetVideos(
func (c *ClientCalcPoints) GetVideos(
ctx context.Context,
broadcastIDs []string,
parts []string,
@@ -130,7 +130,7 @@ func (c *YouTubeClientCalcPoints) GetVideos(
return c.Client.GetVideos(ctx, broadcastIDs, parts)
}
func (c *YouTubeClientCalcPoints) UpdateVideo(
func (c *ClientCalcPoints) UpdateVideo(
ctx context.Context,
video *youtube.Video,
parts []string,
@@ -139,7 +139,7 @@ func (c *YouTubeClientCalcPoints) UpdateVideo(
return c.Client.UpdateVideo(ctx, video, parts)
}
func (c *YouTubeClientCalcPoints) InsertCuepoint(
func (c *ClientCalcPoints) InsertCuepoint(
ctx context.Context,
cuepoint *youtube.Cuepoint,
) (_err error) {
@@ -147,7 +147,7 @@ func (c *YouTubeClientCalcPoints) InsertCuepoint(
return c.Client.InsertCuepoint(ctx, cuepoint)
}
func (c *YouTubeClientCalcPoints) GetPlaylists(
func (c *ClientCalcPoints) GetPlaylists(
ctx context.Context,
playlistParts []string,
) (_ret *youtube.PlaylistListResponse, _err error) {
@@ -155,7 +155,7 @@ func (c *YouTubeClientCalcPoints) GetPlaylists(
return c.Client.GetPlaylists(ctx, playlistParts)
}
func (c *YouTubeClientCalcPoints) GetPlaylistItems(
func (c *ClientCalcPoints) GetPlaylistItems(
ctx context.Context,
playlistID string,
videoID string,
@@ -165,7 +165,7 @@ func (c *YouTubeClientCalcPoints) GetPlaylistItems(
return c.Client.GetPlaylistItems(ctx, playlistID, videoID, parts)
}
func (c *YouTubeClientCalcPoints) InsertPlaylistItem(
func (c *ClientCalcPoints) InsertPlaylistItem(
ctx context.Context,
item *youtube.PlaylistItem,
parts []string,
@@ -174,7 +174,7 @@ func (c *YouTubeClientCalcPoints) InsertPlaylistItem(
return c.Client.InsertPlaylistItem(ctx, item, parts)
}
func (c *YouTubeClientCalcPoints) SetThumbnail(
func (c *ClientCalcPoints) SetThumbnail(
ctx context.Context,
broadcastID string,
thumbnail io.Reader,
@@ -183,7 +183,7 @@ func (c *YouTubeClientCalcPoints) SetThumbnail(
return c.Client.SetThumbnail(ctx, broadcastID, thumbnail)
}
func (c *YouTubeClientCalcPoints) InsertCommentThread(
func (c *ClientCalcPoints) InsertCommentThread(
ctx context.Context,
t *youtube.CommentThread,
parts []string,
@@ -192,7 +192,7 @@ func (c *YouTubeClientCalcPoints) InsertCommentThread(
return c.Client.InsertCommentThread(ctx, t, parts)
}
func (c *YouTubeClientCalcPoints) ListChatMessages(
func (c *ClientCalcPoints) ListChatMessages(
ctx context.Context,
chatID string,
parts []string,
@@ -201,7 +201,7 @@ func (c *YouTubeClientCalcPoints) ListChatMessages(
return c.Client.ListChatMessages(ctx, chatID, parts)
}
func (c *YouTubeClientCalcPoints) DeleteChatMessage(
func (c *ClientCalcPoints) DeleteChatMessage(
ctx context.Context,
messageID string,
) (_err error) {
@@ -209,7 +209,7 @@ func (c *YouTubeClientCalcPoints) DeleteChatMessage(
return c.Client.DeleteChatMessage(ctx, messageID)
}
func (c *YouTubeClientCalcPoints) GetLiveChatMessages(
func (c *ClientCalcPoints) GetLiveChatMessages(
ctx context.Context,
chatID string,
pageToken string,
@@ -219,7 +219,7 @@ func (c *YouTubeClientCalcPoints) GetLiveChatMessages(
return c.Client.GetLiveChatMessages(ctx, chatID, pageToken, parts)
}
func (c *YouTubeClientCalcPoints) Search(
func (c *ClientCalcPoints) Search(
ctx context.Context,
chanID string,
eventType EventType,

View File

@@ -9,21 +9,21 @@ import (
"google.golang.org/api/youtube/v3"
)
type YouTubeClientMock struct{}
type clientMock struct{}
var _ YouTubeClient = (*YouTubeClientMock)(nil)
var _ client = (*clientMock)(nil)
func NewYouTubeClientMock() *YouTubeClientMock {
return &YouTubeClientMock{}
func newClientMock() *clientMock {
return &clientMock{}
}
func (c *YouTubeClientMock) Ping(ctx context.Context) (_err error) {
func (c *clientMock) Ping(ctx context.Context) (_err error) {
logger.Tracef(ctx, "Ping")
defer func() { logger.Tracef(ctx, "/Ping: %v", _err) }()
return nil
}
func (c *YouTubeClientMock) GetBroadcasts(
func (c *clientMock) GetBroadcasts(
ctx context.Context,
t BroadcastType,
ids []string,
@@ -39,7 +39,7 @@ func (c *YouTubeClientMock) GetBroadcasts(
}, nil
}
func (c *YouTubeClientMock) UpdateBroadcast(
func (c *clientMock) UpdateBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -49,7 +49,7 @@ func (c *YouTubeClientMock) UpdateBroadcast(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) InsertBroadcast(
func (c *clientMock) InsertBroadcast(
ctx context.Context,
broadcast *youtube.LiveBroadcast,
parts []string,
@@ -59,7 +59,7 @@ func (c *YouTubeClientMock) InsertBroadcast(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) DeleteBroadcast(
func (c *clientMock) DeleteBroadcast(
ctx context.Context,
broadcastID string,
) (_err error) {
@@ -68,7 +68,7 @@ func (c *YouTubeClientMock) DeleteBroadcast(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) GetStreams(
func (c *clientMock) GetStreams(
ctx context.Context,
parts []string,
) (_ret *youtube.LiveStreamListResponse, _err error) {
@@ -80,7 +80,7 @@ func (c *YouTubeClientMock) GetStreams(
}, nil
}
func (c *YouTubeClientMock) GetVideos(
func (c *clientMock) GetVideos(
ctx context.Context,
broadcastIDs []string,
parts []string,
@@ -90,7 +90,7 @@ func (c *YouTubeClientMock) GetVideos(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) UpdateVideo(
func (c *clientMock) UpdateVideo(
ctx context.Context,
video *youtube.Video,
parts []string,
@@ -100,7 +100,7 @@ func (c *YouTubeClientMock) UpdateVideo(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) InsertCuepoint(
func (c *clientMock) InsertCuepoint(
ctx context.Context,
cuepoint *youtube.Cuepoint,
) (_err error) {
@@ -109,7 +109,7 @@ func (c *YouTubeClientMock) InsertCuepoint(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) GetPlaylists(
func (c *clientMock) GetPlaylists(
ctx context.Context,
playlistParts []string,
) (_ret *youtube.PlaylistListResponse, _err error) {
@@ -118,7 +118,7 @@ func (c *YouTubeClientMock) GetPlaylists(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) GetPlaylistItems(
func (c *clientMock) GetPlaylistItems(
ctx context.Context,
playlistID string,
videoID string,
@@ -129,7 +129,7 @@ func (c *YouTubeClientMock) GetPlaylistItems(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) InsertPlaylistItem(
func (c *clientMock) InsertPlaylistItem(
ctx context.Context,
item *youtube.PlaylistItem,
parts []string,
@@ -139,7 +139,7 @@ func (c *YouTubeClientMock) InsertPlaylistItem(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) SetThumbnail(
func (c *clientMock) SetThumbnail(
ctx context.Context,
broadcastID string,
thumbnail io.Reader,
@@ -149,7 +149,7 @@ func (c *YouTubeClientMock) SetThumbnail(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) InsertCommentThread(
func (c *clientMock) InsertCommentThread(
ctx context.Context,
t *youtube.CommentThread,
parts []string,
@@ -159,7 +159,7 @@ func (c *YouTubeClientMock) InsertCommentThread(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) ListChatMessages(
func (c *clientMock) ListChatMessages(
ctx context.Context,
chatID string,
parts []string,
@@ -169,7 +169,7 @@ func (c *YouTubeClientMock) ListChatMessages(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) DeleteChatMessage(
func (c *clientMock) DeleteChatMessage(
ctx context.Context,
messageID string,
) (_err error) {
@@ -178,7 +178,7 @@ func (c *YouTubeClientMock) DeleteChatMessage(
return fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) GetLiveChatMessages(
func (c *clientMock) GetLiveChatMessages(
ctx context.Context,
chatID string,
pageToken string,
@@ -189,7 +189,7 @@ func (c *YouTubeClientMock) GetLiveChatMessages(
return nil, fmt.Errorf("not implemented")
}
func (c *YouTubeClientMock) Search(
func (c *clientMock) Search(
ctx context.Context,
chanID string,
eventType EventType,

View File

@@ -41,7 +41,7 @@ const (
type YouTube struct {
locker xsync.Mutex
Config Config
YouTubeClient *YouTubeClientCalcPoints
YouTubeClient *ClientCalcPoints
CancelFunc context.CancelFunc
SaveConfigFunc func(Config) error
@@ -208,12 +208,12 @@ func (yt *YouTube) initNoLock(ctx context.Context) (_err error) {
return fmt.Errorf("the token is invalid: %w", err)
}
var youtubeClient YouTubeClient
var youtubeClient client
if debugUseMockClient {
youtubeClient = NewYouTubeClientMock()
youtubeClient = newClientMock()
} else {
var err error
youtubeClient, err = NewYouTubeClientV3(
youtubeClient, err = newClientV3(
ctx,
yt.wrapRequest,
option.WithTokenSource(tokenSource),