diff --git a/_example/advanced_bot/bot.go b/_example/advanced_bot/bot.go index df3f720..f206c62 100644 --- a/_example/advanced_bot/bot.go +++ b/_example/advanced_bot/bot.go @@ -45,14 +45,14 @@ func (bot *Bot) Say(m *gateway.MessageCreateEvent, f bot.RawArguments) (string, if f != "" { return string(f), nil } - return "", errors.New("Missing content.") + return "", errors.New("missing content") } // GuildInfo demonstrates the GuildOnly middleware done in (*Bot).Setup(). func (bot *Bot) GuildInfo(m *gateway.MessageCreateEvent) (string, error) { g, err := bot.Ctx.GuildWithCount(m.GuildID) if err != nil { - return "", fmt.Errorf("Failed to get guild: %v", err) + return "", fmt.Errorf("failed to get guild: %v", err) } return fmt.Sprintf( @@ -86,7 +86,7 @@ func (bot *Bot) Repeat(m *gateway.MessageCreateEvent) (string, error) { }) if v == nil { - return "", errors.New("Timed out waiting for response.") + return "", errors.New("timed out waiting for response") } ev := v.(*gateway.MessageCreateEvent) @@ -110,12 +110,12 @@ func (bot *Bot) Embed(m *gateway.MessageCreateEvent, f arguments.Flag) (*discord } if len(fs.Args()) < 1 { - return nil, fmt.Errorf("Usage: embed [flags] content...\n" + fs.Usage()) + return nil, fmt.Errorf("usage: embed [flags] content...\n" + fs.Usage()) } // Check if the color string is valid. if !strings.HasPrefix(*color, "#") || len(*color) != 7 { - return nil, errors.New("Invalid color, format must be #hhhhhh") + return nil, errors.New("invalid color, format must be #hhhhhh") } // Parse the color into decimal numbers. diff --git a/api/guild.go b/api/guild.go index cf98753..ffbf938 100644 --- a/api/guild.go +++ b/api/guild.go @@ -103,29 +103,95 @@ func (c *Client) GuildWithCount(id discord.Snowflake) (*discord.Guild, error) { ) } -// 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, use GuildsRange. Guilds returned have some fields -// filled only (ID, Name, Icon, Owner, Permissions). +// Guilds returns a list of partial guild objects the current user is a member +// of. This method automatically paginates until it reaches the passed limit, +// or, if the limit is set to 0, has fetched all guilds within the passed +// range. // -// Max can be 0, in which case the function will try and fetch all guilds. -func (c *Client) Guilds(max uint) ([]discord.Guild, error) { - var guilds []discord.Guild - var after discord.Snowflake = 0 +// As the underlying endpoint has a maximum of 100 guilds per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +// +// When fetching the guilds, those with the smallest ID will be fetched first. +// +// Also note that 100 is the maximum number of guilds a non-bot user can join. +// Therefore, pagination is not needed for integrations that need to get a list +// of the users' guilds. +// +// Requires the guilds OAuth2 scope. +func (c *Client) Guilds(limit uint) ([]discord.Guild, error) { + return c.GuildsAfter(0, limit) +} +// GuildsBefore returns a list of partial guild objects the current user is a +// member of. This method automatically paginates until it reaches the +// passed limit, or, if the limit is set to 0, has fetched all guilds within +// the passed range. +// +// As the underlying endpoint has a maximum of 100 guilds per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +// +// Requires the guilds OAuth2 scope. +func (c *Client) GuildsBefore(before discord.Snowflake, limit uint) ([]discord.Guild, error) { + var guilds []discord.Guild + + // this is the limit of max guilds per request,as imposed by Discord const hardLimit int = 100 - unlimited := max == 0 + unlimited := limit == 0 - for fetch := uint(hardLimit); max > 0 || unlimited; fetch = uint(hardLimit) { - if max > 0 { - if fetch > max { - fetch = max + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit } - max -= fetch + limit -= fetch } - g, err := c.GuildsAfter(after, fetch) + g, err := c.guildsRange(before, 0, fetch) + if err != nil { + return guilds, err + } + guilds = append(g, guilds...) + + if len(g) < hardLimit { + break + } + + before = g[0].ID + } + + return guilds, nil +} + +// GuildsAfter returns a list of partial guild objects the current user is a +// member of. This method automatically paginates until it reaches the +// passed limit, or, if the limit is set to 0, has fetched all guilds within +// the passed range. +// +// As the underlying endpoint has a maximum of 100 guilds per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +// +// Requires the guilds OAuth2 scope. +func (c *Client) GuildsAfter(after discord.Snowflake, limit uint) ([]discord.Guild, error) { + var guilds []discord.Guild + + // this is the limit of max guilds per request, as imposed by Discord + const hardLimit int = 100 + + unlimited := limit == 0 + + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit + } + limit -= fetch + } + + g, err := c.guildsRange(0, after, fetch) if err != nil { return guilds, err } @@ -141,29 +207,8 @@ func (c *Client) Guilds(max uint) ([]discord.Guild, error) { return guilds, nil } -// GuildsBefore fetches guilds before the specified ID. Check GuildsRange. -func (c *Client) GuildsBefore(before discord.Snowflake, limit uint) ([]discord.Guild, error) { - return c.GuildsRange(before, 0, limit) -} - -// GuildsAfter fetches guilds after the specified ID. Check GuildsRange. -func (c *Client) GuildsAfter(after discord.Snowflake, limit uint) ([]discord.Guild, error) { - return c.GuildsRange(0, after, limit) -} - -// GuildsRange returns a list of partial guild objects the current user is a -// member of. Requires the guilds OAuth2 scope. -// -// This endpoint returns 100 guilds by default, which is the maximum number -// of guilds a non-bot user can join. Therefore, pagination is not needed -// for integrations that need to get a list of the users' guilds. -func (c *Client) GuildsRange(before, after discord.Snowflake, limit uint) ([]discord.Guild, error) { - switch { - case limit == 0: - limit = 100 - case limit > 100: - limit = 100 - } +func (c *Client) guildsRange( + before, after discord.Snowflake, limit uint) ([]discord.Guild, error) { var param struct { Before discord.Snowflake `schema:"before,omitempty"` @@ -381,7 +426,7 @@ func (c *Client) SyncIntegration(guildID, integrationID discord.Snowflake) error // Requires the MANAGE_GUILD permission. func (c *Client) GuildWidget(guildID discord.Snowflake) (*discord.GuildWidget, error) { var ge *discord.GuildWidget - return ge, c.RequestJSON(&ge, "GET", EndpointGuilds+guildID.String()+"/embed") + return ge, c.RequestJSON(&ge, "GET", EndpointGuilds+guildID.String()+"/widget") } // https://discord.com/developers/docs/resources/guild#guild-embed-object-guild-embed-structure @@ -401,7 +446,7 @@ func (c *Client) ModifyGuildWidget( var w *discord.GuildWidget return w, c.RequestJSON( &w, "PATCH", - EndpointGuilds+guildID.String()+"/embed", + EndpointGuilds+guildID.String()+"/widget", httputil.WithJSONBody(data), ) } diff --git a/api/image.go b/api/image.go index c40a928..89104ae 100644 --- a/api/image.go +++ b/api/image.go @@ -10,9 +10,8 @@ import ( "github.com/pkg/errors" ) -var ErrInvalidImageCT = errors.New("Unknown image content-type") -var ErrInvalidImageData = errors.New("Invalid image data") -var ErrNoImage = errors.New("null image") +var ErrInvalidImageCT = errors.New("unknown image content-type") +var ErrInvalidImageData = errors.New("invalid image data") type ErrImageTooLarge struct { Size, Max int diff --git a/api/member.go b/api/member.go index 1937660..94ca12d 100644 --- a/api/member.go +++ b/api/member.go @@ -12,33 +12,50 @@ func (c *Client) Member(guildID, userID discord.Snowflake) (*discord.Member, err return m, c.RequestJSON(&m, "GET", EndpointGuilds+guildID.String()+"/members/"+userID.String()) } -// Members returns members until it reaches max. This function automatically -// paginates, meaning the normal 1000 limit is handled internally. +// Members returns a list of members of the guild with the passed id. This +// method automatically paginates until it reaches the passed limit, or, if the +// limit is set to 0, has fetched all members within the passed range. // -// Max can be 0, in which case the function will try and fetch all members. -func (c *Client) Members(guildID discord.Snowflake, max uint) ([]discord.Member, error) { +// As the underlying endpoint has a maximum of 1000 members per request, at +// maximum a total of limit/1000 rounded up requests will be made, although +// they may be less, if no more members are available. +// +// When fetching the members, those with the smallest ID will be fetched first. +func (c *Client) Members(guildID discord.Snowflake, limit uint) ([]discord.Member, error) { + return c.MembersAfter(guildID, 0, limit) +} + +// MembersAfter returns a list of members of the guild with the passed id. This +// method automatically paginates until it reaches the passed limit, or, if the +// limit is set to 0, has fetched all members within the passed range. +// +// As the underlying endpoint has a maximum of 1000 members per request, at +// maximum a total of limit/1000 rounded up requests will be made, although +// they may be less, if no more members are available. +func (c *Client) MembersAfter( + guildID, after discord.Snowflake, limit uint) ([]discord.Member, error) { + var mems []discord.Member - var after discord.Snowflake = 0 const hardLimit int = 1000 - unlimited := max == 0 + unlimited := limit == 0 - for fetch := uint(hardLimit); max > 0 || unlimited; fetch = uint(hardLimit) { - if max > 0 { - if fetch > max { - fetch = max + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit } - max -= fetch + limit -= fetch } - m, err := c.MembersAfter(guildID, after, fetch) + m, err := c.membersAfter(guildID, after, fetch) if err != nil { return mems, err } mems = append(mems, m...) - // There aren't any to fetch, even if this is less than max. + // There aren't any to fetch, even if this is less than limit. if len(mems) < hardLimit { break } @@ -49,9 +66,7 @@ func (c *Client) Members(guildID discord.Snowflake, max uint) ([]discord.Member, 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( +func (c *Client) membersAfter( guildID, after discord.Snowflake, limit uint) ([]discord.Member, error) { switch { diff --git a/api/message.go b/api/message.go index 8d0e07d..dbcc10c 100644 --- a/api/message.go +++ b/api/message.go @@ -6,25 +6,93 @@ import ( "github.com/diamondburned/arikawa/utils/json/option" ) -// Messages gets all messages, automatically paginating. Use with care, as -// this could get as many as hundred thousands of messages, making a lot of -// queries. +// Messages returns a list of messages sent in the channel with the passed ID. +// This method automatically paginates until it reaches the passed limit, or, +// if the limit is set to 0, has fetched all guilds within the passed ange. // -// Max can be 0, in which case the function will try and fetch all messages. -func (c *Client) Messages(channelID discord.Snowflake, max uint) ([]discord.Message, error) { - var msgs []discord.Message - var after discord.Snowflake = 0 +// As the underlying endpoint has a maximum of 100 messages per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more messages are available. +// +// When fetching the messages, those with the smallest ID will be fetched +// first. +func (c *Client) Messages(channelID discord.Snowflake, limit uint) ([]discord.Message, error) { + return c.MessagesAfter(channelID, 0, limit) +} +// MessagesAround returns messages around the ID, with a limit of 100. +func (c *Client) MessagesAround( + channelID, around discord.Snowflake, limit uint) ([]discord.Message, error) { + + return c.messagesRange(channelID, 0, 0, around, limit) +} + +// MessagesBefore returns a list messages sent in the channel with the passed +// ID. This method automatically paginates until it reaches the passed limit, +// or, if the limit is set to 0, has fetched all guilds within the passed +// range. +// +// As the underlying endpoint has a maximum of 100 messages per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more messages are available. +func (c *Client) MessagesBefore( + channelID, before discord.Snowflake, limit uint) ([]discord.Message, error) { + + var msgs []discord.Message + + // this is the limit of max messages per request, as imposed by Discord const hardLimit int = 100 - unlimited := max == 0 + unlimited := limit == 0 - for fetch := uint(hardLimit); max > 0 || unlimited; fetch = uint(hardLimit) { - if max > 0 { - if fetch > max { - fetch = max + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit } - max -= fetch + limit -= fetch + } + + m, err := c.messagesRange(channelID, before, 0, 0, fetch) + if err != nil { + return msgs, err + } + msgs = append(m, msgs...) + + if len(m) < hardLimit { + break + } + + before = m[0].Author.ID + } + + return msgs, nil +} + +// MessagesAfter returns a list messages sent in the channel with the passed +// ID. This method automatically paginates until it reaches the passed limit, +// or, if the limit is set to 0, has fetched all guilds within the passed +// range. +// +// As the underlying endpoint has a maximum of 100 messages per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more messages are available. +func (c *Client) MessagesAfter( + channelID, after discord.Snowflake, limit uint) ([]discord.Message, error) { + + var msgs []discord.Message + + // this is the limit of max messages per request, as imposed by Discord + const hardLimit int = 100 + + unlimited := limit == 0 + + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit + } + limit -= fetch } m, err := c.messagesRange(channelID, 0, after, 0, fetch) @@ -43,30 +111,8 @@ func (c *Client) Messages(channelID discord.Snowflake, max uint) ([]discord.Mess 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.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.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.messagesRange(channelID, 0, after, 0, limit) -} - func (c *Client) messagesRange( - channelID, before, after, around discord.Snowflake, - limit uint) ([]discord.Message, error) { + channelID, before, after, around discord.Snowflake, limit uint) ([]discord.Message, error) { switch { case limit == 0: diff --git a/api/message_reaction.go b/api/message_reaction.go index a3da55f..75a0e71 100644 --- a/api/message_reaction.go +++ b/api/message_reaction.go @@ -25,27 +25,88 @@ func (c *Client) Unreact(chID, msgID discord.Snowflake, emoji Emoji) error { return c.DeleteUserReaction(chID, msgID, 0, emoji) } -// Reactions returns reactions up to the specified limit. It will paginate -// automatically. +// Reactions returns a list of users that reacted with the passed Emoji. This +// method automatically paginates until it reaches the passed limit, or, if the +// limit is set to 0, has fetched all users within the passed range. // -// Max can be 0, in which case the function will try and fetch all reactions. +// As the underlying endpoint has a maximum of 100 users per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +// +// When fetching the users, those with the smallest ID will be fetched first. func (c *Client) Reactions( - channelID, messageID discord.Snowflake, max uint, emoji Emoji) ([]discord.User, error) { + channelID, messageID discord.Snowflake, limit uint, emoji Emoji) ([]discord.User, error) { + + return c.ReactionsAfter(channelID, messageID, 0, limit, emoji) +} + +// ReactionsBefore returns a list of users that reacted with the passed Emoji. +// This method automatically paginates until it reaches the passed limit, or, +// if the limit is set to 0, has fetched all users within the passed range. +// +// As the underlying endpoint has a maximum of 100 users per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +func (c *Client) ReactionsBefore( + channelID, messageID, before discord.Snowflake, + limit uint, emoji Emoji) ([]discord.User, error) { var users []discord.User - var after discord.Snowflake = 0 const hardLimit int = 100 - for fetch := uint(hardLimit); max > 0; fetch = uint(hardLimit) { - if max > 0 { - if fetch > max { - fetch = max + unlimited := limit == 0 + + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit } - max -= fetch + limit -= fetch } - r, err := c.ReactionsRange(channelID, messageID, 0, after, fetch, emoji) + r, err := c.reactionsRange(channelID, messageID, before, 0, fetch, emoji) + if err != nil { + return users, err + } + users = append(users, r...) + + if len(r) < hardLimit { + break + } + + before = r[0].ID + } + + return users, nil +} + +// ReactionsAfter returns a list of users that reacted with the passed Emoji. +// This method automatically paginates until it reaches the passed limit, or, +// if the limit is set to 0, has fetched all users within the passed range. +// +// As the underlying endpoint has a maximum of 100 users per request, at +// maximum a total of limit/100 rounded up requests will be made, although they +// may be less, if no more guilds are available. +func (c *Client) ReactionsAfter( + channelID, messageID, after discord.Snowflake, limit uint, emoji Emoji, +) ([]discord.User, error) { + + var users []discord.User + + const hardLimit int = 100 + + unlimited := limit == 0 + + for fetch := uint(hardLimit); limit > 0 || unlimited; fetch = uint(hardLimit) { + if limit > 0 { + if fetch > limit { + fetch = limit + } + limit -= fetch + } + + r, err := c.reactionsRange(channelID, messageID, 0, after, fetch, emoji) if err != nil { return users, err } @@ -61,25 +122,9 @@ func (c *Client) Reactions( return users, nil } -// ReactionsBefore gets all reactions before the passed user ID. -func (c *Client) ReactionsBefore( - channelID, messageID, before discord.Snowflake, - limit uint, emoji Emoji) ([]discord.User, error) { - - return c.ReactionsRange(channelID, messageID, before, 0, limit, emoji) -} - -// Refer to ReactionsRange. -func (c *Client) ReactionsAfter( - channelID, messageID, after discord.Snowflake, - limit uint, emoji Emoji) ([]discord.User, error) { - - return c.ReactionsRange(channelID, messageID, 0, after, limit, emoji) -} - -// ReactionsRange get users before and after IDs. Before, after, and limit are +// 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( +func (c *Client) reactionsRange( channelID, messageID, before, after discord.Snowflake, limit uint, emoji Emoji) ([]discord.User, error) { diff --git a/api/rate/rate.go b/api/rate/rate.go index 0a57634..da1773d 100644 --- a/api/rate/rate.go +++ b/api/rate/rate.go @@ -169,7 +169,7 @@ func (l *Limiter) Release(path string, headers http.Header) error { case retryAfter != "": i, err := strconv.Atoi(retryAfter) if err != nil { - return errors.Wrap(err, "Invalid retryAfter "+retryAfter) + return errors.Wrap(err, "invalid retryAfter "+retryAfter) } at := time.Now().Add(time.Duration(i) * time.Millisecond) @@ -183,7 +183,7 @@ func (l *Limiter) Release(path string, headers http.Header) error { case reset != "": unix, err := strconv.ParseFloat(reset, 64) if err != nil { - return errors.Wrap(err, "Invalid reset "+reset) + return errors.Wrap(err, "invalid reset "+reset) } b.reset = time.Unix(0, int64(unix*float64(time.Second))). @@ -193,7 +193,7 @@ func (l *Limiter) Release(path string, headers http.Header) error { if remaining != "" { u, err := strconv.ParseUint(remaining, 10, 64) if err != nil { - return errors.Wrap(err, "Invalid remaining "+remaining) + return errors.Wrap(err, "invalid remaining "+remaining) } b.remaining = u diff --git a/api/rate/rate_test.go b/api/rate/rate_test.go index ea65c23..300288a 100644 --- a/api/rate/rate_test.go +++ b/api/rate/rate_test.go @@ -46,7 +46,7 @@ func TestRatelimitReset(t *testing.T) { if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 { t.Log("OK", time.Since(sent)) } else { - t.Error("Did not ratelimit correctly, got:", time.Since(sent)) + t.Error("did not ratelimit correctly, got:", time.Since(sent)) } } @@ -71,6 +71,6 @@ func TestRatelimitGlobal(t *testing.T) { if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 { t.Log("OK", time.Since(sent)) } else { - t.Error("Did not ratelimit correctly, got:", time.Since(sent)) + t.Error("did not ratelimit correctly, got:", time.Since(sent)) } } diff --git a/api/send.go b/api/send.go index bf99b34..e8d534e 100644 --- a/api/send.go +++ b/api/send.go @@ -64,21 +64,21 @@ const ( // AllowedMentions' documentation. This will be called on SendMessageComplex. func (am AllowedMentions) Verify() error { if len(am.Roles) > 100 { - return errors.Errorf("Roles slice length %d is over 100", len(am.Roles)) + return errors.Errorf("roles slice length %d is over 100", len(am.Roles)) } if len(am.Users) > 100 { - return errors.Errorf("Users slice length %d is over 100", len(am.Users)) + return errors.Errorf("users slice length %d is over 100", len(am.Users)) } for _, allowed := range am.Parse { switch allowed { case AllowRoleMention: if len(am.Roles) > 0 { - return errors.New(`Parse has AllowRoleMention and Roles slice is not empty`) + return errors.New(`parse has AllowRoleMention and Roles slice is not empty`) } case AllowUserMention: if len(am.Users) > 0 { - return errors.New(`Parse has AllowUserMention and Users slice is not empty`) + return errors.New(`parse has AllowUserMention and Users slice is not empty`) } } } @@ -88,7 +88,7 @@ func (am AllowedMentions) Verify() error { // ErrEmptyMessage is returned if either a SendMessageData or an // ExecuteWebhookData has both an empty Content and no Embed(s). -var ErrEmptyMessage = errors.New("Message is empty") +var ErrEmptyMessage = errors.New("message is empty") // SendMessageFile represents a file to be uploaded to Discord. type SendMessageFile struct { @@ -146,13 +146,13 @@ func (c *Client) SendMessageComplex( if data.AllowedMentions != nil { if err := data.AllowedMentions.Verify(); err != nil { - return nil, errors.Wrap(err, "AllowedMentions error") + return nil, errors.Wrap(err, "allowedMentions error") } } if data.Embed != nil { if err := data.Embed.Validate(); err != nil { - return nil, errors.Wrap(err, "Embed error") + return nil, errors.Wrap(err, "embed error") } } @@ -222,13 +222,13 @@ func (c *Client) ExecuteWebhook( if data.AllowedMentions != nil { if err := data.AllowedMentions.Verify(); err != nil { - return nil, errors.Wrap(err, "AllowedMentions error") + return nil, errors.Wrap(err, "allowedMentions error") } } for i, embed := range data.Embeds { if err := embed.Validate(); err != nil { - return nil, errors.Wrap(err, "Embed error at "+strconv.Itoa(i)) + return nil, errors.Wrap(err, "embed error at "+strconv.Itoa(i)) } } @@ -272,11 +272,11 @@ func writeMultipart(body *multipart.Writer, item interface{}, files []SendMessag // Encode the JSON body first w, err := body.CreateFormField("payload_json") if err != nil { - return errors.Wrap(err, "Failed to create bodypart for JSON") + return errors.Wrap(err, "failed to create bodypart for JSON") } if err := json.EncodeStream(w, item); err != nil { - return errors.Wrap(err, "Failed to encode JSON") + return errors.Wrap(err, "failed to encode JSON") } for i, file := range files { @@ -284,11 +284,11 @@ func writeMultipart(body *multipart.Writer, item interface{}, files []SendMessag w, err := body.CreateFormFile("file"+num, file.Name) if err != nil { - return errors.Wrap(err, "Failed to create bodypart for "+num) + return errors.Wrap(err, "failed to create bodypart for "+num) } if _, err := io.Copy(w, file.Reader); err != nil { - return errors.Wrap(err, "Failed to write for file "+num) + return errors.Wrap(err, "failed to write for file "+num) } } diff --git a/api/send_test.go b/api/send_test.go index 772ac42..c2ffe10 100644 --- a/api/send_test.go +++ b/api/send_test.go @@ -61,7 +61,7 @@ func TestVerifyAllowedMentions(t *testing.T) { } err := am.Verify() - errMustContain(t, err, "Users slice length 101 is over 100") + errMustContain(t, err, "users slice length 101 is over 100") }) t.Run("roles too long", func(t *testing.T) { @@ -70,7 +70,7 @@ func TestVerifyAllowedMentions(t *testing.T) { } err := am.Verify() - errMustContain(t, err, "Roles slice length 101 is over 100") + errMustContain(t, err, "roles slice length 101 is over 100") }) t.Run("valid", func(t *testing.T) { @@ -130,7 +130,7 @@ func TestSendMessage(t *testing.T) { } err := send(data) - errMustContain(t, err, "AllowedMentions error") + errMustContain(t, err, "allowedMentions error") }) t.Run("invalid embed", func(t *testing.T) { @@ -142,7 +142,7 @@ func TestSendMessage(t *testing.T) { } err := send(data) - errMustContain(t, err, "Embed error") + errMustContain(t, err, "embed error") }) } diff --git a/bot/command.go b/bot/command.go index 3077769..7f108eb 100644 --- a/bot/command.go +++ b/bot/command.go @@ -149,7 +149,7 @@ func parseMethod(value reflect.Value, method reflect.Method) *MethodContext { t := methodT.In(i) a, err := newArgument(t, command.Variadic) if err != nil { - panic("Error parsing argument " + t.String() + ": " + err.Error()) + panic("error parsing argument " + t.String() + ": " + err.Error()) } command.Arguments = append(command.Arguments, *a) @@ -228,7 +228,7 @@ func ParseMiddleware(mw interface{}) *MiddlewareContext { // Check the last return's type if the method returns anything. if numOut == 1 { if i := methodT.Out(0); i == nil || !i.Implements(typeIError) { - panic("Unexpected return type (not error) for " + methodT.String()) + panic("unexpected return type (not error) for " + methodT.String()) } } diff --git a/bot/ctx.go b/bot/ctx.go index 4b71d16..67d81b3 100644 --- a/bot/ctx.go +++ b/bot/ctx.go @@ -145,12 +145,12 @@ func Start(token string, cmd interface{}, s, err := state.New("Bot " + token) if err != nil { - return nil, errors.Wrap(err, "Failed to create a dgo session") + return nil, errors.Wrap(err, "failed to create a dgo session") } c, err := New(s, cmd) if err != nil { - return nil, errors.Wrap(err, "Failed to create rfrouter") + return nil, errors.Wrap(err, "failed to create rfrouter") } s.Gateway.ErrorLog = func(err error) { @@ -166,7 +166,7 @@ func Start(token string, cmd interface{}, cancel := c.Start() if err := s.Open(); err != nil { - return nil, errors.Wrap(err, "Failed to connect to Discord") + return nil, errors.Wrap(err, "failed to connect to Discord") } return func() error { @@ -221,7 +221,7 @@ func New(s *state.State, cmd interface{}) (*Context, error) { } if err := ctx.InitCommands(ctx); err != nil { - return nil, errors.Wrap(err, "Failed to initialize with given cmds") + return nil, errors.Wrap(err, "failed to initialize with given cmds") } return ctx, nil @@ -282,7 +282,7 @@ func (ctx *Context) RegisterSubcommand(cmd interface{}) (*Subcommand, error) { func (ctx *Context) RegisterSubcommandCustom(cmd interface{}, name string) (*Subcommand, error) { s, err := NewSubcommand(cmd) if err != nil { - return nil, errors.Wrap(err, "Failed to add subcommand") + return nil, errors.Wrap(err, "failed to add subcommand") } // Register the subcommand's name. @@ -293,13 +293,13 @@ func (ctx *Context) RegisterSubcommandCustom(cmd interface{}, name string) (*Sub } if err := s.InitCommands(ctx); err != nil { - return nil, errors.Wrap(err, "Failed to initialize subcommand") + return nil, errors.Wrap(err, "failed to initialize subcommand") } // Do a collision check for _, sub := range ctx.subcommands { if sub.Command == s.Command { - return nil, errors.New("New subcommand has duplicate name: " + s.Command) + return nil, errors.New("new subcommand has duplicate name: " + s.Command) } } @@ -332,7 +332,7 @@ func (ctx *Context) Start() func() { case *ErrInvalidUsage, *ErrUnknownCommand: // Ignore default: - ctx.ErrorLogger(errors.Wrap(err, "Command error")) + ctx.ErrorLogger(errors.Wrap(err, "command error")) } return diff --git a/bot/ctx_call.go b/bot/ctx_call.go index 9415bc1..f378687 100644 --- a/bot/ctx_call.go +++ b/bot/ctx_call.go @@ -127,7 +127,7 @@ func (ctx *Context) callMessageCreate(mc *gateway.MessageCreateEvent, value refl // parse arguments parts, err := ctx.ParseArgs(content) if err != nil { - return errors.Wrap(err, "Failed to parse command") + return errors.Wrap(err, "failed to parse command") } if len(parts) == 0 { diff --git a/bot/ctx_test.go b/bot/ctx_test.go index 3aba102..f9c5028 100644 --- a/bot/ctx_test.go +++ b/bot/ctx_test.go @@ -254,7 +254,7 @@ func TestContext(t *testing.T) { err := testMessage("joe pls no") - if err == nil || !strings.HasPrefix(err.Error(), "Unknown command:") { + if err == nil || !strings.HasPrefix(err.Error(), "unknown command:") { t.Fatal("unexpected error:", err) } }) @@ -350,7 +350,7 @@ func sendMsg(ctx *Context, given *testc, into interface{}, content string) (call return fmt.Errorf("expected return before error: %w", call) case <-time.After(time.Second): - return errors.New("Timed out while waiting") + return errors.New("timed out while waiting") } } diff --git a/bot/error.go b/bot/error.go index 75ee5b5..7bafdba 100644 --- a/bot/error.go +++ b/bot/error.go @@ -18,12 +18,12 @@ func (err *ErrUnknownCommand) Error() string { } var UnknownCommandString = func(err *ErrUnknownCommand) string { - return "Unknown command: " + strings.Join(err.Parts, " ") + return "unknown command: " + strings.Join(err.Parts, " ") } var ( - ErrTooManyArgs = errors.New("Too many arguments given") - ErrNotEnoughArgs = errors.New("Not enough arguments given") + ErrTooManyArgs = errors.New("too many arguments given") + ErrNotEnoughArgs = errors.New("not enough arguments given") ) type ErrInvalidUsage struct { @@ -47,11 +47,11 @@ func (err *ErrInvalidUsage) Unwrap() error { var InvalidUsageString = func(err *ErrInvalidUsage) string { if err.Index == 0 && err.Wrap != nil { - return "Invalid usage, error: " + err.Wrap.Error() + "." + return "invalid usage, error: " + err.Wrap.Error() + "." } if err.Index == 0 || len(err.Args) == 0 { - return "Missing arguments. Refer to help." + return "missing arguments. Refer to help." } body := "Invalid usage at " + diff --git a/bot/error_test.go b/bot/error_test.go index c86cebb..0982556 100644 --- a/bot/error_test.go +++ b/bot/error_test.go @@ -29,7 +29,7 @@ func TestInvalidUsage(t *testing.T) { err := ErrInvalidUsage{} str := err.Error() - if str != "Missing arguments. Refer to help." { + if str != "missing arguments. Refer to help." { t.Fatal("Unexpected error:", str) } }) @@ -38,7 +38,7 @@ func TestInvalidUsage(t *testing.T) { err := ErrInvalidUsage{Wrap: errors.New("astolfo")} str := err.Error() - if str != "Invalid usage, error: astolfo." { + if str != "invalid usage, error: astolfo." { t.Fatal("Unexpected error:", str) } }) diff --git a/bot/extras/arguments/emoji.go b/bot/extras/arguments/emoji.go index 0232c9b..796c064 100644 --- a/bot/extras/arguments/emoji.go +++ b/bot/extras/arguments/emoji.go @@ -11,7 +11,7 @@ import ( var ( EmojiRegex = regexp.MustCompile(`<(a?):(.+?):(\d+)>`) - ErrInvalidEmoji = errors.New("Invalid emoji") + ErrInvalidEmoji = errors.New("invalid emoji") ) type Emoji struct { diff --git a/bot/extras/arguments/link.go b/bot/extras/arguments/link.go index 06d39e1..9e7afa2 100644 --- a/bot/extras/arguments/link.go +++ b/bot/extras/arguments/link.go @@ -25,7 +25,7 @@ type MessageURL struct { func (url *MessageURL) Parse(arg string) error { u := ParseMessageURL(arg) if u == nil { - return errors.New("Invalid MessageURL format.") + return errors.New("invalid MessageURL format") } *url = *u return nil diff --git a/bot/extras/arguments/mention.go b/bot/extras/arguments/mention.go index 97d8597..062e1de 100644 --- a/bot/extras/arguments/mention.go +++ b/bot/extras/arguments/mention.go @@ -78,12 +78,12 @@ func (m *RoleMention) Mention() string { func grabFirst(reg *regexp.Regexp, item, input string, output *discord.Snowflake) error { matches := reg.FindStringSubmatch(input) if len(matches) < 2 { - return errors.New("Invalid " + item) + return errors.New("invalid " + item) } id, err := discord.ParseSnowflake(matches[1]) if err != nil { - return errors.New("Invalid " + item) + return errors.New("invalid " + item) } *output = id diff --git a/bot/subcommand.go b/bot/subcommand.go index 7b76b9c..2e0ff01 100644 --- a/bot/subcommand.go +++ b/bot/subcommand.go @@ -134,11 +134,11 @@ func NewSubcommand(cmd interface{}) (*Subcommand, error) { } if err := sub.reflectCommands(); err != nil { - return nil, errors.Wrap(err, "Failed to reflect commands") + return nil, errors.Wrap(err, "failed to reflect commands") } if err := sub.parseCommands(); err != nil { - return nil, errors.Wrap(err, "Failed to parse commands") + return nil, errors.Wrap(err, "failed to parse commands") } return &sub, nil @@ -292,7 +292,7 @@ func (sub *Subcommand) fillStruct(ctx *Context) error { return nil } - return errors.New("No fields with *bot.Context found") + return errors.New("no fields with *bot.Context found") } func (sub *Subcommand) parseCommands() error { diff --git a/discord/auditlog.go b/discord/auditlog.go index e79d6bf..7f30ce4 100644 --- a/discord/auditlog.go +++ b/discord/auditlog.go @@ -133,10 +133,10 @@ type AuditLogChange struct { func (a AuditLogChange) UnmarshalValues(old, new interface{}) error { if err := a.NewValue.UnmarshalTo(new); err != nil { - return errors.Wrap(err, "Failed to unmarshal old value") + return errors.Wrap(err, "failed to unmarshal old value") } if err := a.OldValue.UnmarshalTo(old); err != nil { - return errors.Wrap(err, "Failed to unmarshal new value") + return errors.Wrap(err, "failed to unmarshal new value") } return nil } diff --git a/discord/invite.go b/discord/invite.go index c221333..d6fdb4c 100644 --- a/discord/invite.go +++ b/discord/invite.go @@ -1,22 +1,38 @@ package discord +// Invite represents a code that when used, adds a user to a guild or group +// DM channel. +// +// https://discord.com/developers/docs/resources/invite#invite-object type Invite struct { - Code string `json:"code"` - Channel Channel `json:"channel"` // partial - Guild *Guild `json:"guild,omitempty"` // partial - Inviter *User `json:"inviter,omitempty"` + // Code is the invite code (unique ID). + Code string `json:"code"` + // Guild is the partial guild this invite is for. + Guild *Guild `json:"guild,omitempty"` + // Channel is the partial channel this invite is for. + Channel Channel `json:"channel"` + // Inviter is the user who created the invite + Inviter *User `json:"inviter,omitempty"` - ApproxMembers uint `json:"approximate_members_count,omitempty"` - - Target *User `json:"target_user,omitempty"` // partial + // Target is the target user for this invite. + Target *User `json:"target_user,omitempty"` + // Target type is the type of user target for this invite. TargetType InviteUserType `json:"target_user_type,omitempty"` - // Only available if Target is - ApproxPresences uint `json:"approximate_presence_count,omitempty"` + // ApproximatePresences is the approximate count of online members (only + // present when Target is set). + ApproximatePresences uint `json:"approximate_presence_count,omitempty"` + // ApproximateMembers is the approximate count of total members + ApproximateMembers uint `json:"approximate_member_count,omitempty"` - InviteMetadata // only available when fetching ChannelInvites or GuildInvites + // InviteMetadata contains extra information about the invite. + // So far, this field is only available when fetching Channel- or + // GuildInvites. Additionally the Uses field is filled when getting the + // VanityURL of a guild. + InviteMetadata } +// https://discord.com/developers/docs/resources/invite#invite-object-target-user-types type InviteUserType uint8 const ( @@ -25,6 +41,8 @@ const ( ) // Extra information about an invite, will extend the invite object. +// +// https://discord.com/developers/docs/resources/invite#invite-metadata-object type InviteMetadata struct { // Number of times this invite has been used Uses int `json:"uses"` diff --git a/discord/message_embed.go b/discord/message_embed.go index 0544953..4318202 100644 --- a/discord/message_embed.go +++ b/discord/message_embed.go @@ -80,15 +80,15 @@ func (e *Embed) Validate() error { } if len(e.Title) > 256 { - return &ErrOverbound{len(e.Title), 256, "Title"} + return &ErrOverbound{len(e.Title), 256, "title"} } if len(e.Description) > 2048 { - return &ErrOverbound{len(e.Description), 2048, "Description"} + return &ErrOverbound{len(e.Description), 2048, "description"} } if len(e.Fields) > 25 { - return &ErrOverbound{len(e.Fields), 25, "Fields"} + return &ErrOverbound{len(e.Fields), 25, "fields"} } var sum = 0 + @@ -97,7 +97,7 @@ func (e *Embed) Validate() error { if e.Footer != nil { if len(e.Footer.Text) > 2048 { - return &ErrOverbound{len(e.Footer.Text), 2048, "Footer text"} + return &ErrOverbound{len(e.Footer.Text), 2048, "footer text"} } sum += len(e.Footer.Text) @@ -105,7 +105,7 @@ func (e *Embed) Validate() error { if e.Author != nil { if len(e.Author.Name) > 256 { - return &ErrOverbound{len(e.Author.Name), 256, "Author name"} + return &ErrOverbound{len(e.Author.Name), 256, "author name"} } sum += len(e.Author.Name) @@ -126,7 +126,7 @@ func (e *Embed) Validate() error { } if sum > 6000 { - return &ErrOverbound{sum, 6000, "Sum of all characters"} + return &ErrOverbound{sum, 6000, "sum of all characters"} } return nil diff --git a/gateway/commands.go b/gateway/commands.go index 8a77946..a6a2283 100644 --- a/gateway/commands.go +++ b/gateway/commands.go @@ -16,7 +16,7 @@ func (g *Gateway) Identify() error { defer cancel() if err := g.Identifier.Wait(ctx); err != nil { - return errors.Wrap(err, "Can't wait for identify()") + return errors.Wrap(err, "can't wait for identify()") } return g.Send(IdentifyOP, g.Identifier) diff --git a/gateway/gateway.go b/gateway/gateway.go index 04b1c4f..7efba3d 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -112,7 +112,7 @@ type Gateway struct { func NewGateway(token string) (*Gateway, error) { URL, err := URL() if err != nil { - return nil, errors.Wrap(err, "Failed to get gateway endpoint") + return nil, errors.Wrap(err, "failed to get gateway endpoint") } // Parameters for the gateway @@ -200,7 +200,7 @@ func (g *Gateway) ReconnectContext(ctx context.Context) error { // https://discordapp.com/developers/docs/topics/gateway#rate-limiting if err := g.OpenContext(ctx); err != nil { - g.ErrorLog(errors.Wrap(err, "Failed to open gateway")) + g.ErrorLog(errors.Wrap(err, "failed to open gateway")) continue } @@ -218,7 +218,7 @@ func (g *Gateway) Open() error { func (g *Gateway) OpenContext(ctx context.Context) error { // Reconnect to the Gateway if err := g.WS.Dial(ctx); err != nil { - return errors.Wrap(err, "Failed to reconnect") + return errors.Wrap(err, "failed to reconnect") } wsutil.WSDebug("Trying to start...") @@ -262,7 +262,7 @@ func (g *Gateway) start() error { // Wait for an OP 10 Hello var hello HelloEvent if _, err := wsutil.AssertEvent(<-ch, HelloOP, &hello); err != nil { - return errors.Wrap(err, "Error at Hello") + return errors.Wrap(err, "error at Hello") } // Send Discord either the Identify packet (if it's a fresh connection), or @@ -270,11 +270,11 @@ func (g *Gateway) start() error { if g.SessionID == "" { // SessionID is empty, so this is a completely new session. if err := g.Identify(); err != nil { - return errors.Wrap(err, "Failed to identify") + return errors.Wrap(err, "failed to identify") } } else { if err := g.Resume(); err != nil { - return errors.Wrap(err, "Failed to resume") + return errors.Wrap(err, "failed to resume") } } @@ -295,7 +295,7 @@ func (g *Gateway) start() error { }) if err != nil { - return errors.Wrap(err, "First error") + return errors.Wrap(err, "first error") } // Use the pacemaker loop. @@ -327,7 +327,7 @@ func (g *Gateway) Send(code OPCode, v interface{}) error { if v != nil { b, err := json.Marshal(v) if err != nil { - return errors.Wrap(err, "Failed to encode v") + return errors.Wrap(err, "failed to encode v") } op.Data = b @@ -335,7 +335,7 @@ func (g *Gateway) Send(code OPCode, v interface{}) error { b, err := json.Marshal(op) if err != nil { - return errors.Wrap(err, "Failed to encode payload") + return errors.Wrap(err, "failed to encode payload") } // WS should already be thread-safe. diff --git a/gateway/identify.go b/gateway/identify.go index 916795e..4031b26 100644 --- a/gateway/identify.go +++ b/gateway/identify.go @@ -107,10 +107,10 @@ func NewIdentifier(data IdentifyData) *Identifier { func (i *Identifier) Wait(ctx context.Context) error { if err := i.IdentifyShortLimit.Wait(ctx); err != nil { - return errors.Wrap(err, "Can't wait for short limit") + return errors.Wrap(err, "can't wait for short limit") } if err := i.IdentifyGlobalLimit.Wait(ctx); err != nil { - return errors.Wrap(err, "Can't wait for global limit") + return errors.Wrap(err, "can't wait for global limit") } return nil } diff --git a/gateway/op.go b/gateway/op.go index 04724cf..d862307 100644 --- a/gateway/op.go +++ b/gateway/op.go @@ -84,7 +84,7 @@ func (g *Gateway) HandleOP(op *wsutil.OP) error { // Try and parse the event if err := json.Unmarshal(op.Data, ev); err != nil { - return errors.Wrap(err, "Failed to parse event "+op.EventName) + return errors.Wrap(err, "failed to parse event "+op.EventName) } // If the event is a ready, we'll want its sessionID @@ -97,7 +97,7 @@ func (g *Gateway) HandleOP(op *wsutil.OP) error { return nil default: - return fmt.Errorf("Unknown OP code %d (event %s)", op.Code, op.EventName) + return fmt.Errorf("unknown OP code %d (event %s)", op.Code, op.EventName) } return nil diff --git a/handler/handler.go b/handler/handler.go index c16afd8..40480d9 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -157,7 +157,7 @@ func (h *Handler) addHandler(fn interface{}) (rm func(), err error) { // Reflect the handler r, err := reflectFn(fn) if err != nil { - return nil, errors.Wrap(err, "Handler reflect failed") + return nil, errors.Wrap(err, "handler reflect failed") } h.hmutex.Lock() diff --git a/session/session.go b/session/session.go index 65fd284..b54b4bb 100644 --- a/session/session.go +++ b/session/session.go @@ -22,7 +22,7 @@ type Closed struct { Error error } -var ErrMFA = errors.New("Account has 2FA enabled") +var ErrMFA = errors.New("account has 2FA enabled") // Session manages both the API and Gateway. As such, Session inherits all of // API's methods, as well has the Handler used for Gateway. @@ -49,7 +49,7 @@ func New(token string) (*Session, error) { // Create a gateway g, err := gateway.NewGateway(token) if err != nil { - return nil, errors.Wrap(err, "Failed to connect to Gateway") + return nil, errors.Wrap(err, "failed to connect to Gateway") } s.Gateway = g @@ -64,7 +64,7 @@ func Login(email, password, mfa string) (*Session, error) { // Try to login without TOTP l, err := client.Login(email, password) if err != nil { - return nil, errors.Wrap(err, "Failed to login") + return nil, errors.Wrap(err, "failed to login") } if l.Token != "" && !l.MFA { @@ -80,7 +80,7 @@ func Login(email, password, mfa string) (*Session, error) { // Retry logging in with a 2FA token l, err = client.TOTP(mfa, l.Ticket) if err != nil { - return nil, errors.Wrap(err, "Failed to login with 2FA") + return nil, errors.Wrap(err, "failed to login with 2FA") } return New(l.Token) @@ -109,7 +109,7 @@ func (s *Session) Open() error { } if err := s.Gateway.Open(); err != nil { - return errors.Wrap(err, "Failed to start gateway") + return errors.Wrap(err, "failed to start gateway") } return nil diff --git a/state/state.go b/state/state.go index 9a384ad..57d60ed 100644 --- a/state/state.go +++ b/state/state.go @@ -121,34 +121,49 @@ func (s *State) MemberDisplayName(guildID, userID discord.Snowflake) (string, er return member.Nick, nil } -func (s *State) AuthorColor(message *gateway.MessageCreateEvent) discord.Color { - if !message.GuildID.Valid() { - return discord.DefaultMemberColor +func (s *State) AuthorColor(message *gateway.MessageCreateEvent) (discord.Color, error) { + if !message.GuildID.Valid() { // this is a dm + return discord.DefaultMemberColor, nil } if message.Member != nil { guild, err := s.Guild(message.GuildID) if err != nil { - return discord.DefaultMemberColor + return 0, err } - return discord.MemberColor(*guild, *message.Member) + return discord.MemberColor(*guild, *message.Member), nil } return s.MemberColor(message.GuildID, message.Author.ID) } -func (s *State) MemberColor(guildID, userID discord.Snowflake) discord.Color { - member, err := s.Member(guildID, userID) - if err != nil { - return discord.DefaultMemberColor +func (s *State) MemberColor(guildID, userID discord.Snowflake) (discord.Color, error) { + var wg sync.WaitGroup + + g, gerr := s.Store.Guild(guildID) + if gerr != nil { + wg.Add(1) + go func() { + g, gerr = s.Session.Guild(guildID) + wg.Done() + }() } - guild, err := s.Guild(guildID) - if err != nil { - return discord.DefaultMemberColor + m, merr := s.Store.Member(guildID, userID) + if merr != nil { + m, merr = s.Member(guildID, userID) + if merr != nil { + return 0, errors.Wrap(merr, "failed to get member") + } } - return discord.MemberColor(*guild, *member) + wg.Wait() + + if gerr != nil { + return 0, errors.Wrap(merr, "failed to get guild") + } + + return discord.MemberColor(*g, *m), nil } //// @@ -156,17 +171,32 @@ func (s *State) MemberColor(guildID, userID discord.Snowflake) discord.Color { func (s *State) Permissions(channelID, userID discord.Snowflake) (discord.Permissions, error) { ch, err := s.Channel(channelID) if err != nil { - return 0, errors.Wrap(err, "Failed to get channel") + return 0, errors.Wrap(err, "failed to get channel") } - g, err := s.Guild(ch.GuildID) - if err != nil { - return 0, errors.Wrap(err, "Failed to get guild") + var wg sync.WaitGroup + + g, gerr := s.Store.Guild(ch.GuildID) + if gerr != nil { + wg.Add(1) + go func() { + g, gerr = s.Session.Guild(ch.GuildID) + wg.Done() + }() } - m, err := s.Member(ch.GuildID, userID) - if err != nil { - return 0, errors.Wrap(err, "Failed to get member") + m, merr := s.Store.Member(ch.GuildID, userID) + if merr != nil { + m, merr = s.Member(ch.GuildID, userID) + if merr != nil { + return 0, errors.Wrap(merr, "failed to get member") + } + } + + wg.Wait() + + if gerr != nil { + return 0, errors.Wrap(merr, "failed to get guild") } return discord.CalcOverwrites(*g, *ch, *m), nil diff --git a/state/state_events.go b/state/state_events.go index e8dfe34..afc2b77 100644 --- a/state/state_events.go +++ b/state/state_events.go @@ -29,7 +29,7 @@ func (s *State) onEvent(iface interface{}) { p := p if err := s.Store.PresenceSet(0, &p); err != nil { - s.stateErr(err, "Failed to set global presence") + s.stateErr(err, "failed to set global presence") } } @@ -41,13 +41,13 @@ func (s *State) onEvent(iface interface{}) { // Handle private channels for i := range ev.PrivateChannels { if err := s.Store.ChannelSet(&ev.PrivateChannels[i]); err != nil { - s.stateErr(err, "Failed to set channel in state") + s.stateErr(err, "failed to set channel in state") } } // Handle user if err := s.Store.MyselfSet(&ev.User); err != nil { - s.stateErr(err, "Failed to set self in state") + s.stateErr(err, "failed to set self in state") } case *gateway.GuildCreateEvent: @@ -55,17 +55,17 @@ func (s *State) onEvent(iface interface{}) { case *gateway.GuildUpdateEvent: if err := s.Store.GuildSet((*discord.Guild)(ev)); err != nil { - s.stateErr(err, "Failed to update guild in state") + s.stateErr(err, "failed to update guild in state") } case *gateway.GuildDeleteEvent: if err := s.Store.GuildRemove(ev.ID); err != nil { - s.stateErr(err, "Failed to delete guild in state") + s.stateErr(err, "failed to delete guild in state") } case *gateway.GuildMemberAddEvent: if err := s.Store.MemberSet(ev.GuildID, &ev.Member); err != nil { - s.stateErr(err, "Failed to add a member in state") + s.stateErr(err, "failed to add a member in state") } case *gateway.GuildMemberUpdateEvent: @@ -79,12 +79,12 @@ func (s *State) onEvent(iface interface{}) { ev.Update(m) if err := s.Store.MemberSet(ev.GuildID, m); err != nil { - s.stateErr(err, "Failed to update a member in state") + s.stateErr(err, "failed to update a member in state") } case *gateway.GuildMemberRemoveEvent: if err := s.Store.MemberRemove(ev.GuildID, ev.User.ID); err != nil { - s.stateErr(err, "Failed to remove a member in state") + s.stateErr(err, "failed to remove a member in state") } case *gateway.GuildMembersChunkEvent: @@ -92,7 +92,7 @@ func (s *State) onEvent(iface interface{}) { m := m if err := s.Store.MemberSet(ev.GuildID, &m); err != nil { - s.stateErr(err, "Failed to add a member from chunk in state") + s.stateErr(err, "failed to add a member from chunk in state") } } @@ -100,43 +100,43 @@ func (s *State) onEvent(iface interface{}) { p := p if err := s.Store.PresenceSet(ev.GuildID, &p); err != nil { - s.stateErr(err, "Failed to add a presence from chunk in state") + s.stateErr(err, "failed to add a presence from chunk in state") } } case *gateway.GuildRoleCreateEvent: if err := s.Store.RoleSet(ev.GuildID, &ev.Role); err != nil { - s.stateErr(err, "Failed to add a role in state") + s.stateErr(err, "failed to add a role in state") } case *gateway.GuildRoleUpdateEvent: if err := s.Store.RoleSet(ev.GuildID, &ev.Role); err != nil { - s.stateErr(err, "Failed to update a role in state") + s.stateErr(err, "failed to update a role in state") } case *gateway.GuildRoleDeleteEvent: if err := s.Store.RoleRemove(ev.GuildID, ev.RoleID); err != nil { - s.stateErr(err, "Failed to remove a role in state") + s.stateErr(err, "failed to remove a role in state") } case *gateway.GuildEmojisUpdateEvent: if err := s.Store.EmojiSet(ev.GuildID, ev.Emojis); err != nil { - s.stateErr(err, "Failed to update emojis in state") + s.stateErr(err, "failed to update emojis in state") } case *gateway.ChannelCreateEvent: if err := s.Store.ChannelSet((*discord.Channel)(ev)); err != nil { - s.stateErr(err, "Failed to create a channel in state") + s.stateErr(err, "failed to create a channel in state") } case *gateway.ChannelUpdateEvent: if err := s.Store.ChannelSet((*discord.Channel)(ev)); err != nil { - s.stateErr(err, "Failed to update a channel in state") + s.stateErr(err, "failed to update a channel in state") } case *gateway.ChannelDeleteEvent: if err := s.Store.ChannelRemove((*discord.Channel)(ev)); err != nil { - s.stateErr(err, "Failed to remove a channel in state") + s.stateErr(err, "failed to remove a channel in state") } case *gateway.ChannelPinsUpdateEvent: @@ -144,23 +144,23 @@ func (s *State) onEvent(iface interface{}) { case *gateway.MessageCreateEvent: if err := s.Store.MessageSet(&ev.Message); err != nil { - s.stateErr(err, "Failed to add a message in state") + s.stateErr(err, "failed to add a message in state") } case *gateway.MessageUpdateEvent: if err := s.Store.MessageSet(&ev.Message); err != nil { - s.stateErr(err, "Failed to update a message in state") + s.stateErr(err, "failed to update a message in state") } case *gateway.MessageDeleteEvent: if err := s.Store.MessageRemove(ev.ChannelID, ev.ID); err != nil { - s.stateErr(err, "Failed to delete a message in state") + s.stateErr(err, "failed to delete a message in state") } case *gateway.MessageDeleteBulkEvent: for _, id := range ev.IDs { if err := s.Store.MessageRemove(ev.ChannelID, id); err != nil { - s.stateErr(err, "Failed to delete bulk meessages in state") + s.stateErr(err, "failed to delete bulk meessages in state") } } @@ -225,7 +225,7 @@ func (s *State) onEvent(iface interface{}) { case *gateway.PresenceUpdateEvent: if err := s.Store.PresenceSet(ev.GuildID, &ev.Presence); err != nil { - s.stateErr(err, "Failed to update presence in state") + s.stateErr(err, "failed to update presence in state") } case *gateway.PresencesReplaceEvent: @@ -233,7 +233,7 @@ func (s *State) onEvent(iface interface{}) { p := (*ev)[i] if err := s.Store.PresenceSet(p.GuildID, &p); err != nil { - s.stateErr(err, "Failed to update presence in state") + s.stateErr(err, "failed to update presence in state") } } @@ -254,18 +254,18 @@ func (s *State) onEvent(iface interface{}) { case *gateway.UserUpdateEvent: if err := s.Store.MyselfSet(&ev.User); err != nil { - s.stateErr(err, "Failed to update myself from USER_UPDATE") + s.stateErr(err, "failed to update myself from USER_UPDATE") } case *gateway.VoiceStateUpdateEvent: vs := &ev.VoiceState if vs.ChannelID == 0 { if err := s.Store.VoiceStateRemove(vs.GuildID, vs.UserID); err != nil { - s.stateErr(err, "Failed to remove voice state from state") + s.stateErr(err, "failed to remove voice state from state") } } else { if err := s.Store.VoiceStateSet(vs.GuildID, vs); err != nil { - s.stateErr(err, "Failed to update voice state in state") + s.stateErr(err, "failed to update voice state in state") } } } @@ -291,7 +291,7 @@ func (s *State) editMessage(ch, msg discord.Snowflake, fn func(m *discord.Messag return } if err := s.Store.MessageSet(m); err != nil { - s.stateErr(err, "Failed to save message in reaction add") + s.stateErr(err, "failed to save message in reaction add") } } @@ -314,41 +314,41 @@ func handleGuildCreate(store Store, guild *gateway.GuildCreateEvent) []error { stack, error := newErrorStack() if err := store.GuildSet(&guild.Guild); err != nil { - error(err, "Failed to set guild in Ready") + error(err, "failed to set guild in Ready") } // Handle guild emojis if guild.Emojis != nil { if err := store.EmojiSet(guild.ID, guild.Emojis); err != nil { - error(err, "Failed to set guild emojis") + error(err, "failed to set guild emojis") } } // Handle guild member for i := range guild.Members { if err := store.MemberSet(guild.ID, &guild.Members[i]); err != nil { - error(err, "Failed to set guild member in Ready") + error(err, "failed to set guild member in Ready") } } // Handle guild channels for i := range guild.Channels { if err := store.ChannelSet(&guild.Channels[i]); err != nil { - error(err, "Failed to set guild channel in Ready") + error(err, "failed to set guild channel in Ready") } } // Handle guild presences for i := range guild.Presences { if err := store.PresenceSet(guild.ID, &guild.Presences[i]); err != nil { - error(err, "Failed to set guild presence in Ready") + error(err, "failed to set guild presence in Ready") } } // Handle guild voice states for i := range guild.VoiceStates { if err := store.VoiceStateSet(guild.ID, &guild.VoiceStates[i]); err != nil { - error(err, "Failed to set guild voice state in Ready") + error(err, "failed to set guild voice state in Ready") } } diff --git a/state/store_noop.go b/state/store_noop.go index d7888c5..a64deda 100644 --- a/state/store_noop.go +++ b/state/store_noop.go @@ -13,7 +13,7 @@ type NoopStore struct{} var _ Store = (*NoopStore)(nil) -var ErrNotImplemented = errors.New("State is not implemented") +var ErrNotImplemented = errors.New("state is not implemented") func (NoopStore) Reset() error { return nil diff --git a/utils/httputil/client.go b/utils/httputil/client.go index bd85ced..06826f3 100644 --- a/utils/httputil/client.go +++ b/utils/httputil/client.go @@ -165,7 +165,7 @@ func (c *Client) Request(method, url string, opts ...RequestOption) (httpdriver. } if err := c.applyOptions(q, opts); err != nil { - return nil, errors.Wrap(err, "Failed to apply options") + return nil, errors.Wrap(err, "failed to apply options") } r, doErr = c.Client.Do(q) diff --git a/utils/httputil/errors.go b/utils/httputil/errors.go index 9045b35..5e1b5cd 100644 --- a/utils/httputil/errors.go +++ b/utils/httputil/errors.go @@ -22,7 +22,7 @@ type RequestError struct { } func (r RequestError) Error() string { - return "Request failed: " + r.err.Error() + return "request failed: " + r.err.Error() } func (r RequestError) Unwrap() error { diff --git a/utils/wsutil/conn.go b/utils/wsutil/conn.go index effaa8e..86b30ca 100644 --- a/utils/wsutil/conn.go +++ b/utils/wsutil/conn.go @@ -20,7 +20,7 @@ const CopyBufferSize = 2048 var CloseDeadline = time.Second // ErrWebsocketClosed is returned if the websocket is already closed. -var ErrWebsocketClosed = errors.New("Websocket is closed.") +var ErrWebsocketClosed = errors.New("websocket is closed") // Connection is an interface that abstracts around a generic Websocket driver. // This connection expects the driver to handle compression by itself, including @@ -99,7 +99,7 @@ func (c *Conn) Dial(ctx context.Context, addr string) error { c.Conn, _, err = c.dialer.DialContext(ctx, addr, headers) if err != nil { - return errors.Wrap(err, "Failed to dial WS") + return errors.Wrap(err, "failed to dial WS") } // Set up the closer. @@ -191,12 +191,12 @@ func (c *Conn) handle() ([]byte, error) { if c.zlib == nil { z, err := zlib.NewReader(r) if err != nil { - return nil, errors.Wrap(err, "Failed to create a zlib reader") + return nil, errors.Wrap(err, "failed to create a zlib reader") } c.zlib = z } else { if err := c.zlib.(zlib.Resetter).Reset(r, nil); err != nil { - return nil, errors.Wrap(err, "Failed to reset zlib reader") + return nil, errors.Wrap(err, "failed to reset zlib reader") } } diff --git a/utils/wsutil/heart.go b/utils/wsutil/heart.go index dfb549f..4784e06 100644 --- a/utils/wsutil/heart.go +++ b/utils/wsutil/heart.go @@ -85,7 +85,7 @@ func (p *PacemakerLoop) startLoop() error { select { case err := <-p.pacedeath: WSDebug("Pacedeath returned with error:", err) - return errors.Wrap(err, "Pacemaker died, reconnecting") + return errors.Wrap(err, "pacemaker died, reconnecting") case ev, ok := <-p.events: if !ok { @@ -99,7 +99,7 @@ func (p *PacemakerLoop) startLoop() error { o, err := DecodeOP(ev) if err != nil { - return errors.Wrap(err, "Failed to decode OP") + return errors.Wrap(err, "failed to decode OP") } // Check the events before handling. @@ -107,7 +107,7 @@ func (p *PacemakerLoop) startLoop() error { // Handle the event if err := p.handler(o); err != nil { - p.errorLog(errors.Wrap(err, "Handler failed")) + p.errorLog(errors.Wrap(err, "handler failed")) } } } diff --git a/utils/wsutil/op.go b/utils/wsutil/op.go index 09ee868..437cf61 100644 --- a/utils/wsutil/op.go +++ b/utils/wsutil/op.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" ) -var ErrEmptyPayload = errors.New("Empty payload") +var ErrEmptyPayload = errors.New("empty payload") // OPCode is a generic type for websocket OP codes. type OPCode uint8 @@ -58,7 +58,7 @@ func AssertEvent(ev Event, code OPCode, v interface{}) (*OP, error) { } if err := json.Unmarshal(op.Data, v); err != nil { - return op, errors.Wrap(err, "Failed to decode data") + return op, errors.Wrap(err, "failed to decode data") } return op, nil @@ -100,7 +100,7 @@ func WaitForEvent(h EventHandler, ch <-chan Event, fn func(*OP) bool) error { } } - return errors.New("Event not found and event channel is closed.") + return errors.New("event not found and event channel is closed") } type ExtraHandlers struct { diff --git a/utils/wsutil/ws.go b/utils/wsutil/ws.go index 0b87ce2..c4e5c64 100644 --- a/utils/wsutil/ws.go +++ b/utils/wsutil/ws.go @@ -75,11 +75,11 @@ func (ws *Websocket) Dial(ctx context.Context) error { if err := ws.DialLimiter.Wait(ctx); err != nil { // Expired, fatal error - return errors.Wrap(err, "Failed to wait") + return errors.Wrap(err, "failed to wait") } if err := ws.Conn.Dial(ctx, ws.Addr); err != nil { - return errors.Wrap(err, "Failed to dial") + return errors.Wrap(err, "failed to dial") } // Reset the SendLimiter: diff --git a/utils/zlib/zlib.go b/utils/zlib/zlib.go index 606c8d7..ef5dab6 100644 --- a/utils/zlib/zlib.go +++ b/utils/zlib/zlib.go @@ -61,14 +61,14 @@ func (i *Inflator) Flush() ([]byte, error) { if i.zlib == nil { r, err := zlibStreamer(&i.wbuf) if err != nil { - return nil, errors.Wrap(err, "Failed to make a FLATE reader") + return nil, errors.Wrap(err, "failed to make a FLATE reader") } // safe assertion i.zlib = r // } else { // // Reset the FLATE reader for future use: // if err := i.zlib.Reset(&i.wbuf, nil); err != nil { - // return nil, errors.Wrap(err, "Failed to reset zlib reader") + // return nil, errors.Wrap(err, "failed to reset zlib reader") // } } @@ -79,12 +79,12 @@ func (i *Inflator) Flush() ([]byte, error) { // to verify checksum. Discord doesn't send this. if err != nil { // Unexpected error, try and close. - return nil, errors.Wrap(err, "Failed to read from FLATE reader") + return nil, errors.Wrap(err, "failed to read from FLATE reader") } // if err := i.zlib.Close(); err != nil && err != io.ErrUnexpectedEOF { // // Try and close anyway. - // return nil, errors.Wrap(err, "Failed to read from zlib reader") + // return nil, errors.Wrap(err, "failed to read from zlib reader") // } // Copy the bytes. @@ -105,7 +105,7 @@ func (i *Inflator) Flush() ([]byte, error) { // if d.zlib == nil { // r, err := zlib.NewReader(&d.wbuf) // if err != nil { -// return nil, errors.Wrap(err, "Failed to make a zlib reader") +// return nil, errors.Wrap(err, "failed to make a zlib reader") // } // // safe assertion // d.zlib = r @@ -125,12 +125,12 @@ func (i *Inflator) Flush() ([]byte, error) { // // to verify checksum. Discord doesn't send this. // // if err != nil && err != io.ErrUnexpectedEOF { // // // Unexpected error, try and close. -// // return nil, errors.Wrap(err, "Failed to read from zlib reader") +// // return nil, errors.Wrap(err, "failed to read from zlib reader") // // } // if err := d.zlib.Close(); err != nil && err != io.ErrUnexpectedEOF { // // Try and close anyway. -// return nil, errors.Wrap(err, "Failed to read from zlib reader") +// return nil, errors.Wrap(err, "failed to read from zlib reader") // } // // Copy the bytes. diff --git a/voice/session.go b/voice/session.go index df86e17..7b29348 100644 --- a/voice/session.go +++ b/voice/session.go @@ -74,7 +74,7 @@ func (s *Session) UpdateServer(ev *gateway.VoiceServerUpdateEvent) { s.state.Token = ev.Token if err := s.reconnect(); err != nil { - s.ErrorLog(errors.Wrap(err, "Failed to reconnect after voice server update")) + s.ErrorLog(errors.Wrap(err, "failed to reconnect after voice server update")) } } @@ -129,7 +129,7 @@ func (s *Session) JoinChannel(gID, cID discord.Snowflake, muted, deafened bool) SelfDeaf: deafened, }) if err != nil { - return errors.Wrap(err, "Failed to send Voice State Update event") + return errors.Wrap(err, "failed to send Voice State Update event") } // Wait for replies. The above command should reply with these 2 events. @@ -149,7 +149,7 @@ func (s *Session) reconnect() (err error) { // Open the voice gateway. The function will block until Ready is received. if err := s.gateway.Open(); err != nil { - return errors.Wrap(err, "Failed to open voice gateway") + return errors.Wrap(err, "failed to open voice gateway") } // Get the Ready event. @@ -158,7 +158,7 @@ func (s *Session) reconnect() (err error) { // Prepare the UDP voice connection. s.voiceUDP, err = udp.DialConnection(voiceReady.Addr(), voiceReady.SSRC) if err != nil { - return errors.Wrap(err, "Failed to open voice UDP connection") + return errors.Wrap(err, "failed to open voice UDP connection") } // Get the session description from the voice gateway. @@ -171,7 +171,7 @@ func (s *Session) reconnect() (err error) { }, }) if err != nil { - return errors.Wrap(err, "Failed to select protocol") + return errors.Wrap(err, "failed to select protocol") } // Start the UDP loop. @@ -194,7 +194,7 @@ func (s *Session) StopSpeaking() error { // Send 5 frames of silence. for i := 0; i < 5; i++ { if _, err := s.Write(OpusSilence[:]); err != nil { - return errors.Wrapf(err, "Failed to send frame %d", i) + return errors.Wrapf(err, "failed to send frame %d", i) } } return nil @@ -232,7 +232,7 @@ func (s *Session) Disconnect() error { s.ensureClosed() // wrap returns nil if err is nil - return errors.Wrap(err, "Failed to update voice state") + return errors.Wrap(err, "failed to update voice state") } // close ensures everything is closed. It does not acquire the mutex. diff --git a/voice/udp/udp.go b/voice/udp/udp.go index 6ba50a7..96288a5 100644 --- a/voice/udp/udp.go +++ b/voice/udp/udp.go @@ -33,13 +33,13 @@ func DialConnection(addr string, ssrc uint32) (*Connection, error) { // Resolve the host. a, err := net.ResolveUDPAddr("udp", addr) if err != nil { - return nil, errors.Wrap(err, "Failed to resolve host") + return nil, errors.Wrap(err, "failed to resolve host") } // Create a new UDP connection. conn, err := net.DialUDP("udp", nil, a) if err != nil { - return nil, errors.Wrap(err, "Failed to dial host") + return nil, errors.Wrap(err, "failed to dial host") } // https://discordapp.com/developers/docs/topics/voice-connections#ip-discovery @@ -51,7 +51,7 @@ func DialConnection(addr string, ssrc uint32) (*Connection, error) { _, err = conn.Write(ssrcBuffer[:]) if err != nil { - return nil, errors.Wrap(err, "Failed to write SSRC buffer") + return nil, errors.Wrap(err, "failed to write SSRC buffer") } var ipBuffer [70]byte @@ -59,7 +59,7 @@ func DialConnection(addr string, ssrc uint32) (*Connection, error) { // ReadFull makes sure to read all 70 bytes. _, err = io.ReadFull(conn, ipBuffer[:]) if err != nil { - return nil, errors.Wrap(err, "Failed to read IP buffer") + return nil, errors.Wrap(err, "failed to read IP buffer") } ipbody := ipBuffer[4:68] diff --git a/voice/voice.go b/voice/voice.go index a0ec3ce..c59207e 100644 --- a/voice/voice.go +++ b/voice/voice.go @@ -17,7 +17,7 @@ import ( var ( // defaultErrorHandler is the default error handler - defaultErrorHandler = func(err error) { log.Println("Voice gateway error:", err) } + defaultErrorHandler = func(err error) { log.Println("voice gateway error:", err) } // ErrCannotSend is an error when audio is sent to a closed channel. ErrCannotSend = errors.New("cannot send audio to closed channel") @@ -125,7 +125,7 @@ func (v *Voice) JoinChannel(gID, cID discord.Snowflake, muted, deafened bool) (* if !ok { u, err := v.Me() if err != nil { - return nil, errors.Wrap(err, "Failed to get self") + return nil, errors.Wrap(err, "failed to get self") } conn = NewSession(v.Session, u.ID) diff --git a/voice/voicegateway/gateway.go b/voice/voicegateway/gateway.go index 257a334..563839d 100644 --- a/voice/voicegateway/gateway.go +++ b/voice/voicegateway/gateway.go @@ -99,7 +99,7 @@ func (c *Gateway) Open() error { // Connect to the Gateway Gateway. if err := c.ws.Dial(ctx); err != nil { - return errors.Wrap(err, "Failed to connect to voice gateway") + return errors.Wrap(err, "failed to connect to voice gateway") } wsutil.WSDebug("Trying to start...") @@ -141,7 +141,7 @@ func (c *Gateway) __start() error { var hello *HelloEvent _, err := wsutil.AssertEvent(<-ch, HelloOP, &hello) if err != nil { - return errors.Wrap(err, "Error at Hello") + return errors.Wrap(err, "error at Hello") } wsutil.WSDebug("Received Hello") @@ -150,11 +150,11 @@ func (c *Gateway) __start() error { // Turns out Hello is sent right away on connection start. if !c.reconnect.Get() { if err := c.Identify(); err != nil { - return errors.Wrap(err, "Failed to identify") + return errors.Wrap(err, "failed to identify") } } else { if err := c.Resume(); err != nil { - return errors.Wrap(err, "Failed to resume") + return errors.Wrap(err, "failed to resume") } } // This bool is because we should only try and Resume once. @@ -165,7 +165,7 @@ func (c *Gateway) __start() error { return op.Code == ReadyOP || op.Code == ResumedOP }) if err != nil { - return errors.Wrap(err, "Failed to wait for Ready or Resumed") + return errors.Wrap(err, "failed to wait for Ready or Resumed") } // Create an event loop executor. @@ -240,7 +240,7 @@ func (c *Gateway) Reconnect() error { // https://discordapp.com/developers/docs/topics/gateway#rate-limiting if err := c.Open(); err != nil { - return errors.Wrap(err, "Failed to reopen gateway") + return errors.Wrap(err, "failed to reopen gateway") } wsutil.WSDebug("Reconnected successfully.") @@ -263,7 +263,7 @@ func (c *Gateway) SessionDescription(sp SelectProtocol) (*SessionDescriptionEven // Wait for SessionDescriptionOP packet. if err := (<-ch).UnmarshalData(&sesdesc); err != nil { - return nil, errors.Wrap(err, "Failed to unmarshal session description") + return nil, errors.Wrap(err, "failed to unmarshal session description") } return sesdesc, nil @@ -291,7 +291,7 @@ func (c *Gateway) send(code OPCode, v interface{}) error { if v != nil { b, err := json.Marshal(v) if err != nil { - return errors.Wrap(err, "Failed to encode v") + return errors.Wrap(err, "failed to encode v") } op.Data = b @@ -299,7 +299,7 @@ func (c *Gateway) send(code OPCode, v interface{}) error { b, err := json.Marshal(op) if err != nil { - return errors.Wrap(err, "Failed to encode payload") + return errors.Wrap(err, "failed to encode payload") } // WS should already be thread-safe. diff --git a/voice/voicegateway/op.go b/voice/voicegateway/op.go index ee7c49d..019c206 100644 --- a/voice/voicegateway/op.go +++ b/voice/voicegateway/op.go @@ -31,7 +31,7 @@ func (c *Gateway) HandleOP(op *wsutil.OP) error { // Gives information required to make a UDP connection case ReadyOP: if err := unmarshalMutex(op.Data, &c.ready, &c.mutex); err != nil { - return errors.Wrap(err, "Failed to parse READY event") + return errors.Wrap(err, "failed to parse READY event") } // Gives information about the encryption mode and secret key for sending voice packets