diff --git a/Discord.Net.targets b/Discord.Net.targets index 958b2053f5..079ec77492 100644 --- a/Discord.Net.targets +++ b/Discord.Net.targets @@ -16,13 +16,7 @@ $(VersionSuffix)-$(BuildNumber) build-$(BuildNumber) - - - $(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET - - - $(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE - + $(NoWarn);CS1573;CS1591 true diff --git a/docs/_template/light-dark-theme/styles/light.css b/docs/_template/light-dark-theme/styles/light.css index 18660dfe69..a602274a2a 100644 --- a/docs/_template/light-dark-theme/styles/light.css +++ b/docs/_template/light-dark-theme/styles/light.css @@ -10,11 +10,24 @@ body { overflow: visible; } +/* links */ + +a:active, a:hover, a:visited { + color: #0078d7; +} + +a { + color: #0050c5; + cursor: pointer; + text-decoration: none; + word-wrap: break-word; +} + /* code */ code { - color: #222f3d; - background-color: #f9f9f9; + color: #3b5269; + background-color: #ececec; border-radius: 4px; padding: 3px 7px; } diff --git a/docs/docfx.json b/docs/docfx.json index cb6a363607..663a49cdaf 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -43,7 +43,7 @@ "globalMetadata": { "_appTitle": "Discord.Net Documentation", "_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta", - "_enableSearch": true, + "_enableSearch": true }, "noLangKeyword": false, "xrefService": [ diff --git a/docs/guides/commands/samples/command_handler.cs b/docs/guides/commands/samples/command_handler.cs index 470be27073..d63d550ee0 100644 --- a/docs/guides/commands/samples/command_handler.cs +++ b/docs/guides/commands/samples/command_handler.cs @@ -1,12 +1,14 @@ -public class CommandHandle +public class CommandHandler { private readonly DiscordSocketClient _client; private readonly CommandService _commands; + private readonly IServiceProvider _services; - public CommandHandle(DiscordSocketClient client) + public CommandHandler(IServiceProvider services) { - _client = client; - _commands = new CommandService(); + _services = services; + _commands = services.GetRequiredService(); + _client = services.GetRequiredService(); } public async Task InstallCommandsAsync() diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs b/experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs similarity index 100% rename from src/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs rename to experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs diff --git a/samples/02_commands_framework/02_commands_framework.csproj b/samples/02_commands_framework/02_commands_framework.csproj index 77fdc65e12..f479ee0b0e 100644 --- a/samples/02_commands_framework/02_commands_framework.csproj +++ b/samples/02_commands_framework/02_commands_framework.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj index 8ab398ff56..5da3d506d0 100644 --- a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj +++ b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj @@ -7,7 +7,7 @@ netstandard1.3 - + diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 02011bd9f8..49df92a31c 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using System.Threading.Tasks; @@ -13,8 +14,7 @@ private enum ParserPart Parameter, QuotedParameter } - - public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary aliasMap) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); @@ -24,7 +24,27 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma var argList = ImmutableArray.CreateBuilder(); var paramList = ImmutableArray.CreateBuilder(); bool isEscaping = false; - char c; + char c, matchQuote = '\0'; + + // local helper functions + bool IsOpenQuote(IReadOnlyDictionary dict, char ch) + { + // return if the key is contained in the dictionary if it is populated + if (dict.Count != 0) + return dict.ContainsKey(ch); + // or otherwise if it is the default double quote + return c == '\"'; + } + + char GetMatch(IReadOnlyDictionary dict, char ch) + { + // get the corresponding value for the key, if it exists + // and if the dictionary is populated + if (dict.Count != 0 && dict.TryGetValue(c, out var value)) + return value; + // or get the default pair of the default double quote + return '\"'; + } for (int curPos = startPos; curPos <= endPos; curPos++) { @@ -74,9 +94,11 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma argBuilder.Append(c); continue; } - if (c == '\"') + + if (IsOpenQuote(aliasMap, c)) { curPart = ParserPart.QuotedParameter; + matchQuote = GetMatch(aliasMap, c); continue; } curPart = ParserPart.Parameter; @@ -97,7 +119,7 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma } else if (curPart == ParserPart.QuotedParameter) { - if (c == '\"') + if (c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 24db6e9b50..0c23f79d00 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -38,6 +38,7 @@ public class CommandService internal readonly RunMode _defaultRunMode; internal readonly Logger _cmdLogger; internal readonly LogManager _logManager; + internal readonly IReadOnlyDictionary _quotationMarkAliasMap; /// /// Represents all modules loaded within . @@ -73,6 +74,7 @@ public CommandService(CommandServiceConfig config) _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; + _quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary()).ToImmutableDictionary(); if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); @@ -93,6 +95,10 @@ public CommandService(CommandServiceConfig config) _defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]); } + var tsreader = new TimeSpanTypeReader(); + _defaultTypeReaders[typeof(TimeSpan)] = tsreader; + _defaultTypeReaders[typeof(TimeSpan?)] = NullableTypeReader.Create(typeof(TimeSpan), tsreader); + _defaultTypeReaders[typeof(string)] = new PrimitiveTypeReader((string x, out string y) => { y = x; return true; }, 0); @@ -447,7 +453,6 @@ public Task ExecuteAsync(ICommandContext context, int argPos, IServiceP public async Task ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { services = services ?? EmptyServiceProvider.Instance; - var searchResult = Search(context, input); if (!searchResult.IsSuccess) return searchResult; diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index f06289a8dd..f9c90f08e9 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Discord.Commands { @@ -34,8 +35,27 @@ public class CommandServiceConfig public bool ThrowOnError { get; set; } = true; /// - /// Gets or sets whether extra parameters should be ignored. + /// Collection of aliases for matching pairs of string delimiters. + /// The dictionary stores the opening delimiter as a key, and the matching closing delimiter as the value. + /// If no value is supplied will be used, which contains + /// many regional equivalents. + /// Only values that are specified in this map will be used as string delimiters, so if " is removed then + /// it won't be used. + /// If this map is set to null or empty, the default delimiter of " will be used. /// + /// + /// + /// QuotationMarkAliasMap = new Dictionary<char, char%gt;() + /// { + /// {'\"', '\"' }, + /// {'“', '”' }, + /// {'「', '」' }, + /// } + /// + /// + public Dictionary QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap; + + /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; } } diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 9694dd5fcc..a754486ddb 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,15 +1,19 @@ - + Discord.Net.Commands Discord.Commands A Discord.Net extension adding support for bot commands. - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - + + + + \ No newline at end of file diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 3f4429bad2..0f0572be76 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -1,4 +1,4 @@ -using Discord.Commands.Builders; +using Discord.Commands.Builders; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -172,7 +172,8 @@ public async Task ParseAsync(ICommandContext context, int startInde return ParseResult.FromError(preconditionResult); string input = searchResult.Text.Substring(startIndex); - return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false); + + return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false); } public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) diff --git a/src/Discord.Net.Commands/PrimitiveParsers.cs b/src/Discord.Net.Commands/PrimitiveParsers.cs index 6a54ba4026..bf0622c285 100644 --- a/src/Discord.Net.Commands/PrimitiveParsers.cs +++ b/src/Discord.Net.Commands/PrimitiveParsers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -29,7 +29,7 @@ static IReadOnlyDictionary CreateParsers() parserBuilder[typeof(decimal)] = (TryParseDelegate)decimal.TryParse; parserBuilder[typeof(DateTime)] = (TryParseDelegate)DateTime.TryParse; parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate)DateTimeOffset.TryParse; - parserBuilder[typeof(TimeSpan)] = (TryParseDelegate)TimeSpan.TryParse; + //parserBuilder[typeof(TimeSpan)] = (TryParseDelegate)TimeSpan.TryParse; parserBuilder[typeof(char)] = (TryParseDelegate)char.TryParse; return parserBuilder.ToImmutable(); } diff --git a/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs b/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs new file mode 100644 index 0000000000..31ab9d8216 --- /dev/null +++ b/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs @@ -0,0 +1,35 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + internal class TimeSpanTypeReader : TypeReader + { + private static readonly string[] _formats = new[] + { + "%d'd'%h'h'%m'm'%s's'", //4d3h2m1s + "%d'd'%h'h'%m'm'", //4d3h2m + "%d'd'%h'h'%s's'", //4d3h 1s + "%d'd'%h'h'", //4d3h + "%d'd'%m'm'%s's'", //4d 2m1s + "%d'd'%m'm'", //4d 2m + "%d'd'%s's'", //4d 1s + "%d'd'", //4d + "%h'h'%m'm'%s's'", // 3h2m1s + "%h'h'%m'm'", // 3h2m + "%h'h'%s's'", // 3h 1s + "%h'h'", // 3h + "%m'm'%s's'", // 2m1s + "%m'm'", // 2m + "%s's'", // 1s + }; + + public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) + { + return (TimeSpan.TryParseExact(input.ToLowerInvariant(), _formats, CultureInfo.InvariantCulture, out var timeSpan)) + ? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan)) + : Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan")); + } + } +} diff --git a/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs new file mode 100644 index 0000000000..dc2328c1c6 --- /dev/null +++ b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Discord.Commands +{ + /// + /// Utility class which contains the default matching pairs of quotation marks for CommandServiceConfig + /// + internal static class QuotationAliasUtils + { + /// + /// A default map of open-close pairs of quotation marks. + /// Contains many regional and Unicode equivalents. + /// Used in the . + /// + /// + internal static Dictionary GetDefaultAliasMap + { + get + { + // Output of a gist provided by https://gist.github.com/ufcpp + // https://gist.github.com/ufcpp/5b2cf9a9bf7d0b8743714a0b88f7edc5 + // This was not used for the implementation because of incompatibility with netstandard1.1 + return new Dictionary { + {'\"', '\"' }, + {'«', '»' }, + {'‘', '’' }, + {'“', '”' }, + {'„', '‟' }, + {'‹', '›' }, + {'‚', '‛' }, + {'《', '》' }, + {'〈', '〉' }, + {'「', '」' }, + {'『', '』' }, + {'〝', '〞' }, + {'﹁', '﹂' }, + {'﹃', '﹄' }, + {'"', '"' }, + {''', ''' }, + {'「', '」' }, + {'(', ')' }, + {'༺', '༻' }, + {'༼', '༽' }, + {'᚛', '᚜' }, + {'⁅', '⁆' }, + {'⌈', '⌉' }, + {'⌊', '⌋' }, + {'❨', '❩' }, + {'❪', '❫' }, + {'❬', '❭' }, + {'❮', '❯' }, + {'❰', '❱' }, + {'❲', '❳' }, + {'❴', '❵' }, + {'⟅', '⟆' }, + {'⟦', '⟧' }, + {'⟨', '⟩' }, + {'⟪', '⟫' }, + {'⟬', '⟭' }, + {'⟮', '⟯' }, + {'⦃', '⦄' }, + {'⦅', '⦆' }, + {'⦇', '⦈' }, + {'⦉', '⦊' }, + {'⦋', '⦌' }, + {'⦍', '⦎' }, + {'⦏', '⦐' }, + {'⦑', '⦒' }, + {'⦓', '⦔' }, + {'⦕', '⦖' }, + {'⦗', '⦘' }, + {'⧘', '⧙' }, + {'⧚', '⧛' }, + {'⧼', '⧽' }, + {'⸂', '⸃' }, + {'⸄', '⸅' }, + {'⸉', '⸊' }, + {'⸌', '⸍' }, + {'⸜', '⸝' }, + {'⸠', '⸡' }, + {'⸢', '⸣' }, + {'⸤', '⸥' }, + {'⸦', '⸧' }, + {'⸨', '⸩' }, + {'【', '】'}, + {'〔', '〕' }, + {'〖', '〗' }, + {'〘', '〙' }, + {'〚', '〛' } + }; + } + } + } +} diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 7565fa1781..6a58367e67 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,15 +1,15 @@ - + Discord.Net.Core Discord The core components for the Discord.Net library. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - - + + diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 0e46f29601..d0c1c3a84b 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -11,13 +11,21 @@ public class DiscordConfig /// Returns the API version Discord.Net uses. /// /// - /// A 32-bit integer representing the API version that Discord.Net uses to communicate with Discord. + /// An representing the API version that Discord.Net uses to communicate with Discord. /// A list of available API version can be seen on the official /// Discord API documentation /// . /// public const int APIVersion = 6; /// + /// Returns the Voice API version Discord.Net uses. + /// + /// + /// An representing the API version that Discord.Net uses to communicate with Discord's + /// voice server. + /// + public const int VoiceAPIVersion = 3; + /// /// Gets the Discord.Net version, including the build number. /// /// @@ -93,6 +101,19 @@ public class DiscordConfig /// The maximum number of guilds that can be gotten per-batch. /// public const int MaxGuildsPerBatch = 100; + /// + /// Returns the max user reactions allowed to be in a request. + /// + /// + /// The maximum number of user reactions that can be gotten per-batch. + /// + public const int MaxUserReactionsPerBatch = 100; + /// + /// Returns the max audit log entries allowed to be in a request. + /// + /// + /// The maximum number of audit log entries that can be gotten per-batch. + /// public const int MaxAuditLogEntriesPerBatch = 100; /// diff --git a/src/Discord.Net.Core/Entities/Activities/GameAsset.cs b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs index 467b2885f4..7217bded3a 100644 --- a/src/Discord.Net.Core/Entities/Activities/GameAsset.cs +++ b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs @@ -12,15 +12,26 @@ internal GameAsset() { } /// /// Gets the description of the asset. /// + /// + /// A string containing the description of the asset. + /// public string Text { get; internal set; } /// /// Gets the image ID of the asset. /// + /// + /// A string containing the unique image identifier of the asset. + /// public string ImageId { get; internal set; } /// - /// Returns the image URL of the asset, or null when the application ID does not exist. + /// Returns the image URL of the asset. /// + /// The size of the image to return in. This can be any power of two between 16 and 2048. + /// The format to return. + /// + /// A string pointing to the image URL of the asset; null when the application ID does not exist. + /// public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) => ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null; } diff --git a/src/Discord.Net.Core/Entities/Activities/GameParty.cs b/src/Discord.Net.Core/Entities/Activities/GameParty.cs index c3449df36b..0cfa9980d5 100644 --- a/src/Discord.Net.Core/Entities/Activities/GameParty.cs +++ b/src/Discord.Net.Core/Entities/Activities/GameParty.cs @@ -10,11 +10,17 @@ internal GameParty() { } /// /// Gets the ID of the party. /// + /// + /// A string containing the unique identifier of the party. + /// public string Id { get; internal set; } public long Members { get; internal set; } /// /// Gets the party's current and maximum size. /// + /// + /// A representing the capacity of the party. + /// public long Capacity { get; internal set; } } } diff --git a/src/Discord.Net.Core/Entities/Activities/IActivity.cs b/src/Discord.Net.Core/Entities/Activities/IActivity.cs index 30d9369523..ac0c1b5d74 100644 --- a/src/Discord.Net.Core/Entities/Activities/IActivity.cs +++ b/src/Discord.Net.Core/Entities/Activities/IActivity.cs @@ -8,10 +8,16 @@ public interface IActivity /// /// Gets the name of the activity. /// + /// + /// A string containing the name of the activity that the user is doing. + /// string Name { get; } /// /// Gets the type of the activity. /// + /// + /// The type of activity. + /// ActivityType Type { get; } } } diff --git a/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs index e5a4ff30a5..2561a09709 100644 --- a/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs +++ b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs @@ -1,50 +1,122 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Discord { /// - /// The action type within a + /// Representing a type of action within an . /// public enum ActionType { + /// + /// this guild was updated. + /// GuildUpdated = 1, + /// + /// A channel was created. + /// ChannelCreated = 10, + /// + /// A channel was updated. + /// ChannelUpdated = 11, + /// + /// A channel was deleted. + /// ChannelDeleted = 12, + /// + /// A permission overwrite was created for a channel. + /// OverwriteCreated = 13, + /// + /// A permission overwrite was updated for a channel. + /// OverwriteUpdated = 14, + /// + /// A permission overwrite was deleted for a channel. + /// OverwriteDeleted = 15, + /// + /// A user was kicked from this guild. + /// Kick = 20, + /// + /// A prune took place in this guild. + /// Prune = 21, + /// + /// A user banned another user from this guild. + /// Ban = 22, + /// + /// A user unbanned another user from this guild. + /// Unban = 23, + /// + /// A guild member whose information was updated. + /// MemberUpdated = 24, + /// + /// A guild member's role collection was updated. + /// MemberRoleUpdated = 25, + /// + /// A role was created in this guild. + /// RoleCreated = 30, + /// + /// A role was updated in this guild. + /// RoleUpdated = 31, + /// + /// A role was deleted from this guild. + /// RoleDeleted = 32, + /// + /// An invite was created in this guild. + /// InviteCreated = 40, + /// + /// An invite was updated in this guild. + /// InviteUpdated = 41, + /// + /// An invite was deleted from this guild. + /// InviteDeleted = 42, + /// + /// A Webhook was created in this guild. + /// WebhookCreated = 50, + /// + /// A Webhook was updated in this guild. + /// WebhookUpdated = 51, + /// + /// A Webhook was deleted from this guild. + /// WebhookDeleted = 52, + /// + /// An emoji was created in this guild. + /// EmojiCreated = 60, + /// + /// An emoji was updated in this guild. + /// EmojiUpdated = 61, + /// + /// An emoji was deleted from this guild. + /// EmojiDeleted = 62, + /// + /// A message was deleted from this guild. + /// MessageDeleted = 72 } } diff --git a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs index 47aaffb26b..5d87c9d589 100644 --- a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs +++ b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Discord { /// - /// Represents data applied to an + /// Represents data applied to an . /// public interface IAuditLogData { } diff --git a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs index b85730a1de..d8f7f1a9f3 100644 --- a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs +++ b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs @@ -1,34 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Discord { /// - /// Represents an entry in an audit log + /// Represents a generic audit log entry. /// public interface IAuditLogEntry : IEntity { /// - /// The action which occured to create this entry + /// Gets the action which occurred to create this entry. /// + /// + /// The type of action for this audit log entry. + /// ActionType Action { get; } /// - /// The data for this entry. May be if no data was available. + /// Gets the data for this entry. /// + /// + /// An for this audit log entry; null if no data is available. + /// IAuditLogData Data { get; } /// - /// The user responsible for causing the changes + /// Gets the user responsible for causing the changes. /// + /// + /// A user object. + /// IUser User { get; } /// - /// The reason behind the change. May be if no reason was provided. + /// Gets the reason behind the change. /// + /// + /// A string containing the reason for the change; null if none is provided. + /// string Reason { get; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/IChannel.cs b/src/Discord.Net.Core/Entities/Channels/IChannel.cs index 4f14431c5d..04408989bc 100644 --- a/src/Discord.Net.Core/Entities/Channels/IChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IChannel.cs @@ -11,16 +11,32 @@ public interface IChannel : ISnowflakeEntity /// /// Gets the name of this channel. /// + /// + /// A string containing the name of this channel. + /// string Name { get; } - + /// /// Gets a collection of all users in this channel. /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A paged collection containing a collection of users that can access this channel. Flattening the + /// paginated response into a collection of users with + /// is required if you wish to access the users. + /// IAsyncEnumerable> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); - + /// - /// Gets a user in this channel with the provided ID. + /// Gets a user in this channel. /// + /// The snowflake identifier of the user (e.g. 168693960628371456). + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// An awaitable containing a user object that represents the user. + /// Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 3d11e2c6f8..301737aa17 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -20,29 +20,18 @@ public interface IGuildChannel : IChannel, IDeletable /// int Position { get; } - /// - /// Gets the parent ID (category) of this channel in the guild's channel list. - /// - /// - /// The parent category ID associated with this channel, or null if none is set. - /// - ulong? CategoryId { get; } - /// - /// Gets the parent channel (category) of this channel. - /// - Task GetCategoryAsync(); /// /// Gets the guild associated with this channel. /// /// - /// The guild that this channel belongs to. + /// A guild that this channel belongs to. /// IGuild Guild { get; } /// /// Gets the guild ID associated with this channel. /// /// - /// The guild ID that this channel belongs to. + /// A guild snowflake identifier for the guild that this channel belongs to. /// ulong GuildId { get; } /// @@ -66,16 +55,25 @@ public interface IGuildChannel : IChannel, IDeletable /// If true, a user accepting this invite will be kicked from the guild after closing their client. /// /// - /// If true, don't try to reuse a similar invite (useful for creating many unique one time use invites). + /// If true, don't try to reuse a similar invite (useful for creating many unique one time use + /// invites). /// /// /// The options to be used when sending the request. /// + /// + /// An awaitable containing an invite metadata object containing information for the + /// created invite. + /// Task CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); /// /// Returns a collection of all invites to this channel. /// /// The options to be used when sending the request. + /// + /// An awaitable containing a read-only collection of invite metadata that are created + /// for this channel. + /// Task> GetInvitesAsync(RequestOptions options = null); /// @@ -83,29 +81,44 @@ public interface IGuildChannel : IChannel, IDeletable /// /// The properties to modify the channel with. /// The options to be used when sending the request. + /// + /// An awaitable . + /// Task ModifyAsync(Action func, RequestOptions options = null); /// /// Gets the permission overwrite for a specific role, or null if one does not exist. /// /// The role to get the overwrite from. + /// + /// An overwrite object for the targeted role; null if none is set. + /// OverwritePermissions? GetPermissionOverwrite(IRole role); /// /// Gets the permission overwrite for a specific user, or null if one does not exist. /// /// The user to get the overwrite from. + /// + /// An overwrite object for the targeted user; null if none is set. + /// OverwritePermissions? GetPermissionOverwrite(IUser user); /// /// Removes the permission overwrite for the given role, if one exists. /// /// The role to remove the overwrite from. /// The options to be used when sending the request. + /// + /// An awaitable . + /// Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null); /// /// Removes the permission overwrite for the given user, if one exists. /// /// The user to remove the overwrite from. /// The options to be used when sending the request. + /// + /// An awaitable . + /// Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null); /// @@ -114,6 +127,9 @@ public interface IGuildChannel : IChannel, IDeletable /// The role to add the overwrite to. /// The overwrite to add to the role. /// The options to be used when sending the request. + /// + /// An awaitable . + /// Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null); /// /// Adds or updates the permission overwrite for the given user. @@ -121,6 +137,9 @@ public interface IGuildChannel : IChannel, IDeletable /// The user to add the overwrite to. /// The overwrite to add to the user. /// The options to be used when sending the request. + /// + /// An awaitable . + /// Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null); /// @@ -130,6 +149,11 @@ public interface IGuildChannel : IChannel, IDeletable /// The that determines whether the object should be fetched from cache. /// /// The options to be used when sending the request. + /// + /// A paged collection containing a collection of guild users that can access this channel. Flattening the + /// paginated response into a collection of users with + /// is required if you wish to access the users. + /// new IAsyncEnumerable> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a user in this channel with the provided ID. @@ -139,6 +163,9 @@ public interface IGuildChannel : IChannel, IDeletable /// The that determines whether the object should be fetched from cache. /// /// The options to be used when sending the request. + /// + /// An awaitable containing a guild user object that represents the user. + /// new Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 837e906041..803e07ff6c 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -20,8 +20,7 @@ public interface IMessageChannel : IChannel /// /// An awaitable Task containing the message sent to the channel. /// - Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM + Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); /// /// Sends a file to this message channel with an optional caption. /// @@ -39,7 +38,6 @@ public interface IMessageChannel : IChannel /// An awaitable Task containing the message sent to the channel. /// Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#endif /// /// Sends a file to this message channel with an optional caption. /// @@ -121,6 +119,11 @@ IAsyncEnumerable> GetMessagesAsync(IMessage fromMe /// Task> GetPinnedMessagesAsync(RequestOptions options = null); + /// Deletes a message based on the message ID in this channel. + Task DeleteMessageAsync(ulong messageId, RequestOptions options = null); + /// Deletes a message based on the provided message in this channel. + Task DeleteMessageAsync(IMessage message, RequestOptions options = null); + /// /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. /// diff --git a/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs b/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs new file mode 100644 index 0000000000..62984c2a01 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// Represents a type of guild channel that can be nested within a category. + /// + public interface INestedChannel : IGuildChannel + { + /// + /// Gets the parent (category) ID of this channel in the guild's channel list. + /// + /// + /// An representing the snowflake identifier of the parent of this channel; + /// null if none is set. + /// + ulong? CategoryId { get; } + /// + /// Gets the parent (category) channel of this channel. + /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A category channel representing the parent of this channel; null if none is set. + /// + Task GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + } +} diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 29c67bf6bd..a2dce09ecd 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -8,7 +8,7 @@ namespace Discord /// /// Represents a generic channel in a guild that can send and receive messages. /// - public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel + public interface ITextChannel : IMessageChannel, IMentionable, INestedChannel { /// /// Determines whether the channel is NSFW. @@ -80,7 +80,7 @@ public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel /// /// The options to be used when sending the request. /// - /// A collection of webhooks. + /// An awaitable containing a collection of webhooks. /// Task> GetWebhooksAsync(RequestOptions options = null); } diff --git a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs index e1efb1a864..3119a5ae7b 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -4,9 +4,9 @@ namespace Discord { /// - /// Represents a voice channel in a guild. + /// Represents a generic voice channel in a guild. /// - public interface IVoiceChannel : IGuildChannel, IAudioChannel + public interface IVoiceChannel : INestedChannel, IAudioChannel { /// /// Gets the bitrate, in bits per second, clients in this voice channel are requested to use. diff --git a/src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs index 255279fa8a..f114e0ef5e 100644 --- a/src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs +++ b/src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs @@ -8,10 +8,16 @@ public class ReorderChannelProperties /// /// Gets the ID of the channel to apply this position to. /// + /// + /// A representing the snowflake identififer of this channel. + /// public ulong Id { get; } /// /// Gets the new zero-based position of this channel. /// + /// + /// An representing the new position of this channel. + /// public int Position { get; } /// Creates a used to reorder a channel. diff --git a/src/Discord.Net.Core/Entities/Guilds/IBan.cs b/src/Discord.Net.Core/Entities/Guilds/IBan.cs index 3ce76d29b6..617f2fe046 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IBan.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IBan.cs @@ -8,10 +8,16 @@ public interface IBan /// /// Gets the banned user. /// + /// + /// A user that was banned. + /// IUser User { get; } /// - /// Gets the reason why the user is banned. + /// Gets the reason why the user is banned if specified. /// + /// + /// A string containing the reason behind the ban; null if none is specified. + /// string Reason { get; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 581ca551e2..8d6c090b53 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -22,7 +22,8 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// automatically moved to the AFK voice channel. /// /// - /// The amount of time in seconds for a user to be marked as inactive and moved into the AFK voice channel. + /// An representing the amount of time in seconds for a user to be marked as inactive + /// and moved into the AFK voice channel. /// int AFKTimeout { get; } /// @@ -94,8 +95,12 @@ public interface IGuild : IDeletable, ISnowflakeEntity bool Available { get; } /// - /// Gets the ID of the AFK voice channel for this guild, or null if none is set. + /// Gets the ID of the AFK voice channel for this guild. /// + /// + /// An representing the snowflake identifier of the AFK voice channel; null if + /// none is set. + /// ulong? AFKChannelId { get; } /// /// Gets the ID of the the default channel for this guild. @@ -214,18 +219,20 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// Gets a ban object for a banned user. /// /// The banned user. + /// The options to be used when sending the request. /// /// An awaitable containing the ban object, which contains the user information and the - /// reason for the ban; null if the ban entry cannot be found. + /// reason for the ban; null if the ban entry cannot be found. /// Task GetBanAsync(IUser user, RequestOptions options = null); /// /// Gets a ban object for a banned user. /// /// The snowflake identifier for the banned user. + /// The options to be used when sending the request. /// /// An awaitable containing the ban object, which contains the user information and the - /// reason for the ban; null if the ban entry cannot be found. + /// reason for the ban; null if the ban entry cannot be found. /// Task GetBanAsync(ulong userId, RequestOptions options = null); /// @@ -406,24 +413,27 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// null if none is set. /// Task GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// /// Creates a new text channel. /// /// The new name for the text channel. + /// The delegate containing the properties to be applied to the channel upon its creation. /// The options to be used when sending the request. /// /// An awaitable containing the newly created text channel. /// - Task CreateTextChannelAsync(string name, RequestOptions options = null); + Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null); /// /// Creates a new voice channel. /// /// The new name for the voice channel. + /// The delegate containing the properties to be applied to the channel upon its creation. /// The options to be used when sending the request. /// /// An awaitable containing the newly created voice channel. /// - Task CreateVoiceChannelAsync(string name, RequestOptions options = null); + Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null); /// /// Creates a new channel category. /// @@ -537,7 +547,17 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); - /// Gets the specified number of audit log entries for this guild. + /// + /// Gets the specified number of audit log entries for this guild. + /// + /// The number of audit log entries to fetch. + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. + /// + /// An awaitable containing a collection of requested audit log entries. + /// Task> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index 5453027acb..5c775f9f4f 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -1,4 +1,3 @@ -using System; using System.IO; namespace Discord { @@ -22,7 +21,7 @@ public Image(Stream stream) { Stream = stream; } -#if FILESYSTEM + /// /// Create the image from a file path. /// @@ -31,21 +30,21 @@ public Image(Stream stream) /// . /// /// The path to the file. - /// + /// /// is a zero-length string, contains only white space, or contains one or more invalid /// characters as defined by . /// - /// is null. + /// is null. /// /// The specified path, file name, or both exceed the system-defined maximum length. For example, on /// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 /// characters. /// - /// is in an invalid format. + /// is in an invalid format. /// /// The specified is invalid, (for example, it is on an unmapped drive). /// - /// + /// /// specified a directory.-or- The caller does not have the required permission. /// /// The file specified in was not found. @@ -55,6 +54,6 @@ public Image(string path) { Stream = File.OpenRead(path); } -#endif + } } diff --git a/src/Discord.Net.Core/Entities/Invites/IInvite.cs b/src/Discord.Net.Core/Entities/Invites/IInvite.cs index 62e4a4bfcb..90dde74745 100644 --- a/src/Discord.Net.Core/Entities/Invites/IInvite.cs +++ b/src/Discord.Net.Core/Entities/Invites/IInvite.cs @@ -1,5 +1,3 @@ -using System.Threading.Tasks; - namespace Discord { /// @@ -10,44 +8,76 @@ public interface IInvite : IEntity, IDeletable /// /// Gets the unique identifier for this invite. /// + /// + /// A string containing the invite code (e.g. FTqNnyS). + /// string Code { get; } /// - /// Gets the URL used to accept this invite, using Code. + /// Gets the URL used to accept this invite using . /// + /// + /// A string containing the full invite URL (e.g. https://discord.gg/FTqNnyS). + /// string Url { get; } /// /// Gets the channel this invite is linked to. /// + /// + /// A generic channel that the invite points to. + /// IChannel Channel { get; } /// /// Gets the ID of the channel this invite is linked to. /// + /// + /// An representing the channel snowflake identifier that the invite points to. + /// ulong ChannelId { get; } /// /// Gets the name of the channel this invite is linked to. /// + /// + /// A string containing the name of the channel that the invite points to. + /// string ChannelName { get; } /// /// Gets the guild this invite is linked to. /// + /// + /// A guild object representing the guild that the invite points to. + /// IGuild Guild { get; } /// /// Gets the ID of the guild this invite is linked to. /// + /// + /// An representing the guild snowflake identifier that the invite points to. + /// ulong GuildId { get; } /// /// Gets the name of the guild this invite is linked to. /// + /// + /// A string containing the name of the guild that the invite points to. + /// string GuildName { get; } /// /// Gets the approximated count of online members in the guild. /// + /// + /// An representing the approximated online member count of the guild that the + /// invite points to; null if one cannot be obtained. + /// int? PresenceCount { get; } /// /// Gets the approximated count of total members in the guild. /// + /// + /// An representing the approximated total member count of the guild that the + /// invite points to; null if one cannot be obtained. + /// int? MemberCount { get; } } } diff --git a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs index 1c6b1dd79b..20f13a74c1 100644 --- a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs +++ b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs @@ -2,37 +2,63 @@ namespace Discord { - /// Represents additional information regarding the generic invite object. + /// + /// Represents additional information regarding the generic invite object. + /// public interface IInviteMetadata : IInvite { /// /// Gets the user that created this invite. /// + /// + /// A user that created this invite. + /// IUser Inviter { get; } /// - /// Returns true if this invite was revoked. + /// Determines whether the invite has been revoked. /// + /// + /// true if this invite was revoked; otherwise false. + /// bool IsRevoked { get; } /// - /// Returns true if users accepting this invite will be removed from the guild when they - /// log off. + /// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild + /// when the user logs off). /// + /// + /// true if users accepting this invite will be removed from the guild when they log off; otherwise + /// false. + /// bool IsTemporary { get; } /// - /// Gets the time (in seconds) until the invite expires, or null if it never expires. + /// Gets the time (in seconds) until the invite expires. /// + /// + /// An representing the time in seconds until this invite expires; null if this + /// invite never expires. + /// int? MaxAge { get; } /// - /// Gets the max amount of times this invite may be used, or null if there is no limit. + /// Gets the max number of uses this invite may have. /// + /// + /// An representing the number of uses this invite may be accepted until it is removed + /// from the guild; null if none is set. + /// int? MaxUses { get; } /// - /// Gets the amount of times this invite has been used. + /// Gets the number of times this invite has been used. /// + /// + /// An representing the number of times this invite has been used. + /// int Uses { get; } /// /// Gets when this invite was created. /// + /// + /// A representing the time of which the invite was first created. + /// DateTimeOffset CreatedAt { get; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 18ef932660..bad4e36c7b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -42,7 +42,7 @@ public interface IUserMessage : IMessage /// /// Gets all users that reacted to a message with a given emote. /// - Task> GetReactionUsersAsync(IEmote emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); + IAsyncEnumerable> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions options = null); /// /// Transforms this message's text into a human-readable form by resolving its tags. diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 758bc5758d..753be6d516 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -88,12 +88,27 @@ public static ChannelPermissions All(IChannel channel) /// Creates a new with the provided packed value. public ChannelPermissions(ulong rawValue) { RawValue = rawValue; } - private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null, + private ChannelPermissions(ulong initialValue, + bool? createInstantInvite = null, + bool? manageChannel = null, bool? addReactions = null, - bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, - bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, - bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null) + bool? viewChannel = null, + bool? sendMessages = null, + bool? sendTTSMessages = null, + bool? manageMessages = null, + bool? embedLinks = null, + bool? attachFiles = null, + bool? readMessageHistory = null, + bool? mentionEveryone = null, + bool? useExternalEmojis = null, + bool? connect = null, + bool? speak = null, + bool? muteMembers = null, + bool? deafenMembers = null, + bool? moveMembers = null, + bool? useVoiceActivation = null, + bool? manageRoles = null, + bool? manageWebhooks = null) { ulong value = initialValue; @@ -122,27 +137,75 @@ private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, } /// Creates a new with the provided permissions. - public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false, + public ChannelPermissions( + bool createInstantInvite = false, + bool manageChannel = false, bool addReactions = false, - bool viewChannel = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, - bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, - bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, - bool moveMembers = false, bool useVoiceActivation = false, bool manageRoles = false, bool manageWebhooks = false) + bool viewChannel = false, + bool sendMessages = false, + bool sendTTSMessages = false, + bool manageMessages = false, + bool embedLinks = false, + bool attachFiles = false, + bool readMessageHistory = false, + bool mentionEveryone = false, + bool useExternalEmojis = false, + bool connect = false, + bool speak = false, + bool muteMembers = false, + bool deafenMembers = false, + bool moveMembers = false, + bool useVoiceActivation = false, + bool manageRoles = false, + bool manageWebhooks = false) : this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { } /// Creates a new from this one, changing the provided non-null permissions. - public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, + public ChannelPermissions Modify( + bool? createInstantInvite = null, + bool? manageChannel = null, bool? addReactions = null, - bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, - bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, - bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null) - => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, - embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks); + bool? viewChannel = null, + bool? sendMessages = null, + bool? sendTTSMessages = null, + bool? manageMessages = null, + bool? embedLinks = null, + bool? attachFiles = null, + bool? readMessageHistory = null, + bool? mentionEveryone = null, + bool? useExternalEmojis = null, + bool? connect = null, + bool? speak = null, + bool? muteMembers = null, + bool? deafenMembers = null, + bool? moveMembers = null, + bool? useVoiceActivation = null, + bool? manageRoles = null, + bool? manageWebhooks = null) + => new ChannelPermissions(RawValue, + createInstantInvite, + manageChannel, + addReactions, + viewChannel, + sendMessages, + sendTTSMessages, + manageMessages, + embedLinks, + attachFiles, + readMessageHistory, + mentionEveryone, + useExternalEmojis, + connect, + speak, + muteMembers, + deafenMembers, + moveMembers, + useVoiceActivation, + manageRoles, + manageWebhooks); public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission); diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs index 663c7fc6c8..f2724778f1 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs @@ -41,13 +41,9 @@ public enum GuildPermission : ulong /// Allows for viewing of audit logs. /// ViewAuditLog = 0x00_00_00_80, - /// - /// Allows for reading of message. - /// - ReadMessages = 0x00_00_04_00, - /// - /// Allows for sending messages in a channel. - /// + [Obsolete("Use ViewChannel instead.")] + ReadMessages = ViewChannel, + ViewChannel = 0x00_00_04_00, SendMessages = 0x00_00_08_00, /// /// Allows for sending of text-to-speech messages. diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 9a0cb2919a..9ad7d34b44 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics; @@ -34,9 +35,12 @@ public struct GuildPermissions /// If true, a user may view the audit log. public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog); - /// If true, a user may join channels. - public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); - /// If true, a user may send messages. + /// If True, a user may join channels. + [Obsolete("Use ViewChannel instead.")] + public bool ReadMessages => ViewChannel; + /// If True, a user may view channels. + public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel); + /// If True, a user may send messages. public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); /// If true, a user may send text-to-speech messages. public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages); @@ -80,14 +84,35 @@ public struct GuildPermissions /// Creates a new with the provided packed value. public GuildPermissions(ulong rawValue) { RawValue = rawValue; } - private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, - bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, - bool? addReactions = null, bool? viewAuditLog = null, - bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, - bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, - bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, - bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) + private GuildPermissions(ulong initialValue, + bool? createInstantInvite = null, + bool? kickMembers = null, + bool? banMembers = null, + bool? administrator = null, + bool? manageChannels = null, + bool? manageGuild = null, + bool? addReactions = null, + bool? viewAuditLog = null, + bool? viewChannel = null, + bool? sendMessages = null, + bool? sendTTSMessages = null, + bool? manageMessages = null, + bool? embedLinks = null, + bool? attachFiles = null, + bool? readMessageHistory = null, + bool? mentionEveryone = null, + bool? useExternalEmojis = null, + bool? connect = null, + bool? speak = null, + bool? muteMembers = null, + bool? deafenMembers = null, + bool? moveMembers = null, + bool? useVoiceActivation = null, + bool? changeNickname = null, + bool? manageNicknames = null, + bool? manageRoles = null, + bool? manageWebhooks = null, + bool? manageEmojis = null) { ulong value = initialValue; @@ -99,7 +124,7 @@ private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, b Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions); Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog); - Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages); + Permissions.SetValue(ref value, viewChannel, GuildPermission.ViewChannel); Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); @@ -124,34 +149,98 @@ private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, b } /// Creates a new with the provided permissions. - public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, - bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false, - bool addReactions = false, bool viewAuditLog = false, - bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, - bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, - bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, - bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false, - bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false) - : this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers, - administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions, - viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages, - manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory, - mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers, - deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname, - manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, manageEmojis: manageEmojis) + public GuildPermissions( + bool createInstantInvite = false, + bool kickMembers = false, + bool banMembers = false, + bool administrator = false, + bool manageChannels = false, + bool manageGuild = false, + bool addReactions = false, + bool viewAuditLog = false, + bool viewChannel = false, + bool sendMessages = false, + bool sendTTSMessages = false, + bool manageMessages = false, + bool embedLinks = false, + bool attachFiles = false, + bool readMessageHistory = false, + bool mentionEveryone = false, + bool useExternalEmojis = false, + bool connect = false, + bool speak = false, + bool muteMembers = false, + bool deafenMembers = false, + bool moveMembers = false, + bool useVoiceActivation = false, + bool changeNickname = false, + bool manageNicknames = false, + bool manageRoles = false, + bool manageWebhooks = false, + bool manageEmojis = false) + : this(0, + createInstantInvite: createInstantInvite, + manageRoles: manageRoles, + kickMembers: kickMembers, + banMembers: banMembers, + administrator: administrator, + manageChannels: manageChannels, + manageGuild: manageGuild, + addReactions: addReactions, + viewAuditLog: viewAuditLog, + viewChannel: viewChannel, + sendMessages: sendMessages, + sendTTSMessages: sendTTSMessages, + manageMessages: manageMessages, + embedLinks: embedLinks, + attachFiles: attachFiles, + readMessageHistory: readMessageHistory, + mentionEveryone: mentionEveryone, + useExternalEmojis: useExternalEmojis, + connect: connect, + speak: speak, + muteMembers: muteMembers, + deafenMembers: deafenMembers, + moveMembers: moveMembers, + useVoiceActivation: useVoiceActivation, + changeNickname: changeNickname, + manageNicknames: manageNicknames, + manageWebhooks: manageWebhooks, + manageEmojis: manageEmojis) { } /// Creates a new from this one, changing the provided non-null permissions. - public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null, - bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, - bool? addReactions = null, bool? viewAuditLog = null, - bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, - bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, - bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, - bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) + public GuildPermissions Modify( + bool? createInstantInvite = null, + bool? kickMembers = null, + bool? banMembers = null, + bool? administrator = null, + bool? manageChannels = null, + bool? manageGuild = null, + bool? addReactions = null, + bool? viewAuditLog = null, + bool? viewChannel = null, + bool? sendMessages = null, + bool? sendTTSMessages = null, + bool? manageMessages = null, + bool? embedLinks = null, + bool? attachFiles = null, + bool? readMessageHistory = null, + bool? mentionEveryone = null, + bool? useExternalEmojis = null, + bool? connect = null, + bool? speak = null, + bool? muteMembers = null, + bool? deafenMembers = null, + bool? moveMembers = null, + bool? useVoiceActivation = null, + bool? changeNickname = null, + bool? manageNicknames = null, + bool? manageRoles = null, + bool? manageWebhooks = null, + bool? manageEmojis = null) => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, - viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, + viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs index bcc34b98a3..97735c42ce 100644 --- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs @@ -84,12 +84,26 @@ public OverwritePermissions(ulong allowValue, ulong denyValue) DenyValue = denyValue; } - private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null, + private OverwritePermissions(ulong allowValue, ulong denyValue, + PermValue? createInstantInvite = null, + PermValue? manageChannel = null, PermValue? addReactions = null, - PermValue? viewChannel = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, - PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, - PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, - PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null, + PermValue? viewChannel = null, + PermValue? sendMessages = null, + PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, + PermValue? embedLinks = null, + PermValue? attachFiles = null, + PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, + PermValue? useExternalEmojis = null, + PermValue? connect = null, + PermValue? speak = null, + PermValue? muteMembers = null, + PermValue? deafenMembers = null, + PermValue? moveMembers = null, + PermValue? useVoiceActivation = null, + PermValue? manageRoles = null, PermValue? manageWebhooks = null) { Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); @@ -120,13 +134,28 @@ private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? creat /// /// Creates a new with the provided permissions. /// - public OverwritePermissions(PermValue createInstantInvite = PermValue.Inherit, PermValue manageChannel = PermValue.Inherit, + public OverwritePermissions( + PermValue createInstantInvite = PermValue.Inherit, + PermValue manageChannel = PermValue.Inherit, PermValue addReactions = PermValue.Inherit, - PermValue readMessages = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit, - PermValue embedLinks = PermValue.Inherit, PermValue attachFiles = PermValue.Inherit, PermValue readMessageHistory = PermValue.Inherit, PermValue mentionEveryone = PermValue.Inherit, - PermValue useExternalEmojis = PermValue.Inherit, PermValue connect = PermValue.Inherit, PermValue speak = PermValue.Inherit, PermValue muteMembers = PermValue.Inherit, PermValue deafenMembers = PermValue.Inherit, - PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue manageRoles = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit) - : this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, + PermValue viewChannel = PermValue.Inherit, + PermValue sendMessages = PermValue.Inherit, + PermValue sendTTSMessages = PermValue.Inherit, + PermValue manageMessages = PermValue.Inherit, + PermValue embedLinks = PermValue.Inherit, + PermValue attachFiles = PermValue.Inherit, + PermValue readMessageHistory = PermValue.Inherit, + PermValue mentionEveryone = PermValue.Inherit, + PermValue useExternalEmojis = PermValue.Inherit, + PermValue connect = PermValue.Inherit, + PermValue speak = PermValue.Inherit, + PermValue muteMembers = PermValue.Inherit, + PermValue deafenMembers = PermValue.Inherit, + PermValue moveMembers = PermValue.Inherit, + PermValue useVoiceActivation = PermValue.Inherit, + PermValue manageRoles = PermValue.Inherit, + PermValue manageWebhooks = PermValue.Inherit) + : this(0, 0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { } @@ -134,13 +163,28 @@ public OverwritePermissions(PermValue createInstantInvite = PermValue.Inherit, P /// Creates a new from this one, changing the provided non-null /// permissions. /// - public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null, + public OverwritePermissions Modify( + PermValue? createInstantInvite = null, + PermValue? manageChannel = null, PermValue? addReactions = null, - PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, - PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, - PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, - PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null, PermValue? manageWebhooks = null) - => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, + PermValue? viewChannel = null, + PermValue? sendMessages = null, + PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, + PermValue? embedLinks = null, + PermValue? attachFiles = null, + PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, + PermValue? useExternalEmojis = null, + PermValue? connect = null, + PermValue? speak = null, + PermValue? muteMembers = null, + PermValue? deafenMembers = null, + PermValue? moveMembers = null, + PermValue? useVoiceActivation = null, + PermValue? manageRoles = null, + PermValue? manageWebhooks = null) + => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks); diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index fa7624a57d..f24ac883e0 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -1,5 +1,8 @@ using System; using System.Diagnostics; +#if NETSTANDARD2_0 || NET45 +using StandardColor = System.Drawing.Color; +#endif namespace Discord { @@ -125,6 +128,13 @@ public Color(float r, float g, float b) (uint)(b * 255.0f); } +#if NETSTANDARD2_0 || NET45 + public static implicit operator StandardColor(Color color) => + StandardColor.FromArgb((int)color.RawValue); + public static explicit operator Color(StandardColor color) => + new Color((uint)color.ToArgb() << 8 >> 8); +#endif + /// /// Gets the hexadecimal representation of the color (e.g. #000ccc). /// diff --git a/src/Discord.Net.Core/Extensions/UserExtensions.cs b/src/Discord.Net.Core/Extensions/UserExtensions.cs index 41e84c3cb1..7759c8ea30 100644 --- a/src/Discord.Net.Core/Extensions/UserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/UserExtensions.cs @@ -18,7 +18,7 @@ public static class UserExtensions /// An awaitable Task containing the message sent to the channel. /// public static async Task SendMessageAsync(this IUser user, - string text, + string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) @@ -55,7 +55,6 @@ public static async Task SendFileAsync(this IUser user, return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); } -#if FILESYSTEM /// /// Sends a file via DM with an optional caption. /// @@ -81,7 +80,7 @@ public static async Task SendFileAsync(this IUser user, { return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); } -#endif + /// /// Bans the provided user from the guild and optionally prunes their recent messages. /// diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 35727c33d3..a99c45b39d 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -41,7 +41,7 @@ public async Task LogAsync(LogSeverity severity, string source, string message, // ignored } } -#if FORMATSTR + public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) { try @@ -51,52 +51,49 @@ public async Task LogAsync(LogSeverity severity, string source, FormattableStrin } catch { } } -#endif + public Task ErrorAsync(string source, Exception ex) => LogAsync(LogSeverity.Error, source, ex); public Task ErrorAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); -#if FORMATSTR + public Task ErrorAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); -#endif + public Task WarningAsync(string source, Exception ex) => LogAsync(LogSeverity.Warning, source, ex); public Task WarningAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); -#if FORMATSTR + public Task WarningAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); -#endif + public Task InfoAsync(string source, Exception ex) => LogAsync(LogSeverity.Info, source, ex); public Task InfoAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); -#if FORMATSTR public Task InfoAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); -#endif + public Task VerboseAsync(string source, Exception ex) => LogAsync(LogSeverity.Verbose, source, ex); public Task VerboseAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); -#if FORMATSTR public Task VerboseAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); -#endif + public Task DebugAsync(string source, Exception ex) => LogAsync(LogSeverity.Debug, source, ex); public Task DebugAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); -#if FORMATSTR public Task DebugAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); -#endif + public Logger CreateLogger(string name) => new Logger(this, name); diff --git a/src/Discord.Net.Core/Logging/Logger.cs b/src/Discord.Net.Core/Logging/Logger.cs index a8d88b2b43..e71c569921 100644 --- a/src/Discord.Net.Core/Logging/Logger.cs +++ b/src/Discord.Net.Core/Logging/Logger.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord.Logging @@ -20,54 +20,53 @@ public Task LogAsync(LogSeverity severity, Exception exception = null) => _manager.LogAsync(severity, Name, exception); public Task LogAsync(LogSeverity severity, string message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); -#if FORMATSTR public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); -#endif + public Task ErrorAsync(Exception exception) => _manager.ErrorAsync(Name, exception); public Task ErrorAsync(string message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); -#if FORMATSTR + public Task ErrorAsync(FormattableString message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); -#endif + public Task WarningAsync(Exception exception) => _manager.WarningAsync(Name, exception); public Task WarningAsync(string message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); -#if FORMATSTR + public Task WarningAsync(FormattableString message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); -#endif + public Task InfoAsync(Exception exception) => _manager.InfoAsync(Name, exception); public Task InfoAsync(string message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); -#if FORMATSTR + public Task InfoAsync(FormattableString message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); -#endif + public Task VerboseAsync(Exception exception) => _manager.VerboseAsync(Name, exception); public Task VerboseAsync(string message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); -#if FORMATSTR + public Task VerboseAsync(FormattableString message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); -#endif + public Task DebugAsync(Exception exception) => _manager.DebugAsync(Name, exception); public Task DebugAsync(string message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); -#if FORMATSTR + public Task DebugAsync(FormattableString message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); -#endif + } } diff --git a/src/Discord.Net.Core/RequestOptions.cs b/src/Discord.Net.Core/RequestOptions.cs index c2d1f65494..785b518f3e 100644 --- a/src/Discord.Net.Core/RequestOptions.cs +++ b/src/Discord.Net.Core/RequestOptions.cs @@ -25,9 +25,12 @@ public class RequestOptions public RetryMode? RetryMode { get; set; } public bool HeaderOnly { get; internal set; } /// - /// Gets or sets the reason for this action in the guild's audit log. Note that this property may not apply - /// to every action. + /// Gets or sets the reason for this action in the guild's audit log. /// + /// + /// Gets or sets the reason that will be written to the guild's audit log if applicable. This may not apply + /// to all actions. + /// public string AuditLogReason { get; set; } internal bool IgnoreState { get; set; } diff --git a/src/Discord.Net.Core/Utils/DateTimeUtils.cs b/src/Discord.Net.Core/Utils/DateTimeUtils.cs index af21268535..e2a8faa755 100644 --- a/src/Discord.Net.Core/Utils/DateTimeUtils.cs +++ b/src/Discord.Net.Core/Utils/DateTimeUtils.cs @@ -1,57 +1,14 @@ -using System; +using System; namespace Discord { //Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs internal static class DateTimeUtils { -#if !UNIXTIME - private const long UnixEpochTicks = 621_355_968_000_000_000; - private const long UnixEpochSeconds = 62_135_596_800; - private const long UnixEpochMilliseconds = 62_135_596_800_000; -#endif - public static DateTimeOffset FromTicks(long ticks) => new DateTimeOffset(ticks, TimeSpan.Zero); public static DateTimeOffset? FromTicks(long? ticks) => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; - - public static DateTimeOffset FromUnixSeconds(long seconds) - { -#if UNIXTIME - return DateTimeOffset.FromUnixTimeSeconds(seconds); -#else - long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; - return new DateTimeOffset(ticks, TimeSpan.Zero); -#endif - } - public static DateTimeOffset FromUnixMilliseconds(long milliseconds) - { -#if UNIXTIME - return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); -#else - long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; - return new DateTimeOffset(ticks, TimeSpan.Zero); -#endif - } - - public static long ToUnixSeconds(DateTimeOffset dto) - { -#if UNIXTIME - return dto.ToUnixTimeSeconds(); -#else - long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond; - return seconds - UnixEpochSeconds; -#endif - } - public static long ToUnixMilliseconds(DateTimeOffset dto) - { -#if UNIXTIME - return dto.ToUnixTimeMilliseconds(); -#else - long milliseconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; - return milliseconds - UnixEpochMilliseconds; -#endif - } + } } diff --git a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs index c9d0d130be..eecebfb244 100644 --- a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs +++ b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs @@ -5,8 +5,8 @@ namespace Discord public static class SnowflakeUtils { public static DateTimeOffset FromSnowflake(ulong value) - => DateTimeUtils.FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); + => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); public static ulong ToSnowflake(DateTimeOffset value) - => ((ulong)DateTimeUtils.ToUnixMilliseconds(value) - 1420070400000UL) << 22; + => ((ulong)value.ToUnixTimeMilliseconds() - 1420070400000UL) << 22; } } diff --git a/src/Discord.Net.Rest/API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs index 9a7629b963..229249ccf1 100644 --- a/src/Discord.Net.Rest/API/Common/Message.cs +++ b/src/Discord.Net.Rest/API/Common/Message.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; using System; @@ -12,10 +12,16 @@ internal class Message public MessageType Type { get; set; } [JsonProperty("channel_id")] public ulong ChannelId { get; set; } + // ALWAYS sent on WebSocket messages + [JsonProperty("guild_id")] + public Optional GuildId { get; set; } [JsonProperty("webhook_id")] public Optional WebhookId { get; set; } [JsonProperty("author")] public Optional Author { get; set; } + // ALWAYS sent on WebSocket messages + [JsonProperty("member")] + public Optional Member { get; set; } [JsonProperty("content")] public Optional Content { get; set; } [JsonProperty("timestamp")] diff --git a/src/Discord.Net.Rest/API/Common/VoiceState.cs b/src/Discord.Net.Rest/API/Common/VoiceState.cs index 563a5f95b0..b1f937b09a 100644 --- a/src/Discord.Net.Rest/API/Common/VoiceState.cs +++ b/src/Discord.Net.Rest/API/Common/VoiceState.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -11,6 +11,9 @@ internal class VoiceState public ulong? ChannelId { get; set; } [JsonProperty("user_id")] public ulong UserId { get; set; } + // ALWAYS sent over WebSocket, never on REST + [JsonProperty("member")] + public Optional Member { get; set; } [JsonProperty("session_id")] public string SessionId { get; set; } [JsonProperty("deaf")] diff --git a/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs index bae677148b..05cdf4b8af 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Rest @@ -10,9 +10,20 @@ internal class CreateGuildChannelParams public string Name { get; } [JsonProperty("type")] public ChannelType Type { get; } + [JsonProperty("parent_id")] + public Optional CategoryId { get; set; } + //Text channels + [JsonProperty("topic")] + public Optional Topic { get; set; } + [JsonProperty("nsfw")] + public Optional IsNsfw { get; set; } + + //Voice channels [JsonProperty("bitrate")] public Optional Bitrate { get; set; } + [JsonProperty("user_limit")] + public Optional UserLimit { get; set; } public CreateGuildChannelParams(string name, ChannelType type) { diff --git a/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs index d70da5632b..a0967bb75d 100644 --- a/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs @@ -1,4 +1,4 @@ -namespace Discord.API.Rest +namespace Discord.API.Rest { internal class GetReactionUsersParams { diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 0eb07a4b2f..75b69bd043 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,20 +1,19 @@ - + Discord.Net.Rest Discord.Rest A core Discord.Net library containing the REST client and models. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - - - + + - + diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index f235471064..70a2acd154 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -630,11 +630,11 @@ public async Task> GetReactionUsersAsync(ulong channel Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); Preconditions.NotNull(args, nameof(args)); Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); - Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); + Preconditions.AtMost(args.Limit, DiscordConfig.MaxUserReactionsPerBatch, nameof(args.Limit)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); options = RequestOptions.CreateOrClone(options); - int limit = args.Limit.GetValueOrDefault(int.MaxValue); + int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUserReactionsPerBatch); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); var ids = new BucketIds(channelId: channelId); diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs index 4b9d5875f2..2e5331d59a 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to a ban. + /// public class BanAuditLogData : IAuditLogData { private BanAuditLogData(IUser user) @@ -18,6 +21,12 @@ internal static BanAuditLogData Create(BaseDiscordClient discord, Model log, Ent return new BanAuditLogData(RestUser.Create(discord, userInfo)); } + /// + /// Gets the user that was banned. + /// + /// + /// A user object representing the banned user. + /// public IUser Target { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs index ef4787295a..451aac3734 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; @@ -7,6 +6,9 @@ namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to a channel creation. + /// public class ChannelCreateAuditLogData : IAuditLogData { private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) @@ -44,9 +46,33 @@ internal static ChannelCreateAuditLogData Create(BaseDiscordClient discord, Mode return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection()); } + /// + /// Gets the snowflake ID of the created channel. + /// + /// + /// An representing the snowflake identifier for the created channel. + /// public ulong ChannelId { get; } + /// + /// Gets the name of the created channel. + /// + /// + /// A string containing the name of the created channel. + /// public string ChannelName { get; } + /// + /// Gets the type of the created channel. + /// + /// + /// The type of channel that was created. + /// public ChannelType ChannelType { get; } + /// + /// Gets a collection of permission overwrites that was assigned to the created channel. + /// + /// + /// A collection of permission . + /// public IReadOnlyCollection Overwrites { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs index 4816ce770f..7278b7b753 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs @@ -1,14 +1,14 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to a channel deletion. + /// public class ChannelDeleteAuditLogData : IAuditLogData { private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) @@ -37,9 +37,33 @@ internal static ChannelDeleteAuditLogData Create(BaseDiscordClient discord, Mode return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); } + /// + /// Gets the snowflake ID of the deleted channel. + /// + /// + /// An representing the snowflake identifier for the deleted channel. + /// public ulong ChannelId { get; } + /// + /// Gets the name of the deleted channel. + /// + /// + /// A string containing the name of the deleted channel. + /// public string ChannelName { get; } + /// + /// Gets the type of the deleted channel. + /// + /// + /// The type of channel that was deleted. + /// public ChannelType ChannelType { get; } + /// + /// Gets a collection of permission overwrites that was assigned to the deleted channel. + /// + /// + /// A collection of permission . + /// public IReadOnlyCollection Overwrites { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs index e2d6064a9a..82b1296563 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs @@ -1,5 +1,8 @@ namespace Discord.Rest { + /// + /// Represents information for a channel. + /// public struct ChannelInfo { internal ChannelInfo(string name, string topic, int? bitrate, int? limit) @@ -10,9 +13,35 @@ internal ChannelInfo(string name, string topic, int? bitrate, int? limit) UserLimit = limit; } + /// + /// Gets the name of this channel. + /// + /// + /// A string containing the name of this channel. + /// public string Name { get; } + /// + /// Gets the topic of this channel. + /// + /// + /// A string containing the topic of this channel, if any. + /// public string Topic { get; } + /// + /// Gets the bitrate of this channel if applicable. + /// + /// + /// An representing the bitrate set for the voice channel; null if not + /// applicable. + /// public int? Bitrate { get; } + /// + /// Gets the number of users allowed to be in this channel if applicable. + /// + /// + /// An representing the number of users allowed to be in this voice channel; + /// null if not applicable. + /// public int? UserLimit { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs index f3403138da..7cad9eff7d 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to a channel update. + /// public class ChannelUpdateAuditLogData : IAuditLogData { private ChannelUpdateAuditLogData(ulong id, ChannelInfo before, ChannelInfo after) @@ -38,8 +41,14 @@ internal static ChannelUpdateAuditLogData Create(BaseDiscordClient discord, Mode return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); } + /// + /// Gets the snowflake ID of the updated channel. + /// + /// + /// An representing the snowflake identifier for the updated channel. + /// public ulong ChannelId { get; } - public ChannelInfo Before { get; set; } - public ChannelInfo After { get; set; } + public ChannelInfo Before { get; } + public ChannelInfo After { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs index 5d1ef8463d..a114548ccb 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to an emoji creation. + /// public class EmoteCreateAuditLogData : IAuditLogData { private EmoteCreateAuditLogData(ulong id, string name) @@ -25,7 +24,19 @@ internal static EmoteCreateAuditLogData Create(BaseDiscordClient discord, Model return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName); } + /// + /// Gets the snowflake ID of the created emoji. + /// + /// + /// An representing the snowflake identifier for the created emoji. + /// public ulong EmoteId { get; } + /// + /// Gets the name of the created emoji. + /// + /// + /// A string containing the name of the created emoji. + /// public string Name { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs index d0a11191f4..e522b0ee27 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to an emoji deletion. + /// public class EmoteDeleteAuditLogData : IAuditLogData { private EmoteDeleteAuditLogData(ulong id, string name) @@ -22,7 +25,19 @@ internal static EmoteDeleteAuditLogData Create(BaseDiscordClient discord, Model return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName); } + /// + /// Gets the snowflake ID of the deleted emoji. + /// + /// + /// An representing the snowflake identifier for the deleted emoji. + /// public ulong EmoteId { get; } + /// + /// Gets the name of the deleted emoji. + /// + /// + /// A string containing the name of the deleted emoji. + /// public string Name { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs index 60020bcaa4..e8cd765138 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to an emoji update. + /// public class EmoteUpdateAuditLogData : IAuditLogData { private EmoteUpdateAuditLogData(ulong id, string oldName, string newName) @@ -24,8 +27,26 @@ internal static EmoteUpdateAuditLogData Create(BaseDiscordClient discord, Model return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName); } + /// + /// Gets the snowflake ID of the updated emoji. + /// + /// + /// An representing the snowflake identifier of the updated emoji. + /// public ulong EmoteId { get; } + /// + /// Gets the new name of the updated emoji. + /// + /// + /// A string containing the new name of the updated emoji. + /// public string NewName { get; } + /// + /// Gets the old name of the updated emoji. + /// + /// + /// A string containing the old name of the updated emoji. + /// public string OldName { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs index 90865ef722..494aac1338 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs @@ -1,5 +1,8 @@ namespace Discord.Rest { + /// + /// Represents information for a guild. + /// public struct GuildInfo { internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs, @@ -18,14 +21,66 @@ internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs, ContentFilterLevel = filter; } + /// + /// Gets the amount of time (in seconds) a user must be inactive in a voice channel for until they are + /// automatically moved to the AFK voice channel. + /// + /// + /// An representing the amount of time in seconds for a user to be marked as inactive + /// and moved into the AFK voice channel. + /// public int? AfkTimeout { get; } + /// + /// Gets the default message notifications for users who haven't explicitly set their notification settings. + /// public DefaultMessageNotifications? DefaultMessageNotifications { get; } + /// + /// Gets the ID of the AFK voice channel for this guild. + /// + /// + /// An representing the snowflake identifier of the AFK voice channel; null if + /// none is set. + /// public ulong? AfkChannelId { get; } + /// + /// Gets the name of this guild. + /// + /// + /// A string containing the name of this guild. + /// public string Name { get; } + /// + /// Gets the ID of the region hosting this guild's voice channels. + /// public string RegionId { get; } + /// + /// Gets the ID of this guild's icon. + /// + /// + /// A string containing the identifier for the splash image; null if none is set. + /// public string IconHash { get; } + /// + /// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. + /// + /// + /// The level of requirements. + /// public VerificationLevel? VerificationLevel { get; } + /// + /// Gets the owner of this guild. + /// + /// + /// A user object representing the owner of this guild. + /// public IUser Owner { get; } + /// + /// Gets the level of Multi-Factor Authentication requirements a user must fulfill before being allowed to + /// perform administrative actions in this guild. + /// + /// + /// The level of MFA requirement. + /// public MfaLevel? MfaLevel { get; } public int? ContentFilterLevel { get; } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs index 08550ed7a3..b4ffd3bf1e 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to a guild update. + /// public class GuildUpdateAuditLogData : IAuditLogData { private GuildUpdateAuditLogData(GuildInfo before, GuildInfo after) @@ -73,7 +76,19 @@ internal static GuildUpdateAuditLogData Create(BaseDiscordClient discord, Model return new GuildUpdateAuditLogData(before, after); } + /// + /// Gets the guild information before the changes. + /// + /// + /// An information object containing the original guild information before the changes were made. + /// public GuildInfo Before { get; } + /// + /// Gets the guild information after the changes. + /// + /// + /// An information object containing the guild information after the changes were made. + /// public GuildInfo After { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs index 292715420b..df95d13b64 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to an invite creation. + /// public class InviteCreateAuditLogData : IAuditLogData { private InviteCreateAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) @@ -44,12 +47,57 @@ internal static InviteCreateAuditLogData Create(BaseDiscordClient discord, Model return new InviteCreateAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); } + /// + /// Gets the time (in seconds) until the invite expires. + /// + /// + /// An representing the time in seconds until this invite expires. + /// public int MaxAge { get; } + /// + /// Gets the unique identifier for this invite. + /// + /// + /// A string containing the invite code (e.g. FTqNnyS). + /// public string Code { get; } + /// + /// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild + /// when the user logs off). + /// + /// + /// true if users accepting this invite will be removed from the guild when they log off; otherwise + /// false. + /// public bool Temporary { get; } + /// + /// Gets the user that created this invite. + /// + /// + /// A user that created this invite. + /// public IUser Creator { get; } + /// + /// Gets the ID of the channel this invite is linked to. + /// + /// + /// An representing the channel snowflake identifier that the invite points to. + /// public ulong ChannelId { get; } + /// + /// Gets the number of times this invite has been used. + /// + /// + /// An representing the number of times this invite has been used. + /// public int Uses { get; } + /// + /// Gets the max number of uses this invite may have. + /// + /// + /// An representing the number of uses this invite may be accepted until it is removed + /// from the guild; null if none is set. + /// public int MaxUses { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs index 1dc6d518b8..472f227c07 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a piece of audit log data related to an invite removal. + /// public class InviteDeleteAuditLogData : IAuditLogData { private InviteDeleteAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) @@ -44,12 +47,57 @@ internal static InviteDeleteAuditLogData Create(BaseDiscordClient discord, Model return new InviteDeleteAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); } + /// + /// Gets the time (in seconds) until the invite expires. + /// + /// + /// An representing the time in seconds until this invite expires. + /// public int MaxAge { get; } + /// + /// Gets the unique identifier for this invite. + /// + /// + /// A string containing the invite code (e.g. FTqNnyS). + /// public string Code { get; } + /// + /// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild + /// when the user logs off). + /// + /// + /// true if users accepting this invite will be removed from the guild when they log off; otherwise + /// false. + /// public bool Temporary { get; } + /// + /// Gets the user that created this invite. + /// + /// + /// A user that created this invite. + /// public IUser Creator { get; } + /// + /// Gets the ID of the channel this invite is linked to. + /// + /// + /// An representing the channel snowflake identifier that the invite points to. + /// public ulong ChannelId { get; } + /// + /// Gets the number of times this invite has been used. + /// + /// + /// An representing the number of times this invite has been used. + /// public int Uses { get; } + /// + /// Gets the max number of uses this invite may have. + /// + /// + /// An representing the number of uses this invite may be accepted until it is removed + /// from the guild; null if none is set. + /// public int MaxUses { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs index c9840f6cc3..54026dbf38 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs @@ -1,5 +1,8 @@ namespace Discord.Rest { + /// + /// Represents information for an invite. + /// public struct InviteInfo { internal InviteInfo(int? maxAge, string code, bool? temporary, ulong? channelId, int? maxUses) @@ -11,10 +14,45 @@ internal InviteInfo(int? maxAge, string code, bool? temporary, ulong? channelId, MaxUses = maxUses; } + /// + /// Gets the time (in seconds) until the invite expires. + /// + /// + /// An representing the time in seconds until this invite expires; null if this + /// invite never expires or not specified. + /// public int? MaxAge { get; } + /// + /// Gets the unique identifier for this invite. + /// + /// + /// A string containing the invite code (e.g. FTqNnyS). + /// public string Code { get; } + /// + /// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild + /// when the user logs off). + /// + /// + /// true if users accepting this invite will be removed from the guild when they log off, + /// false if not; null if not specified. + /// public bool? Temporary { get; } + /// + /// Gets the ID of the channel this invite is linked to. + /// + /// + /// An representing the channel snowflake identifier that the invite points to; + /// null if not specified. + /// public ulong? ChannelId { get; } + /// + /// Gets the max number of uses this invite may have. + /// + /// + /// An representing the number of uses this invite may be accepted until it is removed + /// from the guild; null if none is specified. + /// public int? MaxUses { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs index b0f0a1fe1b..0d9a2a708f 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +8,7 @@ namespace Discord.Rest { public class MemberRoleAuditLogData : IAuditLogData { - private MemberRoleAuditLogData(IReadOnlyCollection roles, IUser target) + private MemberRoleAuditLogData(IReadOnlyCollection roles, IUser target) { Roles = roles; Target = target; @@ -21,7 +20,7 @@ internal static MemberRoleAuditLogData Create(BaseDiscordClient discord, Model l var roleInfos = changes.SelectMany(x => x.NewValue.ToObject(), (model, role) => new { model.ChangedProperty, Role = role }) - .Select(x => new RoleInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) + .Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) .ToList(); var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); @@ -30,21 +29,7 @@ internal static MemberRoleAuditLogData Create(BaseDiscordClient discord, Model l return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user); } - public IReadOnlyCollection Roles { get; } + public IReadOnlyCollection Roles { get; } public IUser Target { get; } - - public struct RoleInfo - { - internal RoleInfo(string name, ulong roleId, bool added) - { - Name = name; - RoleId = roleId; - Added = added; - } - - public string Name { get; } - public ulong RoleId { get; } - public bool Added { get; } - } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs new file mode 100644 index 0000000000..4838b75c90 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs @@ -0,0 +1,16 @@ +namespace Discord.Rest +{ + public struct MemberRoleEditInfo + { + internal MemberRoleEditInfo(string name, ulong roleId, bool added) + { + Name = name; + RoleId = roleId; + Added = added; + } + + public string Name { get; } + public ulong RoleId { get; } + public bool Added { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs index 38f078848f..7d3d3dba08 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs @@ -1,8 +1,7 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; -using ChangeModel = Discord.API.AuditLogChange; namespace Discord.Rest { diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs index 445c2e3022..dc2f4a6f02 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; -using ChangeModel = Discord.API.AuditLogChange; -using OptionModel = Discord.API.AuditLogOptions; namespace Discord.Rest { diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs index aa951d6e7b..dcc1c6ab65 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleCreateAuditLogData : IAuditLogData { - private RoleCreateAuditLogData(ulong id, RoleInfo props) + private RoleCreateAuditLogData(ulong id, RoleEditInfo props) { RoleId = id; Properties = props; @@ -38,10 +38,10 @@ internal static RoleCreateAuditLogData Create(BaseDiscordClient discord, Model l permissions = new GuildPermissions(permissionsRaw.Value); return new RoleCreateAuditLogData(entry.TargetId.Value, - new RoleInfo(color, mentionable, hoist, name, permissions)); + new RoleEditInfo(color, mentionable, hoist, name, permissions)); } public ulong RoleId { get; } - public RoleInfo Properties { get; } + public RoleEditInfo Properties { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs index e90d70d4d2..263909daf4 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleDeleteAuditLogData : IAuditLogData { - private RoleDeleteAuditLogData(ulong id, RoleInfo props) + private RoleDeleteAuditLogData(ulong id, RoleEditInfo props) { RoleId = id; Properties = props; @@ -38,10 +38,10 @@ internal static RoleDeleteAuditLogData Create(BaseDiscordClient discord, Model l permissions = new GuildPermissions(permissionsRaw.Value); return new RoleDeleteAuditLogData(entry.TargetId.Value, - new RoleInfo(color, mentionable, hoist, name, permissions)); + new RoleEditInfo(color, mentionable, hoist, name, permissions)); } public ulong RoleId { get; } - public RoleInfo Properties { get; } + public RoleEditInfo Properties { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs new file mode 100644 index 0000000000..186ea8d110 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs @@ -0,0 +1,21 @@ +namespace Discord.Rest +{ + public struct RoleEditInfo + { + internal RoleEditInfo(Color? color, bool? mentionable, bool? hoist, string name, + GuildPermissions? permissions) + { + Color = color; + Mentionable = mentionable; + Hoist = hoist; + Name = name; + Permissions = permissions; + } + + public Color? Color { get; } + public bool? Mentionable { get; } + public bool? Hoist { get; } + public string Name { get; } + public GuildPermissions? Permissions { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs deleted file mode 100644 index 2208990e6e..0000000000 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Rest -{ - public struct RoleInfo - { - internal RoleInfo(Color? color, bool? mentionable, bool? hoist, string name, - GuildPermissions? permissions) - { - Color = color; - Mentionable = mentionable; - Hoist = hoist; - Name = name; - Permissions = permissions; - } - - public Color? Color { get; } - public bool? Mentionable { get; } - public bool? Hoist { get; } - public string Name { get; } - public GuildPermissions? Permissions { get; } - } -} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs index be484e2d50..b645ef7ae0 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleUpdateAuditLogData : IAuditLogData { - private RoleUpdateAuditLogData(ulong id, RoleInfo oldProps, RoleInfo newProps) + private RoleUpdateAuditLogData(ulong id, RoleEditInfo oldProps, RoleEditInfo newProps) { RoleId = id; Before = oldProps; @@ -49,14 +49,14 @@ internal static RoleUpdateAuditLogData Create(BaseDiscordClient discord, Model l if (newPermissionsRaw.HasValue) newPermissions = new GuildPermissions(newPermissionsRaw.Value); - var oldProps = new RoleInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions); - var newProps = new RoleInfo(newColor, newMentionable, newHoist, newName, newPermissions); + var oldProps = new RoleEditInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions); + var newProps = new RoleEditInfo(newColor, newMentionable, newHoist, newName, newPermissions); return new RoleUpdateAuditLogData(entry.TargetId.Value, oldProps, newProps); } public ulong RoleId { get; } - public RoleInfo Before { get; } - public RoleInfo After { get; } + public RoleEditInfo Before { get; } + public RoleEditInfo After { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs index 4133d5dff9..2d536d8681 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs index 54da42a8b0..7db7aee36d 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs index 9e30a5014e..0cf616973d 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs @@ -1,10 +1,13 @@ -using System.Linq; +using System.Linq; using Model = Discord.API.AuditLog; using EntryModel = Discord.API.AuditLogEntry; namespace Discord.Rest { + /// + /// Represents a REST-based audit log entry. + /// public class RestAuditLogEntry : RestEntity, IAuditLogEntry { private RestAuditLogEntry(BaseDiscordClient discord, Model fullLog, EntryModel model, IUser user) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 6a3521bff1..0362fa600e 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -162,7 +162,6 @@ public static async Task SendMessageAsync(IMessageChannel chann return RestUserMessage.Create(client, channel, client.CurrentUser, model); } -#if FILESYSTEM /// /// is a zero-length string, contains only white space, or contains one or more /// invalid characters as defined by . @@ -194,7 +193,7 @@ public static async Task SendFileAsync(IMessageChannel channel, using (var file = File.OpenRead(filePath)) return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); } -#endif + /// Message content is too long, length must be less or equal to . public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) @@ -204,6 +203,10 @@ public static async Task SendFileAsync(IMessageChannel channel, return RestUserMessage.Create(client, channel, client.CurrentUser, model); } + public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client, + RequestOptions options) + => MessageHelper.DeleteAsync(channel.Id, messageId, client, options); + public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client, IEnumerable messageIds, RequestOptions options) { @@ -334,6 +337,16 @@ public static async Task> GetWebhooksAsync(ITex return models.Select(x => RestWebhook.Create(client, channel, x)) .ToImmutableArray(); } + // Categories + public static async Task GetCategoryAsync(INestedChannel channel, BaseDiscordClient client, RequestOptions options) + { + // if no category id specified, return null + if (!channel.CategoryId.HasValue) + return null; + // CategoryId will contain a value here + var model = await client.ApiClient.GetChannelAsync(channel.CategoryId.Value, options).ConfigureAwait(false); + return RestCategoryChannel.Create(client, model) as ICategoryChannel; + } //Helpers private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId) @@ -345,10 +358,5 @@ private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel author = RestUser.Create(client, guild, model, webhookId); return author; } - - public static bool IsNsfw(IChannel channel) - => IsNsfw(channel.Name); - public static bool IsNsfw(string channelName) => - channelName == "nsfw" || channelName.StartsWith("nsfw-"); } } diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index b0eed8b255..ac2f27bbdb 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -19,8 +19,7 @@ public interface IRestMessageChannel : IMessageChannel /// /// An awaitable Task containing the message sent to the channel. /// - new Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); /// /// Sends a file to this message channel, with an optional caption. /// @@ -38,7 +37,6 @@ public interface IRestMessageChannel : IMessageChannel /// An awaitable Task containing the message sent to the channel. /// new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#endif /// /// Sends a file to this message channel, with an optional caption. /// diff --git a/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs index eb3fdd94b5..9d69d6bdcd 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs @@ -28,14 +28,6 @@ internal RestCategoryChannel(BaseDiscordClient discord, IGuild guild, ulong id) // IGuildChannel /// /// This method is not supported with category channels. - IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => throw new NotSupportedException(); - /// - /// This method is not supported with category channels. - Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => throw new NotSupportedException(); - /// - /// This method is not supported with category channels. Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) => throw new NotSupportedException(); /// diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs index c6d765787a..dd190199f6 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs @@ -6,6 +6,9 @@ namespace Discord.Rest { + /// + /// Represents a generic REST-based channel. + /// public class RestChannel : RestEntity, IChannel, IUpdateable { /// @@ -26,6 +29,8 @@ internal static RestChannel Create(BaseDiscordClient discord, Model model) case ChannelType.DM: case ChannelType.Group: return CreatePrivate(discord, model) as RestChannel; + case ChannelType.Category: + return RestCategoryChannel.Create(discord, new RestGuild(discord, model.GuildId.Value), model); default: return new RestChannel(discord, model.Id); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index a3161c72cf..eb6fe91053 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -10,7 +10,7 @@ namespace Discord.Rest { /// - /// Represents a REST-based DM channel. + /// Represents a REST-based direct-message channel. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel @@ -74,17 +74,49 @@ public Task> GetPinnedMessagesAsync(RequestOpti => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); /// - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + /// Message content is too long, length must be less or equal to . + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + /// + /// + /// is a zero-length string, contains only white space, or contains one or more + /// invalid characters as defined by . + /// + /// + /// is null. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. For example, on + /// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 + /// characters. + /// + /// + /// The specified path is invalid, (for example, it is on an unmapped drive). + /// + /// + /// specified a directory.-or- The caller does not have the required permission. + /// + /// + /// The file specified in was not found. + /// + /// is in an invalid format. + /// An I/O error occurred while opening the file. + /// Message content is too long, length must be less or equal to . public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif /// + /// Message content is too long, length must be less or equal to . public Task 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 DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + /// + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); @@ -143,12 +175,9 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM - /// async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif - /// + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); /// diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 4b4ccb0578..2f14dc94ff 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -83,11 +83,18 @@ public IAsyncEnumerable> GetMessagesAsync(IMess public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); + /// + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + /// + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + /// /// /// is a zero-length string, contains only white space, or contains one or more @@ -115,7 +122,6 @@ public Task SendMessageAsync(string text, bool isTTS = false, E /// Message content is too long, length must be less or equal to . public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif /// /// Message content is too long, length must be less or equal to . public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) @@ -168,10 +174,9 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task 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 IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 7c3cc53c9b..cc52a9864e 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -8,7 +8,7 @@ namespace Discord.Rest { /// - /// Represents a private REST group channel. + /// Represents a private REST-based group channel. /// public class RestGuildChannel : RestChannel, IGuildChannel { @@ -23,8 +23,6 @@ public class RestGuildChannel : RestChannel, IGuildChannel /// public int Position { get; private set; } /// - public ulong? CategoryId { get; private set; } - /// public ulong GuildId => Guild.Id; internal RestGuildChannel(BaseDiscordClient discord, IGuild guild, ulong id) @@ -43,7 +41,6 @@ internal static RestGuildChannel Create(BaseDiscordClient discord, IGuild guild, case ChannelType.Category: return RestCategoryChannel.Create(discord, guild, model); default: - // TODO: Channel categories return new RestGuildChannel(discord, guild, model.Id); } } @@ -75,14 +72,6 @@ public async Task ModifyAsync(Action func, RequestOption public Task DeleteAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); - /// - public async Task GetCategoryAsync() - { - if (CategoryId.HasValue) - return (await Guild.GetChannelAsync(CategoryId.Value).ConfigureAwait(false)) as ICategoryChannel; - return null; - } - public OverwritePermissions? GetPermissionOverwrite(IUser user) { for (int i = 0; i < _overwrites.Length; i++) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 9752697d60..1cef7ab4c2 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -8,15 +8,22 @@ namespace Discord.Rest { + /// + /// Represents a REST-based channel in a guild that can send and receive messages. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel { + /// public string Topic { get; private set; } + public ulong? CategoryId { get; private set; } + /// public string Mention => MentionUtils.MentionChannel(Id); private bool _nsfw; - public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); + /// + public bool IsNsfw => _nsfw; internal RestTextChannel(BaseDiscordClient discord, IGuild guild, ulong id) : base(discord, guild, id) @@ -31,11 +38,12 @@ internal RestTextChannel(BaseDiscordClient discord, IGuild guild, ulong id) internal override void Update(Model model) { base.Update(model); - + CategoryId = model.CategoryId; Topic = model.Topic.Value; _nsfw = model.Nsfw.GetValueOrDefault(); } + /// public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); @@ -47,36 +55,80 @@ public Task GetUserAsync(ulong id, RequestOptions options = null) public IAsyncEnumerable> GetUsersAsync(RequestOptions options = null) => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); + /// public Task GetMessageAsync(ulong id, RequestOptions options = null) => ChannelHelper.GetMessageAsync(this, Discord, id, options); + /// public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); + /// public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); + /// public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); + /// public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + /// + /// Message content is too long, length must be less or equal to . + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + + /// + /// + /// is a zero-length string, contains only white space, or contains one or more + /// invalid characters as defined by . + /// + /// + /// is null. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. For example, on + /// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 + /// characters. + /// + /// + /// The specified path is invalid, (for example, it is on an unmapped drive). + /// + /// + /// specified a directory.-or- The caller does not have the required permission. + /// + /// + /// The file specified in was not found. + /// + /// is in an invalid format. + /// An I/O error occurred while opening the file. + /// Message content is too long, length must be less or equal to . public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + + /// + /// Message content is too long, length must be less or equal to . public Task 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 DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + /// + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + + /// public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); + /// public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null) => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); + /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) => ChannelHelper.EnterTypingState(this, Discord, options); - + public Task CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null) => ChannelHelper.CreateWebhookAsync(this, Discord, name, avatar, options); public Task GetWebhookAsync(ulong id, RequestOptions options = null) @@ -84,17 +136,24 @@ public Task GetWebhookAsync(ulong id, RequestOptions options = null public Task> GetWebhooksAsync(RequestOptions options = null) => ChannelHelper.GetWebhooksAsync(this, Discord, options); + public Task GetCategoryAsync(RequestOptions options = null) + => ChannelHelper.GetCategoryAsync(this, Discord, options); + private string DebuggerDisplay => $"{Name} ({Id}, Text)"; //ITextChannel + /// async Task ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options) => await CreateWebhookAsync(name, avatar, options).ConfigureAwait(false); + /// async Task ITextChannel.GetWebhookAsync(ulong id, RequestOptions options) => await GetWebhookAsync(id, options).ConfigureAwait(false); + /// async Task> ITextChannel.GetWebhooksAsync(RequestOptions options) => await GetWebhooksAsync(options).ConfigureAwait(false); //IMessageChannel + /// async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -102,6 +161,7 @@ async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, R else return null; } + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -109,6 +169,8 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync else return AsyncEnumerable.Empty>(); } + + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -116,6 +178,7 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync else return AsyncEnumerable.Empty>(); } + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -123,21 +186,26 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync else return AsyncEnumerable.Empty>(); } + /// async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM + /// async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + + /// async Task 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 IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); + /// IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); //IGuildChannel + /// async Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -145,6 +213,7 @@ async Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, Requ else return null; } + /// IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -154,6 +223,7 @@ IAsyncEnumerable> IGuildChannel.GetUsersAsync(Ca } //IChannel + /// async Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -161,6 +231,7 @@ async Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions else return null; } + /// IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -168,5 +239,13 @@ IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mo else return AsyncEnumerable.Empty>(); } + + // INestedChannel + async Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + { + if (CategoryId.HasValue && mode == CacheMode.AllowDownload) + return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel; + return null; + } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index 5b44136e0b..07da22235e 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -8,11 +8,17 @@ namespace Discord.Rest { + /// + /// Represents a REST-based voice channel in a guild. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel { + /// public int Bitrate { get; private set; } + /// public int? UserLimit { get; private set; } + public ulong? CategoryId { get; private set; } internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id) : base(discord, guild, id) @@ -24,20 +30,25 @@ internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id) entity.Update(model); return entity; } + /// internal override void Update(Model model) { base.Update(model); - + CategoryId = model.CategoryId; Bitrate = model.Bitrate.Value; UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } + /// public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); } + public Task GetCategoryAsync(RequestOptions options = null) + => ChannelHelper.GetCategoryAsync(this, Discord, options); + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; //IAudioChannel @@ -46,9 +57,19 @@ public async Task ModifyAsync(Action func, RequestOption Task IAudioChannel.ConnectAsync(Action configAction) => throw new NotSupportedException(); //IGuildChannel + /// Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(null); + /// IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => AsyncEnumerable.Empty>(); + + // INestedChannel + async Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + { + if (CategoryId.HasValue && mode == CacheMode.AllowDownload) + return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel; + return null; + } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs index 049f86c089..0643ecf6c1 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs @@ -33,15 +33,19 @@ public IAsyncEnumerable> GetMessagesAsync(IMess public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM - public Task SendFileAsync(string filePath, string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task 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 DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) @@ -81,10 +85,9 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) - => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + => await SendFileAsync(filePath, text, isTTS, embed, options); + async Task 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 IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 64f195a0dd..195e6f3c0a 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -147,21 +147,37 @@ public static async Task> GetChannelsAsync } /// is null. public static async Task CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, - string name, RequestOptions options) + string name, RequestOptions options, Action func = null) { if (name == null) throw new ArgumentNullException(nameof(name)); - var args = new CreateGuildChannelParams(name, ChannelType.Text); + var props = new TextChannelProperties(); + func?.Invoke(props); + + var args = new CreateGuildChannelParams(name, ChannelType.Text) + { + CategoryId = props.CategoryId, + Topic = props.Topic, + IsNsfw = props.IsNsfw + }; var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); return RestTextChannel.Create(client, guild, model); } /// is null. public static async Task CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, - string name, RequestOptions options) + string name, RequestOptions options, Action func = null) { if (name == null) throw new ArgumentNullException(nameof(name)); - var args = new CreateGuildChannelParams(name, ChannelType.Voice); + var props = new VoiceChannelProperties(); + func?.Invoke(props); + + var args = new CreateGuildChannelParams(name, ChannelType.Voice) + { + CategoryId = props.CategoryId, + Bitrate = props.Bitrate, + UserLimit = props.UserLimit + }; var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); return RestVoiceChannel.Create(client, guild, model); } diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs index 2c65c8b599..ec8f60ae52 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestBan.cs @@ -3,9 +3,18 @@ namespace Discord.Rest { + /// + /// Represents a REST-based ban object. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestBan : IBan { + /// + /// Gets the banned user. + /// + /// + /// A generic object that was banned. + /// public RestUser User { get; } /// public string Reason { get; } @@ -20,6 +29,12 @@ internal static RestBan Create(BaseDiscordClient client, Model model) return new RestBan(RestUser.Create(client, model.User), model.Reason); } + /// + /// Gets the name of the banned user. + /// + /// + /// A string containing the name of the user that was banned. + /// public override string ToString() => User.ToString(); private string DebuggerDisplay => $"{User}: {Reason}"; diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index e301db892d..e003e21308 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -258,10 +258,10 @@ public async Task GetSystemChannelAsync(RequestOptions options } return null; } - public Task CreateTextChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); - public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); + public Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); + public Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -448,11 +448,11 @@ async Task IGuild.GetSystemChannelAsync(CacheMode mode, RequestOpt return null; } /// - async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options) - => await CreateTextChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, Action func, RequestOptions options) + => await CreateTextChannelAsync(name, func, options).ConfigureAwait(false); /// - async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) - => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, Action func, RequestOptions options) + => await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false); /// async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 34e64cd5a4..ac1eacc3a7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -27,10 +27,12 @@ public static async Task ModifyAsync(IMessage msg, BaseDiscordClient clie }; return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } - public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, + public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) + => DeleteAsync(msg.Channel.Id, msg.Id, client, options); + public static async Task DeleteAsync(ulong channelId, ulong msgId, BaseDiscordClient client, RequestOptions options) { - await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); + await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false); } public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) @@ -48,13 +50,38 @@ public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); } - public static async Task> GetReactionUsersAsync(IMessage msg, IEmote emote, - Action func, BaseDiscordClient client, RequestOptions options) + public static IAsyncEnumerable> GetReactionUsersAsync(IMessage msg, IEmote emote, + int? limit, BaseDiscordClient client, RequestOptions options) { - var args = new GetReactionUsersParams(); - func(args); - string emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name); - return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => RestUser.Create(client, u)).ToImmutableArray(); + Preconditions.NotNull(emote, nameof(emote)); + var emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name); + + return new PagedAsyncEnumerable( + DiscordConfig.MaxUserReactionsPerBatch, + async (info, ct) => + { + var args = new GetReactionUsersParams + { + Limit = info.PageSize + }; + + if (info.Position != null) + args.AfterUserId = info.Position.Value; + + var models = await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false); + return models.Select(x => RestUser.Create(client, x)).ToImmutableArray(); + }, + nextPage: (info, lastPage) => + { + if (lastPage.Count != DiscordConfig.MaxUsersPerBatch) + return false; + + info.Position = lastPage.Max(x => x.Id); + return true; + }, + count: limit + ); + } public static async Task PinAsync(IMessage msg, BaseDiscordClient client, diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index de295af94e..7e000fd5f9 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -151,9 +151,9 @@ public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options public Task RemoveAllReactionsAsync(RequestOptions options = null) => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); /// - public Task> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create(); }, Discord, options); - + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); + /// public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); diff --git a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs index 9213c5d750..8a3c1037bb 100644 --- a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs +++ b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs @@ -1,4 +1,4 @@ -using Discord.API; +using Discord.API; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; @@ -25,7 +25,6 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ if (converter != null) { property.Converter = converter; - property.MemberConverter = converter; } } else diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 5894aa7684..a4f87a3e9b 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -230,7 +230,7 @@ private void UpdateRateLimit(int id, RestRequest request, RateLimitInfo info, bo #endif } - var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); + var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); DateTimeOffset? resetTick = null; //Using X-RateLimit-Remaining causes a race condition diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index e9a5fde9ed..d31cc5cddf 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -21,7 +21,7 @@ internal RateLimitInfo(Dictionary headers) Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && int.TryParse(temp, out var remaining) ? remaining : (int?)null; Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && - int.TryParse(temp, out var reset) ? DateTimeUtils.FromUnixSeconds(reset) : (DateTimeOffset?)null; + int.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeSeconds(reset) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) && int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; Lag = headers.TryGetValue("Date", out temp) && diff --git a/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs index 3cce512bdb..5ceae4b7aa 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Gateway @@ -9,6 +9,10 @@ internal class TypingStartEvent public ulong UserId { get; set; } [JsonProperty("channel_id")] public ulong ChannelId { get; set; } + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + [JsonProperty("member")] + public GuildMember Member { get; set; } [JsonProperty("timestamp")] public int Timestamp { get; set; } } diff --git a/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs b/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs new file mode 100644 index 0000000000..8fdb0808fe --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Discord.API.Voice +{ + internal class HelloEvent + { + [JsonProperty("heartbeat_interval")] + public int HeartbeatInterval { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs index 2a134ced18..7188cd8f7c 100644 --- a/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs +++ b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs @@ -1,5 +1,6 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; +using System; namespace Discord.API.Voice { @@ -14,6 +15,7 @@ internal class ReadyEvent [JsonProperty("modes")] public string[] Modes { get; set; } [JsonProperty("heartbeat_interval")] + [Obsolete("This field is errorneous and should not be used", true)] public int HeartbeatInterval { get; set; } } } diff --git a/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs index ae11a4c8f7..67afe6173b 100644 --- a/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs +++ b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 namespace Discord.API.Voice { internal enum VoiceOpCode : byte @@ -11,11 +11,19 @@ internal enum VoiceOpCode : byte Ready = 2, /// C→S - Used to keep the connection alive and measure latency. Heartbeat = 3, - /// C←S - Used to reply to a client's heartbeat. - HeartbeatAck = 3, /// C←S - Used to provide an encryption key to the client. SessionDescription = 4, /// C↔S - Used to inform that a certain user is speaking. - Speaking = 5 + Speaking = 5, + /// C←S - Used to reply to a client's heartbeat. + HeartbeatAck = 6, + /// C→S - Used to resume a connection. + Resume = 7, + /// C←S - Used to inform the client the heartbeat interval. + Hello = 8, + /// C←S - Used to acknowledge a resumed connection. + Resumed = 9, + /// C←S - Used to notify that a client has disconnected. + ClientDisconnect = 13, } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index c3960fa673..e6cf203123 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -107,7 +107,7 @@ public async Task StopAsync() private async Task OnConnectingAsync() { await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); - await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false); + await ApiClient.ConnectAsync("wss://" + _url + "?v=" + DiscordConfig.VoiceAPIVersion).ConfigureAwait(false); await _audioLogger.DebugAsync("Listening on port " + ApiClient.UdpPort).ConfigureAwait(false); await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); @@ -216,6 +216,14 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) { switch (opCode) { + case VoiceOpCode.Hello: + { + await _audioLogger.DebugAsync("Received Hello").ConfigureAwait(false); + var data = (payload as JToken).ToObject(_serializer); + + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); + } + break; case VoiceOpCode.Ready: { await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false); @@ -225,8 +233,6 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) if (!data.Modes.Contains(DiscordVoiceAPIClient.Mode)) throw new InvalidOperationException($"Discord does not support {DiscordVoiceAPIClient.Mode}"); - - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); ApiClient.SetUdpEndpoint(data.Ip, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index c236b10458..db4eb9c94d 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -7,6 +7,17 @@ public partial class BaseSocketClient { //Channels /// Fired when a channel is created. + /// + /// + /// This event is fired when a generic channel has been created. The event handler must return a + /// and accept a as its parameter. + /// + /// + /// The newly created channel is passed into the event handler parameter. The given channel type may + /// include, but not limited to, Private Channels (DM, Group), Guild Channels (Text, Voice, Category); + /// see the derived classes of for more details. + /// + /// public event Func ChannelCreated { add { _channelCreatedEvent.Add(value); } @@ -14,12 +25,35 @@ public event Func ChannelCreated } internal readonly AsyncEvent> _channelCreatedEvent = new AsyncEvent>(); /// Fired when a channel is destroyed. + /// + /// + /// This event is fired when a generic channel has been destroyed. The event handler must return a + /// and accept a as its parameter. + /// + /// + /// The destroyed channel is passed into the event handler parameter. The given channel type may + /// include, but not limited to, Private Channels (DM, Group), Guild Channels (Text, Voice, Category); + /// see the derived classes of for more details. + /// + /// public event Func ChannelDestroyed { add { _channelDestroyedEvent.Add(value); } remove { _channelDestroyedEvent.Remove(value); } } internal readonly AsyncEvent> _channelDestroyedEvent = new AsyncEvent>(); /// Fired when a channel is updated. + /// + /// + /// This event is fired when a generic channel has been destroyed. The event handler must return a + /// and accept 2 as its parameters. + /// + /// + /// The original (prior to update) channel is passed into the first , while + /// the updated channel is passed into the second. The given channel type may include, but not limited + /// to, Private Channels (DM, Group), Guild Channels (Text, Voice, Category); see the derived classes of + /// for more details. + /// + /// public event Func ChannelUpdated { add { _channelUpdatedEvent.Add(value); } remove { _channelUpdatedEvent.Remove(value); } @@ -28,18 +62,72 @@ public event Func ChannelUpdated { //Messages /// Fired when a message is received. + /// + /// + /// This event is fired when a message is received. The event handler must return a + /// and accept a as its parameter. + /// + /// + /// The message that is sent to the client is passed into the event handler parameter as + /// . This message may be a system message (i.e. + /// ) or a user message (i.e. . See the + /// derived clsases of for more details. + /// + /// public event Func MessageReceived { add { _messageReceivedEvent.Add(value); } remove { _messageReceivedEvent.Remove(value); } } internal readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); /// Fired when a message is deleted. + /// + /// + /// This event is fired when a message is deleted. The event handler must return a + /// and accept a and + /// as its parameters. + /// + /// + /// + /// It is not possible to retrieve the message via + /// ; the message cannot be retrieved by Discord + /// after the message has been deleted. + /// + /// If caching is enabled via , the + /// entity will contain the deleted message; otherwise, in event + /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the + /// . + /// + /// + /// The source channel of the removed message will be passed into the + /// parameter. + /// + /// public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } internal readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); /// Fired when a message is updated. + /// + /// + /// This event is fired when a message is updated. The event handler must return a + /// and accept a , , + /// and as its parameters. + /// + /// + /// If caching is enabled via , the + /// entity will contain the original message; otherwise, in event + /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the + /// . + /// + /// + /// The updated message will be passed into the parameter. + /// + /// + /// The source channel of the updated message will be passed into the + /// parameter. + /// + /// public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index 858fec7fed..e9d30a30dc 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -76,7 +76,7 @@ private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config /// /// /// - /// A WebSocket-based generic user; null when the user cannot be found. + /// A generic WebSocket-based user; null when the user cannot be found. /// public abstract SocketUser GetUser(ulong id); @@ -98,7 +98,7 @@ private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config /// /// /// - /// A WebSocket-based generic user; null when the user cannot be found. + /// A generic WebSocket-based user; null when the user cannot be found. /// public abstract SocketUser GetUser(string username, string discriminator); /// diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 251cdb18ba..ddd3b7954c 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,11 +1,11 @@ - + Discord.Net.WebSocket Discord.WebSocket A core Discord.Net library containing the WebSocket client and models. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 true @@ -13,6 +13,6 @@ - + diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index dc4e720208..d147f365be 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -378,7 +378,7 @@ private async Task SendStatusAsync() await ApiClient.SendStatusUpdateAsync( status, status == UserStatus.AFK, - statusSince != null ? DateTimeUtils.ToUnixMilliseconds(_statusSince.Value) : (long?)null, + statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, gameModel).ConfigureAwait(false); } @@ -1104,7 +1104,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty if (author == null) { if (guild != null) - author = guild.AddOrUpdateUser(data.Author.Value); //User has no guild-specific data + author = guild.AddOrUpdateUser(data.Member.Value); //per g250k, we can create an entire member now else if (channel is SocketGroupChannel) author = (channel as SocketGroupChannel).GetOrAddUser(data.Author.Value); else @@ -1374,6 +1374,11 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty } var user = (channel as SocketChannel).GetUser(data.UserId); + if (user == null) + { + if (guild != null) + user = guild.AddOrUpdateUser(data.Member); + } if (user != null) await TimedInvokeAsync(_userIsTypingEvent, nameof(UserIsTyping), user, channel).ConfigureAwait(false); } @@ -1437,7 +1442,9 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty after = SocketVoiceState.Create(null, data); } - user = guild.GetUser(data.UserId); + // per g250k, this should always be sent, but apparently not always + user = guild.GetUser(data.UserId) + ?? (data.Member.IsSpecified ? guild.AddOrUpdateUser(data.Member.Value) : null); if (user == null) { await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index 25dc2cf7b0..0eb92caed7 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Discord.API; using Discord.API.Voice; using Discord.Net.Converters; @@ -129,7 +129,7 @@ public async Task SendAsync(byte[] data, int offset, int bytes) //WebSocket public async Task SendHeartbeatAsync(RequestOptions options = null) { - await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false); + await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false); } public async Task SendIdentityAsync(ulong userId, string sessionId, string token) { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index c37311a41a..73fc3c34c1 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -28,8 +28,7 @@ public interface ISocketMessageChannel : IMessageChannel /// /// An awaitable Task containing the message sent to the channel. /// - new Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); /// /// Sends a file to this message channel, with an optional caption. /// @@ -47,7 +46,6 @@ public interface ISocketMessageChannel : IMessageChannel /// An awaitable Task containing the message sent to the channel. /// new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#endif /// /// Sends a file to this message channel, with an optional caption. /// diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs index 37e6afef10..4c224e09ac 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs @@ -21,7 +21,7 @@ public override IReadOnlyCollection Users ChannelPermission.ViewChannel)).ToImmutableArray(); public IReadOnlyCollection Channels - => Guild.Channels.Where(x => x.CategoryId == Id).ToImmutableArray(); + => Guild.Channels.Where(x => x is INestedChannel nestedChannel && nestedChannel.CategoryId == Id).ToImmutableArray(); internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) : base(discord, id, guild) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 763296590e..af8854f213 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -79,18 +79,22 @@ public Task> GetPinnedMessagesAsync(RequestOpti /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + /// public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif /// /// Message content is too long, length must be less or equal to . public Task 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 DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); @@ -160,12 +164,8 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync /// async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM - /// async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif - /// async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); /// diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 57fcc51a28..5eb2a6e122 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -109,17 +109,21 @@ public Task> GetPinnedMessagesAsync(RequestOpti /// /// Message content is too long, length must be less or equal to . - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + /// public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif /// public Task 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 DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); @@ -235,11 +239,10 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync /// async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM + /// async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif /// async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 1151f0141d..075b2866cf 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -27,17 +27,7 @@ public class SocketGuildChannel : SocketChannel, IGuildChannel /// public string Name { get; private set; } /// - public int Position { get; private set; } - /// - public ulong? CategoryId { get; private set; } - /// - /// Gets the parent category of this channel. - /// - /// - /// A parent category ID associated with this channel, or null if none is set. - /// - public ICategoryChannel Category - => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; + public int Position { get; private set; } /// public IReadOnlyCollection PermissionOverwrites => _overwrites; @@ -73,8 +63,7 @@ internal override void Update(ClientState state, Model model) { Name = model.Name.Value; Position = model.Position.Value; - CategoryId = model.CategoryId; - + var overwrites = model.PermissionOverwrites.Value; var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); for (int i = 0; i < overwrites.Length; i++) @@ -168,10 +157,6 @@ public async Task CreateInviteAsync(int? maxAge = 86400, int /// ulong IGuildChannel.GuildId => Guild.Id; - /// - Task IGuildChannel.GetCategoryAsync() - => Task.FromResult(Category); - /// async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 92bc07c60b..5ff5324b6a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -20,10 +20,13 @@ public class SocketTextChannel : SocketGuildChannel, ITextChannel, ISocketMessag /// public string Topic { get; private set; } + public ulong? CategoryId { get; private set; } + public ICategoryChannel Category + => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; private bool _nsfw; /// - public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); + public bool IsNsfw => _nsfw; /// public string Mention => MentionUtils.MentionChannel(Id); @@ -50,7 +53,7 @@ internal SocketTextChannel(DiscordSocketClient discord, ulong id, SocketGuild gu internal override void Update(ClientState state, Model model) { base.Update(state, model); - + CategoryId = model.CategoryId; Topic = model.Topic.Value; _nsfw = model.Nsfw.GetValueOrDefault(); } @@ -90,13 +93,12 @@ public Task> GetPinnedMessagesAsync(RequestOpti => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); /// - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + /// public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif /// public Task 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); @@ -108,6 +110,11 @@ public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions o public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null) => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); @@ -209,11 +216,10 @@ IAsyncEnumerable> IMessageChannel.GetMessagesAsync /// async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM + /// async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif /// async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); @@ -223,5 +229,9 @@ async Task IMessageChannel.SendMessageAsync(string text, bool isTT /// IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); + + // INestedChannel + Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(Category); } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index 568c5ad7b7..ee932e0cdd 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -20,6 +20,9 @@ public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudi public int Bitrate { get; private set; } /// public int? UserLimit { get; private set; } + public ulong? CategoryId { get; private set; } + public ICategoryChannel Category + => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; /// public override IReadOnlyCollection Users @@ -38,7 +41,7 @@ internal SocketVoiceChannel(DiscordSocketClient discord, ulong id, SocketGuild g internal override void Update(ClientState state, Model model) { base.Update(state, model); - + CategoryId = model.CategoryId; Bitrate = model.Bitrate.Value; UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } @@ -61,7 +64,7 @@ public override SocketGuildUser GetUser(ulong id) return user; return null; } - + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel; @@ -72,5 +75,9 @@ Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOpt /// IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => ImmutableArray.Create>(Users).ToAsyncEnumerable(); + + // INestedChannel + Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(Category); } } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index ca8f54d0b4..81106ec483 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -463,9 +463,8 @@ public SocketVoiceChannel GetVoiceChannel(ulong id) /// /// The created text channel. /// - public Task CreateTextChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); - + public Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); /// /// Creates a voice channel with the provided name. /// @@ -475,9 +474,8 @@ public Task CreateTextChannelAsync(string name, RequestOptions /// /// The created voice channel. /// - public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); - + public Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); /// /// Creates a category channel with the provided name. /// @@ -930,11 +928,11 @@ Task IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions o Task IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) => Task.FromResult(SystemChannel); /// - async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options) - => await CreateTextChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, Action func, RequestOptions options) + => await CreateTextChannelAsync(name, func, options).ConfigureAwait(false); /// - async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) - => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, Action func, RequestOptions options) + => await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false); /// async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index d224646526..7730e53634 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -150,8 +150,8 @@ public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options public Task RemoveAllReactionsAsync(RequestOptions options = null) => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); /// - public Task> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create(); }, Discord, options); + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); /// public Task PinAsync(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs index 6a6194397f..251a761d40 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -1,4 +1,3 @@ -#if DEFAULTUDPCLIENT using System; using System.Net; using System.Net.Sockets; @@ -85,11 +84,7 @@ public async Task StopInternalAsync(bool isDisposing = false) if (_udp != null) { -#if UDPDISPOSE try { _udp.Dispose(); } -#else - try { _udp.Close(); } -#endif catch { } _udp = null; } @@ -132,4 +127,3 @@ private async Task RunAsync(CancellationToken cancelToken) } } } -#endif \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs index 82b6ec4c04..d701fa79ab 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs @@ -4,7 +4,6 @@ namespace Discord.Net.Udp { public static class DefaultUdpSocketProvider { -#if DEFAULTUDPCLIENT public static readonly UdpSocketProvider Instance = () => { try @@ -16,12 +15,5 @@ public static class DefaultUdpSocketProvider throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.", ex); } }; -#else - public static readonly UdpSocketProvider Instance = () => - { - throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.\n" + - "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); - }; -#endif } -} \ No newline at end of file +} diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index 2301d3e456..c60368da06 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -1,4 +1,3 @@ -#if DEFAULTWEBSOCKET using System; using System.Collections.Generic; using System.ComponentModel; @@ -209,14 +208,9 @@ private async Task RunAsync(CancellationToken cancelToken) //Use the internal buffer if we can get it resultCount = (int)stream.Length; -#if MSTRYBUFFER - if (stream.TryGetBuffer(out var streamBuffer)) - result = streamBuffer.Array; - else - result = stream.ToArray(); -#else - result = stream.GetBuffer(); -#endif + + result = stream.TryGetBuffer(out var streamBuffer) ? streamBuffer.Array : stream.ToArray(); + } } else @@ -248,4 +242,3 @@ private async Task RunAsync(CancellationToken cancelToken) } } } -#endif diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs index ca05d1c56c..bc580c4109 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs @@ -5,7 +5,6 @@ namespace Discord.Net.WebSockets { public static class DefaultWebSocketProvider { -#if DEFAULTWEBSOCKET public static readonly WebSocketProvider Instance = Create(); /// The default WebSocketProvider is not supported on this platform. @@ -23,12 +22,5 @@ public static WebSocketProvider Create(IWebProxy proxy = null) } }; } -#else - public static readonly WebSocketProvider Instance = () => - { - throw new PlatformNotSupportedException("The default WebSocketProvider is not supported on this platform.\n" + - "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); - }; -#endif } } diff --git a/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj b/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj index a35e5d5787..ba7bbcff8c 100644 --- a/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj +++ b/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj @@ -1,10 +1,10 @@ - + Discord.Net.Webhook Discord.Webhook A core Discord.Net library containing the Webhook client and models. - netstandard1.1;netstandard1.3 + netstandard1.3 diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 088988c9af..16841e9364 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -62,20 +62,17 @@ private DiscordWebhookClient(DiscordRestConfig config) } private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); - /// Sends a message using to the channel for this webhook. /// Returns the ID of the created message. - public Task SendMessageAsync(string text, bool isTTS = false, IEnumerable embeds = null, + public Task SendMessageAsync(string text = null, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, options); -#if FILESYSTEM /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFileAsync(string filePath, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) => WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl, options); -#endif /// Sends a message to the channel for this webhook with an attachment. /// Returns the ID of the created message. public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs index 07148f34aa..a02cb3e2f6 100644 --- a/src/Discord.Net.Webhook/WebhookClientHelper.cs +++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs @@ -34,7 +34,6 @@ public static async Task SendMessageAsync(DiscordWebhookClient client, var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false); return model.Id; } -#if FILESYSTEM public static async Task SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, RequestOptions options) { @@ -42,7 +41,6 @@ public static async Task SendFileAsync(DiscordWebhookClient client, strin using (var file = File.OpenRead(filePath)) return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, options).ConfigureAwait(false); } -#endif public static async Task SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, RequestOptions options) { diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 204dca5c4f..60491a96fa 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -21,10 +21,10 @@ - - - - - + + + + + diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs index b37a1195e4..dd87c2e24b 100644 --- a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -22,6 +22,27 @@ public Task TestChannelPermission() var copy = perm.Modify(); Assert.Equal((ulong)0, copy.RawValue); + // test modify with no parameters after using all + copy = ChannelPermissions.Text; + var modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Text.RawValue, modified.RawValue); + + copy = ChannelPermissions.Voice; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Voice.RawValue, modified.RawValue); + + copy = ChannelPermissions.Group; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Group.RawValue, modified.RawValue); + + copy = ChannelPermissions.DM; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.DM.RawValue, modified.RawValue); + + copy = new ChannelPermissions(useExternalEmojis: true); + modified = copy.Modify(); + Assert.Equal(copy.RawValue, modified.RawValue); + // test the values that are returned by ChannelPermission.All Assert.Equal((ulong)0, ChannelPermissions.None.RawValue); @@ -47,6 +68,7 @@ public Task TestChannelPermission() ulong voiceChannel = (ulong)( ChannelPermission.CreateInstantInvite | ChannelPermission.ManageChannels + | ChannelPermission.ViewChannel | ChannelPermission.Connect | ChannelPermission.Speak | ChannelPermission.MuteMembers @@ -61,7 +83,7 @@ public Task TestChannelPermission() ulong dmChannel = (ulong)( ChannelPermission.ViewChannel | ChannelPermission.SendMessages - | ChannelPermission.EmbedLinks + | ChannelPermission.EmbedLinks | ChannelPermission.AttachFiles | ChannelPermission.ReadMessageHistory | ChannelPermission.UseExternalEmojis @@ -84,7 +106,7 @@ public Task TestChannelPermission() Assert.Equal(groupChannel, ChannelPermissions.Group.RawValue); return Task.CompletedTask; } - + [Fact] public Task TestChannelPermissionModify() { // test channel permission modify @@ -97,7 +119,7 @@ public Task TestChannelPermissionModify() // ensure that when modified it works perm = perm.Modify(createInstantInvite: true); Assert.True(perm.CreateInstantInvite); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.CreateInstantInvite); + Assert.Equal((ulong)ChannelPermission.CreateInstantInvite, perm.RawValue); // set false again, move on to next permission perm = perm.Modify(createInstantInvite: false); @@ -109,7 +131,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(manageChannel: true); Assert.True(perm.ManageChannel); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageChannels); + Assert.Equal((ulong)ChannelPermission.ManageChannels, perm.RawValue); perm = perm.Modify(manageChannel: false); Assert.False(perm.ManageChannel); @@ -120,7 +142,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(addReactions: true); Assert.True(perm.AddReactions); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.AddReactions); + Assert.Equal((ulong)ChannelPermission.AddReactions, perm.RawValue); perm = perm.Modify(addReactions: false); Assert.False(perm.AddReactions); @@ -131,7 +153,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(viewChannel: true); Assert.True(perm.ViewChannel); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ViewChannel); + Assert.Equal((ulong)ChannelPermission.ViewChannel, perm.RawValue); perm = perm.Modify(viewChannel: false); Assert.False(perm.ViewChannel); @@ -142,7 +164,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(sendMessages: true); Assert.True(perm.SendMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.SendMessages); + Assert.Equal((ulong)ChannelPermission.SendMessages, perm.RawValue); perm = perm.Modify(sendMessages: false); Assert.False(perm.SendMessages); @@ -153,7 +175,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(sendTTSMessages: true); Assert.True(perm.SendTTSMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.SendTTSMessages); + Assert.Equal((ulong)ChannelPermission.SendTTSMessages, perm.RawValue); perm = perm.Modify(sendTTSMessages: false); Assert.False(perm.SendTTSMessages); @@ -164,7 +186,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(manageMessages: true); Assert.True(perm.ManageMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageMessages); + Assert.Equal((ulong)ChannelPermission.ManageMessages, perm.RawValue); perm = perm.Modify(manageMessages: false); Assert.False(perm.ManageMessages); @@ -175,7 +197,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(embedLinks: true); Assert.True(perm.EmbedLinks); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.EmbedLinks); + Assert.Equal((ulong)ChannelPermission.EmbedLinks, perm.RawValue); perm = perm.Modify(embedLinks: false); Assert.False(perm.EmbedLinks); @@ -186,7 +208,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(attachFiles: true); Assert.True(perm.AttachFiles); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.AttachFiles); + Assert.Equal((ulong)ChannelPermission.AttachFiles, perm.RawValue); perm = perm.Modify(attachFiles: false); Assert.False(perm.AttachFiles); @@ -197,7 +219,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(readMessageHistory: true); Assert.True(perm.ReadMessageHistory); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ReadMessageHistory); + Assert.Equal((ulong)ChannelPermission.ReadMessageHistory, perm.RawValue); perm = perm.Modify(readMessageHistory: false); Assert.False(perm.ReadMessageHistory); @@ -208,7 +230,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(mentionEveryone: true); Assert.True(perm.MentionEveryone); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MentionEveryone); + Assert.Equal((ulong)ChannelPermission.MentionEveryone, perm.RawValue); perm = perm.Modify(mentionEveryone: false); Assert.False(perm.MentionEveryone); @@ -219,7 +241,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(useExternalEmojis: true); Assert.True(perm.UseExternalEmojis); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.UseExternalEmojis); + Assert.Equal((ulong)ChannelPermission.UseExternalEmojis, perm.RawValue); perm = perm.Modify(useExternalEmojis: false); Assert.False(perm.UseExternalEmojis); @@ -230,18 +252,18 @@ public Task TestChannelPermissionModify() perm = perm.Modify(connect: true); Assert.True(perm.Connect); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.Connect); + Assert.Equal((ulong)ChannelPermission.Connect, perm.RawValue); perm = perm.Modify(connect: false); Assert.False(perm.Connect); Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); - + // individual permission test Assert.False(perm.Speak); perm = perm.Modify(speak: true); Assert.True(perm.Speak); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.Speak); + Assert.Equal((ulong)ChannelPermission.Speak, perm.RawValue); perm = perm.Modify(speak: false); Assert.False(perm.Speak); @@ -252,7 +274,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(muteMembers: true); Assert.True(perm.MuteMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MuteMembers); + Assert.Equal((ulong)ChannelPermission.MuteMembers, perm.RawValue); perm = perm.Modify(muteMembers: false); Assert.False(perm.MuteMembers); @@ -263,7 +285,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(deafenMembers: true); Assert.True(perm.DeafenMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.DeafenMembers); + Assert.Equal((ulong)ChannelPermission.DeafenMembers, perm.RawValue); perm = perm.Modify(deafenMembers: false); Assert.False(perm.DeafenMembers); @@ -274,7 +296,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(moveMembers: true); Assert.True(perm.MoveMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MoveMembers); + Assert.Equal((ulong)ChannelPermission.MoveMembers, perm.RawValue); perm = perm.Modify(moveMembers: false); Assert.False(perm.MoveMembers); @@ -285,7 +307,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(useVoiceActivation: true); Assert.True(perm.UseVAD); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.UseVAD); + Assert.Equal((ulong)ChannelPermission.UseVAD, perm.RawValue); perm = perm.Modify(useVoiceActivation: false); Assert.False(perm.UseVAD); @@ -296,7 +318,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(manageRoles: true); Assert.True(perm.ManageRoles); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageRoles); + Assert.Equal((ulong)ChannelPermission.ManageRoles, perm.RawValue); perm = perm.Modify(manageRoles: false); Assert.False(perm.ManageRoles); @@ -307,7 +329,7 @@ public Task TestChannelPermissionModify() perm = perm.Modify(manageWebhooks: true); Assert.True(perm.ManageWebhooks); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageWebhooks); + Assert.Equal((ulong)ChannelPermission.ManageWebhooks, perm.RawValue); perm = perm.Modify(manageWebhooks: false); Assert.False(perm.ManageWebhooks); diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs index b528ca5fb5..46e28b9da1 100644 --- a/test/Discord.Net.Tests/Tests.Channels.cs +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -1,4 +1,5 @@ using Discord.Rest; +using System; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -15,17 +16,28 @@ internal static async Task Migration_CreateTextChannels(DiscordRestClient client var text4 = await guild.CreateTextChannelAsync("text4"); var text5 = await guild.CreateTextChannelAsync("text5"); + // create a channel category + var cat1 = await guild.CreateCategoryChannelAsync("cat1"); + + if (text1 == null) + { + // the guild did not have a default channel, so make a new one + text1 = await guild.CreateTextChannelAsync("default"); + } + //Modify #general await text1.ModifyAsync(x => { x.Name = "text1"; x.Position = 1; x.Topic = "Topic1"; + x.CategoryId = cat1.Id; }); await text2.ModifyAsync(x => { x.Position = 2; + x.CategoryId = cat1.Id; }); await text3.ModifyAsync(x => { @@ -49,35 +61,35 @@ public async Task TestTextChannels() } private static void CheckTextChannels(RestGuild guild, params RestTextChannel[] textChannels) { - Assert.Equal(textChannels.Length, 5); + Assert.Equal(5, textChannels.Length); Assert.All(textChannels, x => { Assert.NotNull(x); - Assert.NotEqual(x.Id, 0UL); + Assert.NotEqual(0UL, x.Id); Assert.True(x.Position >= 0); }); - var text1 = textChannels.Where(x => x.Name == "text1").FirstOrDefault(); - var text2 = textChannels.Where(x => x.Name == "text2").FirstOrDefault(); - var text3 = textChannels.Where(x => x.Name == "text3").FirstOrDefault(); - var text4 = textChannels.Where(x => x.Name == "text4").FirstOrDefault(); - var text5 = textChannels.Where(x => x.Name == "text5").FirstOrDefault(); + var text1 = textChannels.FirstOrDefault(x => x.Name == "text1"); + var text2 = textChannels.FirstOrDefault(x => x.Name == "text2"); + var text3 = textChannels.FirstOrDefault(x => x.Name == "text3"); + var text4 = textChannels.FirstOrDefault(x => x.Name == "text4"); + var text5 = textChannels.FirstOrDefault(x => x.Name == "text5"); Assert.NotNull(text1); //Assert.True(text1.Id == guild.DefaultChannelId); - Assert.Equal(text1.Position, 1); - Assert.Equal(text1.Topic, "Topic1"); + Assert.Equal(1, text1.Position); + Assert.Equal("Topic1", text1.Topic); Assert.NotNull(text2); - Assert.Equal(text2.Position, 2); + Assert.Equal(2, text2.Position); Assert.Null(text2.Topic); Assert.NotNull(text3); - Assert.Equal(text3.Topic, "Topic2"); + Assert.Equal("Topic2", text3.Topic); Assert.NotNull(text4); - Assert.Equal(text4.Position, 3); - Assert.Equal(text4.Topic, "Topic2"); + Assert.Equal(3, text4.Position); + Assert.Equal("Topic2", text4.Topic); Assert.NotNull(text5); Assert.Null(text5.Topic); @@ -89,10 +101,13 @@ internal static async Task Migration_CreateVoiceChannels(DiscordRestClient clien var voice2 = await guild.CreateVoiceChannelAsync("voice2"); var voice3 = await guild.CreateVoiceChannelAsync("voice3"); + var cat2 = await guild.CreateCategoryChannelAsync("cat2"); + await voice1.ModifyAsync(x => { x.Bitrate = 96000; x.Position = 1; + x.CategoryId = cat2.Id; }); await voice2.ModifyAsync(x => { @@ -103,6 +118,7 @@ await voice3.ModifyAsync(x => x.Bitrate = 8000; x.Position = 1; x.UserLimit = 16; + x.CategoryId = cat2.Id; }); CheckVoiceChannels(voice1, voice2, voice3); @@ -114,31 +130,89 @@ public async Task TestVoiceChannels() } private static void CheckVoiceChannels(params RestVoiceChannel[] voiceChannels) { - Assert.Equal(voiceChannels.Length, 3); + Assert.Equal(3, voiceChannels.Length); Assert.All(voiceChannels, x => { Assert.NotNull(x); - Assert.NotEqual(x.Id, 0UL); - Assert.NotEqual(x.UserLimit, 0); + Assert.NotEqual(0UL, x.Id); + Assert.NotEqual(0, x.UserLimit); Assert.True(x.Bitrate > 0); Assert.True(x.Position >= 0); }); - var voice1 = voiceChannels.Where(x => x.Name == "voice1").FirstOrDefault(); - var voice2 = voiceChannels.Where(x => x.Name == "voice2").FirstOrDefault(); - var voice3 = voiceChannels.Where(x => x.Name == "voice3").FirstOrDefault(); + var voice1 = voiceChannels.FirstOrDefault(x => x.Name == "voice1"); + var voice2 = voiceChannels.FirstOrDefault(x => x.Name == "voice2"); + var voice3 = voiceChannels.FirstOrDefault(x => x.Name == "voice3"); Assert.NotNull(voice1); - Assert.Equal(voice1.Bitrate, 96000); - Assert.Equal(voice1.Position, 1); + Assert.Equal(96000, voice1.Bitrate); + Assert.Equal(1, voice1.Position); Assert.NotNull(voice2); - Assert.Equal(voice2.UserLimit, null); + Assert.Null(voice2.UserLimit); Assert.NotNull(voice3); - Assert.Equal(voice3.Bitrate, 8000); - Assert.Equal(voice3.Position, 1); - Assert.Equal(voice3.UserLimit, 16); + Assert.Equal(8000, voice3.Bitrate); + Assert.Equal(1, voice3.Position); + Assert.Equal(16, voice3.UserLimit); + } + + [Fact] + public async Task TestChannelCategories() + { + // (await _guild.GetVoiceChannelsAsync()).ToArray() + var channels = await _guild.GetCategoryChannelsAsync(); + + await CheckChannelCategories(channels.ToArray(), (await _guild.GetChannelsAsync()).ToArray()); + } + + private async Task CheckChannelCategories(RestCategoryChannel[] categories, RestGuildChannel[] allChannels) + { + // 2 categories + Assert.Equal(2, categories.Length); + + var cat1 = categories.Where(x => x.Name == "cat1").FirstOrDefault(); + var cat2 = categories.Where(x => x.Name == "cat2").FirstOrDefault(); + + Assert.NotNull(cat1); + Assert.NotNull(cat2); + + // get text1, text2, ensure they have category id == cat1 + var text1 = allChannels.Where(x => x.Name == "text1").FirstOrDefault() as RestTextChannel; + var text2 = allChannels.Where(x => x.Name == "text2").FirstOrDefault() as RestTextChannel; + + Assert.NotNull(text1); + Assert.NotNull(text2); + + // check that CategoryID and .GetCategoryAsync work correctly + // for both of the text channels + Assert.Equal(text1.CategoryId, cat1.Id); + var text1Cat = await text1.GetCategoryAsync(); + Assert.Equal(text1Cat.Id, cat1.Id); + Assert.Equal(text1Cat.Name, cat1.Name); + + Assert.Equal(text2.CategoryId, cat1.Id); + var text2Cat = await text2.GetCategoryAsync(); + Assert.Equal(text2Cat.Id, cat1.Id); + Assert.Equal(text2Cat.Name, cat1.Name); + + // do the same for the voice channels + var voice1 = allChannels.Where(x => x.Name == "voice1").FirstOrDefault() as RestVoiceChannel; + var voice3 = allChannels.Where(x => x.Name == "voice3").FirstOrDefault() as RestVoiceChannel; + + Assert.NotNull(voice1); + Assert.NotNull(voice3); + + Assert.Equal(voice1.CategoryId, cat2.Id); + var voice1Cat = await voice1.GetCategoryAsync(); + Assert.Equal(voice1Cat.Id, cat2.Id); + Assert.Equal(voice1Cat.Name, cat2.Name); + + Assert.Equal(voice3.CategoryId, cat2.Id); + var voice3Cat = await voice3.GetCategoryAsync(); + Assert.Equal(voice3Cat.Id, cat2.Id); + Assert.Equal(voice3Cat.Name, cat2.Name); + } } -} \ No newline at end of file +} diff --git a/test/Discord.Net.Tests/Tests.Colors.cs b/test/Discord.Net.Tests/Tests.Colors.cs index 5917789722..10b0bbdacc 100644 --- a/test/Discord.Net.Tests/Tests.Colors.cs +++ b/test/Discord.Net.Tests/Tests.Colors.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace Discord @@ -12,6 +12,7 @@ public void Color_New() Assert.Equal(uint.MinValue, new Color(uint.MinValue).RawValue); Assert.Equal(uint.MaxValue, new Color(uint.MaxValue).RawValue); } + [Fact] public void Color_Default() { Assert.Equal(0u, Color.Default.RawValue); diff --git a/test/Discord.Net.Tests/Tests.Emotes.cs b/test/Discord.Net.Tests/Tests.Emotes.cs index 334975ce4d..eeadbddf81 100644 --- a/test/Discord.Net.Tests/Tests.Emotes.cs +++ b/test/Discord.Net.Tests/Tests.Emotes.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace Discord @@ -34,6 +34,7 @@ public void Test_Animated_Emote_Parse() Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt); Assert.EndsWith("gif", emote.Url); } + [Fact] public void Test_Invalid_Amimated_Emote_Parse() { Assert.False(Emote.TryParse("", out _)); diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs index a562f4afba..bbd6621b51 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -26,9 +27,25 @@ public Task TestGuildPermission() // ensure that the raw values match Assert.Equal((ulong)0, copy.RawValue); + // test modify with no parameters + copy = GuildPermissions.None.Modify(); + Assert.Equal(GuildPermissions.None.RawValue, copy.RawValue); + + // test modify with no paramters on all permissions + copy = GuildPermissions.All.Modify(); + Assert.Equal(GuildPermissions.All.RawValue, copy.RawValue); + + // test modify with no paramters on webhook permissions + copy = GuildPermissions.Webhook.Modify(); + Assert.Equal(GuildPermissions.Webhook.RawValue, copy.RawValue); + + // Get all distinct values (ReadMessages = ViewChannel) + var enumValues = (Enum.GetValues(typeof(GuildPermission)) as GuildPermission[]) + .Distinct() + .ToArray(); // test GuildPermissions.All ulong sumOfAllGuildPermissions = 0; - foreach(var v in Enum.GetValues(typeof(GuildPermission))) + foreach(var v in enumValues) { sumOfAllGuildPermissions |= (ulong)v; } @@ -37,9 +54,9 @@ public Task TestGuildPermission() Assert.Equal(sumOfAllGuildPermissions, GuildPermissions.All.RawValue); Assert.Equal((ulong)0, GuildPermissions.None.RawValue); - // assert that GuildPermissions.All contains the same number of permissions as the + // assert that GuildPermissions.All contains the same number of permissions as the // GuildPermissions enum - Assert.Equal(Enum.GetValues(typeof(GuildPermission)).Length, GuildPermissions.All.ToList().Count); + Assert.Equal(enumValues.Length, GuildPermissions.All.ToList().Count); // assert that webhook has the same raw value ulong webHookPermissions = (ulong)( @@ -64,7 +81,7 @@ public Task TestGuildPermissionModify() // ensure that when we modify it the parameter works perm = perm.Modify(createInstantInvite: true); Assert.True(perm.CreateInstantInvite); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.CreateInstantInvite); + Assert.Equal((ulong)GuildPermission.CreateInstantInvite, perm.RawValue); // set it false again, then move on to the next permission perm = perm.Modify(createInstantInvite: false); @@ -74,7 +91,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(kickMembers: true); Assert.True(perm.KickMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.KickMembers); + Assert.Equal((ulong)GuildPermission.KickMembers, perm.RawValue); perm = perm.Modify(kickMembers: false); Assert.False(perm.KickMembers); @@ -83,7 +100,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(banMembers: true); Assert.True(perm.BanMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.BanMembers); + Assert.Equal((ulong)GuildPermission.BanMembers, perm.RawValue); perm = perm.Modify(banMembers: false); Assert.False(perm.BanMembers); @@ -92,7 +109,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(administrator: true); Assert.True(perm.Administrator); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Administrator); + Assert.Equal((ulong)GuildPermission.Administrator, perm.RawValue); perm = perm.Modify(administrator: false); Assert.False(perm.Administrator); @@ -101,7 +118,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageChannels: true); Assert.True(perm.ManageChannels); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageChannels); + Assert.Equal((ulong)GuildPermission.ManageChannels, perm.RawValue); perm = perm.Modify(manageChannels: false); Assert.False(perm.ManageChannels); @@ -110,7 +127,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageGuild: true); Assert.True(perm.ManageGuild); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageGuild); + Assert.Equal((ulong)GuildPermission.ManageGuild, perm.RawValue); perm = perm.Modify(manageGuild: false); Assert.False(perm.ManageGuild); @@ -120,7 +137,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(addReactions: true); Assert.True(perm.AddReactions); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.AddReactions); + Assert.Equal((ulong)GuildPermission.AddReactions, perm.RawValue); perm = perm.Modify(addReactions: false); Assert.False(perm.AddReactions); @@ -130,7 +147,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(viewAuditLog: true); Assert.True(perm.ViewAuditLog); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewAuditLog); + Assert.Equal((ulong)GuildPermission.ViewAuditLog, perm.RawValue); perm = perm.Modify(viewAuditLog: false); Assert.False(perm.ViewAuditLog); @@ -138,19 +155,19 @@ public Task TestGuildPermissionModify() // individual permission test - perm = perm.Modify(readMessages: true); - Assert.True(perm.ReadMessages); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessages); + perm = perm.Modify(viewChannel: true); + Assert.True(perm.ViewChannel); + Assert.Equal((ulong)GuildPermission.ViewChannel, perm.RawValue); - perm = perm.Modify(readMessages: false); - Assert.False(perm.ReadMessages); + perm = perm.Modify(viewChannel: false); + Assert.False(perm.ViewChannel); Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); // individual permission test perm = perm.Modify(sendMessages: true); Assert.True(perm.SendMessages); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.SendMessages); + Assert.Equal((ulong)GuildPermission.SendMessages, perm.RawValue); perm = perm.Modify(sendMessages: false); Assert.False(perm.SendMessages); @@ -159,7 +176,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(embedLinks: true); Assert.True(perm.EmbedLinks); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.EmbedLinks); + Assert.Equal((ulong)GuildPermission.EmbedLinks, perm.RawValue); perm = perm.Modify(embedLinks: false); Assert.False(perm.EmbedLinks); @@ -168,7 +185,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(attachFiles: true); Assert.True(perm.AttachFiles); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.AttachFiles); + Assert.Equal((ulong)GuildPermission.AttachFiles, perm.RawValue); perm = perm.Modify(attachFiles: false); Assert.False(perm.AttachFiles); @@ -177,7 +194,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(readMessageHistory: true); Assert.True(perm.ReadMessageHistory); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessageHistory); + Assert.Equal((ulong)GuildPermission.ReadMessageHistory, perm.RawValue); perm = perm.Modify(readMessageHistory: false); Assert.False(perm.ReadMessageHistory); @@ -186,7 +203,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(mentionEveryone: true); Assert.True(perm.MentionEveryone); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MentionEveryone); + Assert.Equal((ulong)GuildPermission.MentionEveryone, perm.RawValue); perm = perm.Modify(mentionEveryone: false); Assert.False(perm.MentionEveryone); @@ -195,7 +212,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(useExternalEmojis: true); Assert.True(perm.UseExternalEmojis); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseExternalEmojis); + Assert.Equal((ulong)GuildPermission.UseExternalEmojis, perm.RawValue); perm = perm.Modify(useExternalEmojis: false); Assert.False(perm.UseExternalEmojis); @@ -204,7 +221,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(connect: true); Assert.True(perm.Connect); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Connect); + Assert.Equal((ulong)GuildPermission.Connect, perm.RawValue); perm = perm.Modify(connect: false); Assert.False(perm.Connect); @@ -213,7 +230,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(speak: true); Assert.True(perm.Speak); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Speak); + Assert.Equal((ulong)GuildPermission.Speak, perm.RawValue); perm = perm.Modify(speak: false); Assert.False(perm.Speak); @@ -222,7 +239,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(muteMembers: true); Assert.True(perm.MuteMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MuteMembers); + Assert.Equal((ulong)GuildPermission.MuteMembers, perm.RawValue); perm = perm.Modify(muteMembers: false); Assert.False(perm.MuteMembers); @@ -231,7 +248,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(deafenMembers: true); Assert.True(perm.DeafenMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.DeafenMembers); + Assert.Equal((ulong)GuildPermission.DeafenMembers, perm.RawValue); perm = perm.Modify(deafenMembers: false); Assert.False(perm.DeafenMembers); @@ -240,7 +257,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(moveMembers: true); Assert.True(perm.MoveMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MoveMembers); + Assert.Equal((ulong)GuildPermission.MoveMembers, perm.RawValue); perm = perm.Modify(moveMembers: false); Assert.False(perm.MoveMembers); @@ -249,7 +266,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(useVoiceActivation: true); Assert.True(perm.UseVAD); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseVAD); + Assert.Equal((ulong)GuildPermission.UseVAD, perm.RawValue); perm = perm.Modify(useVoiceActivation: false); Assert.False(perm.UseVAD); @@ -258,7 +275,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(changeNickname: true); Assert.True(perm.ChangeNickname); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ChangeNickname); + Assert.Equal((ulong)GuildPermission.ChangeNickname, perm.RawValue); perm = perm.Modify(changeNickname: false); Assert.False(perm.ChangeNickname); @@ -267,7 +284,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageNicknames: true); Assert.True(perm.ManageNicknames); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageNicknames); + Assert.Equal((ulong)GuildPermission.ManageNicknames, perm.RawValue); perm = perm.Modify(manageNicknames: false); Assert.False(perm.ManageNicknames); @@ -276,7 +293,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageRoles: true); Assert.True(perm.ManageRoles); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageRoles); + Assert.Equal((ulong)GuildPermission.ManageRoles, perm.RawValue); perm = perm.Modify(manageRoles: false); Assert.False(perm.ManageRoles); @@ -285,7 +302,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageWebhooks: true); Assert.True(perm.ManageWebhooks); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageWebhooks); + Assert.Equal((ulong)GuildPermission.ManageWebhooks, perm.RawValue); perm = perm.Modify(manageWebhooks: false); Assert.False(perm.ManageWebhooks); @@ -294,7 +311,7 @@ public Task TestGuildPermissionModify() // individual permission test perm = perm.Modify(manageEmojis: true); Assert.True(perm.ManageEmojis); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageEmojis); + Assert.Equal((ulong)GuildPermission.ManageEmojis, perm.RawValue); perm = perm.Modify(manageEmojis: false); Assert.False(perm.ManageEmojis); diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index e22659d15f..2f72f272da 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -25,15 +25,15 @@ private void TestHelper(ulong rawValue, ulong flagValue, bool expected) // check that toggling the bit works Permissions.UnsetFlag(ref rawValue, flagValue); - Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); + Assert.False(Permissions.GetValue(rawValue, flagValue)); Permissions.SetFlag(ref rawValue, flagValue); - Assert.Equal(true, Permissions.GetValue(rawValue, flagValue)); + Assert.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)); + Assert.True(Permissions.GetValue(rawValue, flagValue)); Permissions.SetValue(ref rawValue, false, flagValue); - Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); + Assert.False(Permissions.GetValue(rawValue, flagValue)); } /// @@ -77,7 +77,7 @@ private void TestHelper(OverwritePermissions value, ChannelPermission flag, Perm /// /// Tests for the class. - /// + /// /// Tests that text channel permissions get the right value /// from the Has method. /// @@ -114,7 +114,7 @@ public Task TestPermissionsHasChannelPermissionText() /// /// Tests for the class. - /// + /// /// Tests that no channel permissions get the right value /// from the Has method. /// @@ -151,7 +151,7 @@ public Task TestPermissionsHasChannelPermissionNone() /// /// Tests for the class. - /// + /// /// Tests that the dm channel permissions get the right value /// from the Has method. /// @@ -188,7 +188,7 @@ public Task TestPermissionsHasChannelPermissionDM() /// /// Tests for the class. - /// + /// /// Tests that the group channel permissions get the right value /// from the Has method. /// @@ -225,7 +225,7 @@ public Task TestPermissionsHasChannelPermissionGroup() /// /// Tests for the class. - /// + /// /// Tests that the voice channel permissions get the right value /// from the Has method. /// @@ -239,7 +239,7 @@ public Task TestPermissionsHasChannelPermissionVoice() TestHelper(value, ChannelPermission.CreateInstantInvite, true); TestHelper(value, ChannelPermission.ManageChannels, true); TestHelper(value, ChannelPermission.AddReactions, false); - TestHelper(value, ChannelPermission.ViewChannel, false); + TestHelper(value, ChannelPermission.ViewChannel, true); TestHelper(value, ChannelPermission.SendMessages, false); TestHelper(value, ChannelPermission.SendTTSMessages, false); TestHelper(value, ChannelPermission.ManageMessages, false); @@ -262,8 +262,8 @@ public Task TestPermissionsHasChannelPermissionVoice() /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when no permissions are set. /// /// @@ -280,7 +280,7 @@ public Task TestPermissionsHasGuildPermissionNone() TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.ViewAuditLog, false); - TestHelper(value, GuildPermission.ReadMessages, false); + TestHelper(value, GuildPermission.ViewChannel, false); TestHelper(value, GuildPermission.SendMessages, false); TestHelper(value, GuildPermission.SendTTSMessages, false); TestHelper(value, GuildPermission.ManageMessages, false); @@ -305,8 +305,8 @@ public Task TestPermissionsHasGuildPermissionNone() /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when all permissions are set. /// /// @@ -323,7 +323,7 @@ public Task TestPermissionsHasGuildPermissionAll() TestHelper(value, GuildPermission.ManageGuild, true); TestHelper(value, GuildPermission.AddReactions, true); TestHelper(value, GuildPermission.ViewAuditLog, true); - TestHelper(value, GuildPermission.ReadMessages, true); + TestHelper(value, GuildPermission.ViewChannel, true); TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.ManageMessages, true); @@ -349,8 +349,8 @@ public Task TestPermissionsHasGuildPermissionAll() /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when webhook permissions are set. /// /// @@ -367,7 +367,7 @@ public Task TestPermissionsHasGuildPermissionWebhook() TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.ViewAuditLog, false); - TestHelper(value, GuildPermission.ReadMessages, false); + TestHelper(value, GuildPermission.ViewChannel, false); TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.ManageMessages, false); @@ -659,7 +659,7 @@ public Task TestOverwritePermissionsVoice() 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.ViewChannel, PermValue.Allow); TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); @@ -682,7 +682,7 @@ public Task TestOverwritePermissionsVoice() 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.ViewChannel, PermValue.Deny); TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); @@ -702,5 +702,72 @@ public Task TestOverwritePermissionsVoice() return Task.CompletedTask; } + + /// + /// Tests for the + /// method to ensure that the default no-param call does not modify the resulting value + /// of the OverwritePermissions. + /// + /// + [Fact] + public Task TestOverwritePermissionModifyNoParam() + { + // test for all Text allowed, none denied + var original = new OverwritePermissions(ChannelPermissions.Text.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, text denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Text.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // category allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Category.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, category denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Category.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // DM allowed, none denied + original = new OverwritePermissions(ChannelPermissions.DM.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, DM denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.DM.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // voice allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Voice.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, voice denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Voice.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // group allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Group.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, group denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Group.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, none denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + return Task.CompletedTask; + } } }