diff --git a/api/guild.go b/api/guild.go index 60d78e9..eeefc8b 100644 --- a/api/guild.go +++ b/api/guild.go @@ -76,12 +76,44 @@ func (c *Client) DeleteGuild(guildID discord.Snowflake) error { } // Members returns maximum 1000 members. -func (c *Client) Members(guildID discord.Snowflake, limit uint, - after discord.Snowflake) ([]discord.Member, error) { +func (c *Client) Members(guildID discord.Snowflake) ([]discord.Member, error) { + var mems []discord.Member + var after discord.Snowflake = 0 + + for { + m, err := c.MembersAfter(guildID, after, 1000) + if err != nil { + return mems, err + } + mems = append(mems, m...) + + if len(mems) < 1000 { + break + } + + after = mems[999].User.ID + } + + return mems, nil +} + +// MembersAfter returns a list of all guild members, from 1-1000 for limits. The +// default limit is 1 and the maximum limit is 1000. +func (c *Client) MembersAfter(guildID, after discord.Snowflake, + limit uint) ([]discord.Member, error) { + + if limit == 0 { + limit = 1 + } + + if limit > 1000 { + limit = 1000 + } var param struct { - Limit uint `schema:"limit,omitempty"` After discord.Snowflake `schema:"after,omitempty"` + + Limit uint `schema:"limit"` } param.Limit = limit diff --git a/api/message.go b/api/message.go index 53ba08f..943836b 100644 --- a/api/message.go +++ b/api/message.go @@ -8,42 +8,55 @@ import ( "github.com/pkg/errors" ) -func (c *Client) Messages(channelID discord.Snowflake, - limit uint) ([]discord.Message, error) { +// Messages gets all mesesages, automatically paginating. Use with care, as +// this could get as many as hundred thousands of messages, making a lot of +// queries. +func (c *Client) Messages( + channelID discord.Snowflake) ([]discord.Message, error) { - return c.messages(channelID, limit, nil) + var msgs []discord.Message + var after discord.Snowflake = 0 + + for { + m, err := c.messagesRange(channelID, 0, after, 0, 100) + if err != nil { + return msgs, err + } + msgs = append(msgs, m...) + + if len(m) < 100 { + break + } + + after = m[99].Author.ID + } + + return msgs, nil } +// MessagesAround returns messages around the ID, with a limit of 1-100. func (c *Client) MessagesAround(channelID, around discord.Snowflake, limit uint) ([]discord.Message, error) { - return c.messages(channelID, limit, map[string]interface{}{ - "around": around, - }) + return c.messagesRange(channelID, 0, 0, around, limit) } +// MessagesBefore returns messages before the ID, with a limit of 1-100. func (c *Client) MessagesBefore(channelID, before discord.Snowflake, limit uint) ([]discord.Message, error) { - return c.messages(channelID, limit, map[string]interface{}{ - "before": before, - }) + return c.messagesRange(channelID, before, 0, 0, limit) } +// MessagesAfter returns messages after the ID, with a limit of 1-100. func (c *Client) MessagesAfter(channelID, after discord.Snowflake, limit uint) ([]discord.Message, error) { - return c.messages(channelID, limit, map[string]interface{}{ - "after": after, - }) + return c.messagesRange(channelID, 0, after, 0, limit) } -func (c *Client) messages(channelID discord.Snowflake, - limit uint, body map[string]interface{}) ([]discord.Message, error) { - - if body == nil { - body = map[string]interface{}{} - } +func (c *Client) messagesRange(channelID, before, after, + around discord.Snowflake, limit uint) ([]discord.Message, error) { switch { case limit == 0: @@ -52,11 +65,17 @@ func (c *Client) messages(channelID discord.Snowflake, limit = 100 } - body["limit"] = limit + var param struct { + Before discord.Snowflake `schema:"before,omitempty"` + After discord.Snowflake `schema:"after,omitempty"` + Around discord.Snowflake `schema:"around,omitempty"` + + Limit uint `schema:"limit"` + } var msgs []discord.Message return msgs, c.RequestJSON(&msgs, "GET", - EndpointChannels+channelID.String(), httputil.WithSchema(c, body)) + EndpointChannels+channelID.String(), httputil.WithSchema(c, param)) } func (c *Client) Message( diff --git a/api/message_reaction.go b/api/message_reaction.go index 9bc295e..5455c3c 100644 --- a/api/message_reaction.go +++ b/api/message_reaction.go @@ -16,16 +16,45 @@ func (c *Client) React(chID, msgID discord.Snowflake, return c.FastRequest("PUT", msgURL) } +// Reactions returns all reactions. It will paginate automatically. func (c *Client) Reactions(chID, msgID discord.Snowflake, limit uint, emoji EmojiAPI) ([]discord.User, error) { - return c.ReactionRange(chID, msgID, 0, 0, limit, emoji) + var users []discord.User + var after discord.Snowflake = 0 + + for { + r, err := c.ReactionsRange(chID, msgID, 0, after, limit, emoji) + if err != nil { + return users, err + } + users = append(users, r...) + + if len(r) < 100 { + break + } + + after = r[99].ID + } + + return users, nil } -// ReactionRange get users before and after IDs. Before, after, and limit are -// optional. -func (c *Client) ReactionRange( - chID, msgID, before, after discord.Snowflake, +func (c *Client) ReactionsBefore(chID, msgID, before discord.Snowflake, + limit uint, emoji EmojiAPI) ([]discord.User, error) { + + return c.ReactionsRange(chID, msgID, before, 0, limit, emoji) +} + +func (c *Client) ReactionsAfter(chID, msgID, after discord.Snowflake, + limit uint, emoji EmojiAPI) ([]discord.User, error) { + + return c.ReactionsRange(chID, msgID, 0, after, limit, emoji) +} + +// ReactionsRange get users before and after IDs. Before, after, and limit are +// optional. A maximum limit of only 100 reactions could be returned. +func (c *Client) ReactionsRange(chID, msgID, before, after discord.Snowflake, limit uint, emoji EmojiAPI) ([]discord.User, error) { if limit == 0 { @@ -48,12 +77,12 @@ func (c *Client) ReactionRange( param.Limit = limit var users []discord.User - var msgURL = EndpointChannels + chID.String() + - "/messages/" + msgID.String() + - "/reactions/" + emoji - - return users, c.RequestJSON(&users, "GET", msgURL, - httputil.WithSchema(c, param)) + return users, c.RequestJSON( + &users, "GET", EndpointChannels+chID.String()+ + "/messages/"+msgID.String()+ + "/reactions/"+emoji, + httputil.WithSchema(c, param), + ) } // DeleteReaction requires MANAGE_MESSAGES if not @me. diff --git a/api/user.go b/api/user.go index 37a777a..614a229 100644 --- a/api/user.go +++ b/api/user.go @@ -1,6 +1,9 @@ package api -import "github.com/diamondburned/arikawa/discord" +import ( + "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/httputil" +) const EndpointUsers = Endpoint + "users/" const EndpointMe = EndpointUsers + "@me" @@ -26,14 +29,70 @@ func (c *Client) ModifyMe(data ModifySelfData) (*discord.User, error) { return u, c.RequestJSON(&u, "PATCH", EndpointMe) } -// Guilds returns maximum 100 of your guilds. To paginate, call MyGuildsRange. -// Guilds returned have some fields filled only (ID, Name, Icon, Owner, -// Permissions). +// Guilds returns all guilds, automatically paginating. Be careful, as this +// method may abuse the API by requesting thousands or millions of guilds. For +// lower-level access, usee GuildsRange. Guilds returned have some fields +// filled only (ID, Name, Icon, Owner, Permissions). func (c *Client) Guilds() ([]discord.Guild, error) { - var gs []discord.Guild - return gs, c.RequestJSON(&gs, "GET", EndpointMe+"/guilds") + var guilds []discord.Guild + var after discord.Snowflake = 0 + + for { + g, err := c.GuildsAfter(after, 100) + if err != nil { + return guilds, err + } + guilds = append(guilds, g...) + + if len(g) < 100 { + break + } + + after = g[99].ID + } + + return guilds, nil } -// func (c *Client) GuildsRange() +// GuildsBefore fetches guilds. Check GuildsRange. +func (c *Client) GuildsBefore( + before discord.Snowflake, limit uint) ([]discord.Guild, error) { + + return c.GuildsRange(before, 0, limit) +} + +// GuildsAfter fetches guilds. Check GuildsRange. +func (c *Client) GuildsAfter( + after discord.Snowflake, limit uint) ([]discord.Guild, error) { + + return c.GuildsRange(0, after, limit) +} + +// GuildsRange fetches guilds. The limit is 1-100. +func (c *Client) GuildsRange( + before, after discord.Snowflake, limit uint) ([]discord.Guild, error) { + + if limit == 0 { + limit = 100 + } + + if limit > 100 { + limit = 100 + } + + var param struct { + Before discord.Snowflake `schema:"before"` + After discord.Snowflake `schema:"after"` + + Limit uint `schema:"limit"` + } + + var gs []discord.Guild + return gs, c.RequestJSON( + &gs, "GET", + EndpointMe+"/guilds", + httputil.WithSchema(c, param), + ) +} // func (c *Client)