diff --git a/_examples/pagination/examplebot.go b/_examples/pagination/examplebot.go new file mode 100644 index 00000000..11ad470e --- /dev/null +++ b/_examples/pagination/examplebot.go @@ -0,0 +1,38 @@ +package main + +import ( + _ "embed" + "os" + + "github.com/disgoorg/disgo" + "github.com/disgoorg/disgo/rest" + "github.com/disgoorg/log" +) + +var token = os.Getenv("disgo_token") + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + log.SetLevel(log.LevelDebug) + log.Info("starting example...") + log.Info("bot version: ", disgo.Version) + + client := rest.New(rest.NewClient(token)) + + page := client.GetMessagesPage(817327182111571989, 1016790288607498240, 3) + + var i int + for page.Next() { + for _, m := range page.Items { + println(m.ID) + } + println("---") + i++ + if i >= 3 { + break + } + } + if page.Err != nil { + log.Error(page.Err) + } +} diff --git a/rest/channels.go b/rest/channels.go index 1f92ef08..33c2cf1b 100644 --- a/rest/channels.go +++ b/rest/channels.go @@ -28,6 +28,7 @@ type Channels interface { GetMessage(channelID snowflake.ID, messageID snowflake.ID, opts ...RequestOpt) (*discord.Message, error) GetMessages(channelID snowflake.ID, around snowflake.ID, before snowflake.ID, after snowflake.ID, limit int, opts ...RequestOpt) ([]discord.Message, error) + GetMessagesPage(channelID snowflake.ID, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.Message] CreateMessage(channelID snowflake.ID, messageCreate discord.MessageCreate, opts ...RequestOpt) (*discord.Message, error) UpdateMessage(channelID snowflake.ID, messageID snowflake.ID, messageUpdate discord.MessageUpdate, opts ...RequestOpt) (*discord.Message, error) DeleteMessage(channelID snowflake.ID, messageID snowflake.ID, opts ...RequestOpt) error @@ -128,6 +129,18 @@ func (s *channelImpl) GetMessages(channelID snowflake.ID, around snowflake.ID, b return } +func (s *channelImpl) GetMessagesPage(channelID snowflake.ID, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.Message] { + return Page[discord.Message]{ + getItemsFunc: func(before snowflake.ID, after snowflake.ID) ([]discord.Message, error) { + return s.GetMessages(channelID, 0, before, after, limit, opts...) + }, + getIDFunc: func(msg discord.Message) snowflake.ID { + return msg.ID + }, + ID: startID, + } +} + func (s *channelImpl) CreateMessage(channelID snowflake.ID, messageCreate discord.MessageCreate, opts ...RequestOpt) (message *discord.Message, err error) { body, err := messageCreate.ToBody() if err != nil { diff --git a/rest/guild_scheduled_events.go b/rest/guild_scheduled_events.go index 0f5885a4..0201c877 100644 --- a/rest/guild_scheduled_events.go +++ b/rest/guild_scheduled_events.go @@ -18,7 +18,8 @@ type GuildScheduledEvents interface { UpdateGuildScheduledEvent(guildID snowflake.ID, guildScheduledEventID snowflake.ID, guildScheduledEventUpdate discord.GuildScheduledEventUpdate, opts ...RequestOpt) (*discord.GuildScheduledEvent, error) DeleteGuildScheduledEvent(guildID snowflake.ID, guildScheduledEventID snowflake.ID, opts ...RequestOpt) error - GetGuildScheduledEventUsers(guildID snowflake.ID, guildScheduledEventID snowflake.ID, limit int, withMember bool, before snowflake.ID, after snowflake.ID, opts ...RequestOpt) ([]discord.GuildScheduledEventUser, error) + GetGuildScheduledEventUsers(guildID snowflake.ID, guildScheduledEventID snowflake.ID, withMember bool, before snowflake.ID, after snowflake.ID, limit int, opts ...RequestOpt) ([]discord.GuildScheduledEventUser, error) + GetGuildScheduledEventUsersPage(guildID snowflake.ID, guildScheduledEventID snowflake.ID, withMember bool, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.GuildScheduledEventUser] } type guildScheduledEventImpl struct { @@ -57,7 +58,7 @@ func (s *guildScheduledEventImpl) DeleteGuildScheduledEvent(guildID snowflake.ID return s.client.Do(DeleteGuildScheduledEvent.Compile(nil, guildID, guildScheduledEventID), nil, nil, opts...) } -func (s *guildScheduledEventImpl) GetGuildScheduledEventUsers(guildID snowflake.ID, guildScheduledEventID snowflake.ID, limit int, withMember bool, before snowflake.ID, after snowflake.ID, opts ...RequestOpt) (guildScheduledEventUsers []discord.GuildScheduledEventUser, err error) { +func (s *guildScheduledEventImpl) GetGuildScheduledEventUsers(guildID snowflake.ID, guildScheduledEventID snowflake.ID, withMember bool, before snowflake.ID, after snowflake.ID, limit int, opts ...RequestOpt) (guildScheduledEventUsers []discord.GuildScheduledEventUser, err error) { queryValues := discord.QueryValues{} if limit > 0 { queryValues["limit"] = limit @@ -74,3 +75,15 @@ func (s *guildScheduledEventImpl) GetGuildScheduledEventUsers(guildID snowflake. err = s.client.Do(GetGuildScheduledEventUsers.Compile(nil, guildID, guildScheduledEventID), nil, &guildScheduledEventUsers, opts...) return } + +func (s *guildScheduledEventImpl) GetGuildScheduledEventUsersPage(guildID snowflake.ID, guildScheduledEventID snowflake.ID, withMember bool, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.GuildScheduledEventUser] { + return Page[discord.GuildScheduledEventUser]{ + getItemsFunc: func(before snowflake.ID, after snowflake.ID) ([]discord.GuildScheduledEventUser, error) { + return s.GetGuildScheduledEventUsers(guildID, guildScheduledEventID, withMember, before, after, limit, opts...) + }, + getIDFunc: func(user discord.GuildScheduledEventUser) snowflake.ID { + return user.User.ID + }, + ID: startID, + } +} diff --git a/rest/guilds.go b/rest/guilds.go index 47d0c1e8..16df5f7e 100644 --- a/rest/guilds.go +++ b/rest/guilds.go @@ -32,6 +32,7 @@ type Guilds interface { DeleteRole(guildID snowflake.ID, roleID snowflake.ID, opts ...RequestOpt) error GetBans(guildID snowflake.ID, before snowflake.ID, after snowflake.ID, limit int, opts ...RequestOpt) ([]discord.Ban, error) + GetBansPage(guildID snowflake.ID, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.Ban] GetBan(guildID snowflake.ID, userID snowflake.ID, opts ...RequestOpt) (*discord.Ban, error) AddBan(guildID snowflake.ID, userID snowflake.ID, deleteMessageDuration time.Duration, opts ...RequestOpt) error DeleteBan(guildID snowflake.ID, userID snowflake.ID, opts ...RequestOpt) error @@ -42,6 +43,7 @@ type Guilds interface { GetAllWebhooks(guildID snowflake.ID, opts ...RequestOpt) ([]discord.Webhook, error) GetAuditLog(guildID snowflake.ID, userID snowflake.ID, actionType discord.AuditLogEvent, before snowflake.ID, limit int, opts ...RequestOpt) (*discord.AuditLog, error) + GetAuditLogPage(guildID snowflake.ID, userID snowflake.ID, actionType discord.AuditLogEvent, startID snowflake.ID, limit int, opts ...RequestOpt) AuditLogPage } type guildImpl struct { @@ -144,6 +146,18 @@ func (s *guildImpl) GetBans(guildID snowflake.ID, before snowflake.ID, after sno return } +func (s *guildImpl) GetBansPage(guildID snowflake.ID, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.Ban] { + return Page[discord.Ban]{ + getItemsFunc: func(before snowflake.ID, after snowflake.ID) (bans []discord.Ban, err error) { + return s.GetBans(guildID, before, after, limit, opts...) + }, + getIDFunc: func(ban discord.Ban) snowflake.ID { + return ban.User.ID + }, + ID: startID, + } +} + func (s *guildImpl) GetBan(guildID snowflake.ID, userID snowflake.ID, opts ...RequestOpt) (ban *discord.Ban, err error) { err = s.client.Do(GetBan.Compile(nil, guildID, userID), nil, &ban, opts...) return @@ -193,3 +207,17 @@ func (s *guildImpl) GetAuditLog(guildID snowflake.ID, userID snowflake.ID, actio err = s.client.Do(GetAuditLogs.Compile(values, guildID), nil, &auditLog, opts...) return } + +func (s *guildImpl) GetAuditLogPage(guildID snowflake.ID, userID snowflake.ID, actionType discord.AuditLogEvent, startID snowflake.ID, limit int, opts ...RequestOpt) AuditLogPage { + return AuditLogPage{ + getItems: func(before snowflake.ID) (discord.AuditLog, error) { + log, err := s.GetAuditLog(guildID, userID, actionType, before, limit, opts...) + var finalLog discord.AuditLog + if log != nil { + finalLog = *log + } + return finalLog, err + }, + ID: startID, + } +} diff --git a/rest/oauth2.go b/rest/oauth2.go index 3bca0356..d5fe8623 100644 --- a/rest/oauth2.go +++ b/rest/oauth2.go @@ -20,6 +20,7 @@ type OAuth2 interface { GetCurrentUser(bearerToken string, opts ...RequestOpt) (*discord.OAuth2User, error) GetCurrentMember(bearerToken string, guildID snowflake.ID, opts ...RequestOpt) (*discord.Member, error) GetCurrentUserGuilds(bearerToken string, before snowflake.ID, after snowflake.ID, limit int, opts ...RequestOpt) ([]discord.OAuth2Guild, error) + GetCurrentUserGuildsPage(bearerToken string, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.OAuth2Guild] GetCurrentUserConnections(bearerToken string, opts ...RequestOpt) ([]discord.Connection, error) SetGuildCommandPermissions(bearerToken string, applicationID snowflake.ID, guildID snowflake.ID, commandID snowflake.ID, commandPermissions []discord.ApplicationCommandPermission, opts ...RequestOpt) (*discord.ApplicationCommandPermissions, error) @@ -74,6 +75,18 @@ func (s *oAuth2Impl) GetCurrentUserGuilds(bearerToken string, before snowflake.I return } +func (s *oAuth2Impl) GetCurrentUserGuildsPage(bearerToken string, startID snowflake.ID, limit int, opts ...RequestOpt) Page[discord.OAuth2Guild] { + return Page[discord.OAuth2Guild]{ + getItemsFunc: func(before snowflake.ID, after snowflake.ID) ([]discord.OAuth2Guild, error) { + return s.GetCurrentUserGuilds(bearerToken, before, after, limit, opts...) + }, + getIDFunc: func(guild discord.OAuth2Guild) snowflake.ID { + return guild.ID + }, + ID: startID, + } +} + func (s *oAuth2Impl) GetCurrentUserConnections(bearerToken string, opts ...RequestOpt) (connections []discord.Connection, err error) { err = s.client.Do(GetCurrentUserConnections.Compile(nil), nil, &connections, withBearerToken(bearerToken, opts)...) return diff --git a/rest/page.go b/rest/page.go new file mode 100644 index 00000000..d5bec3ec --- /dev/null +++ b/rest/page.go @@ -0,0 +1,77 @@ +package rest + +import ( + "errors" + + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/snowflake/v2" +) + +var ErrNoMorePages = errors.New("no more pages") + +type Page[T any] struct { + getItemsFunc func(before snowflake.ID, after snowflake.ID) ([]T, error) + getIDFunc func(t T) snowflake.ID + + Items []T + Err error + + ID snowflake.ID +} + +func (p *Page[T]) Next() bool { + if p.Err != nil { + return false + } + + if len(p.Items) > 0 { + p.ID = p.getIDFunc(p.Items[0]) + } + + p.Items, p.Err = p.getItemsFunc(0, p.ID) + if p.Err == nil && len(p.Items) == 0 { + p.Err = ErrNoMorePages + } + return p.Err == nil +} + +func (p *Page[T]) Previous() bool { + if p.Err != nil { + return false + } + + if len(p.Items) > 0 { + p.ID = p.getIDFunc(p.Items[len(p.Items)-1]) + } + + p.Items, p.Err = p.getItemsFunc(p.ID, 0) + if p.Err == nil && len(p.Items) == 0 { + p.Err = ErrNoMorePages + } + return p.Err == nil +} + +type AuditLogPage struct { + getItems func(before snowflake.ID) (discord.AuditLog, error) + + discord.AuditLog + Err error + + ID snowflake.ID +} + +func (p *AuditLogPage) Previous() bool { + if p.Err != nil { + return false + } + + if len(p.AuditLogEntries) > 0 { + p.ID = p.AuditLogEntries[len(p.AuditLogEntries)-1].ID + } + + p.AuditLog, p.Err = p.getItems(p.ID) + if p.Err == nil && len(p.AuditLogEntries) == 0 { + p.Err = ErrNoMorePages + } + return p.Err == nil +}