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

View File

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

View File

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

View File

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

View File

@@ -16,9 +16,10 @@ import (
"github.com/nicklaw5/helix/v2" "github.com/nicklaw5/helix/v2"
"github.com/xaionaro-go/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/oauthhandler" "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 { func getScopes() []string {
scopes := map[string]struct{}{ 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 closeFn context.CancelFunc
chatHandlerSub *ChatHandlerSub chatHandlerSub *ChatHandlerSub
chatHandlerIRC *ChatHandlerIRC chatHandlerIRC *ChatHandlerIRC
client *helix.Client client client
config Config config Config
broadcasterID string broadcasterID string
lazyInitOnce sync.Once lazyInitOnce sync.Once
@@ -39,7 +39,10 @@ type Twitch struct {
clientSecret secret.String clientSecret secret.String
} }
const twitchDebug = false const (
twitchDebug = false
debugUseMockClient = false
)
var _ streamcontrol.StreamController[StreamProfile] = (*Twitch)(nil) var _ streamcontrol.StreamController[StreamProfile] = (*Twitch)(nil)
@@ -141,7 +144,7 @@ func New(
func GetUserID( func GetUserID(
_ context.Context, _ context.Context,
client *helix.Client, client client,
login string, login string,
) (string, error) { ) (string, error) {
resp, err := client.GetUsers(&helix.UsersParams{ resp, err := client.GetUsers(&helix.UsersParams{
@@ -613,10 +616,13 @@ func (t *Twitch) getNewTokenByApp(
func (t *Twitch) getClient( func (t *Twitch) getClient(
ctx context.Context, ctx context.Context,
oauthListenPort uint16, oauthListenPort uint16,
) (*helix.Client, error) { ) (client, error) {
logger.Debugf(ctx, "getClient(ctx, %#+v, %v)", t.config, oauthListenPort) logger.Debugf(ctx, "getClient(ctx, %#+v, %v)", t.config, oauthListenPort)
defer func() { logger.Debugf(ctx, "/getClient") }() defer func() { logger.Debugf(ctx, "/getClient") }()
if debugUseMockClient {
return newClientMock(), nil
}
options := &helix.Options{ options := &helix.Options{
ClientID: t.clientID, ClientID: t.clientID,
ClientSecret: t.clientSecret.Get(), 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/buildvars"
"github.com/xaionaro-go/streamctl/pkg/secret" "github.com/xaionaro-go/streamctl/pkg/secret"
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol" streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/auth"
) )
const ID = streamctl.PlatformName("twitch") const ID = streamctl.PlatformName("twitch")
type OAuthHandler = auth.OAuthHandler
type PlatformSpecificConfig struct { type PlatformSpecificConfig struct {
Channel string Channel string
ClientID 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 { type ChatListener struct {
videoID string videoID string
liveChatID string liveChatID string
client YouTubeChatClient client ChatClient
wg sync.WaitGroup wg sync.WaitGroup
cancelFunc context.CancelFunc cancelFunc context.CancelFunc
messagesOutChan chan streamcontrol.ChatMessage messagesOutChan chan streamcontrol.ChatMessage
} }
type YouTubeChatClient interface { type ChatClient interface {
GetLiveChatMessages(ctx context.Context, chatID string, pageToken string, parts []string) (*youtube.LiveChatMessageListResponse, error) GetLiveChatMessages(ctx context.Context, chatID string, pageToken string, parts []string) (*youtube.LiveChatMessageListResponse, error)
} }
func NewChatListener( func NewChatListener(
ctx context.Context, ctx context.Context,
ytClient YouTubeChatClient, ytClient ChatClient,
videoID string, videoID string,
liveChatID string, liveChatID string,
) (*ChatListener, error) { ) (*ChatListener, error) {

View File

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

View File

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

View File

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

View File

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