diff options
author | Wim <wim@42.be> | 2017-01-28 22:45:32 +0100 |
---|---|---|
committer | Wim <wim@42.be> | 2017-01-28 22:45:32 +0100 |
commit | 79e6c9fa6cfca4bfe8913fd891fd9b12bd5bb505 (patch) | |
tree | bbcbbcf10127a0f5d030345b4beabbc9df7713d2 /vendor/github.com/bwmarrin/discordgo/restapi.go | |
parent | 1426ddec5f2250024c80765017b899e847bf2996 (diff) | |
download | matterbridge-msglm-79e6c9fa6cfca4bfe8913fd891fd9b12bd5bb505.tar.gz matterbridge-msglm-79e6c9fa6cfca4bfe8913fd891fd9b12bd5bb505.tar.bz2 matterbridge-msglm-79e6c9fa6cfca4bfe8913fd891fd9b12bd5bb505.zip |
Update vendor
Diffstat (limited to 'vendor/github.com/bwmarrin/discordgo/restapi.go')
-rw-r--r-- | vendor/github.com/bwmarrin/discordgo/restapi.go | 710 |
1 files changed, 591 insertions, 119 deletions
diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go index 337a7f82..5389c860 100644 --- a/vendor/github.com/bwmarrin/discordgo/restapi.go +++ b/vendor/github.com/bwmarrin/discordgo/restapi.go @@ -26,17 +26,19 @@ import ( "net/url" "strconv" "strings" - "sync" "time" ) // ErrJSONUnmarshal is returned for JSON Unmarshall errors. var ErrJSONUnmarshal = errors.New("json unmarshal") -// Request makes a (GET/POST/...) Requests to Discord REST API with JSON data. -// All the other Discord REST Calls in this file use this function. +// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) { + return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0]) +} +// RequestWithBucketID makes a (GET/POST/...) Requests to Discord REST API with JSON data. +func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, bucketID string) (response []byte, err error) { var body []byte if data != nil { body, err = json.Marshal(data) @@ -45,34 +47,19 @@ func (s *Session) Request(method, urlStr string, data interface{}) (response []b } } - return s.request(method, urlStr, "application/json", body) + return s.request(method, urlStr, "application/json", body, bucketID, 0) } // request makes a (GET/POST/...) Requests to Discord REST API. -func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) { - - // rate limit mutex for this url - // TODO: review for performance improvements - // ideally we just ignore endpoints that we've never - // received a 429 on. But this simple method works and - // is a lot less complex :) It also might even be more - // performat due to less checks and maps. - var mu *sync.Mutex - - s.rateLimit.Lock() - if s.rateLimit.url == nil { - s.rateLimit.url = make(map[string]*sync.Mutex) +// Sequence is the sequence number, if it fails with a 502 it will +// retry with sequence+1 until it either succeeds or sequence >= session.MaxRestRetries +func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID string, sequence int) (response []byte, err error) { + if bucketID == "" { + bucketID = strings.SplitN(urlStr, "?", 2)[0] } - bu := strings.Split(urlStr, "?") - mu, _ = s.rateLimit.url[bu[0]] - if mu == nil { - mu = new(sync.Mutex) - s.rateLimit.url[urlStr] = mu - } - s.rateLimit.Unlock() + bucket := s.ratelimiter.LockBucket(bucketID) - mu.Lock() // lock this URL for ratelimiting if s.Debug { log.Printf("API REQUEST %8s :: %s\n", method, urlStr) log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b)) @@ -80,6 +67,7 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons req, err := http.NewRequest(method, urlStr, bytes.NewBuffer(b)) if err != nil { + bucket.Release(nil) return } @@ -102,8 +90,8 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons client := &http.Client{Timeout: (20 * time.Second)} resp, err := client.Do(req) - mu.Unlock() // unlock ratelimit mutex if err != nil { + bucket.Release(nil) return } defer func() { @@ -113,6 +101,11 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons } }() + err = bucket.Release(resp.Header) + if err != nil { + return + } + response, err = ioutil.ReadAll(resp.Body) if err != nil { return @@ -135,29 +128,34 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons // TODO check for 401 response, invalidate token if we get one. - case 429: // TOO MANY REQUESTS - Rate limiting + case http.StatusBadGateway: + // Retry sending request if possible + if sequence < s.MaxRestRetries { - mu.Lock() // lock URL ratelimit mutex + s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status) + response, err = s.request(method, urlStr, contentType, b, bucketID, sequence+1) + } else { + err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response) + } + case 429: // TOO MANY REQUESTS - Rate limiting rl := TooManyRequests{} err = json.Unmarshal(response, &rl) if err != nil { s.log(LogError, "rate limit unmarshal error, %s", err) - mu.Unlock() return } s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter) - s.handle(RateLimit{TooManyRequests: &rl, URL: urlStr}) + s.handleEvent(rateLimitEventType, RateLimit{TooManyRequests: &rl, URL: urlStr}) time.Sleep(rl.RetryAfter * time.Millisecond) // we can make the above smarter - // this method can cause longer delays then required + // this method can cause longer delays than required - mu.Unlock() // we have to unlock here - response, err = s.request(method, urlStr, contentType, b) + response, err = s.request(method, urlStr, contentType, b, bucketID, sequence) default: // Error condition - err = fmt.Errorf("HTTP %s, %s", resp.Status, response) + err = newRestError(req, resp, response) } return @@ -184,7 +182,7 @@ func (s *Session) Login(email, password string) (err error) { Password string `json:"password"` }{email, password} - response, err := s.Request("POST", EndpointLogin, data) + response, err := s.RequestWithBucketID("POST", EndpointLogin, data, EndpointLogin) if err != nil { return } @@ -211,7 +209,7 @@ func (s *Session) Register(username string) (token string, err error) { Username string `json:"username"` }{username} - response, err := s.Request("POST", EndpointRegister, data) + response, err := s.RequestWithBucketID("POST", EndpointRegister, data, EndpointRegister) if err != nil { return } @@ -245,7 +243,7 @@ func (s *Session) Logout() (err error) { Token string `json:"token"` }{s.Token} - _, err = s.Request("POST", EndpointLogout, data) + _, err = s.RequestWithBucketID("POST", EndpointLogout, data, EndpointLogout) return } @@ -257,7 +255,7 @@ func (s *Session) Logout() (err error) { // userID : A user ID or "@me" which is a shortcut of current user ID func (s *Session) User(userID string) (st *User, err error) { - body, err := s.Request("GET", EndpointUser(userID), nil) + body, err := s.RequestWithBucketID("GET", EndpointUser(userID), nil, EndpointUsers) if err != nil { return } @@ -274,7 +272,7 @@ func (s *Session) UserAvatar(userID string) (img image.Image, err error) { return } - body, err := s.Request("GET", EndpointUserAvatar(userID, u.Avatar), nil) + body, err := s.RequestWithBucketID("GET", EndpointUserAvatar(userID, u.Avatar), nil, EndpointUserAvatar("", "")) if err != nil { return } @@ -299,7 +297,7 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri NewPassword string `json:"new_password,omitempty"` }{email, password, username, avatar, newPassword} - body, err := s.Request("PATCH", EndpointUser("@me"), data) + body, err := s.RequestWithBucketID("PATCH", EndpointUser("@me"), data, EndpointUsers) if err != nil { return } @@ -311,7 +309,28 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri // UserSettings returns the settings for a given user func (s *Session) UserSettings() (st *Settings, err error) { - body, err := s.Request("GET", EndpointUserSettings("@me"), nil) + body, err := s.RequestWithBucketID("GET", EndpointUserSettings("@me"), nil, EndpointUserSettings("")) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserUpdateStatus update the user status +// status : The new status (Actual valid status are 'online','idle','dnd','invisible') +func (s *Session) UserUpdateStatus(status Status) (st *Settings, err error) { + if status == StatusOffline { + err = errors.New("You can't set your Status to offline") + return + } + + data := struct { + Status Status `json:"status"` + }{status} + + body, err := s.RequestWithBucketID("PATCH", EndpointUserSettings("@me"), data, EndpointUserSettings("")) if err != nil { return } @@ -324,7 +343,7 @@ func (s *Session) UserSettings() (st *Settings, err error) { // channels. func (s *Session) UserChannels() (st []*Channel, err error) { - body, err := s.Request("GET", EndpointUserChannels("@me"), nil) + body, err := s.RequestWithBucketID("GET", EndpointUserChannels("@me"), nil, EndpointUserChannels("")) if err != nil { return } @@ -341,7 +360,7 @@ func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) RecipientID string `json:"recipient_id"` }{recipientID} - body, err := s.Request("POST", EndpointUserChannels("@me"), data) + body, err := s.RequestWithBucketID("POST", EndpointUserChannels("@me"), data, EndpointUserChannels("")) if err != nil { return } @@ -350,10 +369,10 @@ func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) return } -// UserGuilds returns an array of Guild structures for all guilds. -func (s *Session) UserGuilds() (st []*Guild, err error) { +// UserGuilds returns an array of UserGuild structures for all guilds. +func (s *Session) UserGuilds() (st []*UserGuild, err error) { - body, err := s.Request("GET", EndpointUserGuilds("@me"), nil) + body, err := s.RequestWithBucketID("GET", EndpointUserGuilds("@me"), nil, EndpointUserGuilds("")) if err != nil { return } @@ -367,7 +386,7 @@ func (s *Session) UserGuilds() (st []*Guild, err error) { // settings : The settings to update func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSettingsEdit) (st *UserGuildSettings, err error) { - body, err := s.Request("PATCH", EndpointUserGuildSettings("@me", guildID), settings) + body, err := s.RequestWithBucketID("PATCH", EndpointUserGuildSettings("@me", guildID), settings, EndpointUserGuildSettings("", guildID)) if err != nil { return } @@ -376,11 +395,12 @@ func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSetti return } -// NOTE: This function is now deprecated and will be removed in the future. -// Please see the same function inside state.go // UserChannelPermissions returns the permission of a user in a channel. // userID : The ID of the user to calculate permissions for. // channelID : The ID of the channel to calculate permission for. +// +// NOTE: This function is now deprecated and will be removed in the future. +// Please see the same function inside state.go func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { channel, err := s.State.Channel(channelID) if err != nil || channel == nil { @@ -412,6 +432,13 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions } for _, role := range guild.Roles { + if role.ID == guild.ID { + apermissions |= role.Permissions + break + } + } + + for _, role := range guild.Roles { for _, roleID := range member.Roles { if role.ID == roleID { apermissions |= role.Permissions @@ -420,7 +447,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions } } - if apermissions&PermissionManageRoles > 0 { + if apermissions&PermissionAdministrator > 0 { apermissions |= PermissionAll } @@ -443,7 +470,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions } } - if apermissions&PermissionManageRoles > 0 { + if apermissions&PermissionAdministrator > 0 { apermissions |= PermissionAllChannel } @@ -465,7 +492,7 @@ func (s *Session) Guild(guildID string) (st *Guild, err error) { } } - body, err := s.Request("GET", EndpointGuild(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID)) if err != nil { return } @@ -482,7 +509,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) { Name string `json:"name"` }{name} - body, err := s.Request("POST", EndpointGuilds, data) + body, err := s.RequestWithBucketID("POST", EndpointGuilds, data, EndpointGuilds) if err != nil { return } @@ -530,7 +557,7 @@ func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` }{g.Name, g.Region, g.VerificationLevel} - body, err := s.Request("PATCH", EndpointGuild(guildID), data) + body, err := s.RequestWithBucketID("PATCH", EndpointGuild(guildID), data, EndpointGuild(guildID)) if err != nil { return } @@ -543,7 +570,7 @@ func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error // guildID : The ID of a Guild func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { - body, err := s.Request("DELETE", EndpointGuild(guildID), nil) + body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID)) if err != nil { return } @@ -556,16 +583,16 @@ func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { // guildID : The ID of a Guild func (s *Session) GuildLeave(guildID string) (err error) { - _, err = s.Request("DELETE", EndpointUserGuild("@me", guildID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointUserGuild("@me", guildID), nil, EndpointUserGuild("", guildID)) return } // GuildBans returns an array of User structures for all bans of a // given guild. // guildID : The ID of a Guild. -func (s *Session) GuildBans(guildID string) (st []*User, err error) { +func (s *Session) GuildBans(guildID string) (st []*GuildBan, err error) { - body, err := s.Request("GET", EndpointGuildBans(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildBans(guildID), nil, EndpointGuildBans(guildID)) if err != nil { return } @@ -587,7 +614,7 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { uri = fmt.Sprintf("%s?delete-message-days=%d", uri, days) } - _, err = s.Request("PUT", uri, nil) + _, err = s.RequestWithBucketID("PUT", uri, nil, EndpointGuildBan(guildID, "")) return } @@ -596,22 +623,22 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { // userID : The ID of a User func (s *Session) GuildBanDelete(guildID, userID string) (err error) { - _, err = s.Request("DELETE", EndpointGuildBan(guildID, userID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, "")) return } // GuildMembers returns a list of members for a guild. // guildID : The ID of a Guild. -// offset : A number of members to skip +// after : The id of the member to return members after // limit : max number of members to return (max 1000) -func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, err error) { +func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*Member, err error) { uri := EndpointGuildMembers(guildID) v := url.Values{} - if offset > 0 { - v.Set("offset", strconv.Itoa(offset)) + if after != "" { + v.Set("after", after) } if limit > 0 { @@ -622,7 +649,7 @@ func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, uri = fmt.Sprintf("%s?%s", uri, v.Encode()) } - body, err := s.Request("GET", uri, nil) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID)) if err != nil { return } @@ -636,7 +663,7 @@ func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, // userID : The ID of a User func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { - body, err := s.Request("GET", EndpointGuildMember(guildID, userID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, "")) if err != nil { return } @@ -650,7 +677,7 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { // userID : The ID of a User func (s *Session) GuildMemberDelete(guildID, userID string) (err error) { - _, err = s.Request("DELETE", EndpointGuildMember(guildID, userID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, "")) return } @@ -664,7 +691,7 @@ func (s *Session) GuildMemberEdit(guildID, userID string, roles []string) (err e Roles []string `json:"roles"` }{roles} - _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) if err != nil { return } @@ -684,7 +711,7 @@ func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error) ChannelID string `json:"channel_id"` }{channelID} - _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) if err != nil { return } @@ -701,7 +728,29 @@ func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err err Nick string `json:"nick"` }{nickname} - _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + return +} + +// GuildMemberRoleAdd adds the specified role to a given member +// guildID : The ID of a Guild. +// userID : The ID of a User. +// roleID : The ID of a Role to be assigned to the user. +func (s *Session) GuildMemberRoleAdd(guildID, userID, roleID string) (err error) { + + _, err = s.RequestWithBucketID("PUT", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, userID, roleID)) + + return +} + +// GuildMemberRoleRemove removes the specified role to a given member +// guildID : The ID of a Guild. +// userID : The ID of a User. +// roleID : The ID of a Role to be removed from the user. +func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string) (err error) { + + _, err = s.RequestWithBucketID("DELETE", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, userID, roleID)) + return } @@ -710,7 +759,7 @@ func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err err // guildID : The ID of a Guild. func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) { - body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil) + body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil, EndpointGuildChannels(guildID), 0) if err != nil { return } @@ -731,7 +780,7 @@ func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, Type string `json:"type"` }{name, ctype} - body, err := s.Request("POST", EndpointGuildChannels(guildID), data) + body, err := s.RequestWithBucketID("POST", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID)) if err != nil { return } @@ -745,14 +794,14 @@ func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, // channels : Updated channels. func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err error) { - _, err = s.Request("PATCH", EndpointGuildChannels(guildID), channels) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), channels, EndpointGuildChannels(guildID)) return } // GuildInvites returns an array of Invite structures for the given guild // guildID : The ID of a Guild. func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { - body, err := s.Request("GET", EndpointGuildInvites(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInivtes(guildID)) if err != nil { return } @@ -765,7 +814,7 @@ func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { // guildID : The ID of a Guild. func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { - body, err := s.Request("GET", EndpointGuildRoles(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID)) if err != nil { return } @@ -779,7 +828,7 @@ func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { // guildID: The ID of a Guild. func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) { - body, err := s.Request("POST", EndpointGuildRoles(guildID), nil) + body, err := s.RequestWithBucketID("POST", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID)) if err != nil { return } @@ -796,7 +845,8 @@ func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) { // color : The color of the role (decimal, not hex). // hoist : Whether to display the role's users separately. // perm : The permissions for the role. -func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int) (st *Role, err error) { +// mention : Whether this role is mentionable +func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int, mention bool) (st *Role, err error) { // Prevent sending a color int that is too big. if color > 0xFFFFFF { @@ -804,13 +854,14 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b } data := struct { - Name string `json:"name"` // The color the role should have (as a decimal, not hex) - Color int `json:"color"` // Whether to display the role's users separately - Hoist bool `json:"hoist"` // The role's name (overwrites existing) + Name string `json:"name"` // The role's name (overwrites existing) + Color int `json:"color"` // The color the role should have (as a decimal, not hex) + Hoist bool `json:"hoist"` // Whether to display the role's users separately Permissions int `json:"permissions"` // The overall permissions number of the role (overwrites existing) - }{name, color, hoist, perm} + Mentionable bool `json:"mentionable"` // Whether this role is mentionable + }{name, color, hoist, perm, mention} - body, err := s.Request("PATCH", EndpointGuildRole(guildID, roleID), data) + body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, "")) if err != nil { return } @@ -825,7 +876,7 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b // roles : A list of ordered roles. func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) { - body, err := s.Request("PATCH", EndpointGuildRoles(guildID), roles) + body, err := s.RequestWithBucketID("PATCH", EndpointGuildRoles(guildID), roles, EndpointGuildRoles(guildID)) if err != nil { return } @@ -840,7 +891,72 @@ func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, e // roleID : The ID of a Role. func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { - _, err = s.Request("DELETE", EndpointGuildRole(guildID, roleID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildRole(guildID, roleID), nil, EndpointGuildRole(guildID, "")) + + return +} + +// GuildPruneCount Returns the number of members that would be removed in a prune operation. +// Requires 'KICK_MEMBER' permission. +// guildID : The ID of a Guild. +// days : The number of days to count prune for (1 or more). +func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, err error) { + count = 0 + + if days <= 0 { + err = errors.New("The number of days should be more than or equal to 1.") + return + } + + p := struct { + Pruned uint32 `json:"pruned"` + }{} + + uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID)) + + err = unmarshal(body, &p) + if err != nil { + return + } + + count = p.Pruned + + return +} + +// GuildPrune Begin as prune operation. Requires the 'KICK_MEMBERS' permission. +// Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation. +// guildID : The ID of a Guild. +// days : The number of days to count prune for (1 or more). +func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err error) { + + count = 0 + + if days <= 0 { + err = errors.New("The number of days should be more than or equal to 1.") + return + } + + data := struct { + days uint32 + }{days} + + p := struct { + Pruned uint32 `json:"pruned"` + }{} + + body, err := s.RequestWithBucketID("POST", EndpointGuildPrune(guildID), data, EndpointGuildPrune(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &p) + if err != nil { + return + } + + count = p.Pruned return } @@ -849,7 +965,7 @@ func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { // guildID : The ID of a Guild. func (s *Session) GuildIntegrations(guildID string) (st []*GuildIntegration, err error) { - body, err := s.Request("GET", EndpointGuildIntegrations(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID)) if err != nil { return } @@ -870,7 +986,7 @@ func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID ID string `json:"id"` }{integrationType, integrationID} - _, err = s.Request("POST", EndpointGuildIntegrations(guildID), data) + _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrations(guildID), data, EndpointGuildIntegrations(guildID)) return } @@ -889,7 +1005,7 @@ func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBeha EnableEmoticons bool `json:"enable_emoticons"` }{expireBehavior, expireGracePeriod, enableEmoticons} - _, err = s.Request("PATCH", EndpointGuildIntegration(guildID, integrationID), data) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildIntegration(guildID, integrationID), data, EndpointGuildIntegration(guildID, "")) return } @@ -898,7 +1014,7 @@ func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBeha // integrationID : The ID of an integration. func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) { - _, err = s.Request("DELETE", EndpointGuildIntegration(guildID, integrationID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildIntegration(guildID, integrationID), nil, EndpointGuildIntegration(guildID, "")) return } @@ -907,7 +1023,7 @@ func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err err // integrationID : The ID of an integration. func (s *Session) GuildIntegrationSync(guildID, integrationID string) (err error) { - _, err = s.Request("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil) + _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil, EndpointGuildIntegration(guildID, "")) return } @@ -924,7 +1040,7 @@ func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { return } - body, err := s.Request("GET", EndpointGuildIcon(guildID, g.Icon), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildIcon(guildID, g.Icon), nil, EndpointGuildIcon(guildID, "")) if err != nil { return } @@ -946,7 +1062,7 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { return } - body, err := s.Request("GET", EndpointGuildSplash(guildID, g.Splash), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildSplash(guildID, g.Splash), nil, EndpointGuildSplash(guildID, "")) if err != nil { return } @@ -959,7 +1075,7 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { // guildID : The ID of a Guild. func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) { - body, err := s.Request("GET", EndpointGuildEmbed(guildID), nil) + body, err := s.RequestWithBucketID("GET", EndpointGuildEmbed(guildID), nil, EndpointGuildEmbed(guildID)) if err != nil { return } @@ -974,7 +1090,7 @@ func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) data := GuildEmbed{enabled, channelID} - _, err = s.Request("PATCH", EndpointGuildEmbed(guildID), data) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildEmbed(guildID), data, EndpointGuildEmbed(guildID)) return } @@ -985,7 +1101,7 @@ func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) // Channel returns a Channel strucutre of a specific Channel. // channelID : The ID of the Channel you want returned. func (s *Session) Channel(channelID string) (st *Channel, err error) { - body, err := s.Request("GET", EndpointChannel(channelID), nil) + body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID)) if err != nil { return } @@ -1003,7 +1119,7 @@ func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) { Name string `json:"name"` }{name} - body, err := s.Request("PATCH", EndpointChannel(channelID), data) + body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID)) if err != nil { return } @@ -1016,7 +1132,7 @@ func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) { // channelID : The ID of a Channel func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { - body, err := s.Request("DELETE", EndpointChannel(channelID), nil) + body, err := s.RequestWithBucketID("DELETE", EndpointChannel(channelID), nil, EndpointChannel(channelID)) if err != nil { return } @@ -1030,7 +1146,7 @@ func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { // channelID : The ID of a Channel func (s *Session) ChannelTyping(channelID string) (err error) { - _, err = s.Request("POST", EndpointChannelTyping(channelID), nil) + _, err = s.RequestWithBucketID("POST", EndpointChannelTyping(channelID), nil, EndpointChannelTyping(channelID)) return } @@ -1058,7 +1174,7 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID uri = fmt.Sprintf("%s?%s", uri, v.Encode()) } - body, err := s.Request("GET", uri, nil) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID)) if err != nil { return } @@ -1072,7 +1188,7 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID // messageID : the ID of a Message func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) { - response, err := s.Request("GET", EndpointChannelMessage(channelID, messageID), nil) + response, err := s.RequestWithBucketID("GET", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) if err != nil { return } @@ -1084,9 +1200,15 @@ func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err // ChannelMessageAck acknowledges and marks the given message as read // channeld : The ID of a Channel // messageID : the ID of a Message -func (s *Session) ChannelMessageAck(channelID, messageID string) (err error) { +// lastToken : token returned by last ack +func (s *Session) ChannelMessageAck(channelID, messageID, lastToken string) (st *Ack, err error) { - _, err = s.request("POST", EndpointChannelMessageAck(channelID, messageID), "", nil) + body, err := s.RequestWithBucketID("POST", EndpointChannelMessageAck(channelID, messageID), &Ack{Token: lastToken}, EndpointChannelMessageAck(channelID, "")) + if err != nil { + return + } + + err = unmarshal(body, &st) return } @@ -1103,7 +1225,7 @@ func (s *Session) channelMessageSend(channelID, content string, tts bool) (st *M }{content, tts} // Send the message to the given channel - response, err := s.Request("POST", EndpointChannelMessages(channelID), data) + response, err := s.RequestWithBucketID("POST", EndpointChannelMessages(channelID), data, EndpointChannelMessages(channelID)) if err != nil { return } @@ -1128,6 +1250,28 @@ func (s *Session) ChannelMessageSendTTS(channelID string, content string) (st *M return s.channelMessageSend(channelID, content, true) } +// ChannelMessageSendEmbed sends a message to the given channel with embedded data (bot only). +// channelID : The ID of a Channel. +// embed : The embed data to send. +func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) (st *Message, err error) { + if embed != nil && embed.Type == "" { + embed.Type = "rich" + } + + data := struct { + Embed *MessageEmbed `json:"embed"` + }{embed} + + // Send the message to the given channel + response, err := s.RequestWithBucketID("POST", EndpointChannelMessages(channelID), data, EndpointChannelMessages(channelID)) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + // ChannelMessageEdit edits an existing message, replacing it entirely with // the given content. // channeld : The ID of a Channel @@ -1138,7 +1282,29 @@ func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st * Content string `json:"content"` }{content} - response, err := s.Request("PATCH", EndpointChannelMessage(channelID, messageID), data) + response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(channelID, messageID), data, EndpointChannelMessage(channelID, "")) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + +// ChannelMessageEditEmbed edits an existing message with embedded data (bot only). +// channelID : The ID of a Channel +// messageID : The ID of a Message +// embed : The embed data to send +func (s *Session) ChannelMessageEditEmbed(channelID, messageID string, embed *MessageEmbed) (st *Message, err error) { + if embed != nil && embed.Type == "" { + embed.Type = "rich" + } + + data := struct { + Embed *MessageEmbed `json:"embed"` + }{embed} + + response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(channelID, messageID), data, EndpointChannelMessage(channelID, "")) if err != nil { return } @@ -1150,7 +1316,7 @@ func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st * // ChannelMessageDelete deletes a message from the Channel. func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) { - _, err = s.Request("DELETE", EndpointChannelMessage(channelID, messageID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) return } @@ -1178,7 +1344,7 @@ func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) Messages []string `json:"messages"` }{messages} - _, err = s.Request("POST", EndpointChannelMessagesBulkDelete(channelID), data) + _, err = s.RequestWithBucketID("POST", EndpointChannelMessagesBulkDelete(channelID), data, EndpointChannelMessagesBulkDelete(channelID)) return } @@ -1187,7 +1353,7 @@ func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) // messageID: The ID of a message. func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) { - _, err = s.Request("PUT", EndpointChannelMessagePin(channelID, messageID), nil) + _, err = s.RequestWithBucketID("PUT", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) return } @@ -1196,7 +1362,7 @@ func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) { // messageID: The ID of a message. func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) { - _, err = s.Request("DELETE", EndpointChannelMessagePin(channelID, messageID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) return } @@ -1205,7 +1371,7 @@ func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) { // channelID : The ID of a Channel. func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) { - body, err := s.Request("GET", EndpointChannelMessagesPins(channelID), nil) + body, err := s.RequestWithBucketID("GET", EndpointChannelMessagesPins(channelID), nil, EndpointChannelMessagesPins(channelID)) if err != nil { return @@ -1217,12 +1383,28 @@ func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err er // ChannelFileSend sends a file to the given channel. // channelID : The ID of a Channel. +// name: The name of the file. // io.Reader : A reader for the file contents. func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (st *Message, err error) { + return s.ChannelFileSendWithMessage(channelID, "", name, r) +} + +// ChannelFileSendWithMessage sends a file to the given channel with an message. +// channelID : The ID of a Channel. +// content: Optional Message content. +// name: The name of the file. +// io.Reader : A reader for the file contents. +func (s *Session) ChannelFileSendWithMessage(channelID, content string, name string, r io.Reader) (st *Message, err error) { body := &bytes.Buffer{} bodywriter := multipart.NewWriter(body) + if len(content) != 0 { + if err := bodywriter.WriteField("content", content); err != nil { + return nil, err + } + } + writer, err := bodywriter.CreateFormFile("file", name) if err != nil { return nil, err @@ -1238,7 +1420,7 @@ func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (st *Mess return } - response, err := s.request("POST", EndpointChannelMessages(channelID), bodywriter.FormDataContentType(), body.Bytes()) + response, err := s.request("POST", EndpointChannelMessages(channelID), bodywriter.FormDataContentType(), body.Bytes(), EndpointChannelMessages(channelID), 0) if err != nil { return } @@ -1251,7 +1433,7 @@ func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (st *Mess // channelID : The ID of a Channel func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) { - body, err := s.Request("GET", EndpointChannelInvites(channelID), nil) + body, err := s.RequestWithBucketID("GET", EndpointChannelInvites(channelID), nil, EndpointChannelInvites(channelID)) if err != nil { return } @@ -1273,7 +1455,7 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e XKCDPass string `json:"xkcdpass"` }{i.MaxAge, i.MaxUses, i.Temporary, i.XkcdPass} - body, err := s.Request("POST", EndpointChannelInvites(channelID), data) + body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID)) if err != nil { return } @@ -1294,7 +1476,7 @@ func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, a Deny int `json:"deny"` }{targetID, targetType, allow, deny} - _, err = s.Request("PUT", EndpointChannelPermission(channelID, targetID), data) + _, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, "")) return } @@ -1302,7 +1484,7 @@ func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, a // NOTE: Name of this func may change. func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) { - _, err = s.Request("DELETE", EndpointChannelPermission(channelID, targetID), nil) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelPermission(channelID, targetID), nil, EndpointChannelPermission(channelID, "")) return } @@ -1314,7 +1496,7 @@ func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error // inviteID : The invite code (or maybe xkcdpass?) func (s *Session) Invite(inviteID string) (st *Invite, err error) { - body, err := s.Request("GET", EndpointInvite(inviteID), nil) + body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite("")) if err != nil { return } @@ -1327,7 +1509,7 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) { // inviteID : the code (or maybe xkcdpass?) of an invite func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { - body, err := s.Request("DELETE", EndpointInvite(inviteID), nil) + body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite("")) if err != nil { return } @@ -1340,7 +1522,7 @@ func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { // inviteID : The invite code (or maybe xkcdpass?) func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { - body, err := s.Request("POST", EndpointInvite(inviteID), nil) + body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite("")) if err != nil { return } @@ -1356,7 +1538,7 @@ func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { // VoiceRegions returns the voice server regions func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { - body, err := s.Request("GET", EndpointVoiceRegions, nil) + body, err := s.RequestWithBucketID("GET", EndpointVoiceRegions, nil, EndpointVoiceRegions) if err != nil { return } @@ -1368,7 +1550,7 @@ func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { // VoiceICE returns the voice server ICE information func (s *Session) VoiceICE() (st *VoiceICE, err error) { - body, err := s.Request("GET", EndpointVoiceIce, nil) + body, err := s.RequestWithBucketID("GET", EndpointVoiceIce, nil, EndpointVoiceIce) if err != nil { return } @@ -1384,7 +1566,7 @@ func (s *Session) VoiceICE() (st *VoiceICE, err error) { // Gateway returns the a websocket Gateway address func (s *Session) Gateway() (gateway string, err error) { - response, err := s.Request("GET", EndpointGateway, nil) + response, err := s.RequestWithBucketID("GET", EndpointGateway, nil, EndpointGateway) if err != nil { return } @@ -1399,5 +1581,295 @@ func (s *Session) Gateway() (gateway string, err error) { } gateway = temp.URL + + // Ensure the gateway always has a trailing slash. + // MacOS will fail to connect if we add query params without a trailing slash on the base domain. + if !strings.HasSuffix(gateway, "/") { + gateway += "/" + } + + return +} + +// Functions specific to Webhooks + +// WebhookCreate returns a new Webhook. +// channelID: The ID of a Channel. +// name : The name of the webhook. +// avatar : The avatar of the webhook. +func (s *Session) WebhookCreate(channelID, name, avatar string) (st *Webhook, err error) { + + data := struct { + Name string `json:"name"` + Avatar string `json:"avatar,omitempty"` + }{name, avatar} + + body, err := s.RequestWithBucketID("POST", EndpointChannelWebhooks(channelID), data, EndpointChannelWebhooks(channelID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// ChannelWebhooks returns all webhooks for a given channel. +// channelID: The ID of a channel. +func (s *Session) ChannelWebhooks(channelID string) (st []*Webhook, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointChannelWebhooks(channelID), nil, EndpointChannelWebhooks(channelID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildWebhooks returns all webhooks for a given guild. +// guildID: The ID of a Guild. +func (s *Session) GuildWebhooks(guildID string) (st []*Webhook, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointGuildWebhooks(guildID), nil, EndpointGuildWebhooks(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// Webhook returns a webhook for a given ID +// webhookID: The ID of a webhook. +func (s *Session) Webhook(webhookID string) (st *Webhook, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointWebhook(webhookID), nil, EndpointWebhooks) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookWithToken returns a webhook for a given ID +// webhookID: The ID of a webhook. +// token : The auth token for the webhook. +func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookEdit updates an existing Webhook. +// webhookID: The ID of a webhook. +// name : The name of the webhook. +// avatar : The avatar of the webhook. +func (s *Session) WebhookEdit(webhookID, name, avatar string) (st *Role, err error) { + + data := struct { + Name string `json:"name,omitempty"` + Avatar string `json:"avatar,omitempty"` + }{name, avatar} + + body, err := s.RequestWithBucketID("PATCH", EndpointWebhook(webhookID), data, EndpointWebhooks) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookEditWithToken updates an existing Webhook with an auth token. +// webhookID: The ID of a webhook. +// token : The auth token for the webhook. +// name : The name of the webhook. +// avatar : The avatar of the webhook. +func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (st *Role, err error) { + + data := struct { + Name string `json:"name,omitempty"` + Avatar string `json:"avatar,omitempty"` + }{name, avatar} + + body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", "")) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookDelete deletes a webhook for a given ID +// webhookID: The ID of a webhook. +func (s *Session) WebhookDelete(webhookID string) (st *Webhook, err error) { + + body, err := s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookDeleteWithToken deletes a webhook for a given ID with an auth token. +// webhookID: The ID of a webhook. +// token : The auth token for the webhook. +func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, err error) { + + body, err := s.RequestWithBucketID("DELETE", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// WebhookExecute executes a webhook. +// webhookID: The ID of a webhook. +// token : The auth token for the webhook +func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (err error) { + uri := EndpointWebhookToken(webhookID, token) + + if wait { + uri += "?wait=true" + } + + _, err = s.RequestWithBucketID("POST", uri, data, EndpointWebhookToken("", "")) + + return +} + +// MessageReactionAdd creates an emoji reaction to a message. +// channelID : The channel ID. +// messageID : The message ID. +// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. +func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error { + + _, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", "")) + + return err +} + +// MessageReactionRemove deletes an emoji reaction to a message. +// channelID : The channel ID. +// messageID : The message ID. +// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. +// userID : @me or ID of the user to delete the reaction for. +func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string) error { + + _, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", "")) + + return err +} + +// MessageReactions gets all the users reactions for a specific emoji. +// channelID : The channel ID. +// messageID : The message ID. +// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. +// limit : max number of users to return (max 100) +func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) { + uri := EndpointMessageReactions(channelID, messageID, emojiID) + + v := url.Values{} + + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + + if len(v) > 0 { + uri = fmt.Sprintf("%s?%s", uri, v.Encode()) + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", "")) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Relationships (Friends list) +// ------------------------------------------------------------------------------------------------ + +// RelationshipsGet returns an array of all the relationships of the user. +func (s *Session) RelationshipsGet() (r []*Relationship, err error) { + body, err := s.RequestWithBucketID("GET", EndpointRelationships(), nil, EndpointRelationships()) + if err != nil { + return + } + + err = unmarshal(body, &r) + return +} + +// relationshipCreate creates a new relationship. (I.e. send or accept a friend request, block a user.) +// relationshipType : 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req +func (s *Session) relationshipCreate(userID string, relationshipType int) (err error) { + data := struct { + Type int `json:"type"` + }{relationshipType} + + _, err = s.RequestWithBucketID("PUT", EndpointRelationship(userID), data, EndpointRelationships()) + return +} + +// RelationshipFriendRequestSend sends a friend request to a user. +// userID: ID of the user. +func (s *Session) RelationshipFriendRequestSend(userID string) (err error) { + err = s.relationshipCreate(userID, 4) + return +} + +// RelationshipFriendRequestAccept accepts a friend request from a user. +// userID: ID of the user. +func (s *Session) RelationshipFriendRequestAccept(userID string) (err error) { + err = s.relationshipCreate(userID, 1) + return +} + +// RelationshipUserBlock blocks a user. +// userID: ID of the user. +func (s *Session) RelationshipUserBlock(userID string) (err error) { + err = s.relationshipCreate(userID, 2) + return +} + +// RelationshipDelete removes the relationship with a user. +// userID: ID of the user. +func (s *Session) RelationshipDelete(userID string) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointRelationship(userID), nil, EndpointRelationships()) + return +} + +// RelationshipsMutualGet returns an array of all the users both @me and the given user is friends with. +// userID: ID of the user. +func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) { + body, err := s.RequestWithBucketID("GET", EndpointRelationshipsMutual(userID), nil, EndpointRelationshipsMutual(userID)) + if err != nil { + return + } + + err = unmarshal(body, &mf) return } |