From 1a844d697d0544114785903c9731420b66b32d11 Mon Sep 17 00:00:00 2001 From: Nicholas Stafie Date: Thu, 25 Feb 2016 00:21:09 +0200 Subject: [PATCH 1/9] Add support for guild region and verification level --- restapi.go | 38 ++++++++++++++++++++++++++++++++++---- structs.go | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/restapi.go b/restapi.go index 76b678b..aae606a 100644 --- a/restapi.go +++ b/restapi.go @@ -372,13 +372,43 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) { // GuildEdit edits a new Guild // guildID : The ID of a Guild // name : A name for the Guild (2-100 characters) -func (s *Session) GuildEdit(guildID, name string) (st *Guild, err error) { +func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) { + + // Bounds checking for VerificationLevel, interval: [0, 3] + if g.VerificationLevel != nil { + val := *g.VerificationLevel + if val < 0 || val > 3 { + err = errors.New("VerificationLevel out of bounds, should be between 0 and 3") + return + } + } + + //Bounds checking for regions + if g.Region != "" { + isValid := false + regions, _ := s.VoiceRegions() + for _, r := range regions { + if g.Region == r.ID { + isValid = true + } + } + if !isValid { + var valid []string + for _, r := range regions { + valid = append(valid, r.ID) + } + err = errors.New(fmt.Sprintf("Region not a valid region (%q)", valid)) + return + } + } data := struct { - Name string `json:"name"` - }{name} + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` + VerificationLevel *int `json:"verification_level,omitempty"` + }{g.Name, g.Region, g.VerificationLevel} - body, err := s.Request("POST", GUILD(guildID), data) + body, err := s.Request("PATCH", GUILD(guildID), data) if err != nil { return } diff --git a/structs.go b/structs.go index f364fa4..ffb4801 100644 --- a/structs.go +++ b/structs.go @@ -148,24 +148,32 @@ type Emoji struct { // A Guild holds all data related to a specific Discord Guild. Guilds are also // sometimes referred to as Servers in the Discord client. type Guild struct { - ID string `json:"id"` - Name string `json:"name"` - Icon string `json:"icon"` - Region string `json:"region"` - AfkChannelID string `json:"afk_channel_id"` - EmbedChannelID string `json:"embed_channel_id"` - OwnerID string `json:"owner_id"` - JoinedAt string `json:"joined_at"` // make this a timestamp - Splash string `json:"splash"` - AfkTimeout int `json:"afk_timeout"` - EmbedEnabled bool `json:"embed_enabled"` - Large bool `json:"large"` // ?? - Roles []*Role `json:"roles"` - Emojis []*Emoji `json:"emojis"` - Members []*Member `json:"members"` - Presences []*Presence `json:"presences"` - Channels []*Channel `json:"channels"` - VoiceStates []*VoiceState `json:"voice_states"` + ID string `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + Region string `json:"region"` + AfkChannelID string `json:"afk_channel_id"` + EmbedChannelID string `json:"embed_channel_id"` + OwnerID string `json:"owner_id"` + JoinedAt string `json:"joined_at"` // make this a timestamp + Splash string `json:"splash"` + AfkTimeout int `json:"afk_timeout"` + VerificationLevel int `json:"verification_level"` + EmbedEnabled bool `json:"embed_enabled"` + Large bool `json:"large"` // ?? + Roles []*Role `json:"roles"` + Emojis []*Emoji `json:"emojis"` + Members []*Member `json:"members"` + Presences []*Presence `json:"presences"` + Channels []*Channel `json:"channels"` + VoiceStates []*VoiceState `json:"voice_states"` +} + +// A GuildParams stores all the data needed to update discord guild settings +type GuildParams struct { + Name string `json:"name"` + Region string `json:"region"` + VerificationLevel *int `json:"verification_level"` } // A Role stores information about Discord guild member roles. From f5ac3b10bf36cb32a253b6d02c49ad5d09876b07 Mon Sep 17 00:00:00 2001 From: Nicholas Stafie Date: Thu, 25 Feb 2016 00:31:51 +0200 Subject: [PATCH 2/9] Better document GuildEdit arguments --- restapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi.go b/restapi.go index aae606a..ddd11d8 100644 --- a/restapi.go +++ b/restapi.go @@ -371,7 +371,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) { // GuildEdit edits a new Guild // guildID : The ID of a Guild -// name : A name for the Guild (2-100 characters) +// g : A GuildParams struct with the values Name, Region and VerificationLevel defined. func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) { // Bounds checking for VerificationLevel, interval: [0, 3] From 58fe658fa5405a2e16b06eb8db4a01c3adb96353 Mon Sep 17 00:00:00 2001 From: Nicholas Stafie Date: Fri, 26 Feb 2016 23:42:32 +0200 Subject: [PATCH 3/9] Add VerificationLevel value consts and a typedef for int --- restapi.go | 6 +++--- structs.go | 55 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/restapi.go b/restapi.go index c2ab745..daa5b50 100644 --- a/restapi.go +++ b/restapi.go @@ -403,9 +403,9 @@ func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error } data := struct { - Name string `json:"name,omitempty"` - Region string `json:"region,omitempty"` - VerificationLevel *int `json:"verification_level,omitempty"` + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` + VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` }{g.Name, g.Region, g.VerificationLevel} body, err := s.Request("PATCH", GUILD(guildID), data) diff --git a/structs.go b/structs.go index ffb4801..9924e3b 100644 --- a/structs.go +++ b/structs.go @@ -145,35 +145,46 @@ type Emoji struct { RequireColons bool `json:"require_colons"` } +// Custom VerificationLevel typedef for int +type VerificationLevel int + +// Constants for VerificationLevel levels from 0 to 3 inclusive +const ( + VerificationLevelNone VerificationLevel = iota + VerificationLevelLow VerificationLevel = iota + VerificationLevelMedium VerificationLevel = iota + VerificationLevelHigh VerificationLevel = iota +) + // A Guild holds all data related to a specific Discord Guild. Guilds are also // sometimes referred to as Servers in the Discord client. type Guild struct { - ID string `json:"id"` - Name string `json:"name"` - Icon string `json:"icon"` - Region string `json:"region"` - AfkChannelID string `json:"afk_channel_id"` - EmbedChannelID string `json:"embed_channel_id"` - OwnerID string `json:"owner_id"` - JoinedAt string `json:"joined_at"` // make this a timestamp - Splash string `json:"splash"` - AfkTimeout int `json:"afk_timeout"` - VerificationLevel int `json:"verification_level"` - EmbedEnabled bool `json:"embed_enabled"` - Large bool `json:"large"` // ?? - Roles []*Role `json:"roles"` - Emojis []*Emoji `json:"emojis"` - Members []*Member `json:"members"` - Presences []*Presence `json:"presences"` - Channels []*Channel `json:"channels"` - VoiceStates []*VoiceState `json:"voice_states"` + ID string `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + Region string `json:"region"` + AfkChannelID string `json:"afk_channel_id"` + EmbedChannelID string `json:"embed_channel_id"` + OwnerID string `json:"owner_id"` + JoinedAt string `json:"joined_at"` // make this a timestamp + Splash string `json:"splash"` + AfkTimeout int `json:"afk_timeout"` + VerificationLevel VerificationLevel `json:"verification_level"` + EmbedEnabled bool `json:"embed_enabled"` + Large bool `json:"large"` // ?? + Roles []*Role `json:"roles"` + Emojis []*Emoji `json:"emojis"` + Members []*Member `json:"members"` + Presences []*Presence `json:"presences"` + Channels []*Channel `json:"channels"` + VoiceStates []*VoiceState `json:"voice_states"` } // A GuildParams stores all the data needed to update discord guild settings type GuildParams struct { - Name string `json:"name"` - Region string `json:"region"` - VerificationLevel *int `json:"verification_level"` + Name string `json:"name"` + Region string `json:"region"` + VerificationLevel *VerificationLevel `json:"verification_level"` } // A Role stores information about Discord guild member roles. From cba9b5039e9719ccd33aee2559b31e9cfa979d87 Mon Sep 17 00:00:00 2001 From: Nicholas Stafie Date: Sat, 27 Feb 2016 01:22:02 +0200 Subject: [PATCH 4/9] Refactor VerificationLevel consts --- structs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structs.go b/structs.go index 9924e3b..34fe7f1 100644 --- a/structs.go +++ b/structs.go @@ -150,10 +150,10 @@ type VerificationLevel int // Constants for VerificationLevel levels from 0 to 3 inclusive const ( - VerificationLevelNone VerificationLevel = iota - VerificationLevelLow VerificationLevel = iota - VerificationLevelMedium VerificationLevel = iota - VerificationLevelHigh VerificationLevel = iota + VerificationLevelNone VerificationLevel = iota + VerificationLevelLow + VerificationLevelMedium + VerificationLevelHigh ) // A Guild holds all data related to a specific Discord Guild. Guilds are also From 6c820412cf4e69ce85ec4bb93eb71fad19d8e3ad Mon Sep 17 00:00:00 2001 From: noisypixy Date: Wed, 2 Mar 2016 19:22:57 -0430 Subject: [PATCH 5/9] Replace HTTP status codes for net/http constants. --- restapi.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/restapi.go b/restapi.go index daa5b50..dc26baf 100644 --- a/restapi.go +++ b/restapi.go @@ -104,15 +104,14 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons fmt.Printf("API RESPONSE BODY :: [%s]\n", response) } - // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html switch resp.StatusCode { - case 200: // OK - case 204: // No Content + case http.StatusOK: + case http.StatusNoContent: // TODO check for 401 response, invalidate token if we get one. - case 429: // TOO MANY REQUESTS - Rate limiting + case http.StatusTooManyRequests: // Rate limiting rl := RateLimit{} err = json.Unmarshal(response, &rl) if err != nil { From ce9840f8c43c6d2421c6995b3fbdd3d8839291a9 Mon Sep 17 00:00:00 2001 From: noisypixy Date: Wed, 2 Mar 2016 21:13:53 -0430 Subject: [PATCH 6/9] Constant missing in Go < 1.6 --- restapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi.go b/restapi.go index dc26baf..93b71ea 100644 --- a/restapi.go +++ b/restapi.go @@ -111,7 +111,7 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons // TODO check for 401 response, invalidate token if we get one. - case http.StatusTooManyRequests: // Rate limiting + case 429: // TOO MANY REQUESTS - Rate limiting rl := RateLimit{} err = json.Unmarshal(response, &rl) if err != nil { From cd24674ebbb34bbefa5de8fc0338f88ff1fe4d96 Mon Sep 17 00:00:00 2001 From: abalabahaha Date: Fri, 4 Mar 2016 08:07:32 +0000 Subject: [PATCH 7/9] Added UserChannelPermissions function --- restapi.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ structs.go | 35 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/restapi.go b/restapi.go index 93b71ea..48e4f7a 100644 --- a/restapi.go +++ b/restapi.go @@ -327,6 +327,72 @@ func (s *Session) UserGuilds() (st []*Guild, err error) { return } +// 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. +func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { + + channel, err := s.Channel(channelID) + if err != nil { + return + } + + guild, err := s.Guild(channel.GuildID) + if err != nil { + return + } + + if userID == guild.OwnerID { + apermissions = PermissionAll + return + } + + member, err := s.GuildMember(guild.ID, userID) + if err != nil { + return + } + + apermissions = 0 + + for _, role := range guild.Roles { + for _, roleID := range member.Roles { + if role.ID == roleID { + apermissions = apermissions | role.Permissions + break + } + } + } + + if apermissions & (PermissionManageRoles) > 0 { + apermissions = PermissionAll + } + + // Member overwrites can override role overrides, so do two passes + for _, overwrite := range channel.PermissionOverwrites { + for _, roleID := range member.Roles { + if overwrite.Type == "role" && roleID == overwrite.ID { + apermissions = apermissions & ^overwrite.Deny + apermissions = apermissions | overwrite.Allow + break + } + } + } + + for _, overwrite := range channel.PermissionOverwrites { + if overwrite.Type == "member" && overwrite.ID == userID { + apermissions = apermissions & ^overwrite.Deny + apermissions = apermissions | overwrite.Allow + break + } + } + + if apermissions & PermissionManageRoles > 0 { + apermissions |= PermissionAllChannel + } + + return +} + // ------------------------------------------------------------------------------------------------ // Functions specific to Discord Guilds // ------------------------------------------------------------------------------------------------ diff --git a/structs.go b/structs.go index 34fe7f1..e9396d6 100644 --- a/structs.go +++ b/structs.go @@ -365,3 +365,38 @@ type State struct { Ready MaxMessageCount int } + +// Constants for the different bit offsets of general permissions +const ( + PermissionCreateInstantInvite = 1 << iota + PermissionKickMembers + PermissionBanMembers + PermissionManageRoles + PermissionManageChannels + PermissionManageServer + + PermissionAll = 66321471 + PermissionAllChannel = 66321433 +) + +// Constants for the different bit offsets of text channel permissions +const ( + PermissionReadMessages = 1 << (iota + 10) + PermissionSendMessages + PermissionSendTTSMessages + PermissionManageMessages + PermissionEmbedLinks + PermissionAttachFiles + PermissionReadMessageHistory + PermissionMentionEveryone +) + +// Constants for the different bit offsets of voice permissions +const ( + PermissionVoiceConnect = 1 << (iota + 20) + PermissionVoiceSpeak + PermissionVoiceMuteMembers + PermissionVoiceDeafenMembers + PermissionVoiceMoveMembers + PermissionVoiceUseVAD +) \ No newline at end of file From 539b9d203fae7212f4a81a15617cbcf96831d653 Mon Sep 17 00:00:00 2001 From: abalabahaha Date: Fri, 4 Mar 2016 05:50:11 -0800 Subject: [PATCH 8/9] Fix UserChannelPermissions --- restapi.go | 16 +++++++--------- structs.go | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/restapi.go b/restapi.go index 48e4f7a..7534cae 100644 --- a/restapi.go +++ b/restapi.go @@ -352,27 +352,25 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions return } - apermissions = 0 - for _, role := range guild.Roles { for _, roleID := range member.Roles { if role.ID == roleID { - apermissions = apermissions | role.Permissions + apermissions |= role.Permissions break } } } - if apermissions & (PermissionManageRoles) > 0 { - apermissions = PermissionAll + if apermissions & PermissionManageRoles > 0 { + apermissions |= PermissionAll } // Member overwrites can override role overrides, so do two passes for _, overwrite := range channel.PermissionOverwrites { for _, roleID := range member.Roles { if overwrite.Type == "role" && roleID == overwrite.ID { - apermissions = apermissions & ^overwrite.Deny - apermissions = apermissions | overwrite.Allow + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow break } } @@ -380,8 +378,8 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions for _, overwrite := range channel.PermissionOverwrites { if overwrite.Type == "member" && overwrite.ID == userID { - apermissions = apermissions & ^overwrite.Deny - apermissions = apermissions | overwrite.Allow + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow break } } diff --git a/structs.go b/structs.go index e9396d6..cc5e113 100644 --- a/structs.go +++ b/structs.go @@ -366,19 +366,6 @@ type State struct { MaxMessageCount int } -// Constants for the different bit offsets of general permissions -const ( - PermissionCreateInstantInvite = 1 << iota - PermissionKickMembers - PermissionBanMembers - PermissionManageRoles - PermissionManageChannels - PermissionManageServer - - PermissionAll = 66321471 - PermissionAllChannel = 66321433 -) - // Constants for the different bit offsets of text channel permissions const ( PermissionReadMessages = 1 << (iota + 10) @@ -399,4 +386,17 @@ const ( PermissionVoiceDeafenMembers PermissionVoiceMoveMembers PermissionVoiceUseVAD +) + +// Constants for the different bit offsets of general permissions +const ( + PermissionCreateInstantInvite = 1 << iota + PermissionKickMembers + PermissionBanMembers + PermissionManageRoles + PermissionManageChannels + PermissionManageServer + + PermissionAllChannel = PermissionReadMessages | PermissionSendMessages | PermissionSendTTSMessages | PermissionManageMessages | PermissionEmbedLinks | PermissionAttachFiles | PermissionReadMessageHistory | PermissionMentionEveryone | PermissionVoiceConnect | PermissionVoiceSpeak | PermissionVoiceMuteMembers | PermissionVoiceDeafenMembers | PermissionVoiceMoveMembers | PermissionVoiceUseVAD | PermissionCreateInstantInvite | PermissionManageRoles | PermissionManageChannels + PermissionAll = PermissionAllChannel | PermissionKickMembers | PermissionBanMembers | PermissionManageServer ) \ No newline at end of file From 0969cc3d86ccc74367e8f0a666b2140b17b28c1f Mon Sep 17 00:00:00 2001 From: abalabahaha Date: Fri, 4 Mar 2016 06:12:17 -0800 Subject: [PATCH 9/9] Format the permission constants --- structs.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/structs.go b/structs.go index cc5e113..2646541 100644 --- a/structs.go +++ b/structs.go @@ -397,6 +397,27 @@ const ( PermissionManageChannels PermissionManageServer - PermissionAllChannel = PermissionReadMessages | PermissionSendMessages | PermissionSendTTSMessages | PermissionManageMessages | PermissionEmbedLinks | PermissionAttachFiles | PermissionReadMessageHistory | PermissionMentionEveryone | PermissionVoiceConnect | PermissionVoiceSpeak | PermissionVoiceMuteMembers | PermissionVoiceDeafenMembers | PermissionVoiceMoveMembers | PermissionVoiceUseVAD | PermissionCreateInstantInvite | PermissionManageRoles | PermissionManageChannels - PermissionAll = PermissionAllChannel | PermissionKickMembers | PermissionBanMembers | PermissionManageServer -) \ No newline at end of file + PermissionAllText = PermissionReadMessages | + PermissionSendMessages | + PermissionSendTTSMessages | + PermissionManageMessages | + PermissionEmbedLinks | + PermissionAttachFiles | + PermissionReadMessageHistory | + PermissionMentionEveryone + PermissionAllVoice = PermissionVoiceConnect | + PermissionVoiceSpeak | + PermissionVoiceMuteMembers | + PermissionVoiceDeafenMembers | + PermissionVoiceMoveMembers | + PermissionVoiceUseVAD + PermissionAllChannel = PermissionAllText | + PermissionAllVoice | + PermissionCreateInstantInvite | + PermissionManageRoles | + PermissionManageChannels + PermissionAll = PermissionAllChannel | + PermissionKickMembers | + PermissionBanMembers | + PermissionManageServer +)