fix: use guild active threads for sync

This commit is contained in:
Peter Steinberger 2026-03-16 22:09:49 -07:00
parent 4dcb36be61
commit 9f983581d9
No known key found for this signature in database
6 changed files with 124 additions and 23 deletions

View File

@ -157,6 +157,10 @@ func (f *fakeDiscordClient) ThreadsActive(context.Context, string) ([]*discordgo
return nil, nil
}
func (f *fakeDiscordClient) GuildThreadsActive(context.Context, string) ([]*discordgo.Channel, error) {
return nil, nil
}
func (f *fakeDiscordClient) ThreadsArchived(context.Context, string, bool) ([]*discordgo.Channel, error) {
return nil, nil
}

View File

@ -102,6 +102,16 @@ func (c *Client) ThreadsActive(ctx context.Context, channelID string) ([]*discor
return list.Threads, nil
}
func (c *Client) GuildThreadsActive(ctx context.Context, guildID string) ([]*discordgo.Channel, error) {
reqCtx, cancel := c.requestContext(ctx)
defer cancel()
list, err := c.session.GuildThreadsActive(guildID, discordgo.WithContext(reqCtx))
if err != nil {
return nil, err
}
return list.Threads, nil
}
func (c *Client) ThreadsArchived(ctx context.Context, channelID string, private bool) ([]*discordgo.Channel, error) {
var out []*discordgo.Channel
var before *time.Time

View File

@ -84,7 +84,7 @@ func (s *Syncer) liveChannelList(ctx context.Context, guildID string) ([]*discor
if err := s.appendThreadCatalog(ctx, allChannels, parentIDs); err != nil {
return nil, err
}
} else if err := s.appendActiveThreadCatalog(ctx, allChannels, parentIDs); err != nil {
} else if err := s.appendActiveThreadCatalog(ctx, allChannels, guildID, parentIDs); err != nil {
return nil, err
}
return mapsToSlice(allChannels), nil
@ -113,15 +113,30 @@ func (s *Syncer) appendThreadCatalog(ctx context.Context, allChannels map[string
return nil
}
func (s *Syncer) appendActiveThreadCatalog(ctx context.Context, allChannels map[string]*discordgo.Channel, parents []string) error {
func (s *Syncer) appendActiveThreadCatalog(ctx context.Context, allChannels map[string]*discordgo.Channel, guildID string, parents []string) error {
allowedParents := make(map[string]struct{}, len(parents))
for _, parentID := range uniqueIDs(parents) {
channel := allChannels[parentID]
if !isThreadParent(channel) {
continue
}
if err := s.appendActiveThreads(ctx, allChannels, channel.ID); err != nil {
return err
allowedParents[parentID] = struct{}{}
}
if len(allowedParents) == 0 {
return nil
}
active, err := s.client.GuildThreadsActive(ctx, guildID)
if err != nil {
return err
}
for _, thread := range active {
if thread == nil {
continue
}
if _, ok := allowedParents[thread.ParentID]; !ok {
continue
}
allChannels[thread.ID] = thread
}
return nil
}

View File

@ -244,6 +244,63 @@ func TestFullSyncReusesStoredThreadParents(t *testing.T) {
require.Equal(t, 1, client.messageCalls["t1"])
}
func TestFullSyncUsesGuildActiveThreadsForStoredParents(t *testing.T) {
t.Parallel()
ctx := context.Background()
s, err := store.Open(ctx, filepath.Join(t.TempDir(), "discrawl.db"))
require.NoError(t, err)
defer func() { _ = s.Close() }()
require.NoError(t, s.UpsertGuild(ctx, store.GuildRecord{ID: "g1", Name: "Guild", RawJSON: `{}`}))
require.NoError(t, s.UpsertChannel(ctx, store.ChannelRecord{
ID: "c1",
GuildID: "g1",
Kind: "text",
Name: "general",
RawJSON: `{"id":"c1"}`,
}))
require.NoError(t, s.UpsertChannel(ctx, store.ChannelRecord{
ID: "t1",
GuildID: "g1",
ParentID: "c1",
Kind: "thread_public",
Name: "bug-report",
RawJSON: `{"id":"t1"}`,
}))
require.NoError(t, s.SetSyncState(ctx, channelHistoryCompleteScope("c1"), "1"))
require.NoError(t, s.SetSyncState(ctx, channelHistoryCompleteScope("t1"), "1"))
require.NoError(t, s.SetSyncState(ctx, channelLatestScope("t1"), "10"))
client := &fakeClient{
guilds: []*discordgo.UserGuild{{ID: "g1", Name: "Guild"}},
guildByID: map[string]*discordgo.Guild{
"g1": {ID: "g1", Name: "Guild"},
},
channels: map[string][]*discordgo.Channel{
"g1": {
{ID: "c1", GuildID: "g1", Name: "general", Type: discordgo.ChannelTypeGuildText},
},
},
guildThreads: map[string][]*discordgo.Channel{
"g1": {{
ID: "t1",
GuildID: "g1",
ParentID: "c1",
Name: "bug-report",
Type: discordgo.ChannelTypeGuildPublicThread,
LastMessageID: "10",
}},
},
}
svc := New(client, s, nil)
_, err = svc.Sync(ctx, SyncOptions{Full: true, GuildIDs: []string{"g1"}})
require.NoError(t, err)
require.Equal(t, 1, client.guildThreadCalls)
require.Zero(t, client.threadCalls)
}
func TestFullSyncFallsBackToBroadThreadDiscoveryWithoutStoredThreads(t *testing.T) {
t.Parallel()

View File

@ -20,6 +20,7 @@ type Client interface {
Guild(context.Context, string) (*discordgo.Guild, error)
GuildChannels(context.Context, string) ([]*discordgo.Channel, error)
ThreadsActive(context.Context, string) ([]*discordgo.Channel, error)
GuildThreadsActive(context.Context, string) ([]*discordgo.Channel, error)
ThreadsArchived(context.Context, string, bool) ([]*discordgo.Channel, error)
GuildMembers(context.Context, string) ([]*discordgo.Member, error)
ChannelMessages(context.Context, string, int, string, string) ([]*discordgo.Message, error)

View File

@ -16,25 +16,27 @@ import (
)
type fakeClient struct {
guilds []*discordgo.UserGuild
guildByID map[string]*discordgo.Guild
channels map[string][]*discordgo.Channel
activeThreads map[string][]*discordgo.Channel
publicArchived map[string][]*discordgo.Channel
privateArchive map[string][]*discordgo.Channel
members map[string][]*discordgo.Member
messages map[string][]*discordgo.Message
messageErrors map[string]error
messageCalls map[string]int
beforeErrors map[string]map[string]error
tailCalls int
messageDelay time.Duration
guildChanCalls int
threadCalls int
memberCalls int
mu sync.Mutex
inFlight int
maxInFlight int
guilds []*discordgo.UserGuild
guildByID map[string]*discordgo.Guild
channels map[string][]*discordgo.Channel
activeThreads map[string][]*discordgo.Channel
guildThreads map[string][]*discordgo.Channel
publicArchived map[string][]*discordgo.Channel
privateArchive map[string][]*discordgo.Channel
members map[string][]*discordgo.Member
messages map[string][]*discordgo.Message
messageErrors map[string]error
messageCalls map[string]int
beforeErrors map[string]map[string]error
tailCalls int
messageDelay time.Duration
guildChanCalls int
threadCalls int
guildThreadCalls int
memberCalls int
mu sync.Mutex
inFlight int
maxInFlight int
}
func (f *fakeClient) Self(context.Context) (*discordgo.User, error) {
@ -59,6 +61,18 @@ func (f *fakeClient) ThreadsActive(_ context.Context, channelID string) ([]*disc
return f.activeThreads[channelID], nil
}
func (f *fakeClient) GuildThreadsActive(_ context.Context, guildID string) ([]*discordgo.Channel, error) {
f.guildThreadCalls++
if f.guildThreads != nil {
return f.guildThreads[guildID], nil
}
var out []*discordgo.Channel
for _, threads := range f.activeThreads {
out = append(out, threads...)
}
return out, nil
}
func (f *fakeClient) ThreadsArchived(_ context.Context, channelID string, private bool) ([]*discordgo.Channel, error) {
f.threadCalls++
if private {