1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-11-27 17:23:00 +00:00

Merge branch 'master' of github.com:diamondburned/arikawa

This commit is contained in:
diamondburned (Forefront) 2020-05-17 13:31:15 -07:00
commit dc303a8635
44 changed files with 512 additions and 314 deletions

View file

@ -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.

View file

@ -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),
)
}

View file

@ -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

View file

@ -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 {

View file

@ -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:

View file

@ -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) {

View file

@ -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

View file

@ -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))
}
}

View file

@ -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)
}
}

View file

@ -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")
})
}

View file

@ -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())
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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")
}
}

View file

@ -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 " +

View file

@ -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)
}
})

View file

@ -11,7 +11,7 @@ import (
var (
EmojiRegex = regexp.MustCompile(`<(a?):(.+?):(\d+)>`)
ErrInvalidEmoji = errors.New("Invalid emoji")
ErrInvalidEmoji = errors.New("invalid emoji")
)
type Emoji struct {

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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"`

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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
}

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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")
}
}

View file

@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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")
}
}

View file

@ -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"))
}
}
}

View file

@ -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 {

View file

@ -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:

View file

@ -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.

View file

@ -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.

View file

@ -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]

View file

@ -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)

View file

@ -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.

View file

@ -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