diff --git a/Discord.Net.targets b/Discord.Net.targets index 95eccd790..3f623c619 100644 --- a/Discord.Net.targets +++ b/Discord.Net.targets @@ -1,7 +1,7 @@ - 2.0.0-alpha - + 2.0.0 + beta RogueException discord;discordapp https://github.com/RogueException/Discord.Net diff --git a/README.md b/README.md index 2b58d4579..bd0ef20c7 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![NuGet](https://img.shields.io/nuget/vpre/Discord.Net.svg?maxAge=2592000?style=plastic)](https://www.nuget.org/packages/Discord.Net) [![MyGet](https://img.shields.io/myget/discord-net/vpre/Discord.Net.svg)](https://www.myget.org/feed/Packages/discord-net) [![Build status](https://ci.appveyor.com/api/projects/status/5sb7n8a09w9clute/branch/dev?svg=true)](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev) -[![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/0SBTUU1wZTVjAMPx) +[![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/jkrBmQR) An unofficial .NET API Wrapper for the Discord client (http://discordapp.com). -Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx). +Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/jkrBmQR). ## Installation ### Stable (NuGet) diff --git a/appveyor.yml b/appveyor.yml index d94e2ad68..3bf70c09c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,7 +34,7 @@ after_build: if ($Env:APPVEYOR_REPO_TAG -eq "true") { nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="" } else { - nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-build-$Env:BUILD" + nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-$Env:BUILD" } - ps: Get-ChildItem artifacts\*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } diff --git a/docs/guides/getting_started/intro.md b/docs/guides/getting_started/intro.md index 02f04bec4..db086df21 100644 --- a/docs/guides/getting_started/intro.md +++ b/docs/guides/getting_started/intro.md @@ -74,7 +74,7 @@ async main. [!code-csharp[Async Context](samples/intro/async-context.cs)] -As a result of this, your program will now start and immidiately +As a result of this, your program will now start and immediately jump into an async context. This will allow us to create a connection to Discord later on without needing to worry about setting up the correct async implementation. diff --git a/docs/guides/getting_started/samples/intro/structure.cs b/docs/guides/getting_started/samples/intro/structure.cs index 789ceff76..bdfc12b67 100644 --- a/docs/guides/getting_started/samples/intro/structure.cs +++ b/docs/guides/getting_started/samples/intro/structure.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Discord; @@ -80,7 +81,7 @@ class Program private async Task MainAsync() { - // Centralize the logic for commands into a seperate method. + // Centralize the logic for commands into a separate method. await InitCommands(); // Login and connect. @@ -88,7 +89,7 @@ class Program await _client.StartAsync(); // Wait infinitely so your bot actually stays connected. - await Task.Delay(-1); + await Task.Delay(Timeout.Infinite); } private IServiceProvider _services; @@ -138,7 +139,7 @@ class Program var context = new SocketCommandContext(_client, msg); // Execute the command. (result does not indicate a return value, - // rather an object stating if the command executed succesfully). + // rather an object stating if the command executed successfully). var result = await _commands.ExecuteAsync(context, pos, _services); // Uncomment the following lines if you want the bot diff --git a/docs/guides/getting_started/samples/project.csproj b/docs/guides/getting_started/samples/project.csproj index 8daf71877..feb0b0c40 100644 --- a/docs/guides/getting_started/samples/project.csproj +++ b/docs/guides/getting_started/samples/project.csproj @@ -7,7 +7,7 @@ - + diff --git a/docs/guides/voice/samples/audio_create_ffmpeg.cs b/docs/guides/voice/samples/audio_create_ffmpeg.cs index e24af088b..dda560efe 100644 --- a/docs/guides/voice/samples/audio_create_ffmpeg.cs +++ b/docs/guides/voice/samples/audio_create_ffmpeg.cs @@ -1,11 +1,10 @@ private Process CreateStream(string path) { - var ffmpeg = new ProcessStartInfo + return Process.Start(new ProcessStartInfo { FileName = "ffmpeg", - Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1", + Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", UseShellExecute = false, RedirectStandardOutput = true, - }; - return Process.Start(ffmpeg); -} \ No newline at end of file + }); +} diff --git a/docs/guides/voice/samples/audio_ffmpeg.cs b/docs/guides/voice/samples/audio_ffmpeg.cs index b9430ac11..d36fbbc20 100644 --- a/docs/guides/voice/samples/audio_ffmpeg.cs +++ b/docs/guides/voice/samples/audio_ffmpeg.cs @@ -1,9 +1,11 @@ private async Task SendAsync(IAudioClient client, string path) { // Create FFmpeg using the previous example - var ffmpeg = CreateStream(path); - var output = ffmpeg.StandardOutput.BaseStream; - var discord = client.CreatePCMStream(AudioApplication.Mixed); - await output.CopyToAsync(discord); - await discord.FlushAsync(); + using (var ffmpeg = CreateStream(path)) + using (var output = ffmpeg.StandardOutput.BaseStream) + using (var discord = client.CreatePCMStream(AudioApplication.Mixed)) + { + try { await output.CopyToAsync(discord); } + finally { await discord.FlushAsync(); } + } } diff --git a/docs/guides/voice/samples/joining_audio.cs b/docs/guides/voice/samples/joining_audio.cs index 0cc36978a..4cec67540 100644 --- a/docs/guides/voice/samples/joining_audio.cs +++ b/docs/guides/voice/samples/joining_audio.cs @@ -7,4 +7,4 @@ public async Task JoinChannel(IVoiceChannel channel = null) // For the next step with transmitting audio, you would want to pass this Audio Client in to a service. var audioClient = await channel.ConnectAsync(); -} \ No newline at end of file +} diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs index 49dae6080..209822583 100644 --- a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs @@ -7,6 +7,6 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] public abstract class ParameterPreconditionAttribute : Attribute { - public abstract Task CheckPermissions(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services); + public abstract Task CheckPermissionsAsync(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services); } } \ No newline at end of file diff --git a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs index 3727510d9..367adebf0 100644 --- a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs @@ -8,11 +8,11 @@ namespace Discord.Commands { /// /// Specify a group that this precondition belongs to. Preconditions of the same group require only one - /// of the preconditions to pass in order to be successful (A || B). Specifying = + /// of the preconditions to pass in order to be successful (A || B). Specifying = /// or not at all will require *all* preconditions to pass, just like normal (A && B). /// public string Group { get; set; } = null; - public abstract Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services); + public abstract Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services); } } diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs index b2cd3811c..104252799 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -41,7 +41,7 @@ namespace Discord.Commands GuildPermission = null; } - public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) + public override async Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { IGuildUser guildUser = null; if (context.Guild != null) @@ -57,13 +57,11 @@ namespace Discord.Commands if (ChannelPermission.HasValue) { - var guildChannel = context.Channel as IGuildChannel; - ChannelPermissions perms; - if (guildChannel != null) + if (context.Channel is IGuildChannel guildChannel) perms = guildUser.GetPermissions(guildChannel); else - perms = ChannelPermissions.All(guildChannel); + perms = ChannelPermissions.All(context.Channel); if (!perms.Has(ChannelPermission.Value)) return PreconditionResult.FromError($"Bot requires channel permission {ChannelPermission.Value}"); diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index a221eb4a9..5fa0fb1b9 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -38,7 +38,7 @@ namespace Discord.Commands Contexts = contexts; } - public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) + public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { bool isValid = false; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs index b3cf25365..c8e3bfa82 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireNsfwAttribute.cs @@ -9,7 +9,7 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireNsfwAttribute : PreconditionAttribute { - public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) + public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { if (context.Channel is ITextChannel text && text.IsNsfw) return Task.FromResult(PreconditionResult.FromSuccess()); diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index 2055a358e..e370aeec4 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -12,7 +12,7 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { - public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) + public override async Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { switch (context.Client.TokenType) { diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index f5e3a9fc5..14121f35b 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -42,7 +42,7 @@ namespace Discord.Commands GuildPermission = null; } - public override Task CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services) + public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { var guildUser = context.User as IGuildUser; @@ -56,13 +56,11 @@ namespace Discord.Commands if (ChannelPermission.HasValue) { - var guildChannel = context.Channel as IGuildChannel; - ChannelPermissions perms; - if (guildChannel != null) + if (context.Channel is IGuildChannel guildChannel) perms = guildUser.GetPermissions(guildChannel); else - perms = ChannelPermissions.All(guildChannel); + perms = ChannelPermissions.All(context.Channel); if (!perms.Has(ChannelPermission.Value)) return Task.FromResult(PreconditionResult.FromError($"User requires channel permission {ChannelPermission.Value}")); diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 394f8589d..28e36d54d 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -14,7 +14,7 @@ namespace Discord.Commands QuotedParameter } - public static async Task ParseArgs(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); @@ -111,7 +111,7 @@ namespace Discord.Commands if (curParam == null) return ParseResult.FromError(CommandError.BadArgCount, "The input text has too many parameters."); - var typeReaderResult = await curParam.Parse(context, argString, services).ConfigureAwait(false); + var typeReaderResult = await curParam.ParseAsync(context, argString, services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess && typeReaderResult.Error != CommandError.MultipleMatches) return ParseResult.FromError(typeReaderResult); @@ -134,7 +134,7 @@ namespace Discord.Commands if (curParam != null && curParam.IsRemainder) { - var typeReaderResult = await curParam.Parse(context, argBuilder.ToString(), services).ConfigureAwait(false); + var typeReaderResult = await curParam.ParseAsync(context, argBuilder.ToString(), services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess) return ParseResult.FromError(typeReaderResult); argList.Add(typeReaderResult); diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index c94be525f..6bb621f94 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -78,7 +78,7 @@ namespace Discord.Commands { foreach (PreconditionAttribute precondition in preconditionGroup) { - var result = await precondition.CheckPermissions(context, this, services).ConfigureAwait(false); + var result = await precondition.CheckPermissionsAsync(context, this, services).ConfigureAwait(false); if (!result.IsSuccess) return result; } @@ -87,7 +87,7 @@ namespace Discord.Commands { var results = new List(); foreach (PreconditionAttribute precondition in preconditionGroup) - results.Add(await precondition.CheckPermissions(context, this, services).ConfigureAwait(false)); + results.Add(await precondition.CheckPermissionsAsync(context, this, services).ConfigureAwait(false)); if (!results.Any(p => p.IsSuccess)) return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results); @@ -117,7 +117,7 @@ namespace Discord.Commands return ParseResult.FromError(preconditionResult); string input = searchResult.Text.Substring(startIndex); - return await CommandParser.ParseArgs(this, context, services, input, 0).ConfigureAwait(false); + return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false); } public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) @@ -163,11 +163,11 @@ namespace Discord.Commands switch (RunMode) { case RunMode.Sync: //Always sync - return await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false); + return await ExecuteAsyncInternalAsync(context, args, services).ConfigureAwait(false); case RunMode.Async: //Always async var t2 = Task.Run(async () => { - await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false); + await ExecuteAsyncInternalAsync(context, args, services).ConfigureAwait(false); }); break; } @@ -179,7 +179,7 @@ namespace Discord.Commands } } - private async Task ExecuteAsyncInternal(ICommandContext context, object[] args, IServiceProvider services) + private async Task ExecuteAsyncInternalAsync(ICommandContext context, object[] args, IServiceProvider services) { await Module.Service._cmdLogger.DebugAsync($"Executing {GetLogText(context)}").ConfigureAwait(false); try @@ -199,10 +199,13 @@ namespace Discord.Commands return result; } else + { await task.ConfigureAwait(false); + var result = ExecuteResult.FromSuccess(); + await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false); + } var executeResult = ExecuteResult.FromSuccess(); - await Module.Service._commandExecutedEvent.InvokeAsync(this, context, executeResult).ConfigureAwait(false); return executeResult; } catch (Exception ex) diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index e417b1ab6..4a56415e5 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -48,7 +48,7 @@ namespace Discord.Commands foreach (var precondition in Preconditions) { - var result = await precondition.CheckPermissions(context, this, arg, services).ConfigureAwait(false); + var result = await precondition.CheckPermissionsAsync(context, this, arg, services).ConfigureAwait(false); if (!result.IsSuccess) return result; } @@ -56,10 +56,10 @@ namespace Discord.Commands return PreconditionResult.FromSuccess(); } - public async Task Parse(ICommandContext context, string input, IServiceProvider services = null) + public async Task ParseAsync(ICommandContext context, string input, IServiceProvider services = null) { services = services ?? EmptyServiceProvider.Instance; - return await _reader.Read(context, input, services).ConfigureAwait(false); + return await _reader.ReadAsync(context, input, services).ConfigureAwait(false); } public override string ToString() => Name; diff --git a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs index 72c62282e..3136eb2cb 100644 --- a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs @@ -9,7 +9,7 @@ namespace Discord.Commands internal class ChannelTypeReader : TypeReader where T : class, IChannel { - public override async Task Read(ICommandContext context, string input, IServiceProvider services) + public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { if (context.Guild != null) { diff --git a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs index 383b8e63c..c097e6189 100644 --- a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs @@ -44,7 +44,7 @@ namespace Discord.Commands _enumsByValue = byValueBuilder.ToImmutable(); } - public override Task Read(ICommandContext context, string input, IServiceProvider services) + public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { object enumValue; diff --git a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs index 895713e4f..fe576c3c6 100644 --- a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs @@ -7,7 +7,7 @@ namespace Discord.Commands internal class MessageTypeReader : TypeReader where T : class, IMessage { - public override async Task Read(ICommandContext context, string input, IServiceProvider services) + public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { ulong id; diff --git a/src/Discord.Net.Commands/Readers/NullableTypeReader.cs b/src/Discord.Net.Commands/Readers/NullableTypeReader.cs index 07976fb69..109689e15 100644 --- a/src/Discord.Net.Commands/Readers/NullableTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/NullableTypeReader.cs @@ -24,11 +24,11 @@ namespace Discord.Commands _baseTypeReader = baseTypeReader; } - public override async Task Read(ICommandContext context, string input, IServiceProvider services) + public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { if (string.Equals(input, "null", StringComparison.OrdinalIgnoreCase) || string.Equals(input, "nothing", StringComparison.OrdinalIgnoreCase)) return TypeReaderResult.FromSuccess(new T?()); - return await _baseTypeReader.Read(context, input, services); ; + return await _baseTypeReader.ReadAsync(context, input, services); } } } diff --git a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs index 2656741f0..b19a6bd69 100644 --- a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs @@ -30,7 +30,7 @@ namespace Discord.Commands _score = score; } - public override Task Read(ICommandContext context, string input, IServiceProvider services) + public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { if (_tryParse(input, out T value)) return Task.FromResult(TypeReaderResult.FromSuccess(new TypeReaderValue(value, _score))); diff --git a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs index 17786e6f0..703374c05 100644 --- a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs @@ -9,7 +9,7 @@ namespace Discord.Commands internal class RoleTypeReader : TypeReader where T : class, IRole { - public override Task Read(ICommandContext context, string input, IServiceProvider services) + public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { ulong id; diff --git a/src/Discord.Net.Commands/Readers/TypeReader.cs b/src/Discord.Net.Commands/Readers/TypeReader.cs index 2c4644376..af45a0aac 100644 --- a/src/Discord.Net.Commands/Readers/TypeReader.cs +++ b/src/Discord.Net.Commands/Readers/TypeReader.cs @@ -5,6 +5,6 @@ namespace Discord.Commands { public abstract class TypeReader { - public abstract Task Read(ICommandContext context, string input, IServiceProvider services); + public abstract Task ReadAsync(ICommandContext context, string input, IServiceProvider services); } } diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs index c71dac2d2..ca337aaf6 100644 --- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs @@ -10,7 +10,7 @@ namespace Discord.Commands internal class UserTypeReader : TypeReader where T : class, IUser { - public override async Task Read(ICommandContext context, string input, IServiceProvider services) + public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) { var results = new Dictionary(); IReadOnlyCollection channelUsers = (await context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten().ConfigureAwait(false)).ToArray(); //TODO: must be a better way? diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 3d08a8c51..c7cf0b3c2 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -20,7 +20,7 @@ namespace Discord /// The time (in seconds) until the invite expires. Set to null to never expire. /// The max amount of times this invite may be used. Set to null to have unlimited uses. /// If true, a user accepting this invite will be kicked from the guild after closing their client. - Task CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); + Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); /// Returns a collection of all invites to this channel. Task> GetInvitesAsync(RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs b/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs new file mode 100644 index 000000000..be24d306c --- /dev/null +++ b/src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Discord +{ + public class EmoteProperties + { + public Optional Name { get; set; } + public Optional> Roles { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 909f480ac..0fdd92405 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -122,5 +122,14 @@ namespace Discord Task GetWebhookAsync(ulong id, RequestOptions options = null); /// Gets a collection of all webhooks for this guild. Task> GetWebhooksAsync(RequestOptions options = null); + + /// Gets a specific emote from this guild. + Task GetEmoteAsync(ulong id, RequestOptions options = null); + /// Creates a new emote in this guild. + Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null); + /// Modifies an existing emote in this guild. + Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null); + /// Deletes an existing emote from this guild. + Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); } } \ No newline at end of file diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 22e85263c..4c11d0db0 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -10,7 +10,7 @@ namespace Discord /// Gets a blank ChannelPermissions that grants no permissions. public static readonly ChannelPermissions None = new ChannelPermissions(); /// Gets a ChannelPermissions that grants all permissions for text channels. - public static readonly ChannelPermissions Text = new ChannelPermissions(0b00100_0000000_1111111110001_010001); + public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); /// Gets a ChannelPermissions that grants all permissions for voice channels. public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001); /// Gets a ChannelPermissions that grants all permissions for direct message channels. diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 030ccd587..a880e62ca 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -11,7 +11,7 @@ namespace Discord /// Gets a GuildPermissions that grants all guild permissions for webhook users. public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000); /// Gets a GuildPermissions that grants all guild permissions. - public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_0111111110011_111111); + public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111110011_111111); /// Gets a packed value representing all the permissions in this GuildPermissions. public ulong RawValue { get; } @@ -28,7 +28,7 @@ namespace Discord public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); /// If True, a user may adjust guild properties. public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); - + /// If true, a user may add reactions. public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions); /// If true, a user may view the audit log. @@ -80,13 +80,13 @@ namespace Discord /// Creates a new GuildPermissions with the provided packed value. public GuildPermissions(ulong rawValue) { RawValue = rawValue; } - private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, - bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, + private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, + bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, bool? addReactions = null, bool? viewAuditLog = 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? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = 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? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) { ulong value = initialValue; @@ -124,31 +124,36 @@ namespace Discord } /// Creates a new GuildPermissions with the provided permissions. - public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, + public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false, bool addReactions = false, bool viewAuditLog = 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 useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, - bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false, + bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false, bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false) - : this(0, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions, viewAuditLog, - readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, - manageWebhooks, manageEmojis) { } + : this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers, + administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions, + viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages, + manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory, + mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers, + deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname, + manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, manageEmojis: manageEmojis) + { } /// Creates a new GuildPermissions from this one, changing the provided non-null permissions. - public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null, + public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null, bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, bool? addReactions = null, bool? viewAuditLog = 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? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, + bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) - => new GuildPermissions(RawValue, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions, viewAuditLog, - readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, - manageWebhooks, manageEmojis); + => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, + viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, + readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, + useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); public bool Has(GuildPermission permission) => Permissions.GetValue(RawValue, permission); diff --git a/src/Discord.Net.Core/Utils/Optional.cs b/src/Discord.Net.Core/Utils/Optional.cs index df927b7ea..eb3cbdca2 100644 --- a/src/Discord.Net.Core/Utils/Optional.cs +++ b/src/Discord.Net.Core/Utils/Optional.cs @@ -10,7 +10,7 @@ namespace Discord public static Optional Unspecified => default(Optional); private readonly T _value; - /// Gets the value for this paramter. + /// Gets the value for this parameter. public T Value { get diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index bec8de9dc..300f584e4 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -188,7 +188,8 @@ namespace Discord var minimum = SnowflakeUtils.ToSnowflake(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(14))); for (var i = 0; i < collection.Length; i++) { - if (collection[i] <= minimum) + if (collection[i] == 0) continue; + if (collection[i] <= minimum) throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks old."); } } diff --git a/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs new file mode 100644 index 000000000..308199820 --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildEmoteParams.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + internal class CreateGuildEmoteParams + { + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("image")] + public Image Image { get; set; } + [JsonProperty("roles")] + public Optional RoleIds { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs new file mode 100644 index 000000000..a2295dd5d --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmoteParams.cs @@ -0,0 +1,14 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + internal class ModifyGuildEmoteParams + { + [JsonProperty("name")] + public Optional Name { get; set; } + [JsonProperty("roles")] + public Optional RoleIds { get; set; } + } +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 5cf2a8901..ab47b1e98 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1066,6 +1066,50 @@ namespace Discord.API return await SendJsonAsync>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false); } + //Guild emoji + public async Task GetGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + Preconditions.NotEqual(emoteId, 0, nameof(emoteId)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return await SendAsync("GET", () => $"guilds/{guildId}/emojis/{emoteId}", ids, options: options); + } + + public async Task CreateGuildEmoteAsync(ulong guildId, Rest.CreateGuildEmoteParams args, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + Preconditions.NotNull(args, nameof(args)); + Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); + Preconditions.NotNull(args.Image.Stream, nameof(args.Image)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return await SendJsonAsync("POST", () => $"guilds/{guildId}/emojis", args, ids, options: options); + } + + public async Task ModifyGuildEmoteAsync(ulong guildId, ulong emoteId, ModifyGuildEmoteParams args, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + Preconditions.NotEqual(emoteId, 0, nameof(emoteId)); + Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return await SendJsonAsync("PATCH", () => $"guilds/{guildId}/emojis/{emoteId}", args, ids, options: options); + } + + public async Task DeleteGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + Preconditions.NotEqual(emoteId, 0, nameof(emoteId)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + await SendAsync("DELETE", () => $"guilds/{guildId}/emojis/{emoteId}", ids, options: options); + } + //Users public async Task GetUserAsync(ulong userId, RequestOptions options = null) { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs index 342e57717..04cc5a937 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs @@ -48,8 +48,8 @@ namespace Discord.Rest string IChannel.Name => null; Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => Task.FromResult(null); //Overriden + => Task.FromResult(null); //Overridden IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => AsyncEnumerable.Empty>(); //Overriden + => AsyncEnumerable.Empty>(); //Overridden } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 07832a3a9..1ce1c8368 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -119,7 +119,7 @@ namespace Discord.Rest public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public override string ToString() => Name; @@ -154,14 +154,14 @@ namespace Discord.Rest => await RemovePermissionOverwriteAsync(user, options).ConfigureAwait(false); IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => AsyncEnumerable.Empty>(); //Overriden //Overriden in Text/Voice + => AsyncEnumerable.Empty>(); //Overridden //Overridden in Text/Voice Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => Task.FromResult(null); //Overriden in Text/Voice + => Task.FromResult(null); //Overridden in Text/Voice //IChannel IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => AsyncEnumerable.Empty>(); //Overriden in Text/Voice + => AsyncEnumerable.Empty>(); //Overridden in Text/Voice Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => Task.FromResult(null); //Overriden in Text/Voice + => Task.FromResult(null); //Overridden in Text/Voice } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 029ac70da..7e25b53f3 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -267,5 +267,46 @@ namespace Discord.Rest var models = await client.ApiClient.GetGuildWebhooksAsync(guild.Id, options).ConfigureAwait(false); return models.Select(x => RestWebhook.Create(client, guild, x)).ToImmutableArray(); } + + //Emotes + public static async Task GetEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) + { + var emote = await client.ApiClient.GetGuildEmoteAsync(guild.Id, id, options); + return emote.ToEntity(); + } + public static async Task CreateEmoteAsync(IGuild guild, BaseDiscordClient client, string name, Image image, Optional> roles, + RequestOptions options) + { + var apiargs = new CreateGuildEmoteParams + { + Name = name, + Image = image.ToModel() + }; + if (roles.IsSpecified) + apiargs.RoleIds = roles.Value?.Select(xr => xr.Id)?.ToArray(); + + var emote = await client.ApiClient.CreateGuildEmoteAsync(guild.Id, apiargs, options); + return emote.ToEntity(); + } + public static async Task ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action func, + RequestOptions options) + { + if (func == null) throw new ArgumentNullException(nameof(func)); + + var props = new EmoteProperties(); + func(props); + + var apiargs = new ModifyGuildEmoteParams + { + Name = props.Name + }; + if (props.Roles.IsSpecified) + apiargs.RoleIds = props.Roles.Value?.Select(xr => xr.Id)?.ToArray(); + + var emote = await client.ApiClient.ModifyGuildEmoteAsync(guild.Id, id, apiargs, options); + return emote.ToEntity(); + } + public static Task DeleteEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) + => client.ApiClient.DeleteGuildEmoteAsync(guild.Id, id, options); } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 32df7fb06..3b6a2bfa8 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -266,6 +266,16 @@ namespace Discord.Rest public override string ToString() => Name; private string DebuggerDisplay => $"{Name} ({Id})"; + //Emotes + public Task GetEmoteAsync(ulong id, RequestOptions options = null) + => GuildHelper.GetEmoteAsync(this, Discord, id, options); + public Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null) + => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); + public Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null) + => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); + public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null) + => GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options); + //IGuild bool IGuild.Available => Available; IAudioClient IGuild.AudioClient => null; diff --git a/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs b/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs index cee9a136e..2eb4ed473 100644 --- a/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs +++ b/src/Discord.Net.Rest/Extensions/EmbedBuilderExtensions.cs @@ -1,3 +1,5 @@ +using System; + namespace Discord { public static class EmbedBuilderExtensions @@ -19,5 +21,38 @@ namespace Discord public static EmbedBuilder WithAuthor(this EmbedBuilder builder, IGuildUser user) => builder.WithAuthor($"{user.Nickname ?? user.Username}#{user.Discriminator}", user.GetAvatarUrl()); + + public static EmbedBuilder ToEmbedBuilder(this IEmbed embed) + { + if (embed.Type != EmbedType.Rich) + throw new InvalidOperationException($"Only {nameof(EmbedType.Rich)} embeds may be built."); + + var builder = new EmbedBuilder + { + Author = new EmbedAuthorBuilder + { + Name = embed.Author?.Name, + IconUrl = embed.Author?.IconUrl, + Url = embed.Author?.Url + }, + Color = embed.Color ?? Color.Default, + Description = embed.Description, + Footer = new EmbedFooterBuilder + { + Text = embed.Footer?.Text, + IconUrl = embed.Footer?.IconUrl + }, + ImageUrl = embed.Image?.Url, + ThumbnailUrl = embed.Thumbnail?.Url, + Timestamp = embed.Timestamp, + Title = embed.Title, + Url = embed.Url + }; + + foreach (var field in embed.Fields) + builder.AddField(field.Name, field.Value, field.Inline); + + return builder; + } } } diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 48eb8ec3e..401263555 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -51,7 +51,7 @@ namespace Discord.Rpc public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public override string ToString() => Name; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 1fe9a741f..8e24a5196 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -113,7 +113,7 @@ namespace Discord.WebSocket public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public new virtual SocketGuildUser GetUser(ulong id) => null; @@ -154,8 +154,8 @@ namespace Discord.WebSocket //IChannel IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => ImmutableArray.Create>(Users).ToAsyncEnumerable(); //Overriden in Text/Voice + => ImmutableArray.Create>(Users).ToAsyncEnumerable(); //Overridden in Text/Voice Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => Task.FromResult(GetUser(id)); //Overriden in Text/Voice + => Task.FromResult(GetUser(id)); //Overridden in Text/Voice } } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 17a9116ef..c4158c136 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -439,6 +439,16 @@ namespace Discord.WebSocket public Task> GetWebhooksAsync(RequestOptions options = null) => GuildHelper.GetWebhooksAsync(this, Discord, options); + //Emotes + public Task GetEmoteAsync(ulong id, RequestOptions options = null) + => GuildHelper.GetEmoteAsync(this, Discord, id, options); + public Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null) + => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); + public Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null) + => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); + public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null) + => GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options); + //Voice States internal async Task AddOrUpdateVoiceStateAsync(ClientState state, VoiceStateModel model) { diff --git a/src/Discord.Net/Discord.Net.nuspec b/src/Discord.Net/Discord.Net.nuspec index 309532615..f904f4126 100644 --- a/src/Discord.Net/Discord.Net.nuspec +++ b/src/Discord.Net/Discord.Net.nuspec @@ -2,7 +2,7 @@ Discord.Net - 2.0.0-alpha$suffix$ + 2.0.0-beta$suffix$ Discord.Net Discord.Net Contributors RogueException @@ -13,28 +13,28 @@ false - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs new file mode 100644 index 000000000..92234e88b --- /dev/null +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -0,0 +1,324 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Discord +{ + public partial class Tests + { + [Fact] + public void TestChannelPermission() + { + var perm = new ChannelPermissions(); + + // check initial values + Assert.Equal((ulong)0, perm.RawValue); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // permissions list empty by default + Assert.Empty(perm.ToList()); + + // test modify with no parameters + var copy = perm.Modify(); + Assert.Equal((ulong)0, copy.RawValue); + + // test the values that are returned by ChannelPermission.All + Assert.Equal((ulong)0, ChannelPermissions.None.RawValue); + + // for text channels + ulong textChannel = (ulong)( ChannelPermission.CreateInstantInvite + | ChannelPermission.ManageChannels + | ChannelPermission.AddReactions + | ChannelPermission.ReadMessages + | ChannelPermission.SendMessages + | ChannelPermission.SendTTSMessages + | ChannelPermission.ManageMessages + | ChannelPermission.EmbedLinks + | ChannelPermission.AttachFiles + | ChannelPermission.ReadMessageHistory + | ChannelPermission.MentionEveryone + | ChannelPermission.UseExternalEmojis + | ChannelPermission.ManageRoles + | ChannelPermission.ManageWebhooks); + + Assert.Equal(textChannel, ChannelPermissions.Text.RawValue); + + // voice channels + ulong voiceChannel = (ulong)( + ChannelPermission.CreateInstantInvite + | ChannelPermission.ManageChannels + | ChannelPermission.Connect + | ChannelPermission.Speak + | ChannelPermission.MuteMembers + | ChannelPermission.DeafenMembers + | ChannelPermission.MoveMembers + | ChannelPermission.UseVAD + | ChannelPermission.ManageRoles); + + Assert.Equal(voiceChannel, ChannelPermissions.Voice.RawValue); + + // DM Channels + ulong dmChannel = (ulong)( + ChannelPermission.ReadMessages + | ChannelPermission.SendMessages + | ChannelPermission.EmbedLinks + | ChannelPermission.AttachFiles + | ChannelPermission.ReadMessageHistory + | ChannelPermission.UseExternalEmojis + | ChannelPermission.Connect + | ChannelPermission.Speak + | ChannelPermission.UseVAD + ); + Assert.Equal(dmChannel, ChannelPermissions.DM.RawValue); + + // group channel + ulong groupChannel = (ulong)( + ChannelPermission.SendMessages + | ChannelPermission.EmbedLinks + | ChannelPermission.AttachFiles + | ChannelPermission.SendTTSMessages + | ChannelPermission.Connect + | ChannelPermission.Speak + | ChannelPermission.UseVAD + ); + Assert.Equal(groupChannel, ChannelPermissions.Group.RawValue); + } + + public void TestChannelPermissionModify() + { + // test channel permission modify + + var perm = new ChannelPermissions(); + + // ensure that the permission is initially false + Assert.False(perm.CreateInstantInvite); + + // ensure that when modified it works + perm = perm.Modify(createInstantInvite: true); + Assert.True(perm.CreateInstantInvite); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.CreateInstantInvite); + + // set false again, move on to next permission + perm = perm.Modify(createInstantInvite: false); + Assert.False(perm.CreateInstantInvite); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ManageChannel); + + perm = perm.Modify(manageChannel: true); + Assert.True(perm.ManageChannel); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageChannels); + + perm = perm.Modify(manageChannel: false); + Assert.False(perm.ManageChannel); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.AddReactions); + + perm = perm.Modify(addReactions: true); + Assert.True(perm.AddReactions); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.AddReactions); + + perm = perm.Modify(addReactions: false); + Assert.False(perm.AddReactions); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ReadMessages); + + perm = perm.Modify(readMessages: true); + Assert.True(perm.ReadMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessages); + + perm = perm.Modify(readMessages: false); + Assert.False(perm.ReadMessages); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.SendMessages); + + perm = perm.Modify(sendMessages: true); + Assert.True(perm.SendMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.SendMessages); + + perm = perm.Modify(sendMessages: false); + Assert.False(perm.SendMessages); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.SendTTSMessages); + + perm = perm.Modify(sendTTSMessages: true); + Assert.True(perm.SendTTSMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.SendTTSMessages); + + perm = perm.Modify(sendTTSMessages: false); + Assert.False(perm.SendTTSMessages); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ManageMessages); + + perm = perm.Modify(manageMessages: true); + Assert.True(perm.ManageMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageMessages); + + perm = perm.Modify(manageMessages: false); + Assert.False(perm.ManageMessages); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.EmbedLinks); + + perm = perm.Modify(embedLinks: true); + Assert.True(perm.EmbedLinks); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.EmbedLinks); + + perm = perm.Modify(embedLinks: false); + Assert.False(perm.EmbedLinks); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.AttachFiles); + + perm = perm.Modify(attachFiles: true); + Assert.True(perm.AttachFiles); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.AttachFiles); + + perm = perm.Modify(attachFiles: false); + Assert.False(perm.AttachFiles); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ReadMessageHistory); + + perm = perm.Modify(readMessageHistory: true); + Assert.True(perm.ReadMessageHistory); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessageHistory); + + perm = perm.Modify(readMessageHistory: false); + Assert.False(perm.ReadMessageHistory); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.MentionEveryone); + + perm = perm.Modify(mentionEveryone: true); + Assert.True(perm.MentionEveryone); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MentionEveryone); + + perm = perm.Modify(mentionEveryone: false); + Assert.False(perm.MentionEveryone); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.UseExternalEmojis); + + perm = perm.Modify(useExternalEmojis: true); + Assert.True(perm.UseExternalEmojis); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseExternalEmojis); + + perm = perm.Modify(useExternalEmojis: false); + Assert.False(perm.UseExternalEmojis); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.Connect); + + perm = perm.Modify(connect: true); + Assert.True(perm.Connect); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.Connect); + + perm = perm.Modify(connect: false); + Assert.False(perm.Connect); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.Speak); + + perm = perm.Modify(speak: true); + Assert.True(perm.Speak); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.Speak); + + perm = perm.Modify(speak: false); + Assert.False(perm.Speak); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.MuteMembers); + + perm = perm.Modify(muteMembers: true); + Assert.True(perm.MuteMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MuteMembers); + + perm = perm.Modify(muteMembers: false); + Assert.False(perm.MuteMembers); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.DeafenMembers); + + perm = perm.Modify(deafenMembers: true); + Assert.True(perm.DeafenMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.DeafenMembers); + + perm = perm.Modify(deafenMembers: false); + Assert.False(perm.DeafenMembers); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.MoveMembers); + + perm = perm.Modify(moveMembers: true); + Assert.True(perm.MoveMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MoveMembers); + + perm = perm.Modify(moveMembers: false); + Assert.False(perm.MoveMembers); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.UseVAD); + + perm = perm.Modify(useVoiceActivation: true); + Assert.True(perm.UseVAD); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseVAD); + + perm = perm.Modify(useVoiceActivation: false); + Assert.False(perm.UseVAD); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ManageRoles); + + perm = perm.Modify(manageRoles: true); + Assert.True(perm.ManageRoles); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageRoles); + + perm = perm.Modify(manageRoles: false); + Assert.False(perm.ManageRoles); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + + // individual permission test + Assert.False(perm.ManageWebhooks); + + perm = perm.Modify(manageWebhooks: true); + Assert.True(perm.ManageWebhooks); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageWebhooks); + + perm = perm.Modify(manageWebhooks: false); + Assert.False(perm.ManageWebhooks); + Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); + } + + [Fact] + public void TestChannelTypeResolution() + { + ITextChannel someChannel = null; + // null channels will throw exception + Assert.Throws(() => ChannelPermissions.All(someChannel)); + } + } +} diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs new file mode 100644 index 000000000..dc51600cf --- /dev/null +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -0,0 +1,304 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Discord +{ + public partial class Tests + { + [Fact] + public void TestGuildPermission() + { + // Test Guild Permission Constructors + var perm = new GuildPermissions(); + + // the default raw value is 0 + Assert.Equal((ulong)0, perm.RawValue); + // also check that it is the same as none + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // permissions list is empty by default + Assert.Empty(perm.ToList()); + Assert.NotNull(perm.ToList()); + + // Test modify with no parameters + var copy = perm.Modify(); + // ensure that the raw values match + Assert.Equal((ulong)0, copy.RawValue); + + // test GuildPermissions.All + ulong sumOfAllGuildPermissions = 0; + foreach(var v in Enum.GetValues(typeof(GuildPermission))) + { + sumOfAllGuildPermissions |= (ulong)v; + } + + // assert that the raw values match + Assert.Equal(sumOfAllGuildPermissions, GuildPermissions.All.RawValue); + Assert.Equal((ulong)0, GuildPermissions.None.RawValue); + + // assert that GuildPermissions.All contains the same number of permissions as the + // GuildPermissions enum + Assert.Equal(Enum.GetValues(typeof(GuildPermission)).Length, GuildPermissions.All.ToList().Count); + + // assert that webhook has the same raw value + ulong webHookPermissions = (ulong)( + GuildPermission.SendMessages | GuildPermission.SendTTSMessages | GuildPermission.EmbedLinks | + GuildPermission.AttachFiles); + Assert.Equal(webHookPermissions, GuildPermissions.Webhook.RawValue); + } + + [Fact] + public void TestGuildPermissionModify() + { + var perm = new GuildPermissions(); + + // tests each of the parameters of Modify one by one + + // test modify with each of the parameters + // test initially false state + Assert.False(perm.CreateInstantInvite); + + // ensure that when we modify it the parameter works + perm = perm.Modify(createInstantInvite: true); + Assert.True(perm.CreateInstantInvite); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.CreateInstantInvite); + + // set it false again, then move on to the next permission + perm = perm.Modify(createInstantInvite: false); + Assert.False(perm.CreateInstantInvite); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(kickMembers: true); + Assert.True(perm.KickMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.KickMembers); + + perm = perm.Modify(kickMembers: false); + Assert.False(perm.KickMembers); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(banMembers: true); + Assert.True(perm.BanMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.BanMembers); + + perm = perm.Modify(banMembers: false); + Assert.False(perm.BanMembers); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(administrator: true); + Assert.True(perm.Administrator); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.Administrator); + + perm = perm.Modify(administrator: false); + Assert.False(perm.Administrator); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageChannels: true); + Assert.True(perm.ManageChannels); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageChannels); + + perm = perm.Modify(manageChannels: false); + Assert.False(perm.ManageChannels); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageGuild: true); + Assert.True(perm.ManageGuild); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageGuild); + + perm = perm.Modify(manageGuild: false); + Assert.False(perm.ManageGuild); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + + // individual permission test + perm = perm.Modify(addReactions: true); + Assert.True(perm.AddReactions); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.AddReactions); + + perm = perm.Modify(addReactions: false); + Assert.False(perm.AddReactions); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + + // individual permission test + perm = perm.Modify(viewAuditLog: true); + Assert.True(perm.ViewAuditLog); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewAuditLog); + + perm = perm.Modify(viewAuditLog: false); + Assert.False(perm.ViewAuditLog); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + + // individual permission test + perm = perm.Modify(readMessages: true); + Assert.True(perm.ReadMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessages); + + perm = perm.Modify(readMessages: false); + Assert.False(perm.ReadMessages); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + + // individual permission test + perm = perm.Modify(sendMessages: true); + Assert.True(perm.SendMessages); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.SendMessages); + + perm = perm.Modify(sendMessages: false); + Assert.False(perm.SendMessages); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(embedLinks: true); + Assert.True(perm.EmbedLinks); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.EmbedLinks); + + perm = perm.Modify(embedLinks: false); + Assert.False(perm.EmbedLinks); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(attachFiles: true); + Assert.True(perm.AttachFiles); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.AttachFiles); + + perm = perm.Modify(attachFiles: false); + Assert.False(perm.AttachFiles); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(readMessageHistory: true); + Assert.True(perm.ReadMessageHistory); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessageHistory); + + perm = perm.Modify(readMessageHistory: false); + Assert.False(perm.ReadMessageHistory); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(mentionEveryone: true); + Assert.True(perm.MentionEveryone); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MentionEveryone); + + perm = perm.Modify(mentionEveryone: false); + Assert.False(perm.MentionEveryone); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(useExternalEmojis: true); + Assert.True(perm.UseExternalEmojis); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseExternalEmojis); + + perm = perm.Modify(useExternalEmojis: false); + Assert.False(perm.UseExternalEmojis); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(connect: true); + Assert.True(perm.Connect); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.Connect); + + perm = perm.Modify(connect: false); + Assert.False(perm.Connect); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(speak: true); + Assert.True(perm.Speak); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.Speak); + + perm = perm.Modify(speak: false); + Assert.False(perm.Speak); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(muteMembers: true); + Assert.True(perm.MuteMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MuteMembers); + + perm = perm.Modify(muteMembers: false); + Assert.False(perm.MuteMembers); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(deafenMembers: true); + Assert.True(perm.DeafenMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.DeafenMembers); + + perm = perm.Modify(deafenMembers: false); + Assert.False(perm.DeafenMembers); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(moveMembers: true); + Assert.True(perm.MoveMembers); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.MoveMembers); + + perm = perm.Modify(moveMembers: false); + Assert.False(perm.MoveMembers); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(useVoiceActivation: true); + Assert.True(perm.UseVAD); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseVAD); + + perm = perm.Modify(useVoiceActivation: false); + Assert.False(perm.UseVAD); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(changeNickname: true); + Assert.True(perm.ChangeNickname); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ChangeNickname); + + perm = perm.Modify(changeNickname: false); + Assert.False(perm.ChangeNickname); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageNicknames: true); + Assert.True(perm.ManageNicknames); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageNicknames); + + perm = perm.Modify(manageNicknames: false); + Assert.False(perm.ManageNicknames); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageRoles: true); + Assert.True(perm.ManageRoles); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageRoles); + + perm = perm.Modify(manageRoles: false); + Assert.False(perm.ManageRoles); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageWebhooks: true); + Assert.True(perm.ManageWebhooks); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageWebhooks); + + perm = perm.Modify(manageWebhooks: false); + Assert.False(perm.ManageWebhooks); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + // individual permission test + perm = perm.Modify(manageEmojis: true); + Assert.True(perm.ManageEmojis); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageEmojis); + + perm = perm.Modify(manageEmojis: false); + Assert.False(perm.ManageEmojis); + Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); + + } + + } +}