| @@ -1,7 +1,7 @@ | |||||
| <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
| <PropertyGroup> | <PropertyGroup> | ||||
| <VersionPrefix>2.0.0</VersionPrefix> | <VersionPrefix>2.0.0</VersionPrefix> | ||||
| <VersionSuffix>beta</VersionSuffix> | |||||
| <VersionSuffix>beta2</VersionSuffix> | |||||
| <Authors>RogueException</Authors> | <Authors>RogueException</Authors> | ||||
| <PackageTags>discord;discordapp</PackageTags> | <PackageTags>discord;discordapp</PackageTags> | ||||
| <PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | <PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | ||||
| @@ -0,0 +1,16 @@ | |||||
| # Instructions for Building Documentation | |||||
| The documentation for the Discord.NET library uses [DocFX][docfx-main]. [Instructions for installing this tool can be found here.][docfx-installing] | |||||
| 1. Navigate to the root of the repository. | |||||
| 2. (Optional) If you intend to target a specific version, ensure that you | |||||
| have the correct version checked out. | |||||
| 3. Build the library. Run `dotnet build` in the root of this repository. | |||||
| Ensure that the build passes without errors. | |||||
| 4. Build the docs using `docfx .\docs\docfx.json`. Add the `--serve` parameter | |||||
| to preview the site locally. Some elements of the page may appear incorrect | |||||
| when not hosted by a server. | |||||
| - Remarks: According to the docfx website, this tool does work on Linux under mono. | |||||
| [docfx-main]: https://dotnet.github.io/docfx/ | |||||
| [docfx-installing]: https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html | |||||
| @@ -67,8 +67,8 @@ | |||||
| "default" | "default" | ||||
| ], | ], | ||||
| "globalMetadata": { | "globalMetadata": { | ||||
| "_appFooter": "Discord.Net (c) 2015-2017" | |||||
| "_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta" | |||||
| }, | }, | ||||
| "noLangKeyword": false | "noLangKeyword": false | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -3,7 +3,7 @@ public class Info : ModuleBase<SocketCommandContext> | |||||
| { | { | ||||
| // ~say hello -> hello | // ~say hello -> hello | ||||
| [Command("say")] | [Command("say")] | ||||
| [Summary("Echos a message.")] | |||||
| [Summary("Echoes a message.")] | |||||
| public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo) | public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo) | ||||
| { | { | ||||
| // ReplyAsync is a method on ModuleBase | // ReplyAsync is a method on ModuleBase | ||||
| @@ -38,4 +38,4 @@ public class Sample : ModuleBase<SocketCommandContext> | |||||
| var userInfo = user ?? Context.Client.CurrentUser; | var userInfo = user ?? Context.Client.CurrentUser; | ||||
| await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -19,10 +19,10 @@ class Program | |||||
| private readonly DiscordSocketClient _client; | private readonly DiscordSocketClient _client; | ||||
| // Keep the CommandService and IServiceCollection around for use with commands. | |||||
| // Keep the CommandService and DI container around for use with commands. | |||||
| // These two types require you install the Discord.Net.Commands package. | // These two types require you install the Discord.Net.Commands package. | ||||
| private readonly IServiceCollection _map = new ServiceCollection(); | |||||
| private readonly CommandService _commands = new CommandService(); | |||||
| private readonly CommandService _commands; | |||||
| private readonly IServiceProvider _services; | |||||
| private Program() | private Program() | ||||
| { | { | ||||
| @@ -41,14 +41,45 @@ class Program | |||||
| // add the `using` at the top, and uncomment this line: | // add the `using` at the top, and uncomment this line: | ||||
| //WebSocketProvider = WS4NetProvider.Instance | //WebSocketProvider = WS4NetProvider.Instance | ||||
| }); | }); | ||||
| _commands = new CommandService(new CommandServiceConfig | |||||
| { | |||||
| // Again, log level: | |||||
| LogLevel = LogSeverity.Info, | |||||
| // There's a few more properties you can set, | |||||
| // for example, case-insensitive commands. | |||||
| CaseSensitiveCommands = false, | |||||
| }); | |||||
| // Subscribe the logging handler to both the client and the CommandService. | // Subscribe the logging handler to both the client and the CommandService. | ||||
| _client.Log += Logger; | |||||
| _commands.Log += Logger; | |||||
| _client.Log += Log; | |||||
| _commands.Log += Log; | |||||
| // Setup your DI container. | |||||
| _services = ConfigureServices(), | |||||
| } | |||||
| // If any services require the client, or the CommandService, or something else you keep on hand, | |||||
| // pass them as parameters into this method as needed. | |||||
| // If this method is getting pretty long, you can seperate it out into another file using partials. | |||||
| private static IServiceProvider ConfigureServices() | |||||
| { | |||||
| var map = new ServiceCollection() | |||||
| // Repeat this for all the service classes | |||||
| // and other dependencies that your commands might need. | |||||
| .AddSingleton(new SomeServiceClass()); | |||||
| // When all your required services are in the collection, build the container. | |||||
| // Tip: There's an overload taking in a 'validateScopes' bool to make sure | |||||
| // you haven't made any mistakes in your dependency graph. | |||||
| return map.BuildServiceProvider(); | |||||
| } | } | ||||
| // Example of a logging handler. This can be re-used by addons | // Example of a logging handler. This can be re-used by addons | ||||
| // that ask for a Func<LogMessage, Task>. | // that ask for a Func<LogMessage, Task>. | ||||
| private static Task Logger(LogMessage message) | |||||
| private static Task Log(LogMessage message) | |||||
| { | { | ||||
| switch (message.Severity) | switch (message.Severity) | ||||
| { | { | ||||
| @@ -92,24 +123,15 @@ class Program | |||||
| await Task.Delay(Timeout.Infinite); | await Task.Delay(Timeout.Infinite); | ||||
| } | } | ||||
| private IServiceProvider _services; | |||||
| private async Task InitCommands() | private async Task InitCommands() | ||||
| { | { | ||||
| // Repeat this for all the service classes | |||||
| // and other dependencies that your commands might need. | |||||
| _map.AddSingleton(new SomeServiceClass()); | |||||
| // When all your required services are in the collection, build the container. | |||||
| // Tip: There's an overload taking in a 'validateScopes' bool to make sure | |||||
| // you haven't made any mistakes in your dependency graph. | |||||
| _services = _map.BuildServiceProvider(); | |||||
| // Either search the program and add all Module classes that can be found. | // Either search the program and add all Module classes that can be found. | ||||
| // Module classes MUST be marked 'public' or they will be ignored. | // Module classes MUST be marked 'public' or they will be ignored. | ||||
| await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); | |||||
| // You also need to pass your 'IServiceProvider' instance now, | |||||
| // so make sure that's done before you get here. | |||||
| await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); | |||||
| // Or add Modules manually if you prefer to be a little more explicit: | // Or add Modules manually if you prefer to be a little more explicit: | ||||
| await _commands.AddModuleAsync<SomeModule>(); | |||||
| await _commands.AddModuleAsync<SomeModule>(_services); | |||||
| // Note that the first one is 'Modules' (plural) and the second is 'Module' (singular). | // Note that the first one is 'Modules' (plural) and the second is 'Module' (singular). | ||||
| // Subscribe a handler to see if a message invokes a command. | // Subscribe a handler to see if a message invokes a command. | ||||
| @@ -123,8 +145,6 @@ class Program | |||||
| if (msg == null) return; | if (msg == null) return; | ||||
| // We don't want the bot to respond to itself or other bots. | // We don't want the bot to respond to itself or other bots. | ||||
| // NOTE: Selfbots should invert this first check and remove the second | |||||
| // as they should ONLY be allowed to respond to messages from the same account. | |||||
| if (msg.Author.Id == _client.CurrentUser.Id || msg.Author.IsBot) return; | if (msg.Author.Id == _client.CurrentUser.Id || msg.Author.IsBot) return; | ||||
| // Create a number to track where the prefix ends and the command begins | // Create a number to track where the prefix ends and the command begins | ||||
| @@ -140,10 +160,12 @@ class Program | |||||
| // Execute the command. (result does not indicate a return value, | // Execute the command. (result does not indicate a return value, | ||||
| // rather an object stating if the command executed successfully). | // rather an object stating if the command executed successfully). | ||||
| var result = await _commands.ExecuteAsync(context, pos, _services); | |||||
| var result = await _commands.ExecuteAsync(context, pos); | |||||
| // Uncomment the following lines if you want the bot | // Uncomment the following lines if you want the bot | ||||
| // to send a message if it failed (not advised for most situations). | |||||
| // to send a message if it failed. | |||||
| // This does not catch errors from commands with 'RunMode.Async', | |||||
| // subscribe a handler for '_commands.CommandExecuted' to see those. | |||||
| //if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) | //if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) | ||||
| // await msg.Channel.SendMessageAsync(result.ErrorReason); | // await msg.Channel.SendMessageAsync(result.ErrorReason); | ||||
| } | } | ||||
| @@ -1,4 +1,3 @@ | |||||
| #pragma warning disable CS0618 | |||||
| using System; | using System; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -20,10 +19,6 @@ namespace Discord.Commands | |||||
| if (context.User.Id != application.Owner.Id) | if (context.User.Id != application.Owner.Id) | ||||
| return PreconditionResult.FromError("Command can only be run by the owner of the bot"); | return PreconditionResult.FromError("Command can only be run by the owner of the bot"); | ||||
| return PreconditionResult.FromSuccess(); | return PreconditionResult.FromSuccess(); | ||||
| case TokenType.User: | |||||
| if (context.User.Id != context.Client.CurrentUser.Id) | |||||
| return PreconditionResult.FromError("Command can only be run by the owner of the bot"); | |||||
| return PreconditionResult.FromSuccess(); | |||||
| default: | default: | ||||
| return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}."); | return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}."); | ||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Reflection; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
| @@ -18,6 +19,7 @@ namespace Discord.Commands.Builders | |||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| public string Summary { get; set; } | public string Summary { get; set; } | ||||
| public string Remarks { get; set; } | public string Remarks { get; set; } | ||||
| public string Group { get; set; } | |||||
| public IReadOnlyList<CommandBuilder> Commands => _commands; | public IReadOnlyList<CommandBuilder> Commands => _commands; | ||||
| public IReadOnlyList<ModuleBuilder> Modules => _submodules; | public IReadOnlyList<ModuleBuilder> Modules => _submodules; | ||||
| @@ -25,6 +27,8 @@ namespace Discord.Commands.Builders | |||||
| public IReadOnlyList<Attribute> Attributes => _attributes; | public IReadOnlyList<Attribute> Attributes => _attributes; | ||||
| public IReadOnlyList<string> Aliases => _aliases; | public IReadOnlyList<string> Aliases => _aliases; | ||||
| internal TypeInfo TypeInfo { get; set; } | |||||
| //Automatic | //Automatic | ||||
| internal ModuleBuilder(CommandService service, ModuleBuilder parent) | internal ModuleBuilder(CommandService service, ModuleBuilder parent) | ||||
| { | { | ||||
| @@ -111,17 +115,23 @@ namespace Discord.Commands.Builders | |||||
| return this; | return this; | ||||
| } | } | ||||
| private ModuleInfo BuildImpl(CommandService service, ModuleInfo parent = null) | |||||
| private ModuleInfo BuildImpl(CommandService service, IServiceProvider services, ModuleInfo parent = null) | |||||
| { | { | ||||
| //Default name to first alias | //Default name to first alias | ||||
| if (Name == null) | if (Name == null) | ||||
| Name = _aliases[0]; | Name = _aliases[0]; | ||||
| return new ModuleInfo(this, service, parent); | |||||
| if (TypeInfo != null) | |||||
| { | |||||
| var moduleInstance = ReflectionUtils.CreateObject<IModuleBase>(TypeInfo, service, services); | |||||
| moduleInstance.OnModuleBuilding(service, this); | |||||
| } | |||||
| return new ModuleInfo(this, service, services, parent); | |||||
| } | } | ||||
| public ModuleInfo Build(CommandService service) => BuildImpl(service); | |||||
| public ModuleInfo Build(CommandService service, IServiceProvider services) => BuildImpl(service, services); | |||||
| internal ModuleInfo Build(CommandService service, ModuleInfo parent) => BuildImpl(service, parent); | |||||
| internal ModuleInfo Build(CommandService service, IServiceProvider services, ModuleInfo parent) => BuildImpl(service, services, parent); | |||||
| } | } | ||||
| } | } | ||||
| @@ -42,14 +42,13 @@ namespace Discord.Commands | |||||
| } | } | ||||
| public static Task<Dictionary<Type, ModuleInfo>> BuildAsync(CommandService service, params TypeInfo[] validTypes) => BuildAsync(validTypes, service); | |||||
| public static async Task<Dictionary<Type, ModuleInfo>> BuildAsync(IEnumerable<TypeInfo> validTypes, CommandService service) | |||||
| public static Task<Dictionary<Type, ModuleInfo>> BuildAsync(CommandService service, IServiceProvider services, params TypeInfo[] validTypes) => BuildAsync(validTypes, service, services); | |||||
| public static async Task<Dictionary<Type, ModuleInfo>> BuildAsync(IEnumerable<TypeInfo> validTypes, CommandService service, IServiceProvider services) | |||||
| { | { | ||||
| /*if (!validTypes.Any()) | /*if (!validTypes.Any()) | ||||
| throw new InvalidOperationException("Could not find any valid modules from the given selection");*/ | throw new InvalidOperationException("Could not find any valid modules from the given selection");*/ | ||||
| var topLevelGroups = validTypes.Where(x => x.DeclaringType == null); | |||||
| var subGroups = validTypes.Intersect(topLevelGroups); | |||||
| var topLevelGroups = validTypes.Where(x => x.DeclaringType == null || !IsValidModuleDefinition(x.DeclaringType.GetTypeInfo())); | |||||
| var builtTypes = new List<TypeInfo>(); | var builtTypes = new List<TypeInfo>(); | ||||
| @@ -63,11 +62,11 @@ namespace Discord.Commands | |||||
| var module = new ModuleBuilder(service, null); | var module = new ModuleBuilder(service, null); | ||||
| BuildModule(module, typeInfo, service); | |||||
| BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service); | |||||
| BuildModule(module, typeInfo, service, services); | |||||
| BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service, services); | |||||
| builtTypes.Add(typeInfo); | builtTypes.Add(typeInfo); | ||||
| result[typeInfo.AsType()] = module.Build(service); | |||||
| result[typeInfo.AsType()] = module.Build(service, services); | |||||
| } | } | ||||
| await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} modules.").ConfigureAwait(false); | await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} modules.").ConfigureAwait(false); | ||||
| @@ -75,7 +74,7 @@ namespace Discord.Commands | |||||
| return result; | return result; | ||||
| } | } | ||||
| private static void BuildSubTypes(ModuleBuilder builder, IEnumerable<TypeInfo> subTypes, List<TypeInfo> builtTypes, CommandService service) | |||||
| private static void BuildSubTypes(ModuleBuilder builder, IEnumerable<TypeInfo> subTypes, List<TypeInfo> builtTypes, CommandService service, IServiceProvider services) | |||||
| { | { | ||||
| foreach (var typeInfo in subTypes) | foreach (var typeInfo in subTypes) | ||||
| { | { | ||||
| @@ -87,17 +86,18 @@ namespace Discord.Commands | |||||
| builder.AddModule((module) => | builder.AddModule((module) => | ||||
| { | { | ||||
| BuildModule(module, typeInfo, service); | |||||
| BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service); | |||||
| BuildModule(module, typeInfo, service, services); | |||||
| BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service, services); | |||||
| }); | }); | ||||
| builtTypes.Add(typeInfo); | builtTypes.Add(typeInfo); | ||||
| } | } | ||||
| } | } | ||||
| private static void BuildModule(ModuleBuilder builder, TypeInfo typeInfo, CommandService service) | |||||
| private static void BuildModule(ModuleBuilder builder, TypeInfo typeInfo, CommandService service, IServiceProvider services) | |||||
| { | { | ||||
| var attributes = typeInfo.GetCustomAttributes(); | var attributes = typeInfo.GetCustomAttributes(); | ||||
| builder.TypeInfo = typeInfo; | |||||
| foreach (var attribute in attributes) | foreach (var attribute in attributes) | ||||
| { | { | ||||
| @@ -117,6 +117,7 @@ namespace Discord.Commands | |||||
| break; | break; | ||||
| case GroupAttribute group: | case GroupAttribute group: | ||||
| builder.Name = builder.Name ?? group.Prefix; | builder.Name = builder.Name ?? group.Prefix; | ||||
| builder.Group = group.Prefix; | |||||
| builder.AddAliases(group.Prefix); | builder.AddAliases(group.Prefix); | ||||
| break; | break; | ||||
| case PreconditionAttribute precondition: | case PreconditionAttribute precondition: | ||||
| @@ -140,12 +141,12 @@ namespace Discord.Commands | |||||
| { | { | ||||
| builder.AddCommand((command) => | builder.AddCommand((command) => | ||||
| { | { | ||||
| BuildCommand(command, typeInfo, method, service); | |||||
| BuildCommand(command, typeInfo, method, service, services); | |||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service) | |||||
| private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service, IServiceProvider serviceprovider) | |||||
| { | { | ||||
| var attributes = method.GetCustomAttributes(); | var attributes = method.GetCustomAttributes(); | ||||
| @@ -191,7 +192,7 @@ namespace Discord.Commands | |||||
| { | { | ||||
| builder.AddParameter((parameter) => | builder.AddParameter((parameter) => | ||||
| { | { | ||||
| BuildParameter(parameter, paramInfo, pos++, count, service); | |||||
| BuildParameter(parameter, paramInfo, pos++, count, service, serviceprovider); | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -227,7 +228,7 @@ namespace Discord.Commands | |||||
| builder.Callback = ExecuteCallback; | builder.Callback = ExecuteCallback; | ||||
| } | } | ||||
| private static void BuildParameter(ParameterBuilder builder, System.Reflection.ParameterInfo paramInfo, int position, int count, CommandService service) | |||||
| private static void BuildParameter(ParameterBuilder builder, System.Reflection.ParameterInfo paramInfo, int position, int count, CommandService service, IServiceProvider services) | |||||
| { | { | ||||
| var attributes = paramInfo.GetCustomAttributes(); | var attributes = paramInfo.GetCustomAttributes(); | ||||
| var paramType = paramInfo.ParameterType; | var paramType = paramInfo.ParameterType; | ||||
| @@ -245,7 +246,7 @@ namespace Discord.Commands | |||||
| builder.Summary = summary.Text; | builder.Summary = summary.Text; | ||||
| break; | break; | ||||
| case OverrideTypeReaderAttribute typeReader: | case OverrideTypeReaderAttribute typeReader: | ||||
| builder.TypeReader = GetTypeReader(service, paramType, typeReader.TypeReader); | |||||
| builder.TypeReader = GetTypeReader(service, paramType, typeReader.TypeReader, services); | |||||
| break; | break; | ||||
| case ParamArrayAttribute _: | case ParamArrayAttribute _: | ||||
| builder.IsMultiple = true; | builder.IsMultiple = true; | ||||
| @@ -273,19 +274,12 @@ namespace Discord.Commands | |||||
| if (builder.TypeReader == null) | if (builder.TypeReader == null) | ||||
| { | { | ||||
| var readers = service.GetTypeReaders(paramType); | |||||
| TypeReader reader = null; | |||||
| if (readers != null) | |||||
| reader = readers.FirstOrDefault().Value; | |||||
| else | |||||
| reader = service.GetDefaultTypeReader(paramType); | |||||
| builder.TypeReader = reader; | |||||
| builder.TypeReader = service.GetDefaultTypeReader(paramType) | |||||
| ?? service.GetTypeReaders(paramType)?.FirstOrDefault().Value; | |||||
| } | } | ||||
| } | } | ||||
| private static TypeReader GetTypeReader(CommandService service, Type paramType, Type typeReaderType) | |||||
| private static TypeReader GetTypeReader(CommandService service, Type paramType, Type typeReaderType, IServiceProvider services) | |||||
| { | { | ||||
| var readers = service.GetTypeReaders(paramType); | var readers = service.GetTypeReaders(paramType); | ||||
| TypeReader reader = null; | TypeReader reader = null; | ||||
| @@ -296,7 +290,7 @@ namespace Discord.Commands | |||||
| } | } | ||||
| //We dont have a cached type reader, create one | //We dont have a cached type reader, create one | ||||
| reader = ReflectionUtils.CreateObject<TypeReader>(typeReaderType.GetTypeInfo(), service, EmptyServiceProvider.Instance); | |||||
| reader = ReflectionUtils.CreateObject<TypeReader>(typeReaderType.GetTypeInfo(), service, services); | |||||
| service.AddTypeReader(paramType, reader); | service.AddTypeReader(paramType, reader); | ||||
| return reader; | return reader; | ||||
| @@ -305,7 +299,8 @@ namespace Discord.Commands | |||||
| private static bool IsValidModuleDefinition(TypeInfo typeInfo) | private static bool IsValidModuleDefinition(TypeInfo typeInfo) | ||||
| { | { | ||||
| return _moduleTypeInfo.IsAssignableFrom(typeInfo) && | return _moduleTypeInfo.IsAssignableFrom(typeInfo) && | ||||
| !typeInfo.IsAbstract; | |||||
| !typeInfo.IsAbstract && | |||||
| !typeInfo.ContainsGenericParameters; | |||||
| } | } | ||||
| private static bool IsValidCommandDefinition(MethodInfo methodInfo) | private static bool IsValidCommandDefinition(MethodInfo methodInfo) | ||||
| @@ -1,5 +1,3 @@ | |||||
| using Discord.Commands.Builders; | |||||
| using Discord.Logging; | |||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -8,6 +6,9 @@ using System.Linq; | |||||
| using System.Reflection; | using System.Reflection; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Microsoft.Extensions.DependencyInjection; | |||||
| using Discord.Commands.Builders; | |||||
| using Discord.Logging; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -87,7 +88,8 @@ namespace Discord.Commands | |||||
| var builder = new ModuleBuilder(this, null, primaryAlias); | var builder = new ModuleBuilder(this, null, primaryAlias); | ||||
| buildFunc(builder); | buildFunc(builder); | ||||
| var module = builder.Build(this); | |||||
| var module = builder.Build(this, null); | |||||
| return LoadModuleInternal(module); | return LoadModuleInternal(module); | ||||
| } | } | ||||
| finally | finally | ||||
| @@ -95,9 +97,18 @@ namespace Discord.Commands | |||||
| _moduleLock.Release(); | _moduleLock.Release(); | ||||
| } | } | ||||
| } | } | ||||
| public Task<ModuleInfo> AddModuleAsync<T>() => AddModuleAsync(typeof(T)); | |||||
| public async Task<ModuleInfo> AddModuleAsync(Type type) | |||||
| /// <summary> | |||||
| /// Add a command module from a type | |||||
| /// </summary> | |||||
| /// <typeparam name="T">The type of module</typeparam> | |||||
| /// <param name="services">An IServiceProvider for your dependency injection solution, if using one - otherwise, pass null</param> | |||||
| /// <returns>A built module</returns> | |||||
| public Task<ModuleInfo> AddModuleAsync<T>(IServiceProvider services) => AddModuleAsync(typeof(T), services); | |||||
| public async Task<ModuleInfo> AddModuleAsync(Type type, IServiceProvider services) | |||||
| { | { | ||||
| services = services ?? EmptyServiceProvider.Instance; | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | await _moduleLock.WaitAsync().ConfigureAwait(false); | ||||
| try | try | ||||
| { | { | ||||
| @@ -106,7 +117,7 @@ namespace Discord.Commands | |||||
| if (_typedModuleDefs.ContainsKey(type)) | if (_typedModuleDefs.ContainsKey(type)) | ||||
| throw new ArgumentException($"This module has already been added."); | throw new ArgumentException($"This module has already been added."); | ||||
| var module = (await ModuleClassBuilder.BuildAsync(this, typeInfo).ConfigureAwait(false)).FirstOrDefault(); | |||||
| var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault(); | |||||
| if (module.Value == default(ModuleInfo)) | if (module.Value == default(ModuleInfo)) | ||||
| throw new InvalidOperationException($"Could not build the module {type.FullName}, did you pass an invalid type?"); | throw new InvalidOperationException($"Could not build the module {type.FullName}, did you pass an invalid type?"); | ||||
| @@ -120,13 +131,21 @@ namespace Discord.Commands | |||||
| _moduleLock.Release(); | _moduleLock.Release(); | ||||
| } | } | ||||
| } | } | ||||
| public async Task<IEnumerable<ModuleInfo>> AddModulesAsync(Assembly assembly) | |||||
| /// <summary> | |||||
| /// Add command modules from an assembly | |||||
| /// </summary> | |||||
| /// <param name="assembly">The assembly containing command modules</param> | |||||
| /// <param name="services">An IServiceProvider for your dependency injection solution, if using one - otherwise, pass null</param> | |||||
| /// <returns>A collection of built modules</returns> | |||||
| public async Task<IEnumerable<ModuleInfo>> AddModulesAsync(Assembly assembly, IServiceProvider services) | |||||
| { | { | ||||
| services = services ?? EmptyServiceProvider.Instance; | |||||
| await _moduleLock.WaitAsync().ConfigureAwait(false); | await _moduleLock.WaitAsync().ConfigureAwait(false); | ||||
| try | try | ||||
| { | { | ||||
| var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false); | var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false); | ||||
| var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this).ConfigureAwait(false); | |||||
| var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this, services).ConfigureAwait(false); | |||||
| foreach (var info in moduleDefs) | foreach (var info in moduleDefs) | ||||
| { | { | ||||
| @@ -226,7 +245,7 @@ namespace Discord.Commands | |||||
| var readers = _typeReaders.GetOrAdd(typeof(Nullable<>).MakeGenericType(valueType), x => new ConcurrentDictionary<Type, TypeReader>()); | var readers = _typeReaders.GetOrAdd(typeof(Nullable<>).MakeGenericType(valueType), x => new ConcurrentDictionary<Type, TypeReader>()); | ||||
| var nullableReader = NullableTypeReader.Create(valueType, valueTypeReader); | var nullableReader = NullableTypeReader.Create(valueType, valueTypeReader); | ||||
| readers[nullableReader.GetType()] = nullableReader; | readers[nullableReader.GetType()] = nullableReader; | ||||
| } | |||||
| } | |||||
| internal IDictionary<Type, TypeReader> GetTypeReaders(Type type) | internal IDictionary<Type, TypeReader> GetTypeReaders(Type type) | ||||
| { | { | ||||
| if (_typeReaders.TryGetValue(type, out var definedTypeReaders)) | if (_typeReaders.TryGetValue(type, out var definedTypeReaders)) | ||||
| @@ -274,9 +293,9 @@ namespace Discord.Commands | |||||
| return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); | return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); | ||||
| } | } | ||||
| public Task<IResult> ExecuteAsync(ICommandContext context, int argPos, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
| public Task<IResult> ExecuteAsync(ICommandContext context, int argPos, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
| => ExecuteAsync(context, context.Message.Content.Substring(argPos), services, multiMatchHandling); | => ExecuteAsync(context, context.Message.Content.Substring(argPos), services, multiMatchHandling); | ||||
| public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
| public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) | |||||
| { | { | ||||
| services = services ?? EmptyServiceProvider.Instance; | services = services ?? EmptyServiceProvider.Instance; | ||||
| var searchResult = Search(context, input); | var searchResult = Search(context, input); | ||||
| @@ -331,7 +350,7 @@ namespace Discord.Commands | |||||
| float CalculateScore(CommandMatch match, ParseResult parseResult) | float CalculateScore(CommandMatch match, ParseResult parseResult) | ||||
| { | { | ||||
| float argValuesScore = 0, paramValuesScore = 0; | float argValuesScore = 0, paramValuesScore = 0; | ||||
| if (match.Command.Parameters.Count > 0) | if (match.Command.Parameters.Count > 0) | ||||
| { | { | ||||
| var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | ||||
| @@ -1,3 +1,4 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| @@ -24,5 +25,11 @@ namespace Discord.Commands | |||||
| /// <summary> Determines whether extra parameters should be ignored. </summary> | /// <summary> Determines whether extra parameters should be ignored. </summary> | ||||
| public bool IgnoreExtraArgs { get; set; } = false; | public bool IgnoreExtraArgs { get; set; } = false; | ||||
| ///// <summary> Gets or sets the <see cref="IServiceProvider"/> to use. </summary> | |||||
| //public IServiceProvider ServiceProvider { get; set; } = null; | |||||
| ///// <summary> Gets or sets a factory function for the <see cref="IServiceProvider"/> to use. </summary> | |||||
| //public Func<CommandService, IServiceProvider> ServiceProviderFactory { get; set; } = null; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,6 @@ | |||||
| namespace Discord.Commands | |||||
| using Discord.Commands.Builders; | |||||
| namespace Discord.Commands | |||||
| { | { | ||||
| internal interface IModuleBase | internal interface IModuleBase | ||||
| { | { | ||||
| @@ -7,5 +9,7 @@ | |||||
| void BeforeExecute(CommandInfo command); | void BeforeExecute(CommandInfo command); | ||||
| void AfterExecute(CommandInfo command); | void AfterExecute(CommandInfo command); | ||||
| void OnModuleBuilding(CommandService commandService, ModuleBuilder builder); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,7 +2,7 @@ using System; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Reflection; | |||||
| using Discord.Commands.Builders; | using Discord.Commands.Builders; | ||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| @@ -13,6 +13,7 @@ namespace Discord.Commands | |||||
| public string Name { get; } | public string Name { get; } | ||||
| public string Summary { get; } | public string Summary { get; } | ||||
| public string Remarks { get; } | public string Remarks { get; } | ||||
| public string Group { get; } | |||||
| public IReadOnlyList<string> Aliases { get; } | public IReadOnlyList<string> Aliases { get; } | ||||
| public IReadOnlyList<CommandInfo> Commands { get; } | public IReadOnlyList<CommandInfo> Commands { get; } | ||||
| @@ -22,21 +23,26 @@ namespace Discord.Commands | |||||
| public ModuleInfo Parent { get; } | public ModuleInfo Parent { get; } | ||||
| public bool IsSubmodule => Parent != null; | public bool IsSubmodule => Parent != null; | ||||
| internal ModuleInfo(ModuleBuilder builder, CommandService service, ModuleInfo parent = null) | |||||
| //public TypeInfo TypeInfo { get; } | |||||
| internal ModuleInfo(ModuleBuilder builder, CommandService service, IServiceProvider services, ModuleInfo parent = null) | |||||
| { | { | ||||
| Service = service; | Service = service; | ||||
| Name = builder.Name; | Name = builder.Name; | ||||
| Summary = builder.Summary; | Summary = builder.Summary; | ||||
| Remarks = builder.Remarks; | Remarks = builder.Remarks; | ||||
| Group = builder.Group; | |||||
| Parent = parent; | Parent = parent; | ||||
| //TypeInfo = builder.TypeInfo; | |||||
| Aliases = BuildAliases(builder, service).ToImmutableArray(); | Aliases = BuildAliases(builder, service).ToImmutableArray(); | ||||
| Commands = builder.Commands.Select(x => x.Build(this, service)).ToImmutableArray(); | Commands = builder.Commands.Select(x => x.Build(this, service)).ToImmutableArray(); | ||||
| Preconditions = BuildPreconditions(builder).ToImmutableArray(); | Preconditions = BuildPreconditions(builder).ToImmutableArray(); | ||||
| Attributes = BuildAttributes(builder).ToImmutableArray(); | Attributes = BuildAttributes(builder).ToImmutableArray(); | ||||
| Submodules = BuildSubmodules(builder, service).ToImmutableArray(); | |||||
| Submodules = BuildSubmodules(builder, service, services).ToImmutableArray(); | |||||
| } | } | ||||
| private static IEnumerable<string> BuildAliases(ModuleBuilder builder, CommandService service) | private static IEnumerable<string> BuildAliases(ModuleBuilder builder, CommandService service) | ||||
| @@ -66,12 +72,12 @@ namespace Discord.Commands | |||||
| return result; | return result; | ||||
| } | } | ||||
| private List<ModuleInfo> BuildSubmodules(ModuleBuilder parent, CommandService service) | |||||
| private List<ModuleInfo> BuildSubmodules(ModuleBuilder parent, CommandService service, IServiceProvider services) | |||||
| { | { | ||||
| var result = new List<ModuleInfo>(); | var result = new List<ModuleInfo>(); | ||||
| foreach (var submodule in parent.Modules) | foreach (var submodule in parent.Modules) | ||||
| result.Add(submodule.Build(service, this)); | |||||
| result.Add(submodule.Build(service, services, this)); | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Discord.Commands.Builders; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -10,7 +11,13 @@ namespace Discord.Commands | |||||
| { | { | ||||
| public T Context { get; private set; } | public T Context { get; private set; } | ||||
| protected virtual async Task<IUserMessage> ReplyAsync(string message, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| /// <summary> | |||||
| /// Sends a message to the source channel | |||||
| /// </summary> | |||||
| /// <param name="message">Contents of the message; optional only if <paramref name="embed"/> is specified</param> | |||||
| /// <param name="isTTS">Specifies if Discord should read this message aloud using TTS</param> | |||||
| /// <param name="embed">An embed to be displayed alongside the message</param> | |||||
| protected virtual async Task<IUserMessage> ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| { | { | ||||
| return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); | return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -23,15 +30,18 @@ namespace Discord.Commands | |||||
| { | { | ||||
| } | } | ||||
| protected virtual void OnModuleBuilding(CommandService commandService, ModuleBuilder builder) | |||||
| { | |||||
| } | |||||
| //IModuleBase | //IModuleBase | ||||
| void IModuleBase.SetContext(ICommandContext context) | void IModuleBase.SetContext(ICommandContext context) | ||||
| { | { | ||||
| var newValue = context as T; | var newValue = context as T; | ||||
| Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}"); | Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}"); | ||||
| } | } | ||||
| void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command); | void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command); | ||||
| void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command); | void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command); | ||||
| void IModuleBase.OnModuleBuilding(CommandService commandService, ModuleBuilder builder) => OnModuleBuilding(commandService, builder); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Reflection; | using System.Reflection; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -28,6 +28,9 @@ namespace Discord | |||||
| return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}"; | return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}"; | ||||
| } | } | ||||
| public static string GetSpotifyAlbumArtUrl(string albumArtId) | |||||
| => $"https://i.scdn.co/image/{albumArtId}"; | |||||
| private static string FormatToExtension(ImageFormat format, string imageId) | private static string FormatToExtension(ImageFormat format, string imageId) | ||||
| { | { | ||||
| if (format == ImageFormat.Auto) | if (format == ImageFormat.Auto) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -1,15 +1,15 @@ | |||||
| namespace Discord | |||||
| namespace Discord | |||||
| { | { | ||||
| public class GameAsset | public class GameAsset | ||||
| { | { | ||||
| internal GameAsset() { } | internal GameAsset() { } | ||||
| internal ulong ApplicationId { get; set; } | |||||
| internal ulong? ApplicationId { get; set; } | |||||
| public string Text { get; internal set; } | public string Text { get; internal set; } | ||||
| public string ImageId { get; internal set; } | public string ImageId { get; internal set; } | ||||
| public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | ||||
| => CDN.GetRichAssetUrl(ApplicationId, ImageId, size, format); | |||||
| => ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null; | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,11 +1,11 @@ | |||||
| namespace Discord | |||||
| namespace Discord | |||||
| { | { | ||||
| public class GameParty | public class GameParty | ||||
| { | { | ||||
| internal GameParty() { } | internal GameParty() { } | ||||
| public string Id { get; internal set; } | public string Id { get; internal set; } | ||||
| public int Members { get; internal set; } | |||||
| public int Capacity { get; internal set; } | |||||
| public long Members { get; internal set; } | |||||
| public long Capacity { get; internal set; } | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -7,8 +7,8 @@ namespace Discord | |||||
| { | { | ||||
| internal RichGame() { } | internal RichGame() { } | ||||
| public string Details { get; internal set;} | |||||
| public string State { get; internal set;} | |||||
| public string Details { get; internal set; } | |||||
| public string State { get; internal set; } | |||||
| public ulong ApplicationId { get; internal set; } | public ulong ApplicationId { get; internal set; } | ||||
| public GameAsset SmallAsset { get; internal set; } | public GameAsset SmallAsset { get; internal set; } | ||||
| public GameAsset LargeAsset { get; internal set; } | public GameAsset LargeAsset { get; internal set; } | ||||
| @@ -19,4 +19,4 @@ namespace Discord | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} (Rich)"; | private string DebuggerDisplay => $"{Name} (Rich)"; | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Diagnostics; | |||||
| namespace Discord | |||||
| { | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public class SpotifyGame : Game | |||||
| { | |||||
| public IEnumerable<string> Artists { get; internal set; } | |||||
| public string AlbumArt { get; internal set; } | |||||
| public string AlbumTitle { get; internal set; } | |||||
| public string TrackTitle { get; internal set; } | |||||
| public string SyncId { get; internal set; } | |||||
| public string SessionId { get; internal set; } | |||||
| public TimeSpan? Duration { get; internal set; } | |||||
| internal SpotifyGame() { } | |||||
| public override string ToString() => Name; | |||||
| private string DebuggerDisplay => $"{Name} (Spotify)"; | |||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -11,10 +11,10 @@ namespace Discord | |||||
| Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| #endif | #endif | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | ||||
| Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -13,6 +13,8 @@ namespace Discord | |||||
| public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); | public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); | ||||
| /// <summary> Gets a ChannelPermissions that grants all permissions for voice channels. </summary> | /// <summary> Gets a ChannelPermissions that grants all permissions for voice channels. </summary> | ||||
| public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001); | public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001); | ||||
| /// <summary> Gets a ChannelPermissions that grants all permissions for category channels. </summary> | |||||
| public static readonly ChannelPermissions Category = new ChannelPermissions(0b01100_1111110_1111111110001_010001); | |||||
| /// <summary> Gets a ChannelPermissions that grants all permissions for direct message channels. </summary> | /// <summary> Gets a ChannelPermissions that grants all permissions for direct message channels. </summary> | ||||
| public static readonly ChannelPermissions DM = new ChannelPermissions(0b00000_1000110_1011100110000_000000); | public static readonly ChannelPermissions DM = new ChannelPermissions(0b00000_1000110_1011100110000_000000); | ||||
| /// <summary> Gets a ChannelPermissions that grants all permissions for group channels. </summary> | /// <summary> Gets a ChannelPermissions that grants all permissions for group channels. </summary> | ||||
| @@ -24,6 +26,7 @@ namespace Discord | |||||
| { | { | ||||
| case ITextChannel _: return Text; | case ITextChannel _: return Text; | ||||
| case IVoiceChannel _: return Voice; | case IVoiceChannel _: return Voice; | ||||
| case ICategoryChannel _: return Category; | |||||
| case IDMChannel _: return DM; | case IDMChannel _: return DM; | ||||
| case IGroupChannel _: return Group; | case IGroupChannel _: return Group; | ||||
| default: throw new ArgumentException("Unknown channel type", nameof(channel)); | default: throw new ArgumentException("Unknown channel type", nameof(channel)); | ||||
| @@ -157,4 +160,4 @@ namespace Discord | |||||
| public override string ToString() => RawValue.ToString(); | public override string ToString() => RawValue.ToString(); | ||||
| private string DebuggerDisplay => $"{string.Join(", ", ToList())}"; | private string DebuggerDisplay => $"{string.Join(", ", ToList())}"; | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Threading.Tasks; | |||||
| using System.Threading.Tasks; | |||||
| using System.IO; | using System.IO; | ||||
| namespace Discord | namespace Discord | ||||
| @@ -8,10 +8,10 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// Sends a message to the user via DM. | /// Sends a message to the user via DM. | ||||
| /// </summary> | /// </summary> | ||||
| public static async Task<IUserMessage> SendMessageAsync(this IUser user, | |||||
| string text, | |||||
| public static async Task<IUserMessage> SendMessageAsync(this IUser user, | |||||
| string text, | |||||
| bool isTTS = false, | bool isTTS = false, | ||||
| Embed embed = null, | |||||
| Embed embed = null, | |||||
| RequestOptions options = null) | RequestOptions options = null) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| @@ -25,24 +25,29 @@ namespace Discord | |||||
| string filename, | string filename, | ||||
| string text = null, | string text = null, | ||||
| bool isTTS = false, | bool isTTS = false, | ||||
| Embed embed = null, | |||||
| RequestOptions options = null | RequestOptions options = null | ||||
| ) | ) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| } | } | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| /// <summary> | /// <summary> | ||||
| /// Sends a file to the user via DM. | /// Sends a file to the user via DM. | ||||
| /// </summary> | /// </summary> | ||||
| public static async Task<IUserMessage> SendFileAsync(this IUser user, | |||||
| string filePath, | |||||
| string text = null, | |||||
| bool isTTS = false, | |||||
| public static async Task<IUserMessage> SendFileAsync(this IUser user, | |||||
| string filePath, | |||||
| string text = null, | |||||
| bool isTTS = false, | |||||
| Embed embed = null, | |||||
| RequestOptions options = null) | RequestOptions options = null) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| } | } | ||||
| #endif | #endif | ||||
| public static Task BanAsync(this IGuildUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) | |||||
| => user.Guild.AddBanAsync(user, pruneDays, reason, options); | |||||
| } | } | ||||
| } | } | ||||
| @@ -36,5 +36,7 @@ namespace Discord | |||||
| Task<IVoiceRegion> GetVoiceRegionAsync(string id, RequestOptions options = null); | Task<IVoiceRegion> GetVoiceRegionAsync(string id, RequestOptions options = null); | ||||
| Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | ||||
| Task<int> GetRecommendedShardCountAsync(RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Net; | using System.Net; | ||||
| namespace Discord.Net | namespace Discord.Net | ||||
| @@ -8,11 +8,13 @@ namespace Discord.Net | |||||
| public HttpStatusCode HttpCode { get; } | public HttpStatusCode HttpCode { get; } | ||||
| public int? DiscordCode { get; } | public int? DiscordCode { get; } | ||||
| public string Reason { get; } | public string Reason { get; } | ||||
| public IRequest Request { get; } | |||||
| public HttpException(HttpStatusCode httpCode, int? discordCode = null, string reason = null) | |||||
| public HttpException(HttpStatusCode httpCode, IRequest request, int? discordCode = null, string reason = null) | |||||
| : base(CreateMessage(httpCode, discordCode, reason)) | : base(CreateMessage(httpCode, discordCode, reason)) | ||||
| { | { | ||||
| HttpCode = httpCode; | HttpCode = httpCode; | ||||
| Request = request; | |||||
| DiscordCode = discordCode; | DiscordCode = discordCode; | ||||
| Reason = reason; | Reason = reason; | ||||
| } | } | ||||
| @@ -0,0 +1,10 @@ | |||||
| using System; | |||||
| namespace Discord.Net | |||||
| { | |||||
| public interface IRequest | |||||
| { | |||||
| DateTimeOffset? TimeoutAt { get; } | |||||
| RequestOptions Options { get; } | |||||
| } | |||||
| } | |||||
| @@ -1,12 +1,15 @@ | |||||
| using System; | |||||
| using System; | |||||
| namespace Discord.Net | namespace Discord.Net | ||||
| { | { | ||||
| public class RateLimitedException : TimeoutException | public class RateLimitedException : TimeoutException | ||||
| { | { | ||||
| public RateLimitedException() | |||||
| public IRequest Request { get; } | |||||
| public RateLimitedException(IRequest request) | |||||
| : base("You are being rate limited.") | : base("You are being rate limited.") | ||||
| { | { | ||||
| Request = request; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| using System; | |||||
| using System; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public enum TokenType | public enum TokenType | ||||
| { | { | ||||
| [Obsolete("User logins are being deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827")] | |||||
| [Obsolete("User logins are deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827", error: true)] | |||||
| User, | User, | ||||
| Bearer, | Bearer, | ||||
| Bot, | Bot, | ||||
| @@ -80,7 +80,7 @@ namespace Discord | |||||
| } | } | ||||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||||
| private static bool HasFlag(ulong value, ulong flag) => (value & flag) != 0; | |||||
| private static bool HasFlag(ulong value, ulong flag) => (value & flag) == flag; | |||||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||||
| public static void SetFlag(ref ulong value, ulong flag) => value |= flag; | public static void SetFlag(ref ulong value, ulong flag) => value |= flag; | ||||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||||
| @@ -161,10 +161,10 @@ namespace Discord | |||||
| else if (!GetValue(resolvedPermissions, ChannelPermission.SendMessages)) | else if (!GetValue(resolvedPermissions, ChannelPermission.SendMessages)) | ||||
| { | { | ||||
| //No send permissions on a text channel removes all send-related permissions | //No send permissions on a text channel removes all send-related permissions | ||||
| resolvedPermissions &= ~(1UL << (int)ChannelPermission.SendTTSMessages); | |||||
| resolvedPermissions &= ~(1UL << (int)ChannelPermission.MentionEveryone); | |||||
| resolvedPermissions &= ~(1UL << (int)ChannelPermission.EmbedLinks); | |||||
| resolvedPermissions &= ~(1UL << (int)ChannelPermission.AttachFiles); | |||||
| resolvedPermissions &= ~(ulong)ChannelPermission.SendTTSMessages; | |||||
| resolvedPermissions &= ~(ulong)ChannelPermission.MentionEveryone; | |||||
| resolvedPermissions &= ~(ulong)ChannelPermission.EmbedLinks; | |||||
| resolvedPermissions &= ~(ulong)ChannelPermission.AttachFiles; | |||||
| } | } | ||||
| } | } | ||||
| resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example) | resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example) | ||||
| @@ -173,4 +173,4 @@ namespace Discord | |||||
| return resolvedPermissions; | return resolvedPermissions; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -10,6 +10,6 @@ | |||||
| <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="WebSocket4Net" Version="0.15.0" /> | |||||
| <PackageReference Include="WebSocket4Net" Version="0.15.2" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| </Project> | </Project> | ||||
| @@ -66,7 +66,7 @@ namespace Discord.Net.Providers.WS4Net | |||||
| _cancelTokenSource = new CancellationTokenSource(); | _cancelTokenSource = new CancellationTokenSource(); | ||||
| _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; | _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; | ||||
| _client = new WS4NetSocket(host, customHeaderItems: _headers.ToList()) | |||||
| _client = new WS4NetSocket(host, "", customHeaderItems: _headers.ToList()) | |||||
| { | { | ||||
| EnableAutoSendPing = false, | EnableAutoSendPing = false, | ||||
| NoDelay = true, | NoDelay = true, | ||||
| @@ -163,4 +163,4 @@ namespace Discord.Net.Providers.WS4Net | |||||
| Closed(ex).GetAwaiter().GetResult(); | Closed(ex).GetAwaiter().GetResult(); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using Newtonsoft.Json.Serialization; | using Newtonsoft.Json.Serialization; | ||||
| using System.Runtime.Serialization; | using System.Runtime.Serialization; | ||||
| @@ -29,6 +29,10 @@ namespace Discord.API | |||||
| public Optional<API.GameTimestamps> Timestamps { get; set; } | public Optional<API.GameTimestamps> Timestamps { get; set; } | ||||
| [JsonProperty("instance")] | [JsonProperty("instance")] | ||||
| public Optional<bool> Instance { get; set; } | public Optional<bool> Instance { get; set; } | ||||
| [JsonProperty("sync_id")] | |||||
| public Optional<string> SyncId { get; set; } | |||||
| [JsonProperty("session_id")] | |||||
| public Optional<string> SessionId { get; set; } | |||||
| [OnError] | [OnError] | ||||
| internal void OnError(StreamingContext context, ErrorContext errorContext) | internal void OnError(StreamingContext context, ErrorContext errorContext) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API | namespace Discord.API | ||||
| { | { | ||||
| @@ -7,6 +7,6 @@ namespace Discord.API | |||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id { get; set; } | public string Id { get; set; } | ||||
| [JsonProperty("size")] | [JsonProperty("size")] | ||||
| public int[] Size { get; set; } | |||||
| public long[] Size { get; set; } | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,18 +1,25 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Discord.Net.Converters; | |||||
| using Discord.Net.Rest; | using Discord.Net.Rest; | ||||
| using Newtonsoft.Json; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Globalization; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Text; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| internal class UploadFileParams | internal class UploadFileParams | ||||
| { | { | ||||
| private static JsonSerializer _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | |||||
| public Stream File { get; } | public Stream File { get; } | ||||
| public Optional<string> Filename { get; set; } | public Optional<string> Filename { get; set; } | ||||
| public Optional<string> Content { get; set; } | public Optional<string> Content { get; set; } | ||||
| public Optional<string> Nonce { get; set; } | public Optional<string> Nonce { get; set; } | ||||
| public Optional<bool> IsTTS { get; set; } | public Optional<bool> IsTTS { get; set; } | ||||
| public Optional<Embed> Embed { get; set; } | |||||
| public UploadFileParams(Stream file) | public UploadFileParams(Stream file) | ||||
| { | { | ||||
| @@ -23,12 +30,24 @@ namespace Discord.API.Rest | |||||
| { | { | ||||
| var d = new Dictionary<string, object>(); | var d = new Dictionary<string, object>(); | ||||
| d["file"] = new MultipartFile(File, Filename.GetValueOrDefault("unknown.dat")); | d["file"] = new MultipartFile(File, Filename.GetValueOrDefault("unknown.dat")); | ||||
| var payload = new Dictionary<string, object>(); | |||||
| if (Content.IsSpecified) | if (Content.IsSpecified) | ||||
| d["content"] = Content.Value; | |||||
| payload["content"] = Content.Value; | |||||
| if (IsTTS.IsSpecified) | if (IsTTS.IsSpecified) | ||||
| d["tts"] = IsTTS.Value.ToString(); | |||||
| payload["tts"] = IsTTS.Value.ToString(); | |||||
| if (Nonce.IsSpecified) | if (Nonce.IsSpecified) | ||||
| d["nonce"] = Nonce.Value; | |||||
| payload["nonce"] = Nonce.Value; | |||||
| if (Embed.IsSpecified) | |||||
| payload["embed"] = Embed.Value; | |||||
| var json = new StringBuilder(); | |||||
| using (var text = new StringWriter(json)) | |||||
| using (var writer = new JsonTextWriter(text)) | |||||
| _serializer.Serialize(writer, payload); | |||||
| d["payload_json"] = json.ToString(); | |||||
| return d; | return d; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,7 +1,10 @@ | |||||
| using System.Runtime.CompilerServices; | |||||
| using System.Runtime.CompilerServices; | |||||
| [assembly: InternalsVisibleTo("Discord.Net.Rpc")] | [assembly: InternalsVisibleTo("Discord.Net.Rpc")] | ||||
| [assembly: InternalsVisibleTo("Discord.Net.WebSocket")] | [assembly: InternalsVisibleTo("Discord.Net.WebSocket")] | ||||
| [assembly: InternalsVisibleTo("Discord.Net.Webhook")] | [assembly: InternalsVisibleTo("Discord.Net.Webhook")] | ||||
| [assembly: InternalsVisibleTo("Discord.Net.Commands")] | [assembly: InternalsVisibleTo("Discord.Net.Commands")] | ||||
| [assembly: InternalsVisibleTo("Discord.Net.Tests")] | |||||
| [assembly: InternalsVisibleTo("Discord.Net.Tests")] | |||||
| [assembly: TypeForwardedTo(typeof(Discord.EmbedBuilder))] | |||||
| [assembly: TypeForwardedTo(typeof(Discord.EmbedBuilderExtensions))] | |||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Logging; | |||||
| using Discord.Logging; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -125,6 +125,10 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public void Dispose() => Dispose(true); | public void Dispose() => Dispose(true); | ||||
| /// <inheritdoc /> | |||||
| public Task<int> GetRecommendedShardCountAsync(RequestOptions options = null) | |||||
| => ClientHelper.GetRecommendShardCountAsync(this, options); | |||||
| //IDiscordClient | //IDiscordClient | ||||
| ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; | ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; | ||||
| ISelfUser IDiscordClient.CurrentUser => CurrentUser; | ISelfUser IDiscordClient.CurrentUser => CurrentUser; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.API.Rest; | |||||
| using Discord.API.Rest; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -163,5 +163,11 @@ namespace Discord.Rest | |||||
| var models = await client.ApiClient.GetVoiceRegionsAsync(options).ConfigureAwait(false); | var models = await client.ApiClient.GetVoiceRegionsAsync(options).ConfigureAwait(false); | ||||
| return models.Select(x => RestVoiceRegion.Create(client, x)).FirstOrDefault(x => x.Id == id); | return models.Select(x => RestVoiceRegion.Create(client, x)).FirstOrDefault(x => x.Id == id); | ||||
| } | } | ||||
| public static async Task<int> GetRecommendShardCountAsync(BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| var response = await client.ApiClient.GetBotGatewayAsync(options).ConfigureAwait(false); | |||||
| return response.Shards; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,5 +1,4 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| #pragma warning disable CS0618 | |||||
| using Discord.API.Rest; | using Discord.API.Rest; | ||||
| using Discord.Net; | using Discord.Net; | ||||
| using Discord.Net.Converters; | using Discord.Net.Converters; | ||||
| @@ -70,12 +69,12 @@ namespace Discord.API | |||||
| { | { | ||||
| switch (tokenType) | switch (tokenType) | ||||
| { | { | ||||
| case default(TokenType): | |||||
| return token; | |||||
| case TokenType.Bot: | case TokenType.Bot: | ||||
| return $"Bot {token}"; | return $"Bot {token}"; | ||||
| case TokenType.Bearer: | case TokenType.Bearer: | ||||
| return $"Bearer {token}"; | return $"Bearer {token}"; | ||||
| case TokenType.User: | |||||
| return token; | |||||
| default: | default: | ||||
| throw new ArgumentException("Unknown OAuth token type", nameof(tokenType)); | throw new ArgumentException("Unknown OAuth token type", nameof(tokenType)); | ||||
| } | } | ||||
| @@ -113,7 +112,6 @@ namespace Discord.API | |||||
| { | { | ||||
| _loginCancelToken = new CancellationTokenSource(); | _loginCancelToken = new CancellationTokenSource(); | ||||
| AuthTokenType = TokenType.User; | |||||
| AuthToken = null; | AuthToken = null; | ||||
| await RequestQueue.SetCancelTokenAsync(_loginCancelToken.Token).ConfigureAwait(false); | await RequestQueue.SetCancelTokenAsync(_loginCancelToken.Token).ConfigureAwait(false); | ||||
| RestClient.SetCancelToken(_loginCancelToken.Token); | RestClient.SetCancelToken(_loginCancelToken.Token); | ||||
| @@ -172,8 +170,7 @@ namespace Discord.API | |||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.HeaderOnly = true; | options.HeaderOnly = true; | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| var request = new RestRequest(RestClient, method, endpoint, options); | var request = new RestRequest(RestClient, method, endpoint, options); | ||||
| await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | ||||
| @@ -187,8 +184,7 @@ namespace Discord.API | |||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.HeaderOnly = true; | options.HeaderOnly = true; | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| string json = payload != null ? SerializeJson(payload) : null; | string json = payload != null ? SerializeJson(payload) : null; | ||||
| var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | ||||
| @@ -203,8 +199,7 @@ namespace Discord.API | |||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.HeaderOnly = true; | options.HeaderOnly = true; | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); | var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); | ||||
| await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | ||||
| @@ -217,8 +212,7 @@ namespace Discord.API | |||||
| string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | ||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| var request = new RestRequest(RestClient, method, endpoint, options); | var request = new RestRequest(RestClient, method, endpoint, options); | ||||
| return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | ||||
| @@ -231,8 +225,7 @@ namespace Discord.API | |||||
| string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | ||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| string json = payload != null ? SerializeJson(payload) : null; | string json = payload != null ? SerializeJson(payload) : null; | ||||
| var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | ||||
| @@ -246,8 +239,7 @@ namespace Discord.API | |||||
| string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) | string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) | ||||
| { | { | ||||
| options = options ?? new RequestOptions(); | options = options ?? new RequestOptions(); | ||||
| options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||||
| options.IsClientBucket = AuthTokenType == TokenType.User; | |||||
| options.BucketId = bucketId; | |||||
| var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); | var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); | ||||
| return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | ||||
| @@ -277,6 +269,18 @@ namespace Discord.API | |||||
| await SendAsync("GET", () => "auth/login", new BucketIds(), options: options).ConfigureAwait(false); | await SendAsync("GET", () => "auth/login", new BucketIds(), options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| //Gateway | |||||
| public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) | |||||
| { | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetGatewayResponse>("GET", () => "gateway", new BucketIds(), options: options).ConfigureAwait(false); | |||||
| } | |||||
| public async Task<GetBotGatewayResponse> GetBotGatewayAsync(RequestOptions options = null) | |||||
| { | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetBotGatewayResponse>("GET", () => "gateway/bot", new BucketIds(), options: options).ConfigureAwait(false); | |||||
| } | |||||
| //Channels | //Channels | ||||
| public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) | public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -466,7 +470,7 @@ namespace Discord.API | |||||
| if (!args.Embed.IsSpecified || args.Embed.Value == null) | if (!args.Embed.IsSpecified || args.Embed.Value == null) | ||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content.Length > DiscordConfig.MaxMessageSize) | |||||
| if (args.Content?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| @@ -483,7 +487,7 @@ namespace Discord.API | |||||
| if (!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) | if (!args.Embeds.IsSpecified || args.Embeds.Value == null || args.Embeds.Value.Length == 0) | ||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content.Length > DiscordConfig.MaxMessageSize) | |||||
| if (args.Content?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| @@ -564,7 +568,7 @@ namespace Discord.API | |||||
| { | { | ||||
| if (!args.Embed.IsSpecified) | if (!args.Embed.IsSpecified) | ||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | |||||
| if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | ||||
| } | } | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.API.Rest; | |||||
| using Discord.API.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -170,17 +170,17 @@ namespace Discord.Rest | |||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| string filePath, string text, bool isTTS, RequestOptions options) | |||||
| string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| { | { | ||||
| string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
| using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
| return await SendFileAsync(channel, client, file, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| } | } | ||||
| #endif | #endif | ||||
| public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| { | { | ||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; | |||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed != null ? embed.ToModel() : Optional<API.Embed>.Unspecified }; | |||||
| var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Generic; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -10,10 +10,10 @@ namespace Discord.Rest | |||||
| new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| #endif | #endif | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | /// <summary> Gets a message from this message channel with the given id, or null if not found. </summary> | ||||
| Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null); | Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -37,7 +37,7 @@ namespace Discord.Rest | |||||
| public override async Task UpdateAsync(RequestOptions options = null) | public override async Task UpdateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| var model = await Discord.ApiClient.GetChannelAsync(Id, options).ConfigureAwait(false); | var model = await Discord.ApiClient.GetChannelAsync(Id, options).ConfigureAwait(false); | ||||
| Update(model); | |||||
| Update(model); | |||||
| } | } | ||||
| public Task CloseAsync(RequestOptions options = null) | public Task CloseAsync(RequestOptions options = null) | ||||
| => ChannelHelper.DeleteAsync(this, Discord, options); | => ChannelHelper.DeleteAsync(this, Discord, options); | ||||
| @@ -66,11 +66,11 @@ namespace Discord.Rest | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -122,11 +122,11 @@ namespace Discord.Rest | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Audio; | |||||
| using Discord.Audio; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -19,7 +19,7 @@ namespace Discord.Rest | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| public IReadOnlyCollection<RestGroupUser> Users => _users.ToReadOnlyCollection(); | public IReadOnlyCollection<RestGroupUser> Users => _users.ToReadOnlyCollection(); | ||||
| public IReadOnlyCollection<RestGroupUser> Recipients | |||||
| public IReadOnlyCollection<RestGroupUser> Recipients | |||||
| => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); | => _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1); | ||||
| internal RestGroupChannel(BaseDiscordClient discord, ulong id) | internal RestGroupChannel(BaseDiscordClient discord, ulong id) | ||||
| @@ -79,11 +79,11 @@ namespace Discord.Rest | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -132,11 +132,11 @@ namespace Discord.Rest | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -61,11 +61,11 @@ namespace Discord.Rest | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); | ||||
| @@ -123,18 +123,18 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | ||||
| } | } | ||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | |||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | |||||
| => EnterTypingState(options); | => EnterTypingState(options); | ||||
| //IGuildChannel | //IGuildChannel | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -21,7 +21,7 @@ namespace Discord.Rest | |||||
| { | { | ||||
| return new RestVirtualMessageChannel(discord, id); | return new RestVirtualMessageChannel(discord, id); | ||||
| } | } | ||||
| public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | ||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, options); | => ChannelHelper.GetMessageAsync(this, Discord, id, options); | ||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| @@ -36,11 +36,11 @@ namespace Discord.Rest | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -82,11 +82,11 @@ namespace Discord.Rest | |||||
| => await GetPinnedMessagesAsync(options); | => await GetPinnedMessagesAsync(options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options); | => await SendMessageAsync(text, isTTS, embed, options); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.API.Rest; | |||||
| using Discord.API.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -13,6 +13,9 @@ namespace Discord.Rest | |||||
| public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func, | public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| if (msg.Author.Id != client.CurrentUser.Id) | |||||
| throw new InvalidOperationException("Only the author of a message may change it."); | |||||
| var args = new MessageProperties(); | var args = new MessageProperties(); | ||||
| func(args); | func(args); | ||||
| var apiArgs = new API.Rest.ModifyMessageParams | var apiArgs = new API.Rest.ModifyMessageParams | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json; | |||||
| using System; | using System; | ||||
| namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
| @@ -19,10 +19,18 @@ namespace Discord.Net.Converters | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | ||||
| { | { | ||||
| T obj; | T obj; | ||||
| // custom converters need to be able to safely fail; move this check in here to prevent wasteful casting when parsing primitives | |||||
| if (_innerConverter != null) | if (_innerConverter != null) | ||||
| obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||||
| { | |||||
| object o = _innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||||
| if (o is Optional<T>) | |||||
| return o; | |||||
| obj = (T)o; | |||||
| } | |||||
| else | else | ||||
| obj = serializer.Deserialize<T>(reader); | obj = serializer.Deserialize<T>(reader); | ||||
| return new Optional<T>(obj); | return new Optional<T>(obj); | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
| @@ -11,13 +11,18 @@ namespace Discord.Net.Converters | |||||
| public override bool CanRead => true; | public override bool CanRead => true; | ||||
| public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
| // 1e13 unix ms = year 2286 | |||||
| // necessary to prevent discord.js from sending values in the e15 and overflowing a DTO | |||||
| private const long MaxSaneMs = 1_000_000_000_000_0; | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | ||||
| { | { | ||||
| // Discord doesn't validate if timestamps contain decimals or not | |||||
| if (reader.Value is double d) | |||||
| // Discord doesn't validate if timestamps contain decimals or not, and they also don't validate if timestamps are reasonably sized | |||||
| if (reader.Value is double d && d < MaxSaneMs) | |||||
| return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(d); | return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(d); | ||||
| long offset = (long)reader.Value; | |||||
| return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(offset); | |||||
| else if (reader.Value is long l && l < MaxSaneMs) | |||||
| return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(l); | |||||
| return Optional<DateTimeOffset>.Unspecified; | |||||
| } | } | ||||
| public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | ||||
| @@ -25,4 +30,4 @@ namespace Discord.Net.Converters | |||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,5 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Discord.Net.Converters; | |||||
| using Newtonsoft.Json; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Globalization; | using System.Globalization; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||
| using System; | using System; | ||||
| #if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
| @@ -86,7 +86,7 @@ namespace Discord.Net.Queue | |||||
| Debug.WriteLine($"[{id}] (!) 502"); | Debug.WriteLine($"[{id}] (!) 502"); | ||||
| #endif | #endif | ||||
| if ((request.Options.RetryMode & RetryMode.Retry502) == 0) | if ((request.Options.RetryMode & RetryMode.Retry502) == 0) | ||||
| throw new HttpException(HttpStatusCode.BadGateway, null); | |||||
| throw new HttpException(HttpStatusCode.BadGateway, request, null); | |||||
| continue; //Retry | continue; //Retry | ||||
| default: | default: | ||||
| @@ -106,7 +106,7 @@ namespace Discord.Net.Queue | |||||
| } | } | ||||
| catch { } | catch { } | ||||
| } | } | ||||
| throw new HttpException(response.StatusCode, code, reason); | |||||
| throw new HttpException(response.StatusCode, request, code, reason); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| @@ -163,7 +163,7 @@ namespace Discord.Net.Queue | |||||
| if (!isRateLimited) | if (!isRateLimited) | ||||
| throw new TimeoutException(); | throw new TimeoutException(); | ||||
| else | else | ||||
| throw new RateLimitedException(); | |||||
| throw new RateLimitedException(request); | |||||
| } | } | ||||
| lock (_lock) | lock (_lock) | ||||
| @@ -182,12 +182,12 @@ namespace Discord.Net.Queue | |||||
| } | } | ||||
| if ((request.Options.RetryMode & RetryMode.RetryRatelimit) == 0) | if ((request.Options.RetryMode & RetryMode.RetryRatelimit) == 0) | ||||
| throw new RateLimitedException(); | |||||
| throw new RateLimitedException(request); | |||||
| if (resetAt.HasValue) | if (resetAt.HasValue) | ||||
| { | { | ||||
| if (resetAt > timeoutAt) | if (resetAt > timeoutAt) | ||||
| throw new RateLimitedException(); | |||||
| throw new RateLimitedException(request); | |||||
| int millis = (int)Math.Ceiling((resetAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds); | int millis = (int)Math.Ceiling((resetAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds); | ||||
| #if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
| Debug.WriteLine($"[{id}] Sleeping {millis} ms (Pre-emptive)"); | Debug.WriteLine($"[{id}] Sleeping {millis} ms (Pre-emptive)"); | ||||
| @@ -198,7 +198,7 @@ namespace Discord.Net.Queue | |||||
| else | else | ||||
| { | { | ||||
| if ((timeoutAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds < 500.0) | if ((timeoutAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds < 500.0) | ||||
| throw new RateLimitedException(); | |||||
| throw new RateLimitedException(request); | |||||
| #if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
| Debug.WriteLine($"[{id}] Sleeping 500* ms (Pre-emptive)"); | Debug.WriteLine($"[{id}] Sleeping 500* ms (Pre-emptive)"); | ||||
| #endif | #endif | ||||
| @@ -1,11 +1,11 @@ | |||||
| using Discord.Net.Rest; | |||||
| using Discord.Net.Rest; | |||||
| using System; | using System; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Net.Queue | namespace Discord.Net.Queue | ||||
| { | { | ||||
| public class RestRequest | |||||
| public class RestRequest : IRequest | |||||
| { | { | ||||
| public IRestClient Client { get; } | public IRestClient Client { get; } | ||||
| public string Method { get; } | public string Method { get; } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Net.WebSockets; | |||||
| using Discord.Net.WebSockets; | |||||
| using System; | using System; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Threading; | using System.Threading; | ||||
| @@ -6,7 +6,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.Queue | namespace Discord.Net.Queue | ||||
| { | { | ||||
| public class WebSocketRequest | |||||
| public class WebSocketRequest : IRequest | |||||
| { | { | ||||
| public IWebSocketClient Client { get; } | public IWebSocketClient Client { get; } | ||||
| public string BucketId { get; } | public string BucketId { get; } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Generic; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Discord.API; | using Discord.API; | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.API; | |||||
| using Discord.API; | |||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -75,8 +75,8 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| if (_automaticShards) | if (_automaticShards) | ||||
| { | { | ||||
| var response = await ApiClient.GetBotGatewayAsync().ConfigureAwait(false); | |||||
| _shardIds = Enumerable.Range(0, response.Shards).ToArray(); | |||||
| var shardCount = await GetRecommendedShardCountAsync().ConfigureAwait(false); | |||||
| _shardIds = Enumerable.Range(0, shardCount).ToArray(); | |||||
| _totalShards = _shardIds.Length; | _totalShards = _shardIds.Length; | ||||
| _shards = new DiscordSocketClient[_shardIds.Length]; | _shards = new DiscordSocketClient[_shardIds.Length]; | ||||
| for (int i = 0; i < _shardIds.Length; i++) | for (int i = 0; i < _shardIds.Length; i++) | ||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Discord.API.Gateway; | using Discord.API.Gateway; | ||||
| using Discord.API.Rest; | using Discord.API.Rest; | ||||
| using Discord.Net.Queue; | using Discord.Net.Queue; | ||||
| @@ -207,18 +207,7 @@ namespace Discord.API | |||||
| await RequestQueue.SendAsync(new WebSocketRequest(WebSocketClient, null, bytes, true, options)).ConfigureAwait(false); | await RequestQueue.SendAsync(new WebSocketRequest(WebSocketClient, null, bytes, true, options)).ConfigureAwait(false); | ||||
| await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | ||||
| } | } | ||||
| //Gateway | |||||
| public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null) | |||||
| { | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetGatewayResponse>("GET", () => "gateway", new BucketIds(), options: options).ConfigureAwait(false); | |||||
| } | |||||
| public async Task<GetBotGatewayResponse> GetBotGatewayAsync(RequestOptions options = null) | |||||
| { | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<GetBotGatewayResponse>("GET", () => "gateway/bot", new BucketIds(), options: options).ConfigureAwait(false); | |||||
| } | |||||
| public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, RequestOptions options = null) | public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, RequestOptions options = null) | ||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| @@ -1,4 +1,3 @@ | |||||
| #pragma warning disable CS0618 | |||||
| using Discord.API; | using Discord.API; | ||||
| using Discord.API.Gateway; | using Discord.API.Gateway; | ||||
| using Discord.Logging; | using Discord.Logging; | ||||
| @@ -446,7 +445,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var model = data.Guilds[i]; | var model = data.Guilds[i]; | ||||
| var guild = AddGuild(model, state); | var guild = AddGuild(model, state); | ||||
| if (!guild.IsAvailable || ApiClient.AuthTokenType == TokenType.User) | |||||
| if (!guild.IsAvailable) | |||||
| unavailableGuilds++; | unavailableGuilds++; | ||||
| else | else | ||||
| await GuildAvailableAsync(guild).ConfigureAwait(false); | await GuildAvailableAsync(guild).ConfigureAwait(false); | ||||
| @@ -465,9 +464,6 @@ namespace Discord.WebSocket | |||||
| return; | return; | ||||
| } | } | ||||
| if (ApiClient.AuthTokenType == TokenType.User) | |||||
| await SyncGuildsAsync().ConfigureAwait(false); | |||||
| _lastGuildAvailableTime = Environment.TickCount; | _lastGuildAvailableTime = Environment.TickCount; | ||||
| _guildDownloadTask = WaitForGuildsAsync(_connection.CancelToken, _gatewayLogger) | _guildDownloadTask = WaitForGuildsAsync(_connection.CancelToken, _gatewayLogger) | ||||
| .ContinueWith(async x => | .ContinueWith(async x => | ||||
| @@ -542,8 +538,6 @@ namespace Discord.WebSocket | |||||
| var guild = AddGuild(data, State); | var guild = AddGuild(data, State); | ||||
| if (guild != null) | if (guild != null) | ||||
| { | { | ||||
| if (ApiClient.AuthTokenType == TokenType.User) | |||||
| await SyncGuildsAsync().ConfigureAwait(false); | |||||
| await TimedInvokeAsync(_joinedGuildEvent, nameof(JoinedGuild), guild).ConfigureAwait(false); | await TimedInvokeAsync(_joinedGuildEvent, nameof(JoinedGuild), guild).ConfigureAwait(false); | ||||
| } | } | ||||
| else | else | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -14,10 +14,10 @@ namespace Discord.WebSocket | |||||
| new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| #endif | #endif | ||||
| /// <summary> Sends a file to this text channel, with an optional caption. </summary> | /// <summary> Sends a file to this text channel, with an optional caption. </summary> | ||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); | |||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||||
| SocketMessage GetCachedMessage(ulong id); | SocketMessage GetCachedMessage(ulong id); | ||||
| /// <summary> Gets the last N messages from this message channel. </summary> | /// <summary> Gets the last N messages from this message channel. </summary> | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -15,10 +15,12 @@ namespace Discord.WebSocket | |||||
| public class SocketCategoryChannel : SocketGuildChannel, ICategoryChannel | public class SocketCategoryChannel : SocketGuildChannel, ICategoryChannel | ||||
| { | { | ||||
| public override IReadOnlyCollection<SocketGuildUser> Users | public override IReadOnlyCollection<SocketGuildUser> Users | ||||
| => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); | |||||
| => Guild.Users.Where(x => Permissions.GetValue( | |||||
| Permissions.ResolveChannel(Guild, x, this, Permissions.ResolveGuild(Guild, x)), | |||||
| ChannelPermission.ViewChannel)).ToImmutableArray(); | |||||
| public IReadOnlyCollection<SocketGuildChannel> Channels | public IReadOnlyCollection<SocketGuildChannel> Channels | ||||
| => Guild.Channels.Where(x => x.CategoryId == CategoryId).ToImmutableArray(); | |||||
| => Guild.Channels.Where(x => x.CategoryId == Id).ToImmutableArray(); | |||||
| internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | ||||
| : base(discord, id, guild) | : base(discord, id, guild) | ||||
| @@ -31,14 +33,28 @@ namespace Discord.WebSocket | |||||
| return entity; | return entity; | ||||
| } | } | ||||
| //Users | |||||
| public override SocketGuildUser GetUser(ulong id) | |||||
| { | |||||
| var user = Guild.GetUser(id); | |||||
| if (user != null) | |||||
| { | |||||
| var guildPerms = Permissions.ResolveGuild(Guild, user); | |||||
| var channelPerms = Permissions.ResolveChannel(Guild, user, this, guildPerms); | |||||
| if (Permissions.GetValue(channelPerms, ChannelPermission.ViewChannel)) | |||||
| return user; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| private string DebuggerDisplay => $"{Name} ({Id}, Category)"; | private string DebuggerDisplay => $"{Name} ({Id}, Category)"; | ||||
| internal new SocketCategoryChannel Clone() => MemberwiseClone() as SocketCategoryChannel; | internal new SocketCategoryChannel Clone() => MemberwiseClone() as SocketCategoryChannel; | ||||
| // IGuildChannel | // IGuildChannel | ||||
| IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| => throw new NotSupportedException(); | |||||
| => ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable(); | |||||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => throw new NotSupportedException(); | |||||
| => Task.FromResult<IGuildUser>(GetUser(id)); | |||||
| Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | ||||
| => throw new NotSupportedException(); | => throw new NotSupportedException(); | ||||
| Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | ||||
| @@ -46,8 +62,8 @@ namespace Discord.WebSocket | |||||
| //IChannel | //IChannel | ||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| => throw new NotSupportedException(); | |||||
| => ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable(); | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => throw new NotSupportedException(); | |||||
| => Task.FromResult<IUser>(GetUser(id)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -70,11 +70,11 @@ namespace Discord.WebSocket | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -113,7 +113,7 @@ namespace Discord.WebSocket | |||||
| //IPrivateChannel | //IPrivateChannel | ||||
| IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient); | IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient); | ||||
| //IMessageChannel | //IMessageChannel | ||||
| async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| @@ -131,11 +131,11 @@ namespace Discord.WebSocket | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Audio; | |||||
| using Discord.Audio; | |||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| @@ -61,7 +61,7 @@ namespace Discord.WebSocket | |||||
| users[models[i].Id] = SocketGroupUser.Create(this, state, models[i]); | users[models[i].Id] = SocketGroupUser.Create(this, state, models[i]); | ||||
| _users = users; | _users = users; | ||||
| } | } | ||||
| public Task LeaveAsync(RequestOptions options = null) | public Task LeaveAsync(RequestOptions options = null) | ||||
| => ChannelHelper.DeleteAsync(this, Discord, options); | => ChannelHelper.DeleteAsync(this, Discord, options); | ||||
| @@ -98,11 +98,11 @@ namespace Discord.WebSocket | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -195,11 +195,11 @@ namespace Discord.WebSocket | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -16,7 +16,7 @@ namespace Discord.WebSocket | |||||
| private readonly MessageCache _messages; | private readonly MessageCache _messages; | ||||
| public string Topic { get; private set; } | public string Topic { get; private set; } | ||||
| private bool _nsfw; | private bool _nsfw; | ||||
| public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); | public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); | ||||
| @@ -24,9 +24,9 @@ namespace Discord.WebSocket | |||||
| public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>(); | public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>(); | ||||
| public override IReadOnlyCollection<SocketGuildUser> Users | public override IReadOnlyCollection<SocketGuildUser> Users | ||||
| => Guild.Users.Where(x => Permissions.GetValue( | => Guild.Users.Where(x => Permissions.GetValue( | ||||
| Permissions.ResolveChannel(Guild, x, this, Permissions.ResolveGuild(Guild, x)), | |||||
| Permissions.ResolveChannel(Guild, x, this, Permissions.ResolveGuild(Guild, x)), | |||||
| ChannelPermission.ViewChannel)).ToImmutableArray(); | ChannelPermission.ViewChannel)).ToImmutableArray(); | ||||
| internal SocketTextChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | internal SocketTextChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | ||||
| : base(discord, id, guild) | : base(discord, id, guild) | ||||
| { | { | ||||
| @@ -78,11 +78,11 @@ namespace Discord.WebSocket | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | ||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); | |||||
| #endif | #endif | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); | |||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); | ||||
| @@ -155,14 +155,14 @@ namespace Discord.WebSocket | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| #endif | #endif | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | ||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | ||||
| => EnterTypingState(options); | => EnterTypingState(options); | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,3 @@ | |||||
| #pragma warning disable CS0618 | |||||
| using Discord.Audio; | using Discord.Audio; | ||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using System; | using System; | ||||
| @@ -64,7 +63,7 @@ namespace Discord.WebSocket | |||||
| public Task DownloaderPromise => _downloaderPromise.Task; | public Task DownloaderPromise => _downloaderPromise.Task; | ||||
| public IAudioClient AudioClient => _audioClient; | public IAudioClient AudioClient => _audioClient; | ||||
| public SocketTextChannel DefaultChannel => TextChannels | public SocketTextChannel DefaultChannel => TextChannels | ||||
| .Where(c => CurrentUser.GetPermissions(c).ReadMessages) | |||||
| .Where(c => CurrentUser.GetPermissions(c).ViewChannel) | |||||
| .OrderBy(c => c.Position) | .OrderBy(c => c.Position) | ||||
| .FirstOrDefault(); | .FirstOrDefault(); | ||||
| public SocketVoiceChannel AFKChannel | public SocketVoiceChannel AFKChannel | ||||
| @@ -192,12 +191,9 @@ namespace Discord.WebSocket | |||||
| _syncPromise = new TaskCompletionSource<bool>(); | _syncPromise = new TaskCompletionSource<bool>(); | ||||
| _downloaderPromise = new TaskCompletionSource<bool>(); | _downloaderPromise = new TaskCompletionSource<bool>(); | ||||
| if (Discord.ApiClient.AuthTokenType != TokenType.User) | |||||
| { | |||||
| var _ = _syncPromise.TrySetResultAsync(true); | |||||
| /*if (!model.Large) | |||||
| _ = _downloaderPromise.TrySetResultAsync(true);*/ | |||||
| } | |||||
| var _ = _syncPromise.TrySetResultAsync(true); | |||||
| /*if (!model.Large) | |||||
| _ = _downloaderPromise.TrySetResultAsync(true);*/ | |||||
| } | } | ||||
| internal void Update(ClientState state, Model model) | internal void Update(ClientState state, Model model) | ||||
| { | { | ||||
| @@ -696,7 +692,6 @@ namespace Discord.WebSocket | |||||
| => Task.FromResult<IGuildUser>(CurrentUser); | => Task.FromResult<IGuildUser>(CurrentUser); | ||||
| Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) | Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IGuildUser>(Owner); | => Task.FromResult<IGuildUser>(Owner); | ||||
| Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } | |||||
| async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | ||||
| => await GetWebhookAsync(id, options); | => await GetWebhookAsync(id, options); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -1,9 +1,30 @@ | |||||
| namespace Discord.WebSocket | |||||
| namespace Discord.WebSocket | |||||
| { | { | ||||
| internal static class EntityExtensions | internal static class EntityExtensions | ||||
| { | { | ||||
| public static IActivity ToEntity(this API.Game model) | public static IActivity ToEntity(this API.Game model) | ||||
| { | { | ||||
| // Spotify Game | |||||
| if (model.SyncId.IsSpecified) | |||||
| { | |||||
| var assets = model.Assets.GetValueOrDefault()?.ToEntity(); | |||||
| string albumText = assets?[1]?.Text; | |||||
| string albumArtId = assets?[1]?.ImageId?.Replace("spotify:",""); | |||||
| var timestamps = model.Timestamps.IsSpecified ? model.Timestamps.Value.ToEntity() : null; | |||||
| return new SpotifyGame | |||||
| { | |||||
| Name = model.Name, | |||||
| SessionId = model.SessionId.GetValueOrDefault(), | |||||
| SyncId = model.SyncId.Value, | |||||
| AlbumTitle = albumText, | |||||
| TrackTitle = model.Details.GetValueOrDefault(), | |||||
| Artists = model.State.GetValueOrDefault()?.Split(';'), | |||||
| Duration = timestamps?.End - timestamps?.Start, | |||||
| AlbumArt = albumArtId != null ? CDN.GetSpotifyAlbumArtUrl(albumArtId) : null, | |||||
| Type = ActivityType.Listening | |||||
| }; | |||||
| } | |||||
| // Rich Game | // Rich Game | ||||
| if (model.ApplicationId.IsSpecified) | if (model.ApplicationId.IsSpecified) | ||||
| { | { | ||||
| @@ -34,7 +55,7 @@ | |||||
| } | } | ||||
| // (Small, Large) | // (Small, Large) | ||||
| public static GameAsset[] ToEntity(this API.GameAssets model, ulong appId) | |||||
| public static GameAsset[] ToEntity(this API.GameAssets model, ulong? appId = null) | |||||
| { | { | ||||
| return new GameAsset[] | return new GameAsset[] | ||||
| { | { | ||||
| @@ -56,7 +77,7 @@ | |||||
| public static GameParty ToEntity(this API.GameParty model) | public static GameParty ToEntity(this API.GameParty model) | ||||
| { | { | ||||
| // Discord will probably send bad data since they don't validate anything | // Discord will probably send bad data since they don't validate anything | ||||
| int current = 0, cap = 0; | |||||
| long current = 0, cap = 0; | |||||
| if (model.Size?.Length == 2) | if (model.Size?.Length == 2) | ||||
| { | { | ||||
| current = model.Size[0]; | current = model.Size[0]; | ||||
| @@ -49,7 +49,7 @@ namespace Discord.Webhook | |||||
| if (username != null) | if (username != null) | ||||
| args.Username = username; | args.Username = username; | ||||
| if (avatarUrl != null) | if (avatarUrl != null) | ||||
| args.AvatarUrl = username; | |||||
| args.AvatarUrl = avatarUrl; | |||||
| if (embeds != null) | if (embeds != null) | ||||
| args.Embeds = embeds.Select(x => x.ToModel()).ToArray(); | args.Embeds = embeds.Select(x => x.ToModel()).ToArray(); | ||||
| var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options).ConfigureAwait(false); | var msg = await client.ApiClient.UploadWebhookFileAsync(client.Webhook.Id, args, options).ConfigureAwait(false); | ||||
| @@ -2,7 +2,7 @@ | |||||
| <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | ||||
| <metadata> | <metadata> | ||||
| <id>Discord.Net</id> | <id>Discord.Net</id> | ||||
| <version>2.0.0-beta$suffix$</version> | |||||
| <version>2.0.0-beta2$suffix$</version> | |||||
| <title>Discord.Net</title> | <title>Discord.Net</title> | ||||
| <authors>Discord.Net Contributors</authors> | <authors>Discord.Net Contributors</authors> | ||||
| <owners>RogueException</owners> | <owners>RogueException</owners> | ||||
| @@ -13,25 +13,25 @@ | |||||
| <requireLicenseAcceptance>false</requireLicenseAcceptance> | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||||
| <dependencies> | <dependencies> | ||||
| <group targetFramework="net45"> | <group targetFramework="net45"> | ||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta2$suffix$" /> | |||||
| </group> | </group> | ||||
| <group targetFramework="netstandard1.1"> | <group targetFramework="netstandard1.1"> | ||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta2$suffix$" /> | |||||
| </group> | </group> | ||||
| <group targetFramework="netstandard1.3"> | <group targetFramework="netstandard1.3"> | ||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta$suffix$" /> | |||||
| <dependency id="Discord.Net.Core" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Rest" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.WebSocket" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Commands" version="2.0.0-beta2$suffix$" /> | |||||
| <dependency id="Discord.Net.Webhook" version="2.0.0-beta2$suffix$" /> | |||||
| </group> | </group> | ||||
| </dependencies> | </dependencies> | ||||
| </metadata> | </metadata> | ||||
| @@ -10,6 +10,9 @@ | |||||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| </Content> | </Content> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | |||||
| <None Include="Tests.Permissions.cs" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | <ItemGroup> | ||||
| <ProjectReference Include="../../src/Discord.Net.Commands/Discord.Net.Commands.csproj" /> | <ProjectReference Include="../../src/Discord.Net.Commands/Discord.Net.Commands.csproj" /> | ||||
| <ProjectReference Include="../../src/Discord.Net.Core/Discord.Net.Core.csproj" /> | <ProjectReference Include="../../src/Discord.Net.Core/Discord.Net.Core.csproj" /> | ||||
| @@ -1,10 +1,10 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Xunit; | using Xunit; | ||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public partial class Tests | |||||
| public class ChannelPermissionsTests | |||||
| { | { | ||||
| [Fact] | [Fact] | ||||
| public Task TestChannelPermission() | public Task TestChannelPermission() | ||||
| @@ -1,10 +1,10 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Xunit; | using Xunit; | ||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public partial class Tests | |||||
| public class GuidPermissionsTests | |||||
| { | { | ||||
| [Fact] | [Fact] | ||||
| public Task TestGuildPermission() | public Task TestGuildPermission() | ||||
| @@ -0,0 +1,706 @@ | |||||
| using System; | |||||
| using System.Threading.Tasks; | |||||
| using Xunit; | |||||
| namespace Discord | |||||
| { | |||||
| public class PermissionsTests | |||||
| { | |||||
| private void TestHelper(ChannelPermissions value, ChannelPermission permission, bool expected = false) | |||||
| => TestHelper(value.RawValue, (ulong)permission, expected); | |||||
| private void TestHelper(GuildPermissions value, GuildPermission permission, bool expected = false) | |||||
| => TestHelper(value.RawValue, (ulong)permission, expected); | |||||
| /// <summary> | |||||
| /// Tests the flag of the given permissions value to the expected output | |||||
| /// and then tries to toggle the flag on and off | |||||
| /// </summary> | |||||
| /// <param name="rawValue"></param> | |||||
| /// <param name="flagValue"></param> | |||||
| /// <param name="expected"></param> | |||||
| private void TestHelper(ulong rawValue, ulong flagValue, bool expected) | |||||
| { | |||||
| Assert.Equal(expected, Permissions.GetValue(rawValue, flagValue)); | |||||
| // check that toggling the bit works | |||||
| Permissions.UnsetFlag(ref rawValue, flagValue); | |||||
| Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); | |||||
| Permissions.SetFlag(ref rawValue, flagValue); | |||||
| Assert.Equal(true, Permissions.GetValue(rawValue, flagValue)); | |||||
| // do the same, but with the SetValue method | |||||
| Permissions.SetValue(ref rawValue, true, flagValue); | |||||
| Assert.Equal(true, Permissions.GetValue(rawValue, flagValue)); | |||||
| Permissions.SetValue(ref rawValue, false, flagValue); | |||||
| Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests that flag of the given permissions value to be the expected output | |||||
| /// and then tries cycling through the states of the allow and deny values | |||||
| /// for that flag | |||||
| /// </summary> | |||||
| /// <param name="value"></param> | |||||
| /// <param name="flag"></param> | |||||
| /// <param name="expected"></param> | |||||
| private void TestHelper(OverwritePermissions value, ChannelPermission flag, PermValue expected) | |||||
| { | |||||
| // check that the value matches | |||||
| Assert.Equal(expected, Permissions.GetValue(value.AllowValue, value.DenyValue, flag)); | |||||
| // check toggling bits for both allow and deny | |||||
| // have to make copies to get around read only property | |||||
| ulong allow = value.AllowValue; | |||||
| ulong deny = value.DenyValue; | |||||
| // both unset should be inherit | |||||
| Permissions.UnsetFlag(ref allow, (ulong)flag); | |||||
| Permissions.UnsetFlag(ref deny, (ulong)flag); | |||||
| Assert.Equal(PermValue.Inherit, Permissions.GetValue(allow, deny, flag)); | |||||
| // allow set should be allow | |||||
| Permissions.SetFlag(ref allow, (ulong)flag); | |||||
| Permissions.UnsetFlag(ref deny, (ulong)flag); | |||||
| Assert.Equal(PermValue.Allow, Permissions.GetValue(allow, deny, flag)); | |||||
| // deny should be deny | |||||
| Permissions.UnsetFlag(ref allow, (ulong)flag); | |||||
| Permissions.SetFlag(ref deny, (ulong)flag); | |||||
| Assert.Equal(PermValue.Deny, Permissions.GetValue(allow, deny, flag)); | |||||
| // allow takes precedence | |||||
| Permissions.SetFlag(ref allow, (ulong)flag); | |||||
| Permissions.SetFlag(ref deny, (ulong)flag); | |||||
| Assert.Equal(PermValue.Allow, Permissions.GetValue(allow, deny, flag)); | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Tests that text channel permissions get the right value | |||||
| /// from the Has method. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasChannelPermissionText() | |||||
| { | |||||
| var value = ChannelPermissions.Text; | |||||
| // check that the result of GetValue matches for all properties of text channel | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, true); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, true); | |||||
| TestHelper(value, ChannelPermission.AddReactions, true); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, true); | |||||
| TestHelper(value, ChannelPermission.SendMessages, true); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, true); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, true); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, true); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, true); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, true); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, true); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, true); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, true); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, true); | |||||
| TestHelper(value, ChannelPermission.Connect, false); | |||||
| TestHelper(value, ChannelPermission.Speak, false); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, false); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, false); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, false); | |||||
| TestHelper(value, ChannelPermission.UseVAD, false); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Tests that no channel permissions get the right value | |||||
| /// from the Has method. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasChannelPermissionNone() | |||||
| { | |||||
| // check that none will fail all | |||||
| var value = ChannelPermissions.None; | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, false); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, false); | |||||
| TestHelper(value, ChannelPermission.AddReactions, false); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, false); | |||||
| TestHelper(value, ChannelPermission.SendMessages, false); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, false); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, false); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, false); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, false); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, false); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, false); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, false); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, false); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, false); | |||||
| TestHelper(value, ChannelPermission.Connect, false); | |||||
| TestHelper(value, ChannelPermission.Speak, false); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, false); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, false); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, false); | |||||
| TestHelper(value, ChannelPermission.UseVAD, false); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Tests that the dm channel permissions get the right value | |||||
| /// from the Has method. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasChannelPermissionDM() | |||||
| { | |||||
| // check that none will fail all | |||||
| var value = ChannelPermissions.DM; | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, false); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, false); | |||||
| TestHelper(value, ChannelPermission.AddReactions, false); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, true); | |||||
| TestHelper(value, ChannelPermission.SendMessages, true); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, false); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, false); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, true); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, true); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, true); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, false); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, true); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, false); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, false); | |||||
| TestHelper(value, ChannelPermission.Connect, true); | |||||
| TestHelper(value, ChannelPermission.Speak, true); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, false); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, false); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, false); | |||||
| TestHelper(value, ChannelPermission.UseVAD, true); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Tests that the group channel permissions get the right value | |||||
| /// from the Has method. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasChannelPermissionGroup() | |||||
| { | |||||
| var value = ChannelPermissions.Group; | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, false); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, false); | |||||
| TestHelper(value, ChannelPermission.AddReactions, false); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, false); | |||||
| TestHelper(value, ChannelPermission.SendMessages, true); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, true); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, false); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, true); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, true); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, false); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, false); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, false); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, false); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, false); | |||||
| TestHelper(value, ChannelPermission.Connect, true); | |||||
| TestHelper(value, ChannelPermission.Speak, true); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, false); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, false); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, false); | |||||
| TestHelper(value, ChannelPermission.UseVAD, true); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Tests that the voice channel permissions get the right value | |||||
| /// from the Has method. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasChannelPermissionVoice() | |||||
| { | |||||
| // make a flag with all possible values for Voice channel permissions | |||||
| var value = ChannelPermissions.Voice; | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, true); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, true); | |||||
| TestHelper(value, ChannelPermission.AddReactions, false); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, false); | |||||
| TestHelper(value, ChannelPermission.SendMessages, false); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, false); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, false); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, false); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, false); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, false); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, false); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, false); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, true); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, false); | |||||
| TestHelper(value, ChannelPermission.Connect, true); | |||||
| TestHelper(value, ChannelPermission.Speak, true); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, true); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, true); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, true); | |||||
| TestHelper(value, ChannelPermission.UseVAD, true); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Test that that the Has method of <see cref="Discord.GuildPermissions"/> | |||||
| /// returns the correct value when no permissions are set. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasGuildPermissionNone() | |||||
| { | |||||
| var value = GuildPermissions.None; | |||||
| TestHelper(value, GuildPermission.CreateInstantInvite, false); | |||||
| TestHelper(value, GuildPermission.KickMembers, false); | |||||
| TestHelper(value, GuildPermission.BanMembers, false); | |||||
| TestHelper(value, GuildPermission.Administrator, false); | |||||
| TestHelper(value, GuildPermission.ManageChannels, false); | |||||
| TestHelper(value, GuildPermission.ManageGuild, false); | |||||
| TestHelper(value, GuildPermission.AddReactions, false); | |||||
| TestHelper(value, GuildPermission.ViewAuditLog, false); | |||||
| TestHelper(value, GuildPermission.ReadMessages, false); | |||||
| TestHelper(value, GuildPermission.SendMessages, false); | |||||
| TestHelper(value, GuildPermission.SendTTSMessages, false); | |||||
| TestHelper(value, GuildPermission.ManageMessages, false); | |||||
| TestHelper(value, GuildPermission.EmbedLinks, false); | |||||
| TestHelper(value, GuildPermission.AttachFiles, false); | |||||
| TestHelper(value, GuildPermission.ReadMessageHistory, false); | |||||
| TestHelper(value, GuildPermission.MentionEveryone, false); | |||||
| TestHelper(value, GuildPermission.UseExternalEmojis, false); | |||||
| TestHelper(value, GuildPermission.Connect, false); | |||||
| TestHelper(value, GuildPermission.Speak, false); | |||||
| TestHelper(value, GuildPermission.MuteMembers, false); | |||||
| TestHelper(value, GuildPermission.MoveMembers, false); | |||||
| TestHelper(value, GuildPermission.UseVAD, false); | |||||
| TestHelper(value, GuildPermission.ChangeNickname, false); | |||||
| TestHelper(value, GuildPermission.ManageNicknames, false); | |||||
| TestHelper(value, GuildPermission.ManageRoles, false); | |||||
| TestHelper(value, GuildPermission.ManageWebhooks, false); | |||||
| TestHelper(value, GuildPermission.ManageEmojis, false); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Test that that the Has method of <see cref="Discord.GuildPermissions"/> | |||||
| /// returns the correct value when all permissions are set. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasGuildPermissionAll() | |||||
| { | |||||
| var value = GuildPermissions.All; | |||||
| TestHelper(value, GuildPermission.CreateInstantInvite, true); | |||||
| TestHelper(value, GuildPermission.KickMembers, true); | |||||
| TestHelper(value, GuildPermission.BanMembers, true); | |||||
| TestHelper(value, GuildPermission.Administrator, true); | |||||
| TestHelper(value, GuildPermission.ManageChannels, true); | |||||
| TestHelper(value, GuildPermission.ManageGuild, true); | |||||
| TestHelper(value, GuildPermission.AddReactions, true); | |||||
| TestHelper(value, GuildPermission.ViewAuditLog, true); | |||||
| TestHelper(value, GuildPermission.ReadMessages, true); | |||||
| TestHelper(value, GuildPermission.SendMessages, true); | |||||
| TestHelper(value, GuildPermission.SendTTSMessages, true); | |||||
| TestHelper(value, GuildPermission.ManageMessages, true); | |||||
| TestHelper(value, GuildPermission.EmbedLinks, true); | |||||
| TestHelper(value, GuildPermission.AttachFiles, true); | |||||
| TestHelper(value, GuildPermission.ReadMessageHistory, true); | |||||
| TestHelper(value, GuildPermission.MentionEveryone, true); | |||||
| TestHelper(value, GuildPermission.UseExternalEmojis, true); | |||||
| TestHelper(value, GuildPermission.Connect, true); | |||||
| TestHelper(value, GuildPermission.Speak, true); | |||||
| TestHelper(value, GuildPermission.MuteMembers, true); | |||||
| TestHelper(value, GuildPermission.MoveMembers, true); | |||||
| TestHelper(value, GuildPermission.UseVAD, true); | |||||
| TestHelper(value, GuildPermission.ChangeNickname, true); | |||||
| TestHelper(value, GuildPermission.ManageNicknames, true); | |||||
| TestHelper(value, GuildPermission.ManageRoles, true); | |||||
| TestHelper(value, GuildPermission.ManageWebhooks, true); | |||||
| TestHelper(value, GuildPermission.ManageEmojis, true); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Tests for the <see cref="Discord.Permissions"/> class. | |||||
| /// | |||||
| /// Test that that the Has method of <see cref="Discord.GuildPermissions"/> | |||||
| /// returns the correct value when webhook permissions are set. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestPermissionsHasGuildPermissionWebhook() | |||||
| { | |||||
| var value = GuildPermissions.Webhook; | |||||
| TestHelper(value, GuildPermission.CreateInstantInvite, false); | |||||
| TestHelper(value, GuildPermission.KickMembers, false); | |||||
| TestHelper(value, GuildPermission.BanMembers, false); | |||||
| TestHelper(value, GuildPermission.Administrator, false); | |||||
| TestHelper(value, GuildPermission.ManageChannels, false); | |||||
| TestHelper(value, GuildPermission.ManageGuild, false); | |||||
| TestHelper(value, GuildPermission.AddReactions, false); | |||||
| TestHelper(value, GuildPermission.ViewAuditLog, false); | |||||
| TestHelper(value, GuildPermission.ReadMessages, false); | |||||
| TestHelper(value, GuildPermission.SendMessages, true); | |||||
| TestHelper(value, GuildPermission.SendTTSMessages, true); | |||||
| TestHelper(value, GuildPermission.ManageMessages, false); | |||||
| TestHelper(value, GuildPermission.EmbedLinks, true); | |||||
| TestHelper(value, GuildPermission.AttachFiles, true); | |||||
| TestHelper(value, GuildPermission.ReadMessageHistory, false); | |||||
| TestHelper(value, GuildPermission.MentionEveryone, false); | |||||
| TestHelper(value, GuildPermission.UseExternalEmojis, false); | |||||
| TestHelper(value, GuildPermission.Connect, false); | |||||
| TestHelper(value, GuildPermission.Speak, false); | |||||
| TestHelper(value, GuildPermission.MuteMembers, false); | |||||
| TestHelper(value, GuildPermission.MoveMembers, false); | |||||
| TestHelper(value, GuildPermission.UseVAD, false); | |||||
| TestHelper(value, GuildPermission.ChangeNickname, false); | |||||
| TestHelper(value, GuildPermission.ManageNicknames, false); | |||||
| TestHelper(value, GuildPermission.ManageRoles, false); | |||||
| TestHelper(value, GuildPermission.ManageWebhooks, false); | |||||
| TestHelper(value, GuildPermission.ManageEmojis, false); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Test <see cref="Discord.OverwritePermissions"/> | |||||
| /// for when all text permissions are allowed and denied | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestOverwritePermissionsText() | |||||
| { | |||||
| // allow all for text channel | |||||
| var value = new OverwritePermissions(ChannelPermissions.Text.RawValue, ChannelPermissions.None.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Inherit); | |||||
| value = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Text.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Inherit); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Test <see cref="Discord.OverwritePermissions"/> | |||||
| /// for when none of the permissions are set. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestOverwritePermissionsNone() | |||||
| { | |||||
| // allow all for text channel | |||||
| var value = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.None.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Inherit); | |||||
| value = new OverwritePermissions(); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Inherit); | |||||
| value = OverwritePermissions.InheritAll; | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Inherit); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Test <see cref="Discord.OverwritePermissions"/> | |||||
| /// for when all dm permissions are allowed and denied | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestOverwritePermissionsDM() | |||||
| { | |||||
| // allow all for text channel | |||||
| var value = new OverwritePermissions(ChannelPermissions.DM.RawValue, ChannelPermissions.None.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Allow); | |||||
| value = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.DM.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Deny); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Test <see cref="Discord.OverwritePermissions"/> | |||||
| /// for when all group permissions are allowed and denied | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestOverwritePermissionsGroup() | |||||
| { | |||||
| // allow all for group channels | |||||
| var value = new OverwritePermissions(ChannelPermissions.Group.RawValue, ChannelPermissions.None.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Allow); | |||||
| value = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Group.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Deny); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| /// <summary> | |||||
| /// Test <see cref="Discord.OverwritePermissions"/> | |||||
| /// for when all group permissions are allowed and denied | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| [Fact] | |||||
| public Task TestOverwritePermissionsVoice() | |||||
| { | |||||
| // allow all for group channels | |||||
| var value = new OverwritePermissions(ChannelPermissions.Voice.RawValue, ChannelPermissions.None.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Allow); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Allow); | |||||
| value = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Voice.RawValue); | |||||
| TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageChannels, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.EmbedLinks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.AttachFiles, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ReadMessageHistory, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.MentionEveryone, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.UseExternalEmojis, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.ManageRoles, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.ManageWebhooks, PermValue.Inherit); | |||||
| TestHelper(value, ChannelPermission.Connect, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.Speak, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MuteMembers, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.DeafenMembers, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.MoveMembers, PermValue.Deny); | |||||
| TestHelper(value, ChannelPermission.UseVAD, PermValue.Deny); | |||||
| return Task.CompletedTask; | |||||
| } | |||||
| } | |||||
| } | |||||