From d6ab7b9d525f9802886da02058499dbf6a852307 Mon Sep 17 00:00:00 2001 From: "diamondburned (Forefront)" Date: Mon, 6 Jan 2020 22:45:29 -0800 Subject: [PATCH] Completed guilds, added max pagination API --- api/guild.go | 23 ++++++++++++++------ api/message.go | 17 ++++++++++----- api/message_reaction.go | 17 ++++++++++----- api/user.go | 45 ++++++++++++++++++++++++++++++++++------ discord/guild.go | 6 +++--- discord/guild_const.go | 7 ++++--- discord/message_embed.go | 6 +++--- discord/user.go | 23 ++++++++++++++++++++ 8 files changed, 113 insertions(+), 31 deletions(-) diff --git a/api/guild.go b/api/guild.go index afd951e..89c9646 100644 --- a/api/guild.go +++ b/api/guild.go @@ -75,23 +75,34 @@ func (c *Client) DeleteGuild(guildID discord.Snowflake) error { return c.FastRequest("DELETE", EndpointGuilds+guildID.String()) } -// Members returns maximum 1000 members. -func (c *Client) Members(guildID discord.Snowflake) ([]discord.Member, error) { +// Members returns members until it reaches max. This function automatically +// paginates, meaning the normal 1000 limit is handled internally. +func (c *Client) Members( + guildID discord.Snowflake, max uint) ([]discord.Member, error) { + var mems []discord.Member var after discord.Snowflake = 0 - for { - m, err := c.MembersAfter(guildID, after, 1000) + const hardLimit int = 1000 + + for fetch := uint(hardLimit); max > 0; fetch = uint(hardLimit) { + if fetch > max { + fetch = max + } + max -= fetch + + m, err := c.MembersAfter(guildID, after, fetch) if err != nil { return mems, err } mems = append(mems, m...) - if len(mems) < 1000 { + // There aren't any to fetch, even if this is less than max. + if len(mems) < hardLimit { break } - after = mems[999].User.ID + after = mems[hardLimit-1].User.ID } return mems, nil diff --git a/api/message.go b/api/message.go index 9b3a27a..1eed17a 100644 --- a/api/message.go +++ b/api/message.go @@ -12,23 +12,30 @@ import ( // 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) { + channelID discord.Snowflake, max uint) ([]discord.Message, error) { var msgs []discord.Message var after discord.Snowflake = 0 - for { - m, err := c.messagesRange(channelID, 0, after, 0, 100) + const hardLimit int = 100 + + for fetch := uint(hardLimit); max > 0; fetch = uint(hardLimit) { + if fetch > max { + fetch = max + } + max -= fetch + + m, err := c.messagesRange(channelID, 0, after, 0, fetch) if err != nil { return msgs, err } msgs = append(msgs, m...) - if len(m) < 100 { + if len(m) < hardLimit { break } - after = m[99].Author.ID + after = m[hardLimit-1].Author.ID } return msgs, nil diff --git a/api/message_reaction.go b/api/message_reaction.go index 0412866..74b2118 100644 --- a/api/message_reaction.go +++ b/api/message_reaction.go @@ -19,23 +19,30 @@ func (c *Client) React( // Reactions returns all reactions. It will paginate automatically. func (c *Client) Reactions( channelID, messageID discord.Snowflake, - emoji EmojiAPI) ([]discord.User, error) { + max uint, emoji EmojiAPI) ([]discord.User, error) { var users []discord.User var after discord.Snowflake = 0 - for { - r, err := c.ReactionsRange(channelID, messageID, 0, after, 100, emoji) + const hardLimit int = 100 + + for fetch := uint(hardLimit); max > 0; fetch = uint(hardLimit) { + if fetch > max { + fetch = max + } + max -= fetch + + r, err := c.ReactionsRange(channelID, messageID, 0, after, fetch, emoji) if err != nil { return users, err } users = append(users, r...) - if len(r) < 100 { + if len(r) < hardLimit { break } - after = r[99].ID + after = r[hardLimit-1].ID } return users, nil diff --git a/api/user.go b/api/user.go index 614a229..22d0827 100644 --- a/api/user.go +++ b/api/user.go @@ -33,22 +33,29 @@ func (c *Client) ModifyMe(data ModifySelfData) (*discord.User, error) { // 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) { +func (c *Client) Guilds(max uint) ([]discord.Guild, error) { var guilds []discord.Guild var after discord.Snowflake = 0 - for { - g, err := c.GuildsAfter(after, 100) + const hardLimit int = 100 + + for fetch := uint(hardLimit); max > 0; fetch = uint(hardLimit) { + if fetch > max { + fetch = max + } + max -= fetch + + g, err := c.GuildsAfter(after, fetch) if err != nil { return guilds, err } guilds = append(guilds, g...) - if len(g) < 100 { + if len(g) < hardLimit { break } - after = g[99].ID + after = g[hardLimit-1].ID } return guilds, nil @@ -95,4 +102,30 @@ func (c *Client) GuildsRange( ) } -// func (c *Client) +func (c *Client) LeaveGuild(guildID discord.Snowflake) error { + return c.FastRequest("DELETE", EndpointMe+"/guilds/"+guildID.String()) +} + +func (c *Client) PrivateChannels() ([]discord.Channel, error) { + var dms []discord.Channel + return dms, c.RequestJSON(&dms, "GET", EndpointMe+"/channels") +} + +func (c *Client) CreatePrivateChannel( + recipient discord.Snowflake) (*discord.Channel, error) { + + var param struct { + RecipientID discord.Snowflake `json:"recipient_id"` + } + + param.RecipientID = recipient + + var dm *discord.Channel + return dm, c.RequestJSON(&dm, "POST", EndpointMe+"/channels", + httputil.WithJSONBody(c, param)) +} + +// shitty SDK, don't care, PR welcomed +// func (c *Client) CreateGroup(tokens []string, nicks map[]) + +func (c *Client) UserConnections() ([]discord.Connection, error) {} diff --git a/discord/guild.go b/discord/guild.go index 8717fe6..b5e2376 100644 --- a/discord/guild.go +++ b/discord/guild.go @@ -98,9 +98,9 @@ type Ban struct { } type Integration struct { - ID Snowflake `json:"id"` - Name string `json:"name"` - Type IntegrationType `json:"type"` + ID Snowflake `json:"id"` + Name string `json:"name"` + Type Service `json:"type"` Enabled bool `json:"enabled"` Syncing bool `json:"syncing"` diff --git a/discord/guild_const.go b/discord/guild_const.go index 7bf5732..f7d8b85 100644 --- a/discord/guild_const.go +++ b/discord/guild_const.go @@ -86,9 +86,10 @@ const ( VeryHighVerification ) -type IntegrationType string +// Service is used for guild integrations and user connections. +type Service string const ( - TwitchIntegration IntegrationType = "twitch" - YouTubeIntegration IntegrationType = "youtube" + Twitch Service = "twitch" + YouTube Service = "youtube" ) diff --git a/discord/message_embed.go b/discord/message_embed.go index fd846b7..f2ced5b 100644 --- a/discord/message_embed.go +++ b/discord/message_embed.go @@ -114,9 +114,9 @@ func (e *Embed) Validate() error { type EmbedType string const ( - NormalEmbed = "rich" - ImageEmbed = "image" - VideoEmbed = "video" + NormalEmbed EmbedType = "rich" + ImageEmbed EmbedType = "image" + VideoEmbed EmbedType = "video" // Undocumented ) diff --git a/discord/user.go b/discord/user.go index db81ea4..35611f8 100644 --- a/discord/user.go +++ b/discord/user.go @@ -45,3 +45,26 @@ const ( NitroClassic NitroFull ) + +type Connection struct { + ID Snowflake `json:"id"` + Name string `json:"name"` + Type Service `json:"type"` + + Revoked bool `json:"revoked"` + Verified bool `json:"verified"` + FriendSync bool `json:"friend_sync"` + ShowActivity bool `json:"show_activity"` + + Visibility ConnectionVisibility `json:"visibility"` + + // Only partial + Integratioons []Integration `json:"integrations"` +} + +type ConnectionVisibility uint8 + +const ( + ConnectionNotVisible ConnectionVisibility = iota + ConnectionVisibleEveryone +)