| @@ -1,11 +1,14 @@ | |||
| # Discord.Net v1.0.0-dev | |||
| [](https://www.nuget.org/packages/Discord.Net) [](https://ci.appveyor.com/project/foxbot/discord-net/) [](https://discord.gg/0SBTUU1wZTYLhAAW) | |||
| [](https://ci.appveyor.com/project/foxbot/discord-net/branch/master) | |||
| Discord.Net is an API wrapper for [Discord](http://discordapp.com) written in C#. | |||
| An unofficial .Net API Wrapper for the Discord client (http://discordapp.com). | |||
| Check out the [documentation](https://discordnet.readthedocs.org/en/latest/) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx). | |||
| Check out the [documentation](http://rtd.discord.foxbot.me/en/docs-dev/index.html) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx). | |||
| ## Installing | |||
| ##### Warning: Some of the documentation is outdated. | |||
| It's current being rewritten. Until that's done, feel free to use my [DiscordBot](https://github.com/RogueException/DiscordBot) repo for reference. | |||
| ### Installation | |||
| You can download Discord.Net and its extensions from NuGet: | |||
| - [Discord.Net](https://www.nuget.org/packages/Discord.Net/) | |||
| - [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/) | |||
| @@ -14,9 +17,8 @@ You can download Discord.Net and its extensions from NuGet: | |||
| ### Compiling | |||
| In order to compile Discord.Net, you require at least the following: | |||
| - [Visual Studio 15](https://www.visualstudio.com/downloads/download-visual-studio-vs) | |||
| - [ASP.Net 5 RC1](https://get.asp.net) | |||
| - NuGet 3.3 (available through Visual Studio) | |||
| - [Visual Studio 2015](https://www.visualstudio.com/downloads/download-visual-studio-vs) | |||
| - [Visual Studio 2015 Update 2](https://www.visualstudio.com/en-us/news/vs2015-update2-vs.aspx) | |||
| - [Visual Studio .Net Core Plugin](https://www.microsoft.com/net/core#windows) | |||
| - NuGet 3.3+ (available through Visual Studio) | |||
| ### Known Issues | |||
| - .Net Core support is incomplete on non-Windows systems | |||
| @@ -0,0 +1,6 @@ | |||
| { | |||
| "projects": [ "src", "test" ], | |||
| "sdk": { | |||
| "version": "1.0.0-preview1-002702" | |||
| } | |||
| } | |||
| @@ -10,7 +10,7 @@ namespace Discord.API | |||
| [JsonProperty("is_private")] | |||
| public bool IsPrivate { get; set; } | |||
| [JsonProperty("last_message_id")] | |||
| public ulong LastMessageId { get; set; } | |||
| public ulong? LastMessageId { get; set; } | |||
| //GuildChannel | |||
| [JsonProperty("guild_id")] | |||
| @@ -23,10 +23,16 @@ namespace Discord.API | |||
| public int Position { get; set; } | |||
| [JsonProperty("permission_overwrites")] | |||
| public Overwrite[] PermissionOverwrites { get; set; } | |||
| //TextChannel | |||
| [JsonProperty("topic")] | |||
| public string Topic { get; set; } | |||
| //VoiceChannel | |||
| [JsonProperty("bitrate")] | |||
| public int Bitrate { get; set; } | |||
| [JsonProperty("user_limit")] | |||
| public int UserLimit { get; set; } | |||
| //DMChannel | |||
| [JsonProperty("recipient")] | |||
| @@ -0,0 +1,14 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API | |||
| { | |||
| public class Game | |||
| { | |||
| [JsonProperty("name")] | |||
| public string Name { get; set; } | |||
| [JsonProperty("url")] | |||
| public string StreamUrl { get; set; } | |||
| [JsonProperty("type")] | |||
| public StreamType StreamType { get; set; } | |||
| } | |||
| } | |||
| @@ -8,9 +8,9 @@ namespace Discord.API | |||
| public ulong TargetId { get; set; } | |||
| [JsonProperty("type")] | |||
| public PermissionTarget TargetType { get; set; } | |||
| [JsonProperty("deny")] | |||
| public uint Deny { get; set; } | |||
| [JsonProperty("allow")] | |||
| public uint Allow { get; set; } | |||
| [JsonProperty("deny"), Int53] | |||
| public ulong Deny { get; set; } | |||
| [JsonProperty("allow"), Int53] | |||
| public ulong Allow { get; set; } | |||
| } | |||
| } | |||
| @@ -9,6 +9,6 @@ namespace Discord.API | |||
| [JsonProperty("mention_count")] | |||
| public int MentionCount { get; set; } | |||
| [JsonProperty("last_message_id")] | |||
| public ulong LastMentionId { get; set; } | |||
| public ulong? LastMessageId { get; set; } | |||
| } | |||
| } | |||
| @@ -14,8 +14,8 @@ namespace Discord.API | |||
| public bool? Hoist { get; set; } | |||
| [JsonProperty("position")] | |||
| public int? Position { get; set; } | |||
| [JsonProperty("permissions")] | |||
| public uint? Permissions { get; set; } | |||
| [JsonProperty("permissions"), Int53] | |||
| public ulong? Permissions { get; set; } | |||
| [JsonProperty("managed")] | |||
| public bool? Managed { get; set; } | |||
| } | |||
| @@ -12,7 +12,7 @@ namespace Discord.API | |||
| public string Icon { get; set; } | |||
| [JsonProperty("owner")] | |||
| public bool Owner { get; set; } | |||
| [JsonProperty("permissions")] | |||
| public uint Permissions { get; set; } | |||
| [JsonProperty("permissions"), Int53] | |||
| public ulong Permissions { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public enum GatewayOpCodes : byte | |||
| { | |||
| /// <summary> C←S - Used to send most events. </summary> | |||
| Dispatch = 0, | |||
| /// <summary> C↔S - Used to keep the connection alive and measure latency. </summary> | |||
| Heartbeat = 1, | |||
| /// <summary> C→S - Used to associate a connection with a token and specify configuration. </summary> | |||
| Identify = 2, | |||
| /// <summary> C→S - Used to update client's status and current game id. </summary> | |||
| StatusUpdate = 3, | |||
| /// <summary> C→S - Used to join a particular voice channel. </summary> | |||
| VoiceStateUpdate = 4, | |||
| /// <summary> C→S - Used to ensure the server's voice server is alive. Only send this if voice connection fails or suddenly drops. </summary> | |||
| VoiceServerPing = 5, | |||
| /// <summary> C→S - Used to resume a connection after a redirect occurs. </summary> | |||
| Resume = 6, | |||
| /// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary> | |||
| Reconnect = 7, | |||
| /// <summary> C→S - Used to request all members that were withheld by large_threshold </summary> | |||
| RequestGuildMembers = 8 | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class GuildMembersChunkEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong GuildId { get; set; } | |||
| [JsonProperty("members")] | |||
| public GuildMember[] Members { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class GuildRoleCreateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong GuildId { get; set; } | |||
| [JsonProperty("role")] | |||
| public Role Data { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class GuildRoleUpdateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong GuildId { get; set; } | |||
| [JsonProperty("role")] | |||
| public Role Data { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| using Newtonsoft.Json; | |||
| using System.Collections.Generic; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class IdentifyParams | |||
| { | |||
| [JsonProperty("token")] | |||
| public string Token { get; set; } | |||
| [JsonProperty("properties")] | |||
| public IDictionary<string, string> Properties { get; set; } | |||
| [JsonProperty("large_threshold")] | |||
| public int LargeThreshold { get; set; } | |||
| [JsonProperty("compress")] | |||
| public bool UseCompression { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class ReadyEvent | |||
| { | |||
| public class ReadState | |||
| { | |||
| [JsonProperty("id")] | |||
| public string ChannelId { get; set; } | |||
| [JsonProperty("mention_count")] | |||
| public int MentionCount { get; set; } | |||
| [JsonProperty("last_message_id")] | |||
| public string LastMessageId { get; set; } | |||
| } | |||
| [JsonProperty("v")] | |||
| public int Version { get; set; } | |||
| [JsonProperty("user")] | |||
| public User User { get; set; } | |||
| [JsonProperty("session_id")] | |||
| public string SessionId { get; set; } | |||
| [JsonProperty("read_state")] | |||
| public ReadState[] ReadStates { get; set; } | |||
| [JsonProperty("guilds")] | |||
| public Guild[] Guilds { get; set; } | |||
| [JsonProperty("private_channels")] | |||
| public Channel[] PrivateChannels { get; set; } | |||
| [JsonProperty("heartbeat_interval")] | |||
| public int HeartbeatInterval { get; set; } | |||
| //Ignored | |||
| [JsonProperty("user_settings")] | |||
| public object UserSettings { get; set; } | |||
| [JsonProperty("user_guild_settings")] | |||
| public object UserGuildSettings { get; set; } | |||
| [JsonProperty("tutorial")] | |||
| public object Tutorial { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class RequestMembersParams | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong[] GuildId { get; set; } | |||
| [JsonProperty("query")] | |||
| public string Query { get; set; } | |||
| [JsonProperty("limit")] | |||
| public int Limit { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class ResumeParams | |||
| { | |||
| [JsonProperty("session_id")] | |||
| public string SessionId { get; set; } | |||
| [JsonProperty("seq")] | |||
| public uint Sequence { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class ResumedEvent | |||
| { | |||
| [JsonProperty("heartbeat_interval")] | |||
| public int HeartbeatInterval { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class TypingStartEvent | |||
| { | |||
| [JsonProperty("user_id")] | |||
| public ulong UserId { get; set; } | |||
| [JsonProperty("channel_id")] | |||
| public ulong ChannelId { get; set; } | |||
| [JsonProperty("timestamp")] | |||
| public int Timestamp { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class UpdateStatusParams | |||
| { | |||
| [JsonProperty("idle_since")] | |||
| public long? IdleSince { get; set; } | |||
| [JsonProperty("game")] | |||
| public Game Game { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class UpdateVoiceParams | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong? GuildId { get; set; } | |||
| [JsonProperty("channel_id")] | |||
| public ulong? ChannelId { get; set; } | |||
| [JsonProperty("self_mute")] | |||
| public bool IsSelfMuted { get; set; } | |||
| [JsonProperty("self_deaf")] | |||
| public bool IsSelfDeafened { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public class VoiceServerUpdateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public ulong GuildId { get; set; } | |||
| [JsonProperty("endpoint")] | |||
| public string Endpoint { get; set; } | |||
| [JsonProperty("token")] | |||
| public string Token { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| using System; | |||
| namespace Discord.API | |||
| { | |||
| [AttributeUsage(AttributeTargets.Property)] | |||
| public class ImageAttribute : Attribute { } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| using System; | |||
| namespace Discord.API | |||
| { | |||
| [AttributeUsage(AttributeTargets.Property)] | |||
| public class Int53Attribute : Attribute { } | |||
| } | |||
| @@ -1,13 +1,15 @@ | |||
| using System; | |||
| using System.Diagnostics; | |||
| namespace Discord.API | |||
| { | |||
| //Based on https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Nullable.cs | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public struct Optional<T> : IOptional | |||
| { | |||
| private readonly T _value; | |||
| /// <summary> Gets the value for this paramter, or default(T) if unspecified. </summary> | |||
| /// <summary> Gets the value for this paramter. </summary> | |||
| public T Value | |||
| { | |||
| get | |||
| @@ -20,8 +22,6 @@ namespace Discord.API | |||
| /// <summary> Returns true if this value has been specified. </summary> | |||
| public bool IsSpecified { get; } | |||
| object IOptional.Value => _value; | |||
| /// <summary> Creates a new Parameter with the provided value. </summary> | |||
| public Optional(T value) | |||
| { | |||
| @@ -30,7 +30,7 @@ namespace Discord.API | |||
| } | |||
| public T GetValueOrDefault() => _value; | |||
| public T GetValueOrDefault(T defaultValue) => IsSpecified ? _value : default(T); | |||
| public T GetValueOrDefault(T defaultValue) => IsSpecified ? _value : defaultValue; | |||
| public override bool Equals(object other) | |||
| { | |||
| @@ -38,11 +38,14 @@ namespace Discord.API | |||
| if (other == null) return false; | |||
| return _value.Equals(other); | |||
| } | |||
| public override int GetHashCode() => IsSpecified ? _value.GetHashCode() : 0; | |||
| public override string ToString() => IsSpecified ? _value.ToString() : ""; | |||
| public override string ToString() => IsSpecified ? _value?.ToString() : null; | |||
| private string DebuggerDisplay => IsSpecified ? _value.ToString() : "<unspecified>"; | |||
| public static implicit operator Optional<T>(T value) => new Optional<T>(value); | |||
| public static implicit operator T(Optional<T> value) => value.Value; | |||
| public static explicit operator T(Optional<T> value) => value.Value; | |||
| object IOptional.Value => Value; | |||
| } | |||
| } | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.Converters; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| using System.IO; | |||
| namespace Discord.API.Rest | |||
| @@ -11,7 +10,7 @@ namespace Discord.API.Rest | |||
| [JsonProperty("region")] | |||
| public string Region { get; set; } | |||
| [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | |||
| [JsonProperty("icon"), Image] | |||
| public Optional<Stream> Icon { get; set; } | |||
| } | |||
| } | |||
| @@ -5,8 +5,8 @@ namespace Discord.API.Rest | |||
| public class ModifyChannelPermissionsParams | |||
| { | |||
| [JsonProperty("allow")] | |||
| public Optional<uint> Allow { get; set; } | |||
| public Optional<ulong> Allow { get; set; } | |||
| [JsonProperty("deny")] | |||
| public Optional<uint> Deny { get; set; } | |||
| public Optional<ulong> Deny { get; set; } | |||
| } | |||
| } | |||
| @@ -14,7 +14,7 @@ namespace Discord.API.Rest | |||
| public Optional<string> Password { get; set; } | |||
| [JsonProperty("new_password")] | |||
| public Optional<string> NewPassword { get; set; } | |||
| [JsonProperty("avatar"), JsonConverter(typeof(ImageConverter))] | |||
| [JsonProperty("avatar"), Image] | |||
| public Optional<Stream> Avatar { get; set; } | |||
| } | |||
| } | |||
| @@ -5,7 +5,7 @@ namespace Discord.API.Rest | |||
| public class ModifyGuildChannelsParams | |||
| { | |||
| [JsonProperty("id")] | |||
| public Optional<ulong> Id { get; set; } | |||
| public ulong Id { get; set; } | |||
| [JsonProperty("position")] | |||
| public Optional<int> Position { get; set; } | |||
| } | |||
| @@ -16,11 +16,11 @@ namespace Discord.API.Rest | |||
| public Optional<ulong?> AFKChannelId { get; set; } | |||
| [JsonProperty("afk_timeout")] | |||
| public Optional<int> AFKTimeout { get; set; } | |||
| [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | |||
| [JsonProperty("icon"), Image] | |||
| public Optional<Stream> Icon { get; set; } | |||
| [JsonProperty("owner_id")] | |||
| public Optional<GuildMember> Owner { get; set; } | |||
| [JsonProperty("splash"), JsonConverter(typeof(ImageConverter))] | |||
| [JsonProperty("splash"), Image] | |||
| public Optional<Stream> Splash { get; set; } | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ namespace Discord.API.Rest | |||
| [JsonProperty("name")] | |||
| public Optional<string> Name { get; set; } | |||
| [JsonProperty("permissions")] | |||
| public Optional<uint> Permissions { get; set; } | |||
| public Optional<ulong> Permissions { get; set; } | |||
| [JsonProperty("position")] | |||
| public Optional<int> Position { get; set; } | |||
| [JsonProperty("color")] | |||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||
| public class ModifyGuildRolesParams : ModifyGuildRoleParams | |||
| { | |||
| [JsonProperty("id")] | |||
| public Optional<ulong> Id { get; set; } | |||
| public ulong Id { get; set; } | |||
| } | |||
| } | |||
| @@ -6,5 +6,7 @@ namespace Discord.API.Rest | |||
| { | |||
| [JsonProperty("bitrate")] | |||
| public Optional<int> Bitrate { get; set; } | |||
| [JsonProperty("user_limit")] | |||
| public Optional<int> UserLimit { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| namespace Discord.API.Gateway | |||
| { | |||
| public enum VoiceOpCodes : byte | |||
| { | |||
| /// <summary> C→S - Used to associate a connection with a token. </summary> | |||
| Identify = 0, | |||
| /// <summary> C→S - Used to specify configuration. </summary> | |||
| SelectProtocol = 1, | |||
| /// <summary> C←S - Used to notify that the voice connection was successful and informs the client of available protocols. </summary> | |||
| Ready = 2, | |||
| /// <summary> C↔S - Used to keep the connection alive and measure latency. </summary> | |||
| Heartbeat = 3, | |||
| /// <summary> C←S - Used to provide an encryption key to the client. </summary> | |||
| SessionDescription = 4, | |||
| /// <summary> C↔S - Used to inform that a certain user is speaking. </summary> | |||
| Speaking = 5 | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API | |||
| { | |||
| public class WebSocketMessage | |||
| { | |||
| [JsonProperty("op")] | |||
| public int Operation { get; set; } | |||
| [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Type { get; set; } | |||
| [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] | |||
| public uint? Sequence { get; set; } | |||
| [JsonProperty("d")] | |||
| public object Payload { get; set; } | |||
| } | |||
| } | |||
| @@ -1,7 +0,0 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IIntegrationAccount : IEntity<string> | |||
| { | |||
| string Name { get; } | |||
| } | |||
| } | |||
| @@ -1,10 +0,0 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IPublicInvite : IInvite | |||
| { | |||
| /// <summary> Gets the name of the the channel this invite is linked to. </summary> | |||
| string ChannelName { get; } | |||
| /// <summary> Gets the name of the guild this invite is linked to. </summary> | |||
| string GuildName { get; } | |||
| } | |||
| } | |||
| @@ -1,136 +0,0 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| namespace Discord | |||
| { | |||
| public struct ChannelPermissions | |||
| { | |||
| private static ChannelPermissions _allDM { get; } = new ChannelPermissions(0b000100_000000_0011111111_0000011001); | |||
| private static ChannelPermissions _allText { get; } = new ChannelPermissions(0b000000_000000_0001110011_0000000000); | |||
| private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(0b000100_111111_0000000000_0000011001); | |||
| /// <summary> Gets a blank ChannelPermissions that grants no permissions. </summary> | |||
| public static ChannelPermissions None { get; } = new ChannelPermissions(); | |||
| /// <summary> Gets a ChannelPermissions that grants all permissions for a given channelType. </summary> | |||
| public static ChannelPermissions All(IChannel channel) | |||
| { | |||
| switch (channel) | |||
| { | |||
| case ITextChannel _: return _allText; | |||
| case IVoiceChannel _: return _allVoice; | |||
| case IDMChannel _: return _allDM; | |||
| default: | |||
| throw new ArgumentException("Unknown channel type", nameof(channel)); | |||
| } | |||
| } | |||
| /// <summary> Gets a packed value representing all the permissions in this ChannelPermissions. </summary> | |||
| public uint RawValue { get; } | |||
| /// <summary> If True, a user may create invites. </summary> | |||
| public bool CreateInstantInvite => PermissionUtilities.GetValue(RawValue, ChannelPermission.CreateInstantInvite); | |||
| /// <summary> If True, a user may create, delete and modify this channel. </summary> | |||
| public bool ManageChannel => PermissionUtilities.GetValue(RawValue, ChannelPermission.ManageChannel); | |||
| /// <summary> If True, a user may join channels. </summary> | |||
| public bool ReadMessages => PermissionUtilities.GetValue(RawValue, ChannelPermission.ReadMessages); | |||
| /// <summary> If True, a user may send messages. </summary> | |||
| public bool SendMessages => PermissionUtilities.GetValue(RawValue, ChannelPermission.SendMessages); | |||
| /// <summary> If True, a user may send text-to-speech messages. </summary> | |||
| public bool SendTTSMessages => PermissionUtilities.GetValue(RawValue, ChannelPermission.SendTTSMessages); | |||
| /// <summary> If True, a user may delete messages. </summary> | |||
| public bool ManageMessages => PermissionUtilities.GetValue(RawValue, ChannelPermission.ManageMessages); | |||
| /// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
| public bool EmbedLinks => PermissionUtilities.GetValue(RawValue, ChannelPermission.EmbedLinks); | |||
| /// <summary> If True, a user may send files. </summary> | |||
| public bool AttachFiles => PermissionUtilities.GetValue(RawValue, ChannelPermission.AttachFiles); | |||
| /// <summary> If True, a user may read previous messages. </summary> | |||
| public bool ReadMessageHistory => PermissionUtilities.GetValue(RawValue, ChannelPermission.ReadMessageHistory); | |||
| /// <summary> If True, a user may mention @everyone. </summary> | |||
| public bool MentionEveryone => PermissionUtilities.GetValue(RawValue, ChannelPermission.MentionEveryone); | |||
| /// <summary> If True, a user may connect to a voice channel. </summary> | |||
| public bool Connect => PermissionUtilities.GetValue(RawValue, ChannelPermission.Connect); | |||
| /// <summary> If True, a user may speak in a voice channel. </summary> | |||
| public bool Speak => PermissionUtilities.GetValue(RawValue, ChannelPermission.Speak); | |||
| /// <summary> If True, a user may mute users. </summary> | |||
| public bool MuteMembers => PermissionUtilities.GetValue(RawValue, ChannelPermission.MuteMembers); | |||
| /// <summary> If True, a user may deafen users. </summary> | |||
| public bool DeafenMembers => PermissionUtilities.GetValue(RawValue, ChannelPermission.DeafenMembers); | |||
| /// <summary> If True, a user may move other users between voice channels. </summary> | |||
| public bool MoveMembers => PermissionUtilities.GetValue(RawValue, ChannelPermission.MoveMembers); | |||
| /// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
| public bool UseVAD => PermissionUtilities.GetValue(RawValue, ChannelPermission.UseVAD); | |||
| /// <summary> If True, a user may adjust permissions. This also implictly grants all other permissions. </summary> | |||
| public bool ManagePermissions => PermissionUtilities.GetValue(RawValue, ChannelPermission.ManagePermissions); | |||
| /// <summary> Creates a new ChannelPermissions with the provided packed value. </summary> | |||
| public ChannelPermissions(uint rawValue) { RawValue = rawValue; } | |||
| private ChannelPermissions(uint initialValue, bool? createInstantInvite = null, bool? manageChannel = null, | |||
| bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, | |||
| bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, | |||
| bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, | |||
| bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) | |||
| { | |||
| uint value = initialValue; | |||
| PermissionUtilities.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite); | |||
| PermissionUtilities.SetValue(ref value, manageChannel, ChannelPermission.ManageChannel); | |||
| PermissionUtilities.SetValue(ref value, readMessages, ChannelPermission.ReadMessages); | |||
| PermissionUtilities.SetValue(ref value, sendMessages, ChannelPermission.SendMessages); | |||
| PermissionUtilities.SetValue(ref value, sendTTSMessages, ChannelPermission.SendTTSMessages); | |||
| PermissionUtilities.SetValue(ref value, manageMessages, ChannelPermission.ManageMessages); | |||
| PermissionUtilities.SetValue(ref value, embedLinks, ChannelPermission.EmbedLinks); | |||
| PermissionUtilities.SetValue(ref value, attachFiles, ChannelPermission.AttachFiles); | |||
| PermissionUtilities.SetValue(ref value, readMessageHistory, ChannelPermission.ReadMessageHistory); | |||
| PermissionUtilities.SetValue(ref value, mentionEveryone, ChannelPermission.MentionEveryone); | |||
| PermissionUtilities.SetValue(ref value, connect, ChannelPermission.Connect); | |||
| PermissionUtilities.SetValue(ref value, speak, ChannelPermission.Speak); | |||
| PermissionUtilities.SetValue(ref value, muteMembers, ChannelPermission.MuteMembers); | |||
| PermissionUtilities.SetValue(ref value, deafenMembers, ChannelPermission.DeafenMembers); | |||
| PermissionUtilities.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers); | |||
| PermissionUtilities.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD); | |||
| PermissionUtilities.SetValue(ref value, managePermissions, ChannelPermission.ManagePermissions); | |||
| RawValue = value; | |||
| } | |||
| /// <summary> Creates a new ChannelPermissions with the provided permissions. </summary> | |||
| public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false, | |||
| bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, | |||
| bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, | |||
| bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, | |||
| bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false) | |||
| : this(0, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, | |||
| embedLinks, attachFiles, readMessageHistory, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, managePermissions) { } | |||
| /// <summary> Creates a new ChannelPermissions from this one, changing the provided non-null permissions. </summary> | |||
| public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, | |||
| bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, | |||
| bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, | |||
| bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, | |||
| bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) | |||
| => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, | |||
| embedLinks, attachFiles, readMessageHistory, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, managePermissions); | |||
| /// <inheritdoc /> | |||
| public override string ToString() | |||
| { | |||
| var perms = new List<string>(); | |||
| int x = 1; | |||
| for (byte i = 0; i < 32; i++, x <<= 1) | |||
| { | |||
| if ((RawValue & x) != 0) | |||
| { | |||
| if (Enum.IsDefined(typeof(ChannelPermission), i)) | |||
| perms.Add($"{(ChannelPermission)i}"); | |||
| } | |||
| } | |||
| return string.Join(", ", perms); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,87 +0,0 @@ | |||
| using System.Runtime.CompilerServices; | |||
| namespace Discord | |||
| { | |||
| internal static class PermissionUtilities | |||
| { | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static PermValue GetValue(uint allow, uint deny, ChannelPermission bit) | |||
| => GetValue(allow, deny, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static PermValue GetValue(uint allow, uint deny, GuildPermission bit) | |||
| => GetValue(allow, deny, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static PermValue GetValue(uint allow, uint deny, byte bit) | |||
| { | |||
| if (HasBit(allow, bit)) | |||
| return PermValue.Allow; | |||
| else if (HasBit(deny, bit)) | |||
| return PermValue.Deny; | |||
| else | |||
| return PermValue.Inherit; | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(uint value, ChannelPermission bit) | |||
| => GetValue(value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(uint value, GuildPermission bit) | |||
| => GetValue(value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(uint value, byte bit) => HasBit(value, bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint rawValue, bool? value, ChannelPermission bit) | |||
| => SetValue(ref rawValue, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint rawValue, bool? value, GuildPermission bit) | |||
| => SetValue(ref rawValue, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint rawValue, bool? value, byte bit) | |||
| { | |||
| if (value.HasValue) | |||
| { | |||
| if (value == true) | |||
| SetBit(ref rawValue, bit); | |||
| else | |||
| UnsetBit(ref rawValue, bit); | |||
| } | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint allow, ref uint deny, PermValue? value, ChannelPermission bit) | |||
| => SetValue(ref allow, ref deny, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint allow, ref uint deny, PermValue? value, GuildPermission bit) | |||
| => SetValue(ref allow, ref deny, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref uint allow, ref uint deny, PermValue? value, byte bit) | |||
| { | |||
| if (value.HasValue) | |||
| { | |||
| switch (value) | |||
| { | |||
| case PermValue.Allow: | |||
| SetBit(ref allow, bit); | |||
| UnsetBit(ref deny, bit); | |||
| break; | |||
| case PermValue.Deny: | |||
| UnsetBit(ref allow, bit); | |||
| SetBit(ref deny, bit); | |||
| break; | |||
| default: | |||
| UnsetBit(ref allow, bit); | |||
| UnsetBit(ref deny, bit); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| private static bool HasBit(uint value, byte bit) => (value & (1U << bit)) != 0; | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetBit(ref uint value, byte bit) => value |= (1U << bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void UnsetBit(ref uint value, byte bit) => value &= ~(1U << bit); | |||
| } | |||
| } | |||
| @@ -1,8 +0,0 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IDMUser : IUser | |||
| { | |||
| /// <summary> Gets the private channel with this user. </summary> | |||
| IDMChannel Channel { get; } | |||
| } | |||
| } | |||
| @@ -1,20 +0,0 @@ | |||
| using System; | |||
| namespace Discord.Net.Rest | |||
| { | |||
| public class SentRequestEventArgs : EventArgs | |||
| { | |||
| public string Method { get; } | |||
| public string Endpoint { get; } | |||
| public int ResponseLength { get; } | |||
| public double Milliseconds { get; } | |||
| public SentRequestEventArgs(string method, string endpoint, int responseLength, double milliseconds) | |||
| { | |||
| Method = method; | |||
| Endpoint = endpoint; | |||
| ResponseLength = responseLength; | |||
| Milliseconds = milliseconds; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,13 +0,0 @@ | |||
| using System; | |||
| namespace Discord | |||
| { | |||
| internal static class EventExtensions | |||
| { | |||
| public static void Raise(this EventHandler eventHandler, object sender) | |||
| => eventHandler?.Invoke(sender, EventArgs.Empty); | |||
| public static void Raise<T>(this EventHandler<T> eventHandler, object sender, T eventArgs) | |||
| where T : EventArgs | |||
| => eventHandler?.Invoke(sender, eventArgs); | |||
| } | |||
| } | |||
| @@ -1,64 +0,0 @@ | |||
| namespace Discord | |||
| { | |||
| public static class PermissionHelper | |||
| { | |||
| public static uint Resolve(IGuildUser user) | |||
| { | |||
| var roles = user.Roles; | |||
| uint newPermissions = 0; | |||
| for (int i = 0; i < roles.Count; i++) | |||
| newPermissions |= roles[i].Permissions.RawValue; | |||
| return newPermissions; | |||
| } | |||
| public static uint Resolve(IGuildUser user, IGuildChannel channel) | |||
| { | |||
| uint resolvedPermissions = 0; | |||
| uint mask = ChannelPermissions.All(channel).RawValue; | |||
| if (user.Id == user.Guild.OwnerId || PermissionUtilities.GetValue(resolvedPermissions, GuildPermission.Administrator)) | |||
| resolvedPermissions = mask; //Owners and administrators always have all permissions | |||
| else | |||
| { | |||
| //Start with this user's guild permissions | |||
| resolvedPermissions = Resolve(user); | |||
| var overwrites = channel.PermissionOverwrites; | |||
| Overwrite entry; | |||
| var roles = user.Roles; | |||
| if (roles.Count > 0) | |||
| { | |||
| for (int i = 0; i < roles.Count; i++) | |||
| { | |||
| if (overwrites.TryGetValue(roles[i].Id, out entry)) | |||
| resolvedPermissions &= ~entry.Permissions.DenyValue; | |||
| } | |||
| for (int i = 0; i < roles.Count; i++) | |||
| { | |||
| if (overwrites.TryGetValue(roles[i].Id, out entry)) | |||
| resolvedPermissions |= entry.Permissions.AllowValue; | |||
| } | |||
| } | |||
| if (overwrites.TryGetValue(user.Id, out entry)) | |||
| resolvedPermissions = (resolvedPermissions & ~entry.Permissions.DenyValue) | entry.Permissions.AllowValue; | |||
| switch (channel) | |||
| { | |||
| case ITextChannel _: | |||
| if (!PermissionUtilities.GetValue(resolvedPermissions, ChannelPermission.ReadMessages)) | |||
| resolvedPermissions = 0; //No read permission on a text channel removes all other permissions | |||
| break; | |||
| case IVoiceChannel _: | |||
| if (!PermissionUtilities.GetValue(resolvedPermissions, ChannelPermission.Connect)) | |||
| resolvedPermissions = 0; //No read permission on a text channel removes all other permissions | |||
| break; | |||
| default: | |||
| resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example) | |||
| break; | |||
| } | |||
| } | |||
| return resolvedPermissions; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,475 @@ | |||
| using System; | |||
| using System.Collections; | |||
| using System.Collections.Generic; | |||
| using System.Collections.ObjectModel; | |||
| using System.Diagnostics; | |||
| using System.Threading; | |||
| namespace Discord | |||
| { | |||
| //Based on https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs | |||
| //Copyright (c) .NET Foundation and Contributors | |||
| [DebuggerDisplay("Count = {Count}")] | |||
| internal class ConcurrentHashSet<T> : IEnumerable<T> | |||
| { | |||
| private sealed class Tables | |||
| { | |||
| internal readonly Node[] _buckets; | |||
| internal readonly object[] _locks; | |||
| internal volatile int[] _countPerLock; | |||
| internal Tables(Node[] buckets, object[] locks, int[] countPerLock) | |||
| { | |||
| _buckets = buckets; | |||
| _locks = locks; | |||
| _countPerLock = countPerLock; | |||
| } | |||
| } | |||
| private sealed class Node | |||
| { | |||
| internal readonly T _value; | |||
| internal volatile Node _next; | |||
| internal readonly int _hashcode; | |||
| internal Node(T key, int hashcode, Node next) | |||
| { | |||
| _value = key; | |||
| _next = next; | |||
| _hashcode = hashcode; | |||
| } | |||
| } | |||
| private const int DefaultCapacity = 31; | |||
| private const int MaxLockNumber = 1024; | |||
| private static int GetBucket(int hashcode, int bucketCount) | |||
| { | |||
| int bucketNo = (hashcode & 0x7fffffff) % bucketCount; | |||
| return bucketNo; | |||
| } | |||
| private static void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount) | |||
| { | |||
| bucketNo = (hashcode & 0x7fffffff) % bucketCount; | |||
| lockNo = bucketNo % lockCount; | |||
| } | |||
| private static int DefaultConcurrencyLevel => PlatformHelper.ProcessorCount; | |||
| private volatile Tables _tables; | |||
| private readonly IEqualityComparer<T> _comparer; | |||
| private readonly bool _growLockArray; | |||
| private int _budget; | |||
| public int Count | |||
| { | |||
| get | |||
| { | |||
| int count = 0; | |||
| int acquiredLocks = 0; | |||
| try | |||
| { | |||
| AcquireAllLocks(ref acquiredLocks); | |||
| for (int i = 0; i < _tables._countPerLock.Length; i++) | |||
| count += _tables._countPerLock[i]; | |||
| } | |||
| finally { ReleaseLocks(0, acquiredLocks); } | |||
| return count; | |||
| } | |||
| } | |||
| public bool IsEmpty | |||
| { | |||
| get | |||
| { | |||
| int acquiredLocks = 0; | |||
| try | |||
| { | |||
| // Acquire all locks | |||
| AcquireAllLocks(ref acquiredLocks); | |||
| for (int i = 0; i < _tables._countPerLock.Length; i++) | |||
| { | |||
| if (_tables._countPerLock[i] != 0) | |||
| return false; | |||
| } | |||
| } | |||
| finally { ReleaseLocks(0, acquiredLocks); } | |||
| return true; | |||
| } | |||
| } | |||
| public ReadOnlyCollection<T> Values | |||
| { | |||
| get | |||
| { | |||
| int locksAcquired = 0; | |||
| try | |||
| { | |||
| AcquireAllLocks(ref locksAcquired); | |||
| List<T> values = new List<T>(); | |||
| for (int i = 0; i < _tables._buckets.Length; i++) | |||
| { | |||
| Node current = _tables._buckets[i]; | |||
| while (current != null) | |||
| { | |||
| values.Add(current._value); | |||
| current = current._next; | |||
| } | |||
| } | |||
| return new ReadOnlyCollection<T>(values); | |||
| } | |||
| finally { ReleaseLocks(0, locksAcquired); } | |||
| } | |||
| } | |||
| public ConcurrentHashSet() | |||
| : this(DefaultConcurrencyLevel, DefaultCapacity, true, EqualityComparer<T>.Default) { } | |||
| public ConcurrentHashSet(int concurrencyLevel, int capacity) | |||
| : this(concurrencyLevel, capacity, false, EqualityComparer<T>.Default) { } | |||
| public ConcurrentHashSet(IEnumerable<T> collection) | |||
| : this(collection, EqualityComparer<T>.Default) { } | |||
| public ConcurrentHashSet(IEqualityComparer<T> comparer) | |||
| : this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { } | |||
| public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) | |||
| : this(comparer) | |||
| { | |||
| if (collection == null) throw new ArgumentNullException(nameof(collection)); | |||
| InitializeFromCollection(collection); | |||
| } | |||
| public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer) | |||
| : this(concurrencyLevel, DefaultCapacity, false, comparer) | |||
| { | |||
| if (collection == null) throw new ArgumentNullException(nameof(collection)); | |||
| if (comparer == null) throw new ArgumentNullException(nameof(comparer)); | |||
| InitializeFromCollection(collection); | |||
| } | |||
| public ConcurrentHashSet(int concurrencyLevel, int capacity, IEqualityComparer<T> comparer) | |||
| : this(concurrencyLevel, capacity, false, comparer) { } | |||
| internal ConcurrentHashSet(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<T> comparer) | |||
| { | |||
| if (concurrencyLevel < 1) throw new ArgumentOutOfRangeException(nameof(concurrencyLevel)); | |||
| if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); | |||
| if (comparer == null) throw new ArgumentNullException(nameof(comparer)); | |||
| if (capacity < concurrencyLevel) | |||
| capacity = concurrencyLevel; | |||
| object[] locks = new object[concurrencyLevel]; | |||
| for (int i = 0; i < locks.Length; i++) | |||
| locks[i] = new object(); | |||
| int[] countPerLock = new int[locks.Length]; | |||
| Node[] buckets = new Node[capacity]; | |||
| _tables = new Tables(buckets, locks, countPerLock); | |||
| _comparer = comparer; | |||
| _growLockArray = growLockArray; | |||
| _budget = buckets.Length / locks.Length; | |||
| } | |||
| private void InitializeFromCollection(IEnumerable<T> collection) | |||
| { | |||
| foreach (var value in collection) | |||
| { | |||
| if (value == null) throw new ArgumentNullException("key"); | |||
| if (!TryAddInternal(value, _comparer.GetHashCode(value), false)) | |||
| throw new ArgumentException(); | |||
| } | |||
| if (_budget == 0) | |||
| _budget = _tables._buckets.Length / _tables._locks.Length; | |||
| } | |||
| public bool ContainsKey(T value) | |||
| { | |||
| if (value == null) throw new ArgumentNullException("key"); | |||
| return ContainsKeyInternal(value, _comparer.GetHashCode(value)); | |||
| } | |||
| private bool ContainsKeyInternal(T value, int hashcode) | |||
| { | |||
| Tables tables = _tables; | |||
| int bucketNo = GetBucket(hashcode, tables._buckets.Length); | |||
| Node n = Volatile.Read(ref tables._buckets[bucketNo]); | |||
| while (n != null) | |||
| { | |||
| if (hashcode == n._hashcode && _comparer.Equals(n._value, value)) | |||
| return true; | |||
| n = n._next; | |||
| } | |||
| return false; | |||
| } | |||
| public bool TryAdd(T value) | |||
| { | |||
| if (value == null) throw new ArgumentNullException("key"); | |||
| return TryAddInternal(value, _comparer.GetHashCode(value), true); | |||
| } | |||
| private bool TryAddInternal(T value, int hashcode, bool acquireLock) | |||
| { | |||
| while (true) | |||
| { | |||
| int bucketNo, lockNo; | |||
| Tables tables = _tables; | |||
| GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables._buckets.Length, tables._locks.Length); | |||
| bool resizeDesired = false; | |||
| bool lockTaken = false; | |||
| try | |||
| { | |||
| if (acquireLock) | |||
| Monitor.Enter(tables._locks[lockNo], ref lockTaken); | |||
| if (tables != _tables) | |||
| continue; | |||
| Node prev = null; | |||
| for (Node node = tables._buckets[bucketNo]; node != null; node = node._next) | |||
| { | |||
| if (hashcode == node._hashcode && _comparer.Equals(node._value, value)) | |||
| return false; | |||
| prev = node; | |||
| } | |||
| Volatile.Write(ref tables._buckets[bucketNo], new Node(value, hashcode, tables._buckets[bucketNo])); | |||
| checked { tables._countPerLock[lockNo]++; } | |||
| if (tables._countPerLock[lockNo] > _budget) | |||
| resizeDesired = true; | |||
| } | |||
| finally | |||
| { | |||
| if (lockTaken) | |||
| Monitor.Exit(tables._locks[lockNo]); | |||
| } | |||
| if (resizeDesired) | |||
| GrowTable(tables); | |||
| return true; | |||
| } | |||
| } | |||
| public bool TryRemove(T value) | |||
| { | |||
| if (value == null) throw new ArgumentNullException("key"); | |||
| return TryRemoveInternal(value); | |||
| } | |||
| private bool TryRemoveInternal(T value) | |||
| { | |||
| int hashcode = _comparer.GetHashCode(value); | |||
| while (true) | |||
| { | |||
| Tables tables = _tables; | |||
| int bucketNo, lockNo; | |||
| GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables._buckets.Length, tables._locks.Length); | |||
| lock (tables._locks[lockNo]) | |||
| { | |||
| if (tables != _tables) | |||
| continue; | |||
| Node prev = null; | |||
| for (Node curr = tables._buckets[bucketNo]; curr != null; curr = curr._next) | |||
| { | |||
| if (hashcode == curr._hashcode && _comparer.Equals(curr._value, value)) | |||
| { | |||
| if (prev == null) | |||
| Volatile.Write(ref tables._buckets[bucketNo], curr._next); | |||
| else | |||
| prev._next = curr._next; | |||
| value = curr._value; | |||
| tables._countPerLock[lockNo]--; | |||
| return true; | |||
| } | |||
| prev = curr; | |||
| } | |||
| } | |||
| value = default(T); | |||
| return false; | |||
| } | |||
| } | |||
| public void Clear() | |||
| { | |||
| int locksAcquired = 0; | |||
| try | |||
| { | |||
| AcquireAllLocks(ref locksAcquired); | |||
| Tables newTables = new Tables(new Node[DefaultCapacity], _tables._locks, new int[_tables._countPerLock.Length]); | |||
| _tables = newTables; | |||
| _budget = Math.Max(1, newTables._buckets.Length / newTables._locks.Length); | |||
| } | |||
| finally | |||
| { | |||
| ReleaseLocks(0, locksAcquired); | |||
| } | |||
| } | |||
| public IEnumerator<T> GetEnumerator() | |||
| { | |||
| Node[] buckets = _tables._buckets; | |||
| for (int i = 0; i < buckets.Length; i++) | |||
| { | |||
| Node current = Volatile.Read(ref buckets[i]); | |||
| while (current != null) | |||
| { | |||
| yield return current._value; | |||
| current = current._next; | |||
| } | |||
| } | |||
| } | |||
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |||
| private void GrowTable(Tables tables) | |||
| { | |||
| const int MaxArrayLength = 0X7FEFFFFF; | |||
| int locksAcquired = 0; | |||
| try | |||
| { | |||
| AcquireLocks(0, 1, ref locksAcquired); | |||
| if (tables != _tables) | |||
| return; | |||
| long approxCount = 0; | |||
| for (int i = 0; i < tables._countPerLock.Length; i++) | |||
| approxCount += tables._countPerLock[i]; | |||
| if (approxCount < tables._buckets.Length / 4) | |||
| { | |||
| _budget = 2 * _budget; | |||
| if (_budget < 0) | |||
| _budget = int.MaxValue; | |||
| return; | |||
| } | |||
| int newLength = 0; | |||
| bool maximizeTableSize = false; | |||
| try | |||
| { | |||
| checked | |||
| { | |||
| newLength = tables._buckets.Length * 2 + 1; | |||
| while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) | |||
| newLength += 2; | |||
| if (newLength > MaxArrayLength) | |||
| maximizeTableSize = true; | |||
| } | |||
| } | |||
| catch (OverflowException) | |||
| { | |||
| maximizeTableSize = true; | |||
| } | |||
| if (maximizeTableSize) | |||
| { | |||
| newLength = MaxArrayLength; | |||
| _budget = int.MaxValue; | |||
| } | |||
| AcquireLocks(1, tables._locks.Length, ref locksAcquired); | |||
| object[] newLocks = tables._locks; | |||
| if (_growLockArray && tables._locks.Length < MaxLockNumber) | |||
| { | |||
| newLocks = new object[tables._locks.Length * 2]; | |||
| Array.Copy(tables._locks, 0, newLocks, 0, tables._locks.Length); | |||
| for (int i = tables._locks.Length; i < newLocks.Length; i++) | |||
| newLocks[i] = new object(); | |||
| } | |||
| Node[] newBuckets = new Node[newLength]; | |||
| int[] newCountPerLock = new int[newLocks.Length]; | |||
| for (int i = 0; i < tables._buckets.Length; i++) | |||
| { | |||
| Node current = tables._buckets[i]; | |||
| while (current != null) | |||
| { | |||
| Node next = current._next; | |||
| int newBucketNo, newLockNo; | |||
| GetBucketAndLockNo(current._hashcode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length); | |||
| newBuckets[newBucketNo] = new Node(current._value, current._hashcode, newBuckets[newBucketNo]); | |||
| checked { newCountPerLock[newLockNo]++; } | |||
| current = next; | |||
| } | |||
| } | |||
| _budget = Math.Max(1, newBuckets.Length / newLocks.Length); | |||
| _tables = new Tables(newBuckets, newLocks, newCountPerLock); | |||
| } | |||
| finally { ReleaseLocks(0, locksAcquired); } | |||
| } | |||
| private void AcquireAllLocks(ref int locksAcquired) | |||
| { | |||
| AcquireLocks(0, 1, ref locksAcquired); | |||
| AcquireLocks(1, _tables._locks.Length, ref locksAcquired); | |||
| } | |||
| private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired) | |||
| { | |||
| object[] locks = _tables._locks; | |||
| for (int i = fromInclusive; i < toExclusive; i++) | |||
| { | |||
| bool lockTaken = false; | |||
| try | |||
| { | |||
| Monitor.Enter(locks[i], ref lockTaken); | |||
| } | |||
| finally | |||
| { | |||
| if (lockTaken) | |||
| locksAcquired++; | |||
| } | |||
| } | |||
| } | |||
| private void ReleaseLocks(int fromInclusive, int toExclusive) | |||
| { | |||
| for (int i = fromInclusive; i < toExclusive; i++) | |||
| Monitor.Exit(_tables._locks[i]); | |||
| } | |||
| } | |||
| //https://github.com/dotnet/corefx/blob/d0dc5fc099946adc1035b34a8b1f6042eddb0c75/src/System.Threading.Tasks.Parallel/src/System/Threading/PlatformHelper.cs | |||
| //Copyright (c) .NET Foundation and Contributors | |||
| internal static class PlatformHelper | |||
| { | |||
| private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; | |||
| private static volatile int s_processorCount; | |||
| private static volatile int s_lastProcessorCountRefreshTicks; | |||
| internal static int ProcessorCount | |||
| { | |||
| get | |||
| { | |||
| int now = Environment.TickCount; | |||
| if (s_processorCount == 0 || (now - s_lastProcessorCountRefreshTicks) >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS) | |||
| { | |||
| s_processorCount = Environment.ProcessorCount; | |||
| s_lastProcessorCountRefreshTicks = now; | |||
| } | |||
| return s_processorCount; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| namespace Discord | |||
| { | |||
| public enum ConnectionState : byte | |||
| { | |||
| Disconnected, | |||
| Connecting, | |||
| Connected, | |||
| Disconnecting | |||
| } | |||
| } | |||
| @@ -2,9 +2,10 @@ | |||
| namespace Discord | |||
| { | |||
| internal static class DateTimeHelper | |||
| internal static class DateTimeUtils | |||
| { | |||
| private const ulong EpochTicks = 621355968000000000UL; | |||
| private const ulong DiscordEpochMillis = 1420070400000UL; | |||
| public static DateTime FromEpochMilliseconds(ulong value) | |||
| => new DateTime((long)(value * TimeSpan.TicksPerMillisecond + EpochTicks), DateTimeKind.Utc); | |||
| @@ -12,6 +13,6 @@ namespace Discord | |||
| => new DateTime((long)(value * TimeSpan.TicksPerSecond + EpochTicks), DateTimeKind.Utc); | |||
| public static DateTime FromSnowflake(ulong value) | |||
| => FromEpochMilliseconds((value >> 22) + 1420070400000UL); | |||
| => FromEpochMilliseconds((value >> 22) + DiscordEpochMillis); | |||
| } | |||
| } | |||
| @@ -1,223 +0,0 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProjectGuid>{18F6FE23-73F6-4CA6-BBD9-F0139DC3EE90}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>Discord</RootNamespace> | |||
| <AssemblyName>Discord.Net</AssemblyName> | |||
| <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <TargetFrameworkProfile /> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>TRACE;DEBUG;__DEMO__,__DEMO_EXPERIMENTAL__</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core" /> | |||
| <Reference Include="System.Xml.Linq" /> | |||
| <Reference Include="System.Data.DataSetExtensions" /> | |||
| <Reference Include="Microsoft.CSharp" /> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Net.Http" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="API\CDN.cs" /> | |||
| <Compile Include="API\Common\Attachment.cs" /> | |||
| <Compile Include="API\Common\Channel.cs" /> | |||
| <Compile Include="API\Common\Connection.cs" /> | |||
| <Compile Include="API\Common\Embed.cs" /> | |||
| <Compile Include="API\Common\EmbedProvider.cs" /> | |||
| <Compile Include="API\Common\EmbedThumbnail.cs" /> | |||
| <Compile Include="API\Common\Emoji.cs" /> | |||
| <Compile Include="API\Common\Guild.cs" /> | |||
| <Compile Include="API\Common\GuildEmbed.cs" /> | |||
| <Compile Include="API\Common\GuildMember.cs" /> | |||
| <Compile Include="API\Common\Integration.cs" /> | |||
| <Compile Include="API\Common\IntegrationAccount.cs" /> | |||
| <Compile Include="API\Common\Invite.cs" /> | |||
| <Compile Include="API\Common\InviteChannel.cs" /> | |||
| <Compile Include="API\Common\InviteGuild.cs" /> | |||
| <Compile Include="API\Common\InviteMetadata.cs" /> | |||
| <Compile Include="API\Common\Message.cs" /> | |||
| <Compile Include="API\Common\Overwrite.cs" /> | |||
| <Compile Include="API\Common\ReadState.cs" /> | |||
| <Compile Include="API\Common\Role.cs" /> | |||
| <Compile Include="API\Common\User.cs" /> | |||
| <Compile Include="API\Common\UserGuild.cs" /> | |||
| <Compile Include="API\Common\VoiceRegion.cs" /> | |||
| <Compile Include="API\Common\VoiceState.cs" /> | |||
| <Compile Include="API\IOptional.cs" /> | |||
| <Compile Include="API\Optional.cs" /> | |||
| <Compile Include="API\Rest\DeleteMessagesParam.cs" /> | |||
| <Compile Include="API\Rest\GetGuildMembersParams.cs" /> | |||
| <Compile Include="API\Rest\LoginParams.cs" /> | |||
| <Compile Include="API\Rest\LoginResponse.cs" /> | |||
| <Compile Include="API\Rest\ModifyCurrentUserNickParams.cs" /> | |||
| <Compile Include="API\Rest\UploadFileParams.cs" /> | |||
| <Compile Include="API\Rest\GuildPruneParams.cs" /> | |||
| <Compile Include="API\Rest\CreateChannelInviteParams.cs" /> | |||
| <Compile Include="API\Rest\CreateDMChannelParams.cs" /> | |||
| <Compile Include="API\Rest\CreateGuildParams.cs" /> | |||
| <Compile Include="API\Rest\CreateGuildBanParams.cs" /> | |||
| <Compile Include="API\Rest\CreateGuildChannelParams.cs" /> | |||
| <Compile Include="API\Rest\CreateGuildIntegrationParams.cs" /> | |||
| <Compile Include="API\Rest\CreateMessageParams.cs" /> | |||
| <Compile Include="API\Rest\GetChannelMessagesParams.cs" /> | |||
| <Compile Include="API\Rest\GetGatewayResponse.cs" /> | |||
| <Compile Include="API\Rest\GetGuildPruneCountResponse.cs" /> | |||
| <Compile Include="API\Rest\ModifyChannelPermissionsParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyCurrentUserParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildChannelsParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildChannelParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildEmbedParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildIntegrationParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildMemberParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildRolesParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyGuildRoleParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyMessageParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyTextChannelParams.cs" /> | |||
| <Compile Include="API\Rest\ModifyVoiceChannelParams.cs" /> | |||
| <Compile Include="DiscordConfig.cs" /> | |||
| <Compile Include="API\DiscordRawClient.cs" /> | |||
| <Compile Include="Net\Converters\OptionalContractResolver.cs" /> | |||
| <Compile Include="Net\Converters\OptionalConverter.cs" /> | |||
| <Compile Include="Net\RateLimitException.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\BucketGroup.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\GlobalBucket.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\GuildBucket.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\RequestQueue.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\RequestQueueBucket.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\RestRequest.cs" /> | |||
| <Compile Include="Rest\DiscordClient.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IGuildEmbed.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IIntegrationAccount.cs" /> | |||
| <Compile Include="Common\Entities\Users\IConnection.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IGuildIntegration.cs" /> | |||
| <Compile Include="Common\Entities\Invites\IPublicInvite.cs" /> | |||
| <Compile Include="Common\Entities\Messages\Attachment.cs" /> | |||
| <Compile Include="Common\Entities\Channels\IChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\IDMChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\IGuildChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\IMessageChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\ITextChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\IVoiceChannel.cs" /> | |||
| <Compile Include="Common\Entities\Channels\ChannelType.cs" /> | |||
| <Compile Include="Common\Entities\Roles\Color.cs" /> | |||
| <Compile Include="Common\Entities\Messages\Direction.cs" /> | |||
| <Compile Include="Common\Entities\Messages\Embed.cs" /> | |||
| <Compile Include="Common\Entities\Messages\EmbedProvider.cs" /> | |||
| <Compile Include="Common\Entities\Messages\EmbedThumbnail.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\Emoji.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IUserGuild.cs" /> | |||
| <Compile Include="Common\Entities\IDeletable.cs" /> | |||
| <Compile Include="Common\Entities\IEntity.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IGuild.cs" /> | |||
| <Compile Include="Common\Entities\IMentionable.cs" /> | |||
| <Compile Include="Common\Entities\Messages\IMessage.cs" /> | |||
| <Compile Include="Common\Entities\Invites\IGuildInvite.cs" /> | |||
| <Compile Include="Common\Entities\Invites\IInvite.cs" /> | |||
| <Compile Include="Common\Entities\Roles\IRole.cs" /> | |||
| <Compile Include="Common\Entities\ISnowflakeEntity.cs" /> | |||
| <Compile Include="Common\Entities\IUpdateable.cs" /> | |||
| <Compile Include="Common\Entities\Guilds\IVoiceRegion.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\ChannelPermission.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\GuildPermission.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\ChannelPermissions.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\GuildPermissions.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\Overwrite.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\OverwritePermissions.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\PermissionUtilities.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\PermissionTarget.cs" /> | |||
| <Compile Include="Common\Entities\Permissions\PermValue.cs" /> | |||
| <Compile Include="Common\Entities\Users\UserStatus.cs" /> | |||
| <Compile Include="Common\Entities\Users\IDMUser.cs" /> | |||
| <Compile Include="Common\Entities\Users\IGuildUser.cs" /> | |||
| <Compile Include="Common\Entities\Users\ISelfUser.cs" /> | |||
| <Compile Include="Common\Entities\Users\IUser.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\Guild.cs" /> | |||
| <Compile Include="Rest\Entities\Channels\GuildChannel.cs" /> | |||
| <Compile Include="Rest\Entities\Channels\TextChannel.cs" /> | |||
| <Compile Include="Rest\Entities\Channels\VoiceChannel.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\GuildEmbed.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\GuildIntegration.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\IntegrationAccount.cs" /> | |||
| <Compile Include="Rest\Entities\Users\Connection.cs" /> | |||
| <Compile Include="Common\Helpers\PermissionHelper.cs" /> | |||
| <Compile Include="Rest\Entities\Invites\GuildInvite.cs" /> | |||
| <Compile Include="Rest\Entities\Invites\Invite.cs" /> | |||
| <Compile Include="Rest\Entities\Invites\PublicInvite.cs" /> | |||
| <Compile Include="Rest\Entities\Message.cs" /> | |||
| <Compile Include="Rest\Entities\Role.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\UserGuild.cs" /> | |||
| <Compile Include="Rest\Entities\Users\DMUser.cs" /> | |||
| <Compile Include="Rest\Entities\Users\GuildUser.cs" /> | |||
| <Compile Include="Rest\Entities\Users\PublicUser.cs" /> | |||
| <Compile Include="Rest\Entities\Guilds\VoiceRegion.cs" /> | |||
| <Compile Include="Common\Events\LogMessageEventArgs.cs" /> | |||
| <Compile Include="Common\Events\SentRequestEventArgs.cs" /> | |||
| <Compile Include="Common\Helpers\DateTimeHelper.cs" /> | |||
| <Compile Include="Common\Helpers\EventExtensions.cs" /> | |||
| <Compile Include="Common\Helpers\MentionHelper.cs" /> | |||
| <Compile Include="IDiscordClient.cs" /> | |||
| <Compile Include="Logging\ILogger.cs" /> | |||
| <Compile Include="Logging\Logger.cs" /> | |||
| <Compile Include="Logging\LogManager.cs" /> | |||
| <Compile Include="LogSeverity.cs" /> | |||
| <Compile Include="Net\Converters\ChannelTypeConverter.cs" /> | |||
| <Compile Include="Net\Converters\ImageConverter.cs" /> | |||
| <Compile Include="Net\Converters\NullableUInt64Converter.cs" /> | |||
| <Compile Include="Net\Converters\PermissionTargetConverter.cs" /> | |||
| <Compile Include="Net\Converters\StringEntityConverter.cs" /> | |||
| <Compile Include="Net\Converters\UInt64ArrayConverter.cs" /> | |||
| <Compile Include="Net\Converters\UInt64Converter.cs" /> | |||
| <Compile Include="Net\Converters\UInt64EntityConverter.cs" /> | |||
| <Compile Include="Net\Converters\UserStatusConverter.cs" /> | |||
| <Compile Include="Net\HttpException.cs" /> | |||
| <Compile Include="Net\Rest\DefaultRestClient.cs" /> | |||
| <Compile Include="Net\Rest\RequestQueue\IRequestQueue.cs" /> | |||
| <Compile Include="Net\Rest\IRestClient.cs" /> | |||
| <Compile Include="Net\Rest\MultipartFile.cs" /> | |||
| <Compile Include="Net\Rest\RestClientProvider.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| <Compile Include="Rest\Entities\Channels\DMChannel.cs" /> | |||
| <Compile Include="Rest\Entities\Users\SelfUser.cs" /> | |||
| <Compile Include="Rest\Entities\Users\User.cs" /> | |||
| <Compile Include="TokenType.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="Common\Entities\Users\IVoiceState.cs.old" /> | |||
| <None Include="project.json" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Folder Include="Net\WebSockets\" /> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -0,0 +1,19 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||
| <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||
| </PropertyGroup> | |||
| <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||
| <PropertyGroup Label="Globals"> | |||
| <ProjectGuid>91e9e7bd-75c9-4e98-84aa-2c271922e5c2</ProjectGuid> | |||
| <RootNamespace>Discord</RootNamespace> | |||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||
| <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||
| <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||
| </PropertyGroup> | |||
| <PropertyGroup> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| </PropertyGroup> | |||
| <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> | |||
| </Project> | |||
| @@ -3,12 +3,15 @@ using System.Reflection; | |||
| namespace Discord | |||
| { | |||
| //TODO: Add socket config items in their own class | |||
| public class DiscordConfig | |||
| { | |||
| public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown"; | |||
| public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; | |||
| public const int GatewayAPIVersion = 3; | |||
| public const int GatewayAPIVersion = 3; //TODO: Upgrade to 4 | |||
| public const string GatewayEncoding = "json"; | |||
| public const string ClientAPIUrl = "https://discordapp.com/api/"; | |||
| public const string CDNUrl = "https://cdn.discordapp.com/"; | |||
| @@ -26,6 +29,6 @@ namespace Discord | |||
| public LogSeverity LogLevel { get; set; } = LogSeverity.Info; | |||
| /// <summary> Gets or sets the provider used to generate new REST connections. </summary> | |||
| public RestClientProvider RestClientProvider { get; set; } = (url, ct) => new DefaultRestClient(url, ct); | |||
| public RestClientProvider RestClientProvider { get; set; } = url => new DefaultRestClient(url); | |||
| } | |||
| } | |||
| @@ -7,6 +7,8 @@ namespace Discord | |||
| { | |||
| /// <summary> Gets a collection of all users in this channel. </summary> | |||
| Task<IEnumerable<IUser>> GetUsers(); | |||
| /// <summary> Gets a paginated collection of all users in this channel. </summary> | |||
| Task<IEnumerable<IUser>> GetUsers(int limit, int offset = 0); | |||
| /// <summary> Gets a user in this channel with the provided id.</summary> | |||
| Task<IUser> GetUser(ulong id); | |||
| } | |||
| @@ -5,7 +5,7 @@ namespace Discord | |||
| public interface IDMChannel : IMessageChannel, IUpdateable | |||
| { | |||
| /// <summary> Gets the recipient of all messages in this channel. </summary> | |||
| IDMUser Recipient { get; } | |||
| IUser Recipient { get; } | |||
| /// <summary> Closes this private channel, removing it from your channel list. </summary> | |||
| Task Close(); | |||
| @@ -20,9 +20,9 @@ namespace Discord | |||
| /// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param> | |||
| /// <param name="isTemporary"> If true, a user accepting this invite will be kicked from the guild after closing their client. </param> | |||
| /// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param> | |||
| Task<IGuildInvite> CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool withXkcd = false); | |||
| Task<IInviteMetadata> CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool withXkcd = false); | |||
| /// <summary> Returns a collection of all invites to this channel. </summary> | |||
| Task<IEnumerable<IGuildInvite>> GetInvites(); | |||
| Task<IEnumerable<IInviteMetadata>> GetInvites(); | |||
| /// <summary> Gets a collection of permission overwrites for this channel. </summary> | |||
| IReadOnlyDictionary<ulong, Overwrite> PermissionOverwrites { get; } | |||
| @@ -6,8 +6,12 @@ namespace Discord | |||
| { | |||
| public interface IMessageChannel : IChannel | |||
| { | |||
| /// <summary> Gets the message in this message channel with the given id, or null if none was found. </summary> | |||
| Task<IMessage> GetMessage(ulong id); | |||
| /// <summary> Gets all messages in this channel's cache. </summary> | |||
| IEnumerable<IMessage> CachedMessages { get; } | |||
| /// <summary> Gets the message from this channel's cache with the given id, or null if none was found. </summary> | |||
| Task<IMessage> GetCachedMessage(ulong id); | |||
| /// <summary> Gets the last N messages from this message channel. </summary> | |||
| Task<IEnumerable<IMessage>> GetMessages(int limit = DiscordConfig.MaxMessagesPerBatch); | |||
| /// <summary> Gets a collection of messages in this channel. </summary> | |||
| @@ -8,6 +8,8 @@ namespace Discord | |||
| { | |||
| /// <summary> Gets the bitrate, in bits per second, clients in this voice channel are requested to use. </summary> | |||
| int Bitrate { get; } | |||
| /// <summary> Gets the max amount of users allowed to be connected to this channel at one time. A value of 0 represents no limit. </summary> | |||
| int UserLimit { get; } | |||
| /// <summary> Modifies this voice channel. </summary> | |||
| Task Modify(Action<ModifyVoiceChannelParams> func); | |||
| @@ -70,13 +70,13 @@ namespace Discord | |||
| Task<IVoiceChannel> CreateVoiceChannel(string name); | |||
| /// <summary> Gets a collection of all invites to this guild. </summary> | |||
| Task<IEnumerable<IGuildInvite>> GetInvites(); | |||
| Task<IEnumerable<IInviteMetadata>> GetInvites(); | |||
| /// <summary> Creates a new invite to this guild. </summary> | |||
| /// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param> | |||
| /// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param> | |||
| /// <param name="isTemporary"> If true, a user accepting this invite will be kicked from the guild after closing their client. </param> | |||
| /// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param> | |||
| Task<IGuildInvite> CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool withXkcd = false); | |||
| Task<IInviteMetadata> CreateInvite(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool withXkcd = false); | |||
| /// <summary> Gets a collection of all roles in this guild. </summary> | |||
| Task<IEnumerable<IRole>> GetRoles(); | |||
| @@ -12,10 +12,10 @@ namespace Discord | |||
| ulong ExpireBehavior { get; } | |||
| ulong ExpireGracePeriod { get; } | |||
| DateTime SyncedAt { get; } | |||
| IntegrationAccount Account { get; } | |||
| IGuild Guild { get; } | |||
| IUser User { get; } | |||
| IRole Role { get; } | |||
| IIntegrationAccount Account { get; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| using System.Diagnostics; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||
| public struct IntegrationAccount | |||
| { | |||
| /// <inheritdoc /> | |||
| public string Id { get; } | |||
| /// <inheritdoc /> | |||
| public string Name { get; private set; } | |||
| public override string ToString() => Name; | |||
| private string DebuggerDisplay => $"{Name} ({Id})"; | |||
| } | |||
| } | |||
| @@ -1,7 +1,9 @@ | |||
| using Model = Discord.API.VoiceRegion; | |||
| using System.Diagnostics; | |||
| using Model = Discord.API.VoiceRegion; | |||
| namespace Discord.Rest | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||
| public class VoiceRegion : IVoiceRegion | |||
| { | |||
| /// <inheritdoc /> | |||
| @@ -27,6 +29,7 @@ namespace Discord.Rest | |||
| SamplePort = model.SamplePort; | |||
| } | |||
| public override string ToString() => $"{Name ?? Id.ToString()}"; | |||
| public override string ToString() => Name; | |||
| private string DebuggerDisplay => $"{Name} ({Id}{(IsVip ? ", VIP" : "")}{(IsOptimal ? ", Optimal" : "")})"; | |||
| } | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IInvite : IEntity<string> | |||
| public interface IInvite : IEntity<string>, IDeletable | |||
| { | |||
| /// <summary> Gets the unique identifier for this invite. </summary> | |||
| string Code { get; } | |||
| @@ -1,6 +1,6 @@ | |||
| namespace Discord | |||
| { | |||
| public interface IGuildInvite : IDeletable, IInvite | |||
| public interface IInviteMetadata : IInvite | |||
| { | |||
| /// <summary> Returns true if this invite was revoked. </summary> | |||
| bool IsRevoked { get; } | |||
| @@ -12,8 +12,5 @@ | |||
| int? MaxUses { get; } | |||
| /// <summary> Gets the amount of times this invite has been used. </summary> | |||
| int Uses { get; } | |||
| /// <summary> Gets the guild this invite is linked to. </summary> | |||
| IGuild Guild { get; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| using System.Diagnostics; | |||
| using System.Threading.Tasks; | |||
| using Model = Discord.API.Invite; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class Invite : IInvite | |||
| { | |||
| /// <inheritdoc /> | |||
| public string Code { get; } | |||
| internal IDiscordClient Discord { get; } | |||
| /// <inheritdoc /> | |||
| public ulong GuildId { get; private set; } | |||
| /// <inheritdoc /> | |||
| public ulong ChannelId { get; private set; } | |||
| /// <inheritdoc /> | |||
| public string XkcdCode { get; private set; } | |||
| /// <inheritdoc /> | |||
| public string GuildName { get; private set; } | |||
| /// <inheritdoc /> | |||
| public string ChannelName { get; private set; } | |||
| /// <inheritdoc /> | |||
| public string Url => $"{DiscordConfig.InviteUrl}/{XkcdCode ?? Code}"; | |||
| /// <inheritdoc /> | |||
| public string XkcdUrl => XkcdCode != null ? $"{DiscordConfig.InviteUrl}/{XkcdCode}" : null; | |||
| internal Invite(IDiscordClient discord, Model model) | |||
| { | |||
| Discord = discord; | |||
| Code = model.Code; | |||
| Update(model); | |||
| } | |||
| protected virtual void Update(Model model) | |||
| { | |||
| XkcdCode = model.XkcdPass; | |||
| GuildId = model.Guild.Id; | |||
| ChannelId = model.Channel.Id; | |||
| GuildName = model.Guild.Name; | |||
| ChannelName = model.Channel.Name; | |||
| } | |||
| /// <inheritdoc /> | |||
| public async Task Accept() | |||
| { | |||
| await Discord.ApiClient.AcceptInvite(Code).ConfigureAwait(false); | |||
| } | |||
| /// <inheritdoc /> | |||
| public async Task Delete() | |||
| { | |||
| await Discord.ApiClient.DeleteInvite(Code).ConfigureAwait(false); | |||
| } | |||
| /// <inheritdoc /> | |||
| public override string ToString() => XkcdUrl ?? Url; | |||
| private string DebuggerDisplay => $"{XkcdUrl ?? Url} ({GuildName} / {ChannelName})"; | |||
| string IEntity<string>.Id => Code; | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| using Model = Discord.API.InviteMetadata; | |||
| namespace Discord | |||
| { | |||
| public class InviteMetadata : Invite, IInviteMetadata | |||
| { | |||
| /// <inheritdoc /> | |||
| public bool IsRevoked { get; private set; } | |||
| /// <inheritdoc /> | |||
| public bool IsTemporary { get; private set; } | |||
| /// <inheritdoc /> | |||
| public int? MaxAge { get; private set; } | |||
| /// <inheritdoc /> | |||
| public int? MaxUses { get; private set; } | |||
| /// <inheritdoc /> | |||
| public int Uses { get; private set; } | |||
| internal InviteMetadata(IDiscordClient client, Model model) | |||
| : base(client, model) | |||
| { | |||
| Update(model); | |||
| } | |||
| private void Update(Model model) | |||
| { | |||
| IsRevoked = model.Revoked; | |||
| IsTemporary = model.Temporary; | |||
| MaxAge = model.MaxAge != 0 ? model.MaxAge : (int?)null; | |||
| MaxUses = model.MaxUses; | |||
| Uses = model.Uses; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Threading.Tasks; | |||
| using Discord.API.Rest; | |||
| using System.Collections.Generic; | |||
| namespace Discord | |||
| { | |||
| @@ -1,6 +1,6 @@ | |||
| namespace Discord | |||
| { | |||
| internal enum ChannelPermission : byte | |||
| public enum ChannelPermission : byte | |||
| { | |||
| //General | |||
| CreateInstantInvite = 0, | |||
| @@ -0,0 +1,151 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | |||
| public struct ChannelPermissions | |||
| { | |||
| #if CSHARP7 | |||
| private static ChannelPermissions _allDM { get; } = new ChannelPermissions(0b000100_000000_0011111111_0000011001); | |||
| private static ChannelPermissions _allText { get; } = new ChannelPermissions(0b000000_000000_0001110011_0000000000); | |||
| private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(0b000100_111111_0000000000_0000011001); | |||
| #else | |||
| private static ChannelPermissions _allDM { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000000111111110000011001", 2)); | |||
| private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000000011100110000000000", 2)); | |||
| private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(Convert.ToUInt64("00010011111100000000000000011001", 2)); | |||
| #endif | |||
| /// <summary> Gets a blank ChannelPermissions that grants no permissions. </summary> | |||
| public static ChannelPermissions None { get; } = new ChannelPermissions(); | |||
| /// <summary> Gets a ChannelPermissions that grants all permissions for a given channelType. </summary> | |||
| public static ChannelPermissions All(IChannel channel) | |||
| { | |||
| #if CSHARP7 | |||
| switch (channel) | |||
| { | |||
| case ITextChannel _: return _allText; | |||
| case IVoiceChannel _: return _allVoice; | |||
| case IDMChannel _: return _allDM; | |||
| default: | |||
| throw new ArgumentException("Unknown channel type", nameof(channel)); | |||
| } | |||
| #else | |||
| if (channel is ITextChannel) return _allText; | |||
| if (channel is IVoiceChannel) return _allVoice; | |||
| if (channel is IDMChannel) return _allDM; | |||
| throw new ArgumentException("Unknown channel type", nameof(channel)); | |||
| #endif | |||
| } | |||
| /// <summary> Gets a packed value representing all the permissions in this ChannelPermissions. </summary> | |||
| public ulong RawValue { get; } | |||
| /// <summary> If True, a user may create invites. </summary> | |||
| public bool CreateInstantInvite => Permissions.GetValue(RawValue, ChannelPermission.CreateInstantInvite); | |||
| /// <summary> If True, a user may create, delete and modify this channel. </summary> | |||
| public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel); | |||
| /// <summary> If True, a user may join channels. </summary> | |||
| public bool ReadMessages => Permissions.GetValue(RawValue, ChannelPermission.ReadMessages); | |||
| /// <summary> If True, a user may send messages. </summary> | |||
| public bool SendMessages => Permissions.GetValue(RawValue, ChannelPermission.SendMessages); | |||
| /// <summary> If True, a user may send text-to-speech messages. </summary> | |||
| public bool SendTTSMessages => Permissions.GetValue(RawValue, ChannelPermission.SendTTSMessages); | |||
| /// <summary> If True, a user may delete messages. </summary> | |||
| public bool ManageMessages => Permissions.GetValue(RawValue, ChannelPermission.ManageMessages); | |||
| /// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
| public bool EmbedLinks => Permissions.GetValue(RawValue, ChannelPermission.EmbedLinks); | |||
| /// <summary> If True, a user may send files. </summary> | |||
| public bool AttachFiles => Permissions.GetValue(RawValue, ChannelPermission.AttachFiles); | |||
| /// <summary> If True, a user may read previous messages. </summary> | |||
| public bool ReadMessageHistory => Permissions.GetValue(RawValue, ChannelPermission.ReadMessageHistory); | |||
| /// <summary> If True, a user may mention @everyone. </summary> | |||
| public bool MentionEveryone => Permissions.GetValue(RawValue, ChannelPermission.MentionEveryone); | |||
| /// <summary> If True, a user may connect to a voice channel. </summary> | |||
| public bool Connect => Permissions.GetValue(RawValue, ChannelPermission.Connect); | |||
| /// <summary> If True, a user may speak in a voice channel. </summary> | |||
| public bool Speak => Permissions.GetValue(RawValue, ChannelPermission.Speak); | |||
| /// <summary> If True, a user may mute users. </summary> | |||
| public bool MuteMembers => Permissions.GetValue(RawValue, ChannelPermission.MuteMembers); | |||
| /// <summary> If True, a user may deafen users. </summary> | |||
| public bool DeafenMembers => Permissions.GetValue(RawValue, ChannelPermission.DeafenMembers); | |||
| /// <summary> If True, a user may move other users between voice channels. </summary> | |||
| public bool MoveMembers => Permissions.GetValue(RawValue, ChannelPermission.MoveMembers); | |||
| /// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
| public bool UseVAD => Permissions.GetValue(RawValue, ChannelPermission.UseVAD); | |||
| /// <summary> If True, a user may adjust permissions. This also implictly grants all other permissions. </summary> | |||
| public bool ManagePermissions => Permissions.GetValue(RawValue, ChannelPermission.ManagePermissions); | |||
| /// <summary> Creates a new ChannelPermissions with the provided packed value. </summary> | |||
| public ChannelPermissions(ulong rawValue) { RawValue = rawValue; } | |||
| private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null, | |||
| bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, | |||
| bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, | |||
| bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, | |||
| bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) | |||
| { | |||
| ulong value = initialValue; | |||
| Permissions.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite); | |||
| Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannel); | |||
| Permissions.SetValue(ref value, readMessages, ChannelPermission.ReadMessages); | |||
| Permissions.SetValue(ref value, sendMessages, ChannelPermission.SendMessages); | |||
| Permissions.SetValue(ref value, sendTTSMessages, ChannelPermission.SendTTSMessages); | |||
| Permissions.SetValue(ref value, manageMessages, ChannelPermission.ManageMessages); | |||
| Permissions.SetValue(ref value, embedLinks, ChannelPermission.EmbedLinks); | |||
| Permissions.SetValue(ref value, attachFiles, ChannelPermission.AttachFiles); | |||
| Permissions.SetValue(ref value, readMessageHistory, ChannelPermission.ReadMessageHistory); | |||
| Permissions.SetValue(ref value, mentionEveryone, ChannelPermission.MentionEveryone); | |||
| Permissions.SetValue(ref value, connect, ChannelPermission.Connect); | |||
| Permissions.SetValue(ref value, speak, ChannelPermission.Speak); | |||
| Permissions.SetValue(ref value, muteMembers, ChannelPermission.MuteMembers); | |||
| Permissions.SetValue(ref value, deafenMembers, ChannelPermission.DeafenMembers); | |||
| Permissions.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers); | |||
| Permissions.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD); | |||
| Permissions.SetValue(ref value, managePermissions, ChannelPermission.ManagePermissions); | |||
| RawValue = value; | |||
| } | |||
| /// <summary> Creates a new ChannelPermissions with the provided permissions. </summary> | |||
| public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false, | |||
| bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, | |||
| bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, | |||
| bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, | |||
| bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false) | |||
| : this(0, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, | |||
| embedLinks, attachFiles, readMessageHistory, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, managePermissions) { } | |||
| /// <summary> Creates a new ChannelPermissions from this one, changing the provided non-null permissions. </summary> | |||
| public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, | |||
| bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, | |||
| bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, | |||
| bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, | |||
| bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) | |||
| => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, | |||
| embedLinks, attachFiles, readMessageHistory, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, managePermissions); | |||
| public List<ChannelPermission> ToList() | |||
| { | |||
| var perms = new List<ChannelPermission>(); | |||
| ulong x = 1; | |||
| for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1) | |||
| { | |||
| if ((RawValue & x) != 0) | |||
| perms.Add((ChannelPermission)i); | |||
| } | |||
| return perms; | |||
| } | |||
| /// <inheritdoc /> | |||
| public override string ToString() => RawValue.ToString(); | |||
| private string DebuggerDisplay => $"{RawValue} ({string.Join(", ", ToList())})"; | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| namespace Discord | |||
| { | |||
| internal enum GuildPermission : byte | |||
| public enum GuildPermission : byte | |||
| { | |||
| //General | |||
| CreateInstantInvite = 0, | |||
| @@ -1,71 +1,78 @@ | |||
| using System.Collections.Generic; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public struct GuildPermissions | |||
| { | |||
| /// <summary> Gets a blank GuildPermissions that grants no permissions. </summary> | |||
| public static readonly GuildPermissions None = new GuildPermissions(); | |||
| /// <summary> Gets a GuildPermissions that grants all permissions. </summary> | |||
| #if CSHARP7 | |||
| public static readonly GuildPermissions All = new GuildPermissions(0b000111_111111_0011111111_0000111111); | |||
| #else | |||
| public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("00011111111100111111110000111111", 2)); | |||
| #endif | |||
| /// <summary> Gets a packed value representing all the permissions in this GuildPermissions. </summary> | |||
| public uint RawValue { get; } | |||
| public ulong RawValue { get; } | |||
| /// <summary> If True, a user may create invites. </summary> | |||
| public bool CreateInstantInvite => PermissionUtilities.GetValue(RawValue, GuildPermission.CreateInstantInvite); | |||
| public bool CreateInstantInvite => Permissions.GetValue(RawValue, GuildPermission.CreateInstantInvite); | |||
| /// <summary> If True, a user may ban users from the guild. </summary> | |||
| public bool BanMembers => PermissionUtilities.GetValue(RawValue, GuildPermission.BanMembers); | |||
| public bool BanMembers => Permissions.GetValue(RawValue, GuildPermission.BanMembers); | |||
| /// <summary> If True, a user may kick users from the guild. </summary> | |||
| public bool KickMembers => PermissionUtilities.GetValue(RawValue, GuildPermission.KickMembers); | |||
| public bool KickMembers => Permissions.GetValue(RawValue, GuildPermission.KickMembers); | |||
| /// <summary> If True, a user is granted all permissions, and cannot have them revoked via channel permissions. </summary> | |||
| public bool Administrator => PermissionUtilities.GetValue(RawValue, GuildPermission.Administrator); | |||
| public bool Administrator => Permissions.GetValue(RawValue, GuildPermission.Administrator); | |||
| /// <summary> If True, a user may create, delete and modify channels. </summary> | |||
| public bool ManageChannels => PermissionUtilities.GetValue(RawValue, GuildPermission.ManageChannels); | |||
| public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); | |||
| /// <summary> If True, a user may adjust guild properties. </summary> | |||
| public bool ManageGuild => PermissionUtilities.GetValue(RawValue, GuildPermission.ManageGuild); | |||
| public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); | |||
| /// <summary> If True, a user may join channels. </summary> | |||
| public bool ReadMessages => PermissionUtilities.GetValue(RawValue, GuildPermission.ReadMessages); | |||
| public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); | |||
| /// <summary> If True, a user may send messages. </summary> | |||
| public bool SendMessages => PermissionUtilities.GetValue(RawValue, GuildPermission.SendMessages); | |||
| public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); | |||
| /// <summary> If True, a user may send text-to-speech messages. </summary> | |||
| public bool SendTTSMessages => PermissionUtilities.GetValue(RawValue, GuildPermission.SendTTSMessages); | |||
| public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages); | |||
| /// <summary> If True, a user may delete messages. </summary> | |||
| public bool ManageMessages => PermissionUtilities.GetValue(RawValue, GuildPermission.ManageMessages); | |||
| public bool ManageMessages => Permissions.GetValue(RawValue, GuildPermission.ManageMessages); | |||
| /// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
| public bool EmbedLinks => PermissionUtilities.GetValue(RawValue, GuildPermission.EmbedLinks); | |||
| public bool EmbedLinks => Permissions.GetValue(RawValue, GuildPermission.EmbedLinks); | |||
| /// <summary> If True, a user may send files. </summary> | |||
| public bool AttachFiles => PermissionUtilities.GetValue(RawValue, GuildPermission.AttachFiles); | |||
| public bool AttachFiles => Permissions.GetValue(RawValue, GuildPermission.AttachFiles); | |||
| /// <summary> If True, a user may read previous messages. </summary> | |||
| public bool ReadMessageHistory => PermissionUtilities.GetValue(RawValue, GuildPermission.ReadMessageHistory); | |||
| public bool ReadMessageHistory => Permissions.GetValue(RawValue, GuildPermission.ReadMessageHistory); | |||
| /// <summary> If True, a user may mention @everyone. </summary> | |||
| public bool MentionEveryone => PermissionUtilities.GetValue(RawValue, GuildPermission.MentionEveryone); | |||
| public bool MentionEveryone => Permissions.GetValue(RawValue, GuildPermission.MentionEveryone); | |||
| /// <summary> If True, a user may connect to a voice channel. </summary> | |||
| public bool Connect => PermissionUtilities.GetValue(RawValue, GuildPermission.Connect); | |||
| public bool Connect => Permissions.GetValue(RawValue, GuildPermission.Connect); | |||
| /// <summary> If True, a user may speak in a voice channel. </summary> | |||
| public bool Speak => PermissionUtilities.GetValue(RawValue, GuildPermission.Speak); | |||
| public bool Speak => Permissions.GetValue(RawValue, GuildPermission.Speak); | |||
| /// <summary> If True, a user may mute users. </summary> | |||
| public bool MuteMembers => PermissionUtilities.GetValue(RawValue, GuildPermission.MuteMembers); | |||
| public bool MuteMembers => Permissions.GetValue(RawValue, GuildPermission.MuteMembers); | |||
| /// <summary> If True, a user may deafen users. </summary> | |||
| public bool DeafenMembers => PermissionUtilities.GetValue(RawValue, GuildPermission.DeafenMembers); | |||
| public bool DeafenMembers => Permissions.GetValue(RawValue, GuildPermission.DeafenMembers); | |||
| /// <summary> If True, a user may move other users between voice channels. </summary> | |||
| public bool MoveMembers => PermissionUtilities.GetValue(RawValue, GuildPermission.MoveMembers); | |||
| public bool MoveMembers => Permissions.GetValue(RawValue, GuildPermission.MoveMembers); | |||
| /// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
| public bool UseVAD => PermissionUtilities.GetValue(RawValue, GuildPermission.UseVAD); | |||
| public bool UseVAD => Permissions.GetValue(RawValue, GuildPermission.UseVAD); | |||
| /// <summary> If True, a user may change their own nickname. </summary> | |||
| public bool ChangeNickname => PermissionUtilities.GetValue(RawValue, GuildPermission.ChangeNickname); | |||
| public bool ChangeNickname => Permissions.GetValue(RawValue, GuildPermission.ChangeNickname); | |||
| /// <summary> If True, a user may change the nickname of other users. </summary> | |||
| public bool ManageNicknames => PermissionUtilities.GetValue(RawValue, GuildPermission.ManageNicknames); | |||
| public bool ManageNicknames => Permissions.GetValue(RawValue, GuildPermission.ManageNicknames); | |||
| /// <summary> If True, a user may adjust roles. </summary> | |||
| public bool ManageRoles => PermissionUtilities.GetValue(RawValue, GuildPermission.ManageRoles); | |||
| public bool ManageRoles => Permissions.GetValue(RawValue, GuildPermission.ManageRoles); | |||
| /// <summary> Creates a new GuildPermissions with the provided packed value. </summary> | |||
| public GuildPermissions(uint rawValue) { RawValue = rawValue; } | |||
| public GuildPermissions(ulong rawValue) { RawValue = rawValue; } | |||
| private GuildPermissions(uint initialValue, bool? createInstantInvite = null, bool? kickMembers = null, | |||
| private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, | |||
| bool? banMembers = null, bool? administrator = null, bool? manageChannel = null, bool? manageGuild = null, | |||
| bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, | |||
| bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, | |||
| @@ -73,31 +80,31 @@ namespace Discord | |||
| bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, | |||
| bool? manageRoles = null) | |||
| { | |||
| uint value = initialValue; | |||
| ulong value = initialValue; | |||
| PermissionUtilities.SetValue(ref value, createInstantInvite, GuildPermission.CreateInstantInvite); | |||
| PermissionUtilities.SetValue(ref value, banMembers, GuildPermission.BanMembers); | |||
| PermissionUtilities.SetValue(ref value, kickMembers, GuildPermission.KickMembers); | |||
| PermissionUtilities.SetValue(ref value, administrator, GuildPermission.Administrator); | |||
| PermissionUtilities.SetValue(ref value, manageChannel, GuildPermission.ManageChannels); | |||
| PermissionUtilities.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); | |||
| PermissionUtilities.SetValue(ref value, readMessages, GuildPermission.ReadMessages); | |||
| PermissionUtilities.SetValue(ref value, sendMessages, GuildPermission.SendMessages); | |||
| PermissionUtilities.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); | |||
| PermissionUtilities.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); | |||
| PermissionUtilities.SetValue(ref value, embedLinks, GuildPermission.EmbedLinks); | |||
| PermissionUtilities.SetValue(ref value, attachFiles, GuildPermission.AttachFiles); | |||
| PermissionUtilities.SetValue(ref value, readMessageHistory, GuildPermission.ReadMessageHistory); | |||
| PermissionUtilities.SetValue(ref value, mentionEveryone, GuildPermission.MentionEveryone); | |||
| PermissionUtilities.SetValue(ref value, connect, GuildPermission.Connect); | |||
| PermissionUtilities.SetValue(ref value, speak, GuildPermission.Speak); | |||
| PermissionUtilities.SetValue(ref value, muteMembers, GuildPermission.MuteMembers); | |||
| PermissionUtilities.SetValue(ref value, deafenMembers, GuildPermission.DeafenMembers); | |||
| PermissionUtilities.SetValue(ref value, moveMembers, GuildPermission.MoveMembers); | |||
| PermissionUtilities.SetValue(ref value, useVoiceActivation, GuildPermission.UseVAD); | |||
| PermissionUtilities.SetValue(ref value, changeNickname, GuildPermission.ChangeNickname); | |||
| PermissionUtilities.SetValue(ref value, manageNicknames, GuildPermission.ManageNicknames); | |||
| PermissionUtilities.SetValue(ref value, manageRoles, GuildPermission.ManageRoles); | |||
| Permissions.SetValue(ref value, createInstantInvite, GuildPermission.CreateInstantInvite); | |||
| Permissions.SetValue(ref value, banMembers, GuildPermission.BanMembers); | |||
| Permissions.SetValue(ref value, kickMembers, GuildPermission.KickMembers); | |||
| Permissions.SetValue(ref value, administrator, GuildPermission.Administrator); | |||
| Permissions.SetValue(ref value, manageChannel, GuildPermission.ManageChannels); | |||
| Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); | |||
| Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages); | |||
| Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); | |||
| Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); | |||
| Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); | |||
| Permissions.SetValue(ref value, embedLinks, GuildPermission.EmbedLinks); | |||
| Permissions.SetValue(ref value, attachFiles, GuildPermission.AttachFiles); | |||
| Permissions.SetValue(ref value, readMessageHistory, GuildPermission.ReadMessageHistory); | |||
| Permissions.SetValue(ref value, mentionEveryone, GuildPermission.MentionEveryone); | |||
| Permissions.SetValue(ref value, connect, GuildPermission.Connect); | |||
| Permissions.SetValue(ref value, speak, GuildPermission.Speak); | |||
| Permissions.SetValue(ref value, muteMembers, GuildPermission.MuteMembers); | |||
| Permissions.SetValue(ref value, deafenMembers, GuildPermission.DeafenMembers); | |||
| Permissions.SetValue(ref value, moveMembers, GuildPermission.MoveMembers); | |||
| Permissions.SetValue(ref value, useVoiceActivation, GuildPermission.UseVAD); | |||
| Permissions.SetValue(ref value, changeNickname, GuildPermission.ChangeNickname); | |||
| Permissions.SetValue(ref value, manageNicknames, GuildPermission.ManageNicknames); | |||
| Permissions.SetValue(ref value, manageRoles, GuildPermission.ManageRoles); | |||
| RawValue = value; | |||
| } | |||
| @@ -125,21 +132,20 @@ namespace Discord | |||
| => new GuildPermissions(RawValue, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, readMessages, | |||
| sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles); | |||
| /// <inheritdoc /> | |||
| public override string ToString() | |||
| public List<GuildPermission> ToList() | |||
| { | |||
| var perms = new List<string>(); | |||
| int x = 1; | |||
| for (byte i = 0; i < 32; i++, x <<= 1) | |||
| var perms = new List<GuildPermission>(); | |||
| ulong x = 1; | |||
| for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1) | |||
| { | |||
| if ((RawValue & x) != 0) | |||
| { | |||
| if (System.Enum.IsDefined(typeof(GuildPermission), i)) | |||
| perms.Add($"{(GuildPermission)i}"); | |||
| } | |||
| perms.Add((GuildPermission)i); | |||
| } | |||
| return string.Join(", ", perms); | |||
| return perms; | |||
| } | |||
| /// <inheritdoc /> | |||
| public override string ToString() => RawValue.ToString(); | |||
| private string DebuggerDisplay => $"{RawValue} ({string.Join(", ", ToList())})"; | |||
| } | |||
| } | |||
| @@ -1,8 +1,10 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public struct OverwritePermissions | |||
| { | |||
| /// <summary> Gets a blank OverwritePermissions that inherits all permissions. </summary> | |||
| @@ -15,77 +17,77 @@ namespace Discord | |||
| => new OverwritePermissions(0, ChannelPermissions.All(channel).RawValue); | |||
| /// <summary> Gets a packed value representing all the allowed permissions in this OverwritePermissions. </summary> | |||
| public uint AllowValue { get; } | |||
| public ulong AllowValue { get; } | |||
| /// <summary> Gets a packed value representing all the denied permissions in this OverwritePermissions. </summary> | |||
| public uint DenyValue { get; } | |||
| public ulong DenyValue { get; } | |||
| /// <summary> If True, a user may create invites. </summary> | |||
| public PermValue CreateInstantInvite => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.CreateInstantInvite); | |||
| public PermValue CreateInstantInvite => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.CreateInstantInvite); | |||
| /// <summary> If True, a user may create, delete and modify this channel. </summary> | |||
| public PermValue ManageChannel => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.ManageChannel); | |||
| public PermValue ManageChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageChannel); | |||
| /// <summary> If True, a user may join channels. </summary> | |||
| public PermValue ReadMessages => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.ReadMessages); | |||
| public PermValue ReadMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ReadMessages); | |||
| /// <summary> If True, a user may send messages. </summary> | |||
| public PermValue SendMessages => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.SendMessages); | |||
| public PermValue SendMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.SendMessages); | |||
| /// <summary> If True, a user may send text-to-speech messages. </summary> | |||
| public PermValue SendTTSMessages => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.SendTTSMessages); | |||
| public PermValue SendTTSMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.SendTTSMessages); | |||
| /// <summary> If True, a user may delete messages. </summary> | |||
| public PermValue ManageMessages => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.ManageMessages); | |||
| public PermValue ManageMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageMessages); | |||
| /// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
| public PermValue EmbedLinks => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.EmbedLinks); | |||
| public PermValue EmbedLinks => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.EmbedLinks); | |||
| /// <summary> If True, a user may send files. </summary> | |||
| public PermValue AttachFiles => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.AttachFiles); | |||
| public PermValue AttachFiles => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AttachFiles); | |||
| /// <summary> If True, a user may read previous messages. </summary> | |||
| public PermValue ReadMessageHistory => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.ReadMessageHistory); | |||
| public PermValue ReadMessageHistory => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ReadMessageHistory); | |||
| /// <summary> If True, a user may mention @everyone. </summary> | |||
| public PermValue MentionEveryone => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.MentionEveryone); | |||
| public PermValue MentionEveryone => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.MentionEveryone); | |||
| /// <summary> If True, a user may connect to a voice channel. </summary> | |||
| public PermValue Connect => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.Connect); | |||
| public PermValue Connect => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.Connect); | |||
| /// <summary> If True, a user may speak in a voice channel. </summary> | |||
| public PermValue Speak => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.Speak); | |||
| public PermValue Speak => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.Speak); | |||
| /// <summary> If True, a user may mute users. </summary> | |||
| public PermValue MuteMembers => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.MuteMembers); | |||
| public PermValue MuteMembers => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.MuteMembers); | |||
| /// <summary> If True, a user may deafen users. </summary> | |||
| public PermValue DeafenMembers => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.DeafenMembers); | |||
| public PermValue DeafenMembers => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.DeafenMembers); | |||
| /// <summary> If True, a user may move other users between voice channels. </summary> | |||
| public PermValue MoveMembers => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.MoveMembers); | |||
| public PermValue MoveMembers => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.MoveMembers); | |||
| /// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
| public PermValue UseVAD => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.UseVAD); | |||
| public PermValue UseVAD => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.UseVAD); | |||
| /// <summary> If True, a user may adjust permissions. This also implictly grants all other permissions. </summary> | |||
| public PermValue ManagePermissions => PermissionUtilities.GetValue(AllowValue, DenyValue, ChannelPermission.ManagePermissions); | |||
| public PermValue ManagePermissions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManagePermissions); | |||
| /// <summary> Creates a new OverwritePermissions with the provided allow and deny packed values. </summary> | |||
| public OverwritePermissions(uint allowValue, uint denyValue) | |||
| public OverwritePermissions(ulong allowValue, ulong denyValue) | |||
| { | |||
| AllowValue = allowValue; | |||
| DenyValue = denyValue; | |||
| } | |||
| private OverwritePermissions(uint allowValue, uint denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null, | |||
| private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null, | |||
| PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, | |||
| PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, | |||
| PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, | |||
| PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null) | |||
| { | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannel); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, readMessages, ChannelPermission.ReadMessages); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, sendMessages, ChannelPermission.SendMessages); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, sendTTSMessages, ChannelPermission.SendTTSMessages); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, manageMessages, ChannelPermission.ManageMessages); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, embedLinks, ChannelPermission.EmbedLinks); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, attachFiles, ChannelPermission.AttachFiles); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, readMessageHistory, ChannelPermission.ReadMessageHistory); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, mentionEveryone, ChannelPermission.MentionEveryone); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, connect, ChannelPermission.Connect); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, speak, ChannelPermission.Speak); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, muteMembers, ChannelPermission.MuteMembers); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, deafenMembers, ChannelPermission.DeafenMembers); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, moveMembers, ChannelPermission.MoveMembers); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, useVoiceActivation, ChannelPermission.UseVAD); | |||
| PermissionUtilities.SetValue(ref allowValue, ref denyValue, managePermissions, ChannelPermission.ManagePermissions); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannel); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, readMessages, ChannelPermission.ReadMessages); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, sendMessages, ChannelPermission.SendMessages); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, sendTTSMessages, ChannelPermission.SendTTSMessages); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, manageMessages, ChannelPermission.ManageMessages); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, embedLinks, ChannelPermission.EmbedLinks); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, attachFiles, ChannelPermission.AttachFiles); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, readMessageHistory, ChannelPermission.ReadMessageHistory); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, mentionEveryone, ChannelPermission.MentionEveryone); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, connect, ChannelPermission.Connect); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, speak, ChannelPermission.Speak); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, muteMembers, ChannelPermission.MuteMembers); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, deafenMembers, ChannelPermission.DeafenMembers); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, moveMembers, ChannelPermission.MoveMembers); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, useVoiceActivation, ChannelPermission.UseVAD); | |||
| Permissions.SetValue(ref allowValue, ref denyValue, managePermissions, ChannelPermission.ManagePermissions); | |||
| AllowValue = allowValue; | |||
| DenyValue = denyValue; | |||
| @@ -111,25 +113,32 @@ namespace Discord | |||
| embedLinks, attachFiles, readMessageHistory, mentionEveryone, connect, speak, muteMembers, deafenMembers, | |||
| moveMembers, useVoiceActivation, managePermissions); | |||
| /// <inheritdoc /> | |||
| public override string ToString() | |||
| public List<ChannelPermission> ToAllowList() | |||
| { | |||
| var perms = new List<string>(); | |||
| int x = 1; | |||
| for (byte i = 0; i < 32; i++, x <<= 1) | |||
| var perms = new List<ChannelPermission>(); | |||
| ulong x = 1; | |||
| for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1) | |||
| { | |||
| if ((AllowValue & x) != 0) | |||
| { | |||
| if (Enum.IsDefined(typeof(GuildPermission), i)) | |||
| perms.Add($"+{(GuildPermission)i}"); | |||
| } | |||
| else if ((DenyValue & x) != 0) | |||
| { | |||
| if (Enum.IsDefined(typeof(GuildPermission), i)) | |||
| perms.Add($"-{(GuildPermission)i}"); | |||
| } | |||
| perms.Add((ChannelPermission)i); | |||
| } | |||
| return perms; | |||
| } | |||
| public List<ChannelPermission> ToDenyList() | |||
| { | |||
| var perms = new List<ChannelPermission>(); | |||
| ulong x = 1; | |||
| for (byte i = 0; i < Permissions.MaxBits; i++, x <<= 1) | |||
| { | |||
| if ((DenyValue & x) != 0) | |||
| perms.Add((ChannelPermission)i); | |||
| } | |||
| return string.Join(", ", perms); | |||
| return perms; | |||
| } | |||
| /// <inheritdoc /> | |||
| public override string ToString() => $"Allow {AllowValue}, Deny {DenyValue}"; | |||
| private string DebuggerDisplay => | |||
| $"Allow {AllowValue} ({string.Join(", ", ToAllowList())})\n" + | |||
| $"Deny {DenyValue} ({string.Join(", ", ToDenyList())})"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,159 @@ | |||
| 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 bit) | |||
| => GetValue(allow, deny, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static PermValue GetValue(ulong allow, ulong deny, GuildPermission bit) | |||
| => GetValue(allow, deny, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static PermValue GetValue(ulong allow, ulong deny, byte bit) | |||
| { | |||
| if (HasBit(allow, bit)) | |||
| return PermValue.Allow; | |||
| else if (HasBit(deny, bit)) | |||
| return PermValue.Deny; | |||
| else | |||
| return PermValue.Inherit; | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(ulong value, ChannelPermission bit) | |||
| => GetValue(value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(ulong value, GuildPermission bit) | |||
| => GetValue(value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static bool GetValue(ulong value, byte bit) => HasBit(value, bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission bit) | |||
| => SetValue(ref rawValue, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong rawValue, bool? value, GuildPermission bit) | |||
| => SetValue(ref rawValue, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong rawValue, bool? value, byte bit) | |||
| { | |||
| if (value.HasValue) | |||
| { | |||
| if (value == true) | |||
| SetBit(ref rawValue, bit); | |||
| else | |||
| UnsetBit(ref rawValue, bit); | |||
| } | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission bit) | |||
| => SetValue(ref allow, ref deny, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission bit) | |||
| => SetValue(ref allow, ref deny, value, (byte)bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, byte bit) | |||
| { | |||
| if (value.HasValue) | |||
| { | |||
| switch (value) | |||
| { | |||
| case PermValue.Allow: | |||
| SetBit(ref allow, bit); | |||
| UnsetBit(ref deny, bit); | |||
| break; | |||
| case PermValue.Deny: | |||
| UnsetBit(ref allow, bit); | |||
| SetBit(ref deny, bit); | |||
| break; | |||
| default: | |||
| UnsetBit(ref allow, bit); | |||
| UnsetBit(ref deny, bit); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| private static bool HasBit(ulong value, byte bit) => (value & (1U << bit)) != 0; | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void SetBit(ref ulong value, byte bit) => value |= (1U << bit); | |||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
| public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit); | |||
| public static ulong ResolveGuild(IGuildUser user) | |||
| { | |||
| var roles = user.Roles; | |||
| ulong newPermissions = 0; | |||
| for (int i = 0; i < roles.Count; i++) | |||
| newPermissions |= roles[i].Permissions.RawValue; | |||
| return newPermissions; | |||
| } | |||
| /*public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel) | |||
| { | |||
| return ResolveChannel(user, channel, ResolveGuild(user)); | |||
| }*/ | |||
| public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel, ulong guildPermissions) | |||
| { | |||
| ulong resolvedPermissions = 0; | |||
| ulong mask = ChannelPermissions.All(channel).RawValue; | |||
| if (user.Id == user.Guild.OwnerId || GetValue(resolvedPermissions, GuildPermission.Administrator)) | |||
| resolvedPermissions = mask; //Owners and administrators always have all permissions | |||
| else | |||
| { | |||
| //Start with this user's guild permissions | |||
| resolvedPermissions = guildPermissions; | |||
| var overwrites = channel.PermissionOverwrites; | |||
| Overwrite entry; | |||
| var roles = user.Roles; | |||
| if (roles.Count > 0) | |||
| { | |||
| for (int i = 0; i < roles.Count; i++) | |||
| { | |||
| if (overwrites.TryGetValue(roles[i].Id, out entry)) | |||
| resolvedPermissions &= ~entry.Permissions.DenyValue; | |||
| } | |||
| for (int i = 0; i < roles.Count; i++) | |||
| { | |||
| if (overwrites.TryGetValue(roles[i].Id, out entry)) | |||
| resolvedPermissions |= entry.Permissions.AllowValue; | |||
| } | |||
| } | |||
| if (overwrites.TryGetValue(user.Id, out entry)) | |||
| resolvedPermissions = (resolvedPermissions & ~entry.Permissions.DenyValue) | entry.Permissions.AllowValue; | |||
| #if CSHARP7 | |||
| switch (channel) | |||
| { | |||
| case ITextChannel _: | |||
| if (!GetValue(resolvedPermissions, ChannelPermission.ReadMessages)) | |||
| resolvedPermissions = 0; //No read permission on a text channel removes all other permissions | |||
| break; | |||
| case IVoiceChannel _: | |||
| if (!GetValue(resolvedPermissions, ChannelPermission.Connect)) | |||
| resolvedPermissions = 0; //No read permission on a text channel removes all other permissions | |||
| break; | |||
| } | |||
| #else | |||
| var textChannel = channel as ITextChannel; | |||
| var voiceChannel = channel as IVoiceChannel; | |||
| if (textChannel != null && !GetValue(resolvedPermissions, ChannelPermission.ReadMessages)) | |||
| resolvedPermissions = 0; //No read permission on a text channel removes all other permissions | |||
| else if (voiceChannel != null && !GetValue(resolvedPermissions, ChannelPermission.Connect)) | |||
| resolvedPermissions = 0; //No connect permission on a voice channel removes all other permissions | |||
| #endif | |||
| resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example) | |||
| } | |||
| return resolvedPermissions; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| using Model = Discord.API.Connection; | |||
| namespace Discord | |||
| { | |||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
| public class Connection : IConnection | |||
| { | |||
| public string Id { get; } | |||
| public string Type { get; } | |||
| public string Name { get; } | |||
| public bool IsRevoked { get; } | |||
| public IEnumerable<ulong> IntegrationIds { get; } | |||
| public Connection(Model model) | |||
| { | |||
| Id = model.Id; | |||
| Type = model.Type; | |||
| Name = model.Name; | |||
| IsRevoked = model.Revoked; | |||
| IntegrationIds = model.Integrations; | |||
| } | |||
| public override string ToString() => Name; | |||
| private string DebuggerDisplay => $"{Name} ({Id}, Type = {Type}{(IsRevoked ? ", Revoked" : "")})"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| namespace Discord | |||
| { | |||
| public struct Game | |||
| { | |||
| public string Name { get; } | |||
| public string StreamUrl { get; } | |||
| public StreamType StreamType { get; } | |||
| public Game(string name) | |||
| { | |||
| Name = name; | |||
| StreamUrl = null; | |||
| StreamType = StreamType.NotStreaming; | |||
| } | |||
| public Game(string name, string streamUrl, StreamType type) | |||
| { | |||
| Name = name; | |||
| StreamUrl = streamUrl; | |||
| StreamType = type; | |||
| } | |||
| } | |||
| } | |||
| @@ -9,6 +9,6 @@ namespace Discord | |||
| string Name { get; } | |||
| bool IsRevoked { get; } | |||
| IEnumerable<ulong> Integrations { get; } | |||
| IEnumerable<ulong> IntegrationIds { get; } | |||
| } | |||
| } | |||
| @@ -21,17 +21,14 @@ namespace Discord | |||
| IGuild Guild { get; } | |||
| /// <summary> Returns a collection of the roles this user is a member of in this guild, including the guild's @everyone role. </summary> | |||
| IReadOnlyList<IRole> Roles { get; } | |||
| /// <summary> Gets the id of the voice channel this user is currently in, if any. </summary> | |||
| ulong? VoiceChannelId { get; } | |||
| /// <summary> Gets the voice channel this user is currently in, if any. </summary> | |||
| IVoiceChannel VoiceChannel { get; } | |||
| /// <summary> Gets the guild-level permissions granted to this user by their roles. </summary> | |||
| GuildPermissions GetGuildPermissions(); | |||
| /// <summary> Gets the channel-level permissions granted to this user for a given channel. </summary> | |||
| ChannelPermissions GetPermissions(IGuildChannel channel); | |||
| /// <summary> Return true if this user has the provided role. </summary> | |||
| bool HasRole(IRole role); | |||
| /// <summary> Kicks this user from this guild. </summary> | |||
| Task Kick(); | |||
| /// <summary> Modifies this user's properties in this guild. </summary> | |||
| @@ -7,7 +7,7 @@ namespace Discord | |||
| /// <summary> Gets the url to this user's avatar. </summary> | |||
| string AvatarUrl { get; } | |||
| /// <summary> Gets the game this user is currently playing, if any. </summary> | |||
| string CurrentGame { get; } | |||
| Game? CurrentGame { get; } | |||
| /// <summary> Gets the per-username unique id for this user. </summary> | |||
| ushort Discriminator { get; } | |||
| /// <summary> Returns true if this user is a bot account. </summary> | |||
| @@ -17,6 +17,7 @@ namespace Discord | |||
| /// <summary> Gets the username for this user. </summary> | |||
| string Username { get; } | |||
| //TODO: CreateDMChannel is a candidate to move to IGuildUser, and User made a common class, depending on next friends list update | |||
| /// <summary> Returns a private message channel to this user, creating one if it does not already exist. </summary> | |||
| Task<IDMChannel> CreateDMChannel(); | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| namespace Discord | |||
| { | |||
| public enum StreamType | |||
| { | |||
| NotStreaming = 0, | |||
| Twitch = 1 | |||
| } | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| internal static class EventExtensions | |||
| { | |||
| //TODO: Optimize these for if there is only 1 subscriber (can we do this?) | |||
| public static async Task Raise(this Func<Task> eventHandler) | |||
| { | |||
| var subscriptions = eventHandler?.GetInvocationList(); | |||
| if (subscriptions != null) | |||
| { | |||
| for (int i = 0; i < subscriptions.Length; i++) | |||
| await (subscriptions[i] as Func<Task>).Invoke().ConfigureAwait(false); | |||
| } | |||
| } | |||
| public static async Task Raise<T>(this Func<T, Task> eventHandler, T arg) | |||
| { | |||
| var subscriptions = eventHandler?.GetInvocationList(); | |||
| if (subscriptions != null) | |||
| { | |||
| for (int i = 0; i < subscriptions.Length; i++) | |||
| await (subscriptions[i] as Func<T, Task>).Invoke(arg).ConfigureAwait(false); | |||
| } | |||
| } | |||
| public static async Task Raise<T1, T2>(this Func<T1, T2, Task> eventHandler, T1 arg1, T2 arg2) | |||
| { | |||
| var subscriptions = eventHandler?.GetInvocationList(); | |||
| if (subscriptions != null) | |||
| { | |||
| for (int i = 0; i < subscriptions.Length; i++) | |||
| await (subscriptions[i] as Func<T1, T2, Task>).Invoke(arg1, arg2).ConfigureAwait(false); | |||
| } | |||
| } | |||
| public static async Task Raise<T1, T2, T3>(this Func<T1, T2, T3, Task> eventHandler, T1 arg1, T2 arg2, T3 arg3) | |||
| { | |||
| var subscriptions = eventHandler?.GetInvocationList(); | |||
| if (subscriptions != null) | |||
| { | |||
| for (int i = 0; i < subscriptions.Length; i++) | |||
| await (subscriptions[i] as Func<T1, T2, T3, Task>).Invoke(arg1, arg2, arg3).ConfigureAwait(false); | |||
| } | |||
| } | |||
| } | |||
| } | |||