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:
commit
dc303a8635
|
@ -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.
|
||||
|
|
125
api/guild.go
125
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),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
118
api/message.go
118
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:
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
26
api/send.go
26
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
bot/ctx.go
16
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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
bot/error.go
10
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 " +
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
var (
|
||||
EmojiRegex = regexp.MustCompile(`<(a?):(.+?):(\d+)>`)
|
||||
|
||||
ErrInvalidEmoji = errors.New("Invalid emoji")
|
||||
ErrInvalidEmoji = errors.New("invalid emoji")
|
||||
)
|
||||
|
||||
type Emoji struct {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue