feat: add twitch channel points miner runtime

This commit is contained in:
2026-04-29 22:33:55 +02:00
parent 1147f4d25b
commit b631d201b0
12 changed files with 1640 additions and 19 deletions
+54 -7
View File
@@ -59,6 +59,30 @@ func (f *fakeStreamerService) PlaybackAccessToken(_ context.Context, login strin
return &twitch.PlaybackToken{Signature: "sig", Value: "token"}, nil
}
func (f *fakeStreamerService) LoadChannelPointsContext(context.Context, string) (*twitch.ChannelPointsContext, error) {
return &twitch.ChannelPointsContext{Balance: 0}, nil
}
func (f *fakeStreamerService) ClaimCommunityPoints(context.Context, string, string) error {
return nil
}
func (f *fakeStreamerService) StreamMetadata(context.Context, string) (*twitch.StreamMetadata, error) {
return &twitch.StreamMetadata{BroadcastID: "broadcast"}, nil
}
func (f *fakeStreamerService) FetchSpadeURL(context.Context, string) (string, error) {
return "https://spade.test", nil
}
func (f *fakeStreamerService) TouchPlayback(context.Context, string, *twitch.PlaybackToken) error {
return nil
}
func (f *fakeStreamerService) SendMinuteWatched(context.Context, string, twitch.MinuteWatchedPayload) error {
return nil
}
type fakeIRCSyncer struct {
calls [][]string
}
@@ -71,6 +95,21 @@ func (f *fakeIRCSyncer) Sync(_ context.Context, _, _ string, targets []irc.Targe
f.calls = append(f.calls, logins)
}
type fakeMinerSyncer struct {
calls [][]string
}
func (f *fakeMinerSyncer) Sync(_ context.Context, _ *auth.State, _ *twitch.Viewer, entries []twitch.StreamerEntry) {
logins := make([]string, 0, len(entries))
for _, entry := range entries {
if entry.ChannelID == "" {
continue
}
logins = append(logins, entry.ChannelID)
}
f.calls = append(f.calls, logins)
}
func TestResolveStreamerEntries(t *testing.T) {
service := streamService(
channel("alpha", "1", "alpha_live"),
@@ -78,7 +117,7 @@ func TestResolveStreamerEntries(t *testing.T) {
live("1", true),
)
updates := collectUpdates(t, service, []string{"alpha", "beta"}, nil, cancelAfter(0))
updates := collectUpdates(t, service, []string{"alpha", "beta"}, nil, nil, cancelAfter(0))
if len(updates) != 3 {
t.Fatalf("len(updates) = %d", len(updates))
}
@@ -98,7 +137,7 @@ func TestResolveStreamerEntriesPlaybackFailureStillMarksLive(t *testing.T) {
channel("alpha", "1", "alpha_live"),
live("1", true),
playbackErr("alpha_live", errors.New("token lookup failed")),
), []string{"alpha"}, nil, cancelAfter(0))
), []string{"alpha"}, nil, nil, cancelAfter(0))
if entry := updates[1].Entry; entry == nil || entry.Status != twitch.StreamerReady || !entry.Live {
t.Fatalf("entry = %#v", entry)
@@ -109,7 +148,7 @@ func TestResolveStreamerEntriesRefreshesLiveState(t *testing.T) {
updates := collectUpdates(t, streamService(
channel("alpha", "1", "alpha_live"),
streams("1", false, true),
), []string{"alpha"}, nil, cancelAfter(1))
), []string{"alpha"}, nil, nil, cancelAfter(1))
if len(updates) != 3 {
t.Fatalf("len(updates) = %d", len(updates))
@@ -124,6 +163,7 @@ func TestResolveStreamerEntriesRefreshesLiveState(t *testing.T) {
func TestResolveStreamerEntriesSyncsTopTwoLiveChannels(t *testing.T) {
syncer := &fakeIRCSyncer{}
miner := &fakeMinerSyncer{}
collectUpdates(t, streamService(
channel("alpha", "1", "alpha_live"),
channel("beta", "2", "beta_live"),
@@ -131,7 +171,7 @@ func TestResolveStreamerEntriesSyncsTopTwoLiveChannels(t *testing.T) {
live("1", true),
live("2", true),
live("3", true),
), []string{"alpha", "beta", "gamma"}, syncer, cancelAfter(0))
), []string{"alpha", "beta", "gamma"}, syncer, miner, cancelAfter(0))
assertSyncCalls(t, syncer.calls, [][]string{
{},
@@ -139,16 +179,23 @@ func TestResolveStreamerEntriesSyncsTopTwoLiveChannels(t *testing.T) {
{"alpha_live", "beta_live"},
{"alpha_live", "beta_live"},
})
assertSyncCalls(t, miner.calls, [][]string{
{},
{"1"},
{"1", "2"},
{"1", "2", "3"},
})
}
func TestResolveStreamerEntriesRefreshUpdatesWatchedChannels(t *testing.T) {
syncer := &fakeIRCSyncer{}
miner := &fakeMinerSyncer{}
collectUpdates(t, streamService(
channel("alpha", "1", "alpha_live"),
channel("beta", "2", "beta_live"),
streams("1", true, false),
streams("2", false, true),
), []string{"alpha", "beta"}, syncer, cancelAfter(1))
), []string{"alpha", "beta"}, syncer, miner, cancelAfter(1))
assertSyncCalls(t, syncer.calls, [][]string{
{},
@@ -205,10 +252,10 @@ func playbackErr(login string, err error) serviceOption {
}
}
func collectUpdates(t *testing.T, service *fakeStreamerService, logins []string, syncer ircSyncer, sleep func(context.Context, time.Duration) error) []StreamerUpdate {
func collectUpdates(t *testing.T, service *fakeStreamerService, logins []string, ircSyncer ircSyncer, minerSyncer minerSyncer, sleep func(context.Context, time.Duration) error) []StreamerUpdate {
t.Helper()
var updates []StreamerUpdate
err := resolveStreamerEntriesWithSleep(context.Background(), service, &auth.State{Login: "viewer", AccessToken: "token"}, logins, syncer, func(update StreamerUpdate) {
err := resolveStreamerEntriesWithSleep(context.Background(), service, &auth.State{Login: "viewer", AccessToken: "token"}, logins, ircSyncer, minerSyncer, func(update StreamerUpdate) {
updates = append(updates, update)
}, 0, sleep)
if !errors.Is(err, context.Canceled) {