using System.Runtime.CompilerServices; namespace Discord { internal static class Permissions { public const int MaxBits = 53; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission flag) => GetValue(allow, deny, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PermValue GetValue(ulong allow, ulong deny, GuildPermission flag) => GetValue(allow, deny, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PermValue GetValue(ulong allow, ulong deny, ulong flag) { if (HasFlag(allow, flag)) return PermValue.Allow; else if (HasFlag(deny, flag)) return PermValue.Deny; else return PermValue.Inherit; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool GetValue(ulong value, ChannelPermission flag) => GetValue(value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool GetValue(ulong value, GuildPermission flag) => GetValue(value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool GetValue(ulong value, ulong flag) => HasFlag(value, flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission flag) => SetValue(ref rawValue, value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong rawValue, bool? value, GuildPermission flag) => SetValue(ref rawValue, value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong rawValue, bool? value, ulong flag) { if (value.HasValue) { if (value == true) SetFlag(ref rawValue, flag); else UnsetFlag(ref rawValue, flag); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission flag) => SetValue(ref allow, ref deny, value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission flag) => SetValue(ref allow, ref deny, value, (ulong)flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ulong flag) { if (value.HasValue) { switch (value) { case PermValue.Allow: SetFlag(ref allow, flag); UnsetFlag(ref deny, flag); break; case PermValue.Deny: UnsetFlag(ref allow, flag); SetFlag(ref deny, flag); break; default: UnsetFlag(ref allow, flag); UnsetFlag(ref deny, flag); break; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool HasFlag(ulong value, ulong flag) => (value & flag) == flag; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetFlag(ref ulong value, ulong flag) => value |= flag; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnsetFlag(ref ulong value, ulong flag) => value &= ~flag; public static ChannelPermissions ToChannelPerms(IGuildChannel channel, ulong guildPermissions) => new ChannelPermissions(guildPermissions & ChannelPermissions.All(channel).RawValue); public static ulong ResolveGuild(IGuild guild, IGuildUser user) { ulong resolvedPermissions = 0; if (user.Id == guild.OwnerId) resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions else if (user.IsWebhook) resolvedPermissions = GuildPermissions.Webhook.RawValue; else { foreach (var roleId in user.RoleIds) resolvedPermissions |= guild.GetRole(roleId)?.Permissions.RawValue ?? 0; if (GetValue(resolvedPermissions, GuildPermission.Administrator)) resolvedPermissions = GuildPermissions.All.RawValue; //Administrators always have all permissions } return resolvedPermissions; } /*public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel) { return ResolveChannel(user, channel, ResolveGuild(user)); }*/ public static ulong ResolveChannel(IGuild guild, IGuildUser user, IGuildChannel channel, ulong guildPermissions) { ulong resolvedPermissions = 0; ulong mask = ChannelPermissions.All(channel).RawValue; if (GetValue(guildPermissions, GuildPermission.Administrator)) //Includes owner resolvedPermissions = mask; //Owners and administrators always have all permissions else { //Start with this user's guild permissions resolvedPermissions = guildPermissions; //Give/Take Everyone permissions var perms = channel.GetPermissionOverwrite(guild.EveryoneRole); if (perms != null) resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; //Give/Take Role permissions ulong deniedPermissions = 0UL, allowedPermissions = 0UL; foreach (var roleId in user.RoleIds) { IRole role; if (roleId != guild.EveryoneRole.Id && (role = guild.GetRole(roleId)) != null) { perms = channel.GetPermissionOverwrite(role); if (perms != null) { allowedPermissions |= perms.Value.AllowValue; deniedPermissions |= perms.Value.DenyValue; } } } resolvedPermissions = (resolvedPermissions & ~deniedPermissions) | allowedPermissions; //Give/Take User permissions perms = channel.GetPermissionOverwrite(user); if (perms != null) resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; if (channel is ITextChannel) { if (!GetValue(resolvedPermissions, ChannelPermission.ViewChannel)) { //No read permission on a text channel removes all other permissions resolvedPermissions = 0; } else if (!GetValue(resolvedPermissions, ChannelPermission.SendMessages)) { //No send permissions on a text channel removes all send-related permissions resolvedPermissions &= ~(ulong)ChannelPermission.SendTTSMessages; resolvedPermissions &= ~(ulong)ChannelPermission.MentionEveryone; resolvedPermissions &= ~(ulong)ChannelPermission.EmbedLinks; resolvedPermissions &= ~(ulong)ChannelPermission.AttachFiles; } } resolvedPermissions &= mask; //Ensure we didn't get any permissions this channel doesn't support (from guildPerms, for example) } return resolvedPermissions; } } }