diff --git a/restapi.go b/restapi.go index 8f73c5d..7534cae 100644 --- a/restapi.go +++ b/restapi.go @@ -104,11 +104,10 @@ 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. @@ -328,6 +327,70 @@ 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 + } + + for _, role := range guild.Roles { + for _, roleID := range member.Roles { + if role.ID == roleID { + 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 &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + } + + for _, overwrite := range channel.PermissionOverwrites { + if overwrite.Type == "member" && overwrite.ID == userID { + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + + if apermissions & PermissionManageRoles > 0 { + apermissions |= PermissionAllChannel + } + + return +} + // ------------------------------------------------------------------------------------------------ // Functions specific to Discord Guilds // ------------------------------------------------------------------------------------------------ @@ -371,14 +434,44 @@ 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) { +// 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] + 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 *VerificationLevel `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..2646541 100644 --- a/structs.go +++ b/structs.go @@ -145,27 +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 + VerificationLevelMedium + VerificationLevelHigh +) + // 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 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 *VerificationLevel `json:"verification_level"` } // A Role stores information about Discord guild member roles. @@ -346,3 +365,59 @@ type State struct { Ready MaxMessageCount int } + +// 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 +) + +// Constants for the different bit offsets of general permissions +const ( + PermissionCreateInstantInvite = 1 << iota + PermissionKickMembers + PermissionBanMembers + PermissionManageRoles + PermissionManageChannels + PermissionManageServer + + 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 +)