diff --git a/docs/docfx.json b/docs/docfx.json index 8691c2732..ccd271999 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -37,6 +37,7 @@ "default", "_template/light-dark-theme" ], + "postProcessors": [ "ExtractSearchIndex" ], "overwrite": "_overwrites/**/**.md", "globalMetadata": { "_appTitle": "Discord.Net Documentation", diff --git a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs index a70a70f31..1ea2d2574 100644 --- a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs @@ -19,6 +19,7 @@ namespace Discord.Commands /// /// The to be used with the parameter. + /// The given does not inherit from . public OverrideTypeReaderAttribute(Type overridenTypeReader) { if (!TypeReaderTypeInfo.IsAssignableFrom(overridenTypeReader.GetTypeInfo())) diff --git a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs index 50a63c582..58d9c8ba4 100644 --- a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs @@ -13,7 +13,7 @@ namespace Discord.Commands /// /// of the same group require only one of the preconditions to pass in order to /// be successful (A || B). Specifying = or not at all will - /// require *all* preconditions to pass, just like normal (A && B). + /// require *all* preconditions to pass, just like normal (A && B). /// public string Group { get; set; } = null; diff --git a/src/Discord.Net.Commands/CommandContext.cs b/src/Discord.Net.Commands/CommandContext.cs index 9e0766a68..393cdf97a 100644 --- a/src/Discord.Net.Commands/CommandContext.cs +++ b/src/Discord.Net.Commands/CommandContext.cs @@ -16,7 +16,12 @@ namespace Discord.Commands /// Indicates whether the channel that the command is executed in is a private channel. public bool IsPrivate => Channel is IPrivateChannel; - + + /// + /// Initializes a new class with the provided client and message. + /// + /// The underlying client. + /// The underlying message. public CommandContext(IDiscordClient client, IUserMessage msg) { Client = client; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b4511a90c..57809d78b 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -13,11 +13,14 @@ namespace Discord.Commands { public class CommandService { + /// + /// Occurs when a command-related information is received. + /// public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); /// - /// Fired when a command is successfully executed without any runtime error. + /// Occurs when a command is successfully executed without any runtime error. /// public event Func CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } } internal readonly AsyncEvent> _commandExecutedEvent = new AsyncEvent>(); @@ -51,7 +54,16 @@ namespace Discord.Commands /// public ILookup TypeReaders => _typeReaders.SelectMany(x => x.Value.Select(y => new { y.Key, y.Value })).ToLookup(x => x.Key, x => x.Value); + /// + /// Initializes a new class. + /// public CommandService() : this(new CommandServiceConfig()) { } + + /// + /// Initializes a new class with the provided configuration. + /// + /// The configuration class. + /// The is set to . public CommandService(CommandServiceConfig config) { _caseSensitive = config.CaseSensitiveCommands; @@ -121,6 +133,7 @@ namespace Discord.Commands /// A built module. /// public Task AddModuleAsync(IServiceProvider services) => AddModuleAsync(typeof(T), services); + /// /// Adds a command module from a . /// @@ -132,6 +145,8 @@ namespace Discord.Commands /// /// A built module. /// + /// This module has already been added. + /// The fails to be built; an invalid type may have been provided. public async Task AddModuleAsync(Type type, IServiceProvider services) { services = services ?? EmptyServiceProvider.Instance; @@ -209,7 +224,7 @@ namespace Discord.Commands /// /// The to be removed from the service. /// - /// Returns whether the is successfully removed. + /// Returns whether the module is successfully removed. /// public async Task RemoveModuleAsync(ModuleInfo module) { @@ -223,7 +238,21 @@ namespace Discord.Commands _moduleLock.Release(); } } + /// + /// Removes the command module. + /// + /// The of the module. + /// + /// Returns whether the module is successfully removed. + /// public Task RemoveModuleAsync() => RemoveModuleAsync(typeof(T)); + /// + /// Removes the command module. + /// + /// The of the module. + /// + /// Returns whether the module is successfully removed. + /// public async Task RemoveModuleAsync(Type type) { await _moduleLock.WaitAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 77c5b2262..5d33ddf11 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -2,23 +2,40 @@ using System; namespace Discord.Commands { + /// + /// Represents a configuration class for . + /// public class CommandServiceConfig { - /// Gets or sets the default RunMode commands should have, if one is not specified on the Command attribute or builder. + /// + /// Gets or sets the default commands should have, if one is not specified on the + /// Command attribute or builder. + /// public RunMode DefaultRunMode { get; set; } = RunMode.Sync; + /// + /// Gets or sets the that separates an argument with another. + /// public char SeparatorChar { get; set; } = ' '; - /// Determines whether commands should be case-sensitive. + /// + /// Gets or sets whether commands should be case-sensitive. + /// public bool CaseSensitiveCommands { get; set; } = false; - /// Gets or sets the minimum log level severity that will be sent to the Log event. + /// + /// Gets or sets the minimum log level severity that will be sent to the event. + /// public LogSeverity LogLevel { get; set; } = LogSeverity.Info; - /// Determines whether RunMode.Sync commands should push exceptions up to the caller. + /// + /// Gets or sets whether commands should push exceptions up to the caller. + /// public bool ThrowOnError { get; set; } = true; - /// Determines whether extra parameters should be ignored. + /// + /// Gets or sets whether extra parameters should be ignored. + /// public bool IgnoreExtraArgs { get; set; } = false; ///// Gets or sets the to use. diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index 77d945899..7239cac60 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -9,32 +9,45 @@ namespace Discord.Commands public abstract class ModuleBase : IModuleBase where T : class, ICommandContext { + /// + /// The underlying context of the command. + /// + /// + /// public T Context { get; private set; } /// - /// Sends a message to the source channel + /// Sends a message to the source channel. /// - /// Contents of the message; optional only if is specified - /// Specifies if Discord should read this message aloud using TTS - /// An embed to be displayed alongside the message + /// + /// Contents of the message; optional only if is specified. + /// + /// Specifies if Discord should read this aloud using text-to-speech. + /// An embed to be displayed alongside the . protected virtual async Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) { return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); } /// - /// The method to execute before executing the command. + /// The method to execute before executing the command. /// + /// The of the command to be executed. protected virtual void BeforeExecute(CommandInfo command) { } /// - /// The method to execute after executing the command. + /// The method to execute after executing the command. /// - /// + /// The of the command to be executed. protected virtual void AfterExecute(CommandInfo command) { } + /// + /// The method to execute when building the module. + /// + /// The used to create the module. + /// The builder used to build the module. protected virtual void OnModuleBuilding(CommandService commandService, ModuleBuilder builder) { } diff --git a/src/Discord.Net.Commands/MultiMatchHandling.cs b/src/Discord.Net.Commands/MultiMatchHandling.cs index 5dc84e266..319e58edd 100644 --- a/src/Discord.Net.Commands/MultiMatchHandling.cs +++ b/src/Discord.Net.Commands/MultiMatchHandling.cs @@ -1,5 +1,8 @@ namespace Discord.Commands { + /// + /// Specifies the behavior when multiple matches are found during the command parsing stage. + /// public enum MultiMatchHandling { /// Indicates that when multiple results are found, an exception should be thrown. diff --git a/src/Discord.Net.Commands/RunMode.cs b/src/Discord.Net.Commands/RunMode.cs index b5ab1116b..8e230b500 100644 --- a/src/Discord.Net.Commands/RunMode.cs +++ b/src/Discord.Net.Commands/RunMode.cs @@ -1,5 +1,10 @@ namespace Discord.Commands { + /// + /// Specifies the behavior of the command execution workflow. + /// + /// + /// public enum RunMode { /// diff --git a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs index be1bac363..fdbd0447c 100644 --- a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs +++ b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs @@ -20,16 +20,13 @@ namespace Discord /// When modifying an , the /// MUST be alphanumeric with dashes. It must match the following RegEx: [a-z0-9-_]{2,100} /// - /// - /// A BadRequest will be thrown if the name does not match the above RegEx. - /// public Optional Name { get; set; } /// - /// Moves the channel to the following position. This is 0-based! + /// Moves the channel to the following position. This property is zero-based. /// public Optional Position { get; set; } /// - /// Gets or sets the category for this channel. + /// Gets or sets the category ID for this channel. /// public Optional CategoryId { get; set; } } diff --git a/src/Discord.Net.Core/Entities/Channels/IChannel.cs b/src/Discord.Net.Core/Entities/Channels/IChannel.cs index 85138ad60..4f14431c5 100644 --- a/src/Discord.Net.Core/Entities/Channels/IChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IChannel.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a generic Discord channel. + /// Represents a generic channel. /// public interface IChannel : ISnowflakeEntity { diff --git a/src/Discord.Net.Core/Entities/Channels/IDMChannel.cs b/src/Discord.Net.Core/Entities/Channels/IDMChannel.cs index bde44b2ed..49e58038a 100644 --- a/src/Discord.Net.Core/Entities/Channels/IDMChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IDMChannel.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a generic DM channel. + /// Represents a generic direct-message channel. /// public interface IDMChannel : IMessageChannel, IPrivateChannel { @@ -15,6 +15,7 @@ namespace Discord /// /// Closes this private channel, removing it from your channel list. /// + /// The options to be used when sending the request. Task CloseAsync(RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs index 75795f582..8ee2b622c 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGroupChannel.cs @@ -3,13 +3,14 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a private generic group channel. + /// Represents a generic private group channel. /// public interface IGroupChannel : IMessageChannel, IPrivateChannel, IAudioChannel { /// /// Leaves this group. /// + /// The options to be used when sending the request. Task LeaveAsync(RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 9a2651dfa..fdbe77653 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -5,34 +5,52 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a guild channel (text, voice, category). + /// Represents a generic guild channel. /// + /// + /// + /// public interface IGuildChannel : IChannel, IDeletable { /// - /// Gets the position of this channel in the guild's channel list, relative to others of the same type. + /// Gets the position of this channel. /// + /// + /// The position of this channel in the guild's channel list, relative to others of the same type. + /// 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 if none is set. + /// ulong? CategoryId { get; } /// /// Gets the parent channel (category) of this channel. /// Task GetCategoryAsync(); /// - /// Gets the guild this channel is a member of. + /// Gets the guild associated with this channel. /// + /// + /// The guild that this channel belongs to. + /// IGuild Guild { get; } /// - /// Gets the id of the guild this channel is a member of. + /// Gets the guild ID associated with this channel. /// + /// + /// The guild ID that this channel belongs to. + /// ulong GuildId { get; } /// /// Gets a collection of permission overwrites for this channel. /// + /// + /// A collection of overwrites associated with this channel. + /// IReadOnlyCollection PermissionOverwrites { get; } /// @@ -50,49 +68,77 @@ namespace Discord /// /// If , 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. + /// 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. Task> GetInvitesAsync(RequestOptions options = null); /// /// Modifies this guild channel. /// + /// The properties to modify the channel with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); /// /// Gets the permission overwrite for a specific role, or if one does not exist. /// + /// The role to get the overwrite from. OverwritePermissions? GetPermissionOverwrite(IRole role); /// /// Gets the permission overwrite for a specific user, or if one does not exist. /// + /// The user to get the overwrite from. 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. 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. Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null); + /// /// Adds or updates the permission overwrite for the given role. /// + /// The role to add the overwrite to. + /// The overwrite to add to the role. + /// The options to be used when sending the request. Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null); /// /// Adds or updates the permission overwrite for the given user. /// + /// The user to add the overwrite to. + /// The overwrite to add to the user. + /// The options to be used when sending the request. Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null); /// /// 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. new IAsyncEnumerable> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a user in this channel with the provided ID. /// + /// The ID of the user. + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. 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 1d855ba08..9837a3048 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -13,50 +13,115 @@ namespace Discord /// /// Sends a message to this message channel. /// + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if FILESYSTEM /// /// Sends a file to this message channel, with an optional caption. /// + /// The file path of the file. + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. + /// + /// If you wish to upload an image and have it embedded in a embed, you may + /// upload the file and refer to the file with "attachment://filename.ext" in the + /// . + /// 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. /// + /// The of the file to be sent. + /// The name of the attachment. + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. + /// + /// If you wish to upload an image and have it embedded in a embed, you may + /// upload the file and refer to the file with "attachment://filename.ext" in the + /// . + /// Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); /// /// Gets a message from this message channel with the given id, or if not found. /// + /// The ID of the message. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// The message gotten from either the cache or the download, or if none is found. + /// Task GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// /// Gets the last N messages from this message channel. /// + /// The numbers of message to be gotten from. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. Flattening the paginated response into a collection of messages with + /// is required if you wish to access the messages. + /// IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a collection of messages in this channel. /// + /// The ID of the starting message to get the messages from. + /// The direction of the messages to be gotten from. + /// The numbers of message to be gotten from. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. Flattening the paginated response into a collection of messages with + /// is required if you wish to access the messages. + /// IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// /// Gets a collection of messages in this channel. /// + /// The starting message to get the messages from. + /// The direction of the messages to be gotten from. + /// The numbers of message to be gotten from. + /// The that determines whether the object should be fetched from + /// cache. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. Flattening the paginated response into a collection of messages with + /// is required if you wish to access the messages. + /// IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a collection of pinned messages in this channel. /// + /// The options to be used when sending the request. + /// + /// A collection of messages. + /// Task> GetPinnedMessagesAsync(RequestOptions options = null); /// /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. /// + /// The options to be used when sending the request. Task TriggerTypingAsync(RequestOptions options = null); /// /// Continuously broadcasts the "user is typing" message to all users in this channel until the returned /// object is disposed. /// + /// The options to be used when sending the request. IDisposable EnterTypingState(RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs b/src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs index fecb4fc19..41b409141 100644 --- a/src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IPrivateChannel.cs @@ -8,8 +8,11 @@ namespace Discord public interface IPrivateChannel : IChannel { /// - /// Users that can access this channel. + /// Gets the users that can access this channel. /// + /// + /// A collection of users that can access this channel. + /// IReadOnlyCollection Recipients { get; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 1998083af..d1b2465ad 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -11,40 +11,67 @@ namespace Discord public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel { /// - /// Gets whether the channel is NSFW. + /// Determines whether the channel is NSFW. /// + /// + /// if the channel has the NSFW flag enabled; otherwise, . + /// bool IsNsfw { get; } /// /// Gets the current topic for this text channel. /// + /// + /// The topic set in the channel, or if none is set. + /// string Topic { get; } /// - /// Bulk deletes multiple messages. + /// Bulk-deletes multiple messages. /// + /// The messages to be bulk-deleted. + /// The options to be used when sending the request. Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null); /// - /// Bulk deletes multiple messages. + /// Bulk-deletes multiple messages. /// + /// The IDs of the messages to be bulk-deleted. + /// The options to be used when sending the request. Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null); /// /// Modifies this text channel. /// + /// The properties to modify the channel with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); /// /// Creates a webhook in this text channel. /// + /// The name of the webhook. + /// The avatar of the webhook. + /// The options to be used when sending the request. + /// + /// The created webhook. + /// Task CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null); /// - /// Gets the webhook in this text channel with the provided ID, or if not found. + /// Gets the webhook in this text channel with the provided ID. /// + /// The ID of the webhook. + /// The options to be used when sending the request. + /// + /// A webhook associated with the , or if not found. + /// Task GetWebhookAsync(ulong id, RequestOptions options = null); /// /// Gets the webhooks for this text channel. /// + /// The options to be used when sending the request. + /// + /// 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 e6e589235..f69e6e22e 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -21,6 +21,8 @@ namespace Discord /// /// Modifies this voice channel. /// + /// The properties to modify the channel with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Emotes/Emoji.cs b/src/Discord.Net.Core/Entities/Emotes/Emoji.cs index da3c512c5..c7a2dc5eb 100644 --- a/src/Discord.Net.Core/Entities/Emotes/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Emotes/Emoji.cs @@ -6,10 +6,8 @@ namespace Discord public class Emoji : IEmote { // TODO: need to constrain this to Unicode-only emojis somehow - - /// - /// Gets the Unicode representation of this emote. - /// + + /// public string Name { get; } /// /// Gets the Unicode representation of this emote. @@ -28,7 +26,7 @@ namespace Discord /// /// Determines whether the specified emoji is equal to the current emoji. /// - /// The object to compare with the current object. + /// The object to compare with the current object. public override bool Equals(object other) { if (other == null) return false; diff --git a/src/Discord.Net.Core/Entities/Emotes/Emote.cs b/src/Discord.Net.Core/Entities/Emotes/Emote.cs index 682ca33b7..32d49fede 100644 --- a/src/Discord.Net.Core/Entities/Emotes/Emote.cs +++ b/src/Discord.Net.Core/Entities/Emotes/Emote.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Globalization; namespace Discord @@ -6,15 +7,12 @@ namespace Discord /// /// A custom image-based emote. /// + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class Emote : IEmote, ISnowflakeEntity { - /// - /// Gets the display name (tooltip) of this emote. - /// + /// public string Name { get; } - /// - /// Gets the ID of this emote. - /// + /// public ulong Id { get; } /// /// Gets whether this emote is animated. @@ -36,6 +34,10 @@ namespace Discord Animated = animated; } + /// + /// Determines whether the specified emote is equal to the current emote. + /// + /// The object to compare with the current object. public override bool Equals(object other) { if (other == null) return false; @@ -47,6 +49,7 @@ namespace Discord return string.Equals(Name, otherEmote.Name) && Id == otherEmote.Id; } + /// public override int GetHashCode() { unchecked @@ -57,7 +60,8 @@ namespace Discord /// Parses an from its raw format. /// The raw encoding of an emote; for example, <:dab:277855270321782784>. - /// An emote + /// An emote. + /// Invalid emote format. public static Emote Parse(string text) { if (TryParse(text, out Emote result)) @@ -65,6 +69,9 @@ namespace Discord throw new ArgumentException("Invalid emote format.", nameof(text)); } + /// Tries to parse an from its raw format. + /// The raw encoding of an emote; for example, <:dab:277855270321782784>. + /// An emote. public static bool TryParse(string text, out Emote result) { result = null; @@ -89,6 +96,9 @@ namespace Discord } private string DebuggerDisplay => $"{Name} ({Id})"; + /// + /// Returns the raw representation of the emote. + /// public override string ToString() => $"<{(Animated ? "a" : "")}:{Name}:{Id}>"; } } diff --git a/src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs b/src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs index 149a0f284..f734c3648 100644 --- a/src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs +++ b/src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs @@ -31,7 +31,7 @@ namespace Discord private string DebuggerDisplay => $"{Name} ({Id})"; /// - /// Gets the raw representation of the emoji. + /// Gets the raw representation of the emote. /// public override string ToString() => $"<{(Animated ? "a" : "")}:{Name}:{Id}>"; } diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs index fc33f3fe4..3c136b579 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs @@ -8,9 +8,8 @@ namespace Discord /// await Context.Guild.ModifyAsync(async x => /// { /// x.Name = "aaaaaah"; - /// x.RegionId = (await Context.Client.GetOptimalVoiceRegionAsync()).Id; /// }); - /// + /// /// /// public class GuildProperties @@ -61,11 +60,11 @@ namespace Discord /// public Optional AfkChannelId { get; set; } /// - /// Gets or sets the where System messages should be sent. + /// Gets or sets the where system messages should be sent. /// public Optional SystemChannel { get; set; } /// - /// Gets or sets the ID of the where System messages should be sent. + /// Gets or sets the ID of the where system messages should be sent. /// public Optional SystemChannelId { get; set; } /// diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 56c094621..664ac017d 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -20,8 +20,11 @@ namespace Discord /// int AFKTimeout { get; } /// - /// Returns if this guild is embeddable (e.g. widget). + /// Determines if this guild is embeddable (i.e. can use widget). /// + /// + /// Returns if this guild can be embedded via widgets. + /// bool IsEmbeddable { get; } /// /// Gets the default message notifications for users who haven't explicitly set their notification settings. @@ -37,29 +40,32 @@ namespace Discord /// VerificationLevel VerificationLevel { get; } /// - /// Returns the ID of this guild's icon, or if one is not set. + /// Returns the ID of this guild's icon, or if none is set. /// string IconId { get; } /// - /// Returns the URL of this guild's icon, or if one is not set. + /// Returns the URL of this guild's icon, or if none is set. /// string IconUrl { get; } /// - /// Returns the ID of this guild's splash image, or if one is not set. + /// Returns the ID of this guild's splash image, or if none is set. /// string SplashId { get; } /// - /// Returns the URL of this guild's splash image, or if one is not set. + /// Returns the URL of this guild's splash image, or if none is set. /// string SplashUrl { get; } /// + /// Determines if this guild is currently connected and ready to be used. + /// + /// /// Returns if this guild is currently connected and ready to be used. Only applies /// to the WebSocket client. - /// + /// bool Available { get; } /// - /// Gets the ID of the AFK voice channel for this guild if set, or if not. + /// Gets the ID of the AFK voice channel for this guild, or if none is set. /// ulong? AFKChannelId { get; } /// @@ -67,11 +73,11 @@ namespace Discord /// ulong DefaultChannelId { get; } /// - /// Gets the ID of the embed channel for this guild if set, or if not. + /// Gets the ID of the embed channel set in the widget settings of this guild, or if none is set. /// ulong? EmbedChannelId { get; } /// - /// Gets the ID of the channel where randomized welcome messages are sent if set, or if not. + /// Gets the ID of the channel where randomized welcome messages are sent, or if none is set. /// ulong? SystemChannelId { get; } /// @@ -106,57 +112,62 @@ namespace Discord /// /// Modifies this guild. /// + /// The properties to modify the guild with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); /// /// Modifies this guild's embed channel. /// + /// The properties to modify the guild widget with. + /// The options to be used when sending the request. Task ModifyEmbedAsync(Action func, RequestOptions options = null); /// /// Bulk modifies the order of channels in this guild. /// + /// The properties to modify the channel positions with. + /// The options to be used when sending the request. Task ReorderChannelsAsync(IEnumerable args, RequestOptions options = null); /// /// Bulk modifies the order of roles in this guild. /// + /// The properties to modify the role positions with. + /// The options to be used when sending the request. Task ReorderRolesAsync(IEnumerable args, RequestOptions options = null); /// - /// Leaves this guild. If you are the owner, use - /// instead. + /// Leaves this guild. If you are the owner, use instead. /// + /// The options to be used when sending the request. Task LeaveAsync(RequestOptions options = null); /// /// Gets a collection of all users banned on this guild. /// + /// The options to be used when sending the request. Task> GetBansAsync(RequestOptions options = null); /// - /// Bans the provided from this guild and optionally prunes their recent messages. + /// Bans the provided user from this guild and optionally prunes their recent messages. /// - /// - /// The user to ban. - /// + /// The user to ban. /// - /// The number of days to remove messages from this for - must be between [0, 7] - /// - /// - /// The reason of the ban to be written in the audit log. + /// The number of days to remove messages from this for - must be between [0, 7]. /// + /// The reason of the ban to be written in the audit log. + /// The options to be used when sending the request. + /// is not between 0 to 7. Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null); /// /// Bans the provided user ID from this guild and optionally prunes their recent messages. /// - /// - /// The ID of the user to ban. - /// + /// The ID of the user to ban. /// - /// The number of days to remove messages from this user for - must be between [0, 7] - /// - /// - /// The reason of the ban to be written in the audit log. + /// The number of days to remove messages from this user for - must be between [0, 7]. /// + /// The reason of the ban to be written in the audit log. + /// The options to be used when sending the request. + /// is not between 0 to 7. Task AddBanAsync(ulong userId, int pruneDays = 0, string reason = null, RequestOptions options = null); /// - /// Unbans the provided if they are currently banned. + /// Unbans the provided user if they are currently banned. /// Task RemoveBanAsync(IUser user, RequestOptions options = null); /// @@ -167,66 +178,114 @@ namespace Discord /// /// Gets a collection of all channels in this guild. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task> GetChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the channel in this guild with the provided ID, or if not found. + /// Gets the channel in this guild with the provided ID, or if not found. /// /// The channel ID. + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a collection of all text channels in this guild. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task> GetTextChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets a text channel in this guild with the provided ID, or if not found. + /// Gets a text channel in this guild with the provided ID, or if not found. /// /// The text channel ID. + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetTextChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a collection of all voice channels in this guild. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task> GetVoiceChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets a collection of all category channels in this guild. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task> GetCategoriesAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the voice channel in this guild with the provided ID, or if not found. + /// Gets the voice channel in this guild with the provided ID, or if not found. /// /// The text channel ID. + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the voice AFK channel in this guild with the provided ID, or if not found. + /// Gets the voice AFK channel in this guild with the provided ID, or if not found. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetAFKChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the default system text channel in this guild with the provided ID, or if + /// Gets the default system text channel in this guild with the provided ID, or if /// none is set. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetSystemChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the top viewable text channel in this guild with the provided ID, or if not + /// Gets the top viewable text channel in this guild with the provided ID, or if not /// found. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// - /// Gets the embed channel in this guild. + /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild, or + /// if none is set. /// + /// + /// The that determines whether the object should be fetched from cache. + /// + /// The options to be used when sending the request. Task GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Creates a new text channel. /// /// The new name for the text channel. + /// The options to be used when sending the request. Task CreateTextChannelAsync(string name, RequestOptions options = null); /// /// Creates a new voice channel. /// /// The new name for the voice channel. + /// The options to be used when sending the request. Task CreateVoiceChannelAsync(string name, RequestOptions options = null); /// /// Creates a new channel category. /// /// The new name for the category. + /// The options to be used when sending the request. Task CreateCategoryAsync(string name, RequestOptions options = null); Task> GetIntegrationsAsync(RequestOptions options = null); @@ -249,24 +308,33 @@ namespace Discord /// The guild permission that the role should possess. /// The color of the role. /// Whether the role is separated from others on the sidebar. + /// The options to be used when sending the request. Task CreateRoleAsync(string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false, RequestOptions options = null); /// /// Gets a collection of all users in this guild. /// - Task> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); //TODO: shouldnt this be paged? + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + Task> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets the user in this guild with the provided ID, or if not found. /// /// The user ID. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets the current user for this guild. /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. Task GetCurrentUserAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Gets the owner of this guild. /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. Task GetOwnerAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// /// Downloads all users for this guild if the current list is incomplete. @@ -274,11 +342,12 @@ namespace Discord Task DownloadUsersAsync(); /// /// Removes all users from this guild if they have not logged on in a provided number of - /// or, if is true, returns the number of users that - /// would be removed. + /// or, if is , returns the + /// number of users that would be removed. /// /// The number of days required for the users to be kicked. /// Whether this prune action is a simulation. + /// The options to be used when sending the request. /// /// The number of users removed from this guild. /// @@ -288,32 +357,41 @@ namespace Discord /// Gets the webhook in this guild with the provided ID, or if not found. /// /// The webhook ID. + /// The options to be used when sending the request. Task GetWebhookAsync(ulong id, RequestOptions options = null); /// - /// Gets a collection of all webhooks from this guild. + /// Gets a collection of all webhook from this guild. /// + /// The options to be used when sending the request. Task> GetWebhooksAsync(RequestOptions options = null); - + /// /// Gets a specific emote from this guild. /// /// The guild emote ID. + /// The options to be used when sending the request. Task GetEmoteAsync(ulong id, RequestOptions options = null); /// - /// Creates a new emote in this guild. + /// Creates a new in this guild. /// /// The name of the guild emote. /// The image of the new emote. /// The roles to limit the emote usage to. + /// The options to be used when sending the request. Task CreateEmoteAsync(string name, Image image, Optional> roles = default(Optional>), RequestOptions options = null); + /// - /// Modifies an existing in this guild. + /// Modifies an existing in this guild. /// + /// The emote to be modified. + /// The properties to modify the emote with. + /// The options to be used when sending the request. Task ModifyEmoteAsync(GuildEmote emote, Action func, RequestOptions options = null); /// - /// Deletes an existing from this guild. + /// Deletes an existing from this guild. /// - /// The guild emote to delete. + /// The emote to delete. + /// The options to be used when sending the request. Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/IDeletable.cs b/src/Discord.Net.Core/Entities/IDeletable.cs index ce019edcf..9696eb838 100644 --- a/src/Discord.Net.Core/Entities/IDeletable.cs +++ b/src/Discord.Net.Core/Entities/IDeletable.cs @@ -3,13 +3,14 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents whether the object is deletable or not. + /// Determines whether the object is deletable or not. /// public interface IDeletable { /// /// Deletes this object and all its children. /// + /// The options to be used when sending the request. Task DeleteAsync(RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/IMentionable.cs b/src/Discord.Net.Core/Entities/IMentionable.cs index 1fd9400b3..225806723 100644 --- a/src/Discord.Net.Core/Entities/IMentionable.cs +++ b/src/Discord.Net.Core/Entities/IMentionable.cs @@ -1,13 +1,16 @@ namespace Discord { /// - /// Represents whether the object is mentionable or not. + /// Determines whether the object is mentionable or not. /// public interface IMentionable { /// /// Returns a special string used to mention this object. /// + /// + /// A string that is recognized by Discord as a mention (e.g. <@168693960628371456>). + /// string Mention { get; } } } diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index 4bdd4be66..921902f5d 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -1,3 +1,4 @@ +using System; using System.IO; namespace Discord { @@ -11,7 +12,7 @@ namespace Discord /// public Stream Stream { get; } /// - /// Create the image with a . + /// Create the image with a . /// /// /// The to create the image with. Note that this must be some type of stream @@ -26,10 +27,30 @@ namespace Discord /// Create the image from a file path. /// /// - /// This file is NOT validated, and is passed directly into a - /// + /// This file path is NOT validated and is passed directly into a + /// . /// /// 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 . + /// + /// 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. + /// + /// 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. + /// + /// An I/O error occurred while opening the file. public Image(string path) { Stream = File.OpenRead(path); diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index fa7d87410..d2c39d1bd 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -41,6 +41,9 @@ namespace Discord } /// Gets or sets the title of an . + /// Title length exceeds the maximum allowed by Discord. + /// + /// The title of the embed. public string Title { get => _title; @@ -50,7 +53,10 @@ namespace Discord _title = value; } } + /// Gets or sets the description of an . + /// Description length exceeds the maximum allowed by Discord. + /// The description of the embed. public string Description { get => _description; @@ -62,6 +68,8 @@ namespace Discord } /// Gets or sets the URL of an . + /// Url is not a well-formed . + /// The URL of the embed. public string Url { get => _url; @@ -72,6 +80,8 @@ namespace Discord } } /// Gets or sets the thumbnail URL of an . + /// Url is not a well-formed . + /// The thumbnail URL of the embed. public string ThumbnailUrl { get => _thumbnail?.Url; @@ -82,6 +92,8 @@ namespace Discord } } /// Gets or sets the image URL of an . + /// Url is not a well-formed . + /// The image URL of the embed. public string ImageUrl { get => _image?.Url; @@ -91,7 +103,13 @@ namespace Discord _image = new EmbedImage(value, null, null, null); } } + /// Gets or sets the list of of an . + /// An embed builder's fields collection is set to + /// . + /// Description length exceeds the maximum allowed by + /// Discord. + /// The list of existing . public List Fields { get => _fields; @@ -103,18 +121,42 @@ namespace Discord } } - /// Gets or sets the timestamp of an . + /// + /// Gets or sets the timestamp of an . + /// + /// + /// The timestamp of the embed, or if none is set. + /// public DateTimeOffset? Timestamp { get; set; } - /// Gets or sets the sidebar color of an . + /// + /// Gets or sets the sidebar color of an . + /// + /// + /// The color of the embed, or if none is set. + /// public Color? Color { get; set; } - /// Gets or sets the of an . + /// + /// Gets or sets the of an . + /// + /// + /// The author field builder of the embed, or if none is set. + /// public EmbedAuthorBuilder Author { get; set; } - /// Gets or sets the of an . + /// + /// Gets or sets the of an . + /// + /// + /// The footer field builder of the embed, or if none is set. + /// public EmbedFooterBuilder Footer { get; set; } /// /// Gets the total length of all embed properties. /// + /// + /// The combined length of , , , + /// , , and . + /// public int Length { get @@ -130,9 +172,12 @@ namespace Discord } /// - /// Sets the title of an . + /// Sets the title of an . /// /// The title to be set. + /// + /// The current builder. + /// public EmbedBuilder WithTitle(string title) { Title = title; @@ -142,6 +187,9 @@ namespace Discord /// Sets the description of an . /// /// The description to be set. + /// + /// The current builder. + /// public EmbedBuilder WithDescription(string description) { Description = description; @@ -151,6 +199,9 @@ namespace Discord /// Sets the URL of an . /// /// The URL to be set. + /// + /// The current builder. + /// public EmbedBuilder WithUrl(string url) { Url = url; @@ -160,15 +211,21 @@ namespace Discord /// Sets the thumbnail URL of an . /// /// The thumbnail URL to be set. + /// + /// The current builder. + /// public EmbedBuilder WithThumbnailUrl(string thumbnailUrl) { ThumbnailUrl = thumbnailUrl; return this; } /// - /// Sets the image URL of an . + /// Sets the image URL of an . /// /// The image URL to be set. + /// + /// The current builder. + /// public EmbedBuilder WithImageUrl(string imageUrl) { ImageUrl = imageUrl; @@ -177,24 +234,33 @@ namespace Discord /// /// Sets the timestamp of an to the current time. /// + /// + /// The current builder. + /// public EmbedBuilder WithCurrentTimestamp() { Timestamp = DateTimeOffset.UtcNow; return this; } /// - /// Sets the timestamp of an . + /// Sets the timestamp of an . /// /// The timestamp to be set. + /// + /// The current builder. + /// public EmbedBuilder WithTimestamp(DateTimeOffset dateTimeOffset) { Timestamp = dateTimeOffset; return this; } /// - /// Sets the sidebar color of an . + /// Sets the sidebar color of an . /// /// The color to be set. + /// + /// The current builder. + /// public EmbedBuilder WithColor(Color color) { Color = color; @@ -202,9 +268,12 @@ namespace Discord } /// - /// Sets the of an . + /// Sets the of an . /// /// The author builder class containing the author field properties. + /// + /// The current builder. + /// public EmbedBuilder WithAuthor(EmbedAuthorBuilder author) { Author = author; @@ -214,6 +283,9 @@ namespace Discord /// Sets the author field of an with the provided properties. /// /// The containing the author field properties. + /// + /// The current builder. + /// public EmbedBuilder WithAuthor(Action action) { var author = new EmbedAuthorBuilder(); @@ -227,6 +299,9 @@ namespace Discord /// The title of the author field. /// The icon URL of the author field. /// The URL of the author field. + /// + /// The current builder. + /// public EmbedBuilder WithAuthor(string name, string iconUrl = null, string url = null) { var author = new EmbedAuthorBuilder @@ -239,9 +314,12 @@ namespace Discord return this; } /// - /// Sets the of an . + /// Sets the of an . /// /// The footer builder class containing the footer field properties. + /// + /// The current builder. + /// public EmbedBuilder WithFooter(EmbedFooterBuilder footer) { Footer = footer; @@ -251,6 +329,9 @@ namespace Discord /// Sets the footer field of an with the provided properties. /// /// The containing the footer field properties. + /// + /// The current builder. + /// public EmbedBuilder WithFooter(Action action) { var footer = new EmbedFooterBuilder(); @@ -263,6 +344,9 @@ namespace Discord /// /// The title of the footer field. /// The icon URL of the footer field. + /// + /// The current builder. + /// public EmbedBuilder WithFooter(string text, string iconUrl = null) { var footer = new EmbedFooterBuilder @@ -280,6 +364,9 @@ namespace Discord /// The title of the field. /// The value of the field. /// Indicates whether the field is in-line or not. + /// + /// The current builder. + /// public EmbedBuilder AddField(string name, object value, bool inline = false) { var field = new EmbedFieldBuilder() @@ -289,11 +376,16 @@ namespace Discord AddField(field); return this; } + /// /// Adds a field with the provided to an /// . /// /// The field builder class containing the field properties. + /// Field count exceeds the maximum allowed by Discord. + /// + /// The current builder. + /// public EmbedBuilder AddField(EmbedFieldBuilder field) { if (Fields.Count >= MaxFieldCount) @@ -308,6 +400,9 @@ namespace Discord /// Adds an field with the provided properties. /// /// The containing the field properties. + /// + /// The current builder. + /// public EmbedBuilder AddField(Action action) { var field = new EmbedFieldBuilder(); @@ -317,11 +412,12 @@ namespace Discord } /// - /// Builds the into a Rich Embed format. + /// Builds the into a Rich Embed ready to be sent. /// /// /// The built embed object. /// + /// Total embed length exceeds the maximum allowed by Discord. public Embed Build() { if (Length > MaxEmbedLength) @@ -335,6 +431,9 @@ namespace Discord } } + /// + /// Represents a builder class for an embed field. + /// public class EmbedFieldBuilder { private string _name; @@ -352,6 +451,14 @@ namespace Discord /// /// Gets or sets the field name. /// + /// + /// Field name is , empty or entirely whitespace. + /// - or - + /// Field name length exceeds . + /// + /// + /// The name of the field. + /// public string Name { get => _name; @@ -366,19 +473,27 @@ namespace Discord /// /// Gets or sets the field value. /// + /// + /// Field value is , empty or entirely whitespace. + /// - or - + /// Field value length exceeds . + /// + /// + /// The value of the field. + /// public object Value { get => _value; set { var stringValue = value?.ToString(); - if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException($"Field value must not be null or empty.", nameof(Value)); + if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException("Field value must not be null or empty.", nameof(Value)); if (stringValue.Length > MaxFieldValueLength) throw new ArgumentException($"Field value length must be less than or equal to {MaxFieldValueLength}.", nameof(Value)); _value = stringValue; } } /// - /// Gets or sets whether the field should be in-line with each other. + /// Determines whether the field should be in-line with each other. /// public bool IsInline { get; set; } @@ -386,6 +501,9 @@ namespace Discord /// Sets the field name. /// /// The name to set the field name to. + /// + /// The current builder. + /// public EmbedFieldBuilder WithName(string name) { Name = name; @@ -395,14 +513,20 @@ namespace Discord /// Sets the field value. /// /// The value to set the field value to. + /// + /// The current builder. + /// public EmbedFieldBuilder WithValue(object value) { Value = value; return this; } /// - /// Sets whether the field should be in-line with each other. + /// Determines whether the field should be in-line with each other. /// + /// + /// The current builder. + /// public EmbedFieldBuilder WithIsInline(bool isInline) { IsInline = isInline; @@ -410,19 +534,42 @@ namespace Discord } /// - /// Builds the field builder into a class. + /// Builds the field builder into a class. /// + /// + /// The current builder. + /// + /// + /// or is , empty or entirely whitespace. + /// - or - + /// or length exceeds the maximum allowed by Discord. + /// public EmbedField Build() => new EmbedField(Name, Value.ToString(), IsInline); } + /// + /// Represents a builder class for a author field. + /// public class EmbedAuthorBuilder { private string _name; private string _url; private string _iconUrl; + /// + /// Gets the maximum author name length allowed by Discord. + /// public const int MaxAuthorNameLength = 256; + /// + /// Gets or sets the author name. + /// + /// + /// Author name length is longer than . + /// + /// + /// The author name. + /// public string Name { get => _name; @@ -432,6 +579,13 @@ namespace Discord _name = value; } } + /// + /// Gets or sets the URL of the author field. + /// + /// Url is not a well-formed . + /// + /// The URL of the author field. + /// public string Url { get => _url; @@ -441,6 +595,13 @@ namespace Discord _url = value; } } + /// + /// Gets or sets the icon URL of the author field. + /// + /// Url is not a well-formed . + /// + /// The icon URL of the author field. + /// public string IconUrl { get => _iconUrl; @@ -451,33 +612,82 @@ namespace Discord } } + /// + /// Sets the name of the author field. + /// + /// The name of the author field. + /// + /// The current builder. + /// public EmbedAuthorBuilder WithName(string name) { Name = name; return this; } + /// + /// Sets the URL of the author field. + /// + /// The URL of the author field. + /// + /// The current builder. + /// public EmbedAuthorBuilder WithUrl(string url) { Url = url; return this; } + /// + /// Sets the icon URL of the author field. + /// + /// The icon URL of the author field. + /// + /// The current builder. + /// public EmbedAuthorBuilder WithIconUrl(string iconUrl) { IconUrl = iconUrl; return this; } + /// + /// Builds the author field to be used. + /// + /// + /// Author name length is longer than . + /// - or - + /// is not a well-formed . + /// - or - + /// is not a well-formed . + /// + /// + /// The built author field. + /// public EmbedAuthor Build() => new EmbedAuthor(Name, Url, IconUrl, null); } + /// + /// Represents a builder class for an embed footer. + /// public class EmbedFooterBuilder { private string _text; private string _iconUrl; + /// + /// Gets the maximum footer length allowed by Discord. + /// public const int MaxFooterTextLength = 2048; + /// + /// Gets or sets the footer text. + /// + /// + /// Author name length is longer than . + /// + /// + /// The footer text. + /// public string Text { get => _text; @@ -487,6 +697,13 @@ namespace Discord _text = value; } } + /// + /// Gets or sets the icon URL of the footer field. + /// + /// Url is not a well-formed . + /// + /// The icon URL of the footer field. + /// public string IconUrl { get => _iconUrl; @@ -497,17 +714,43 @@ namespace Discord } } + /// + /// Sets the name of the footer field. + /// + /// The text of the footer field. + /// + /// The current builder. + /// public EmbedFooterBuilder WithText(string text) { Text = text; return this; } + /// + /// Sets the icon URL of the footer field. + /// + /// The icon URL of the footer field. + /// + /// The current builder. + /// public EmbedFooterBuilder WithIconUrl(string iconUrl) { IconUrl = iconUrl; return this; } + /// + /// Builds the footer field to be used. + /// + /// + /// + /// length is longer than . + /// - or - + /// is not a well-formed . + /// + /// + /// A built footer field. + /// public EmbedFooter Build() => new EmbedFooter(Text, IconUrl, null); } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedField.cs b/src/Discord.Net.Core/Entities/Messages/EmbedField.cs index 40404167d..d7f37befa 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedField.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedField.cs @@ -17,7 +17,7 @@ namespace Discord /// public string Value { get; internal set; } /// - /// Gets whether the field should be in-line with each other. + /// Determines whether the field should be in-line with each other. /// public bool Inline { get; internal set; } diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index b84bbb313..cdee5284d 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -83,12 +83,14 @@ namespace Discord ((uint)g << 8) | (uint)b; } + /// /// Initializes a struct with the given RGB value. /// /// The value that represents the red color. Must be within 0~255. /// The value that represents the green color. Must be within 0~255. /// The value that represents the blue color. Must be within 0~255. + /// The argument value is not between 0 to 255. public Color(int r, int g, int b) { if (r < 0 || r > 255) @@ -108,6 +110,7 @@ namespace Discord /// The value that represents the red color. Must be within 0~1. /// The value that represents the green color. Must be within 0~1. /// The value that represents the blue color. Must be within 0~1. + /// The argument value is not between 0 to 1. public Color(float r, float g, float b) { if (r < 0.0f || r > 1.0f) diff --git a/src/Discord.Net.Core/Entities/Roles/IRole.cs b/src/Discord.Net.Core/Entities/Roles/IRole.cs index 9aa509cd4..f4cb4c64d 100644 --- a/src/Discord.Net.Core/Entities/Roles/IRole.cs +++ b/src/Discord.Net.Core/Entities/Roles/IRole.cs @@ -18,16 +18,28 @@ namespace Discord /// Color Color { get; } /// - /// Returns if users of this role are separated in the user list. + /// Determines whether the role can be separated in the user list. /// + /// + /// Returns if users of this role are separated in the user list; otherwise, returns + /// . + /// bool IsHoisted { get; } /// - /// Returns if this role is automatically managed by Discord. + /// Determines whether the role is managed by Discord. /// + /// + /// Returns if this role is automatically managed by Discord; otherwise, returns + /// . + /// bool IsManaged { get; } /// - /// Returns if this role may be mentioned in messages. + /// Determines whether the role is mentionable. /// + /// + /// Returns if this role may be mentioned in messages; otherwise, returns + /// . + /// bool IsMentionable { get; } /// /// Gets the name of this role. @@ -45,6 +57,8 @@ namespace Discord /// /// Modifies this role. /// + /// The properties to modify the role with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index 57093b3fd..4d54a49c4 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a Discord user that is in a guild. + /// Represents a generic guild user. /// public interface IGuildUser : IUser, IVoiceState { @@ -46,31 +46,38 @@ namespace Discord /// Kicks this user from this guild. /// /// The reason for the kick which will be recorded in the audit log. + /// The options to be used when sending the request. Task KickAsync(string reason = null, RequestOptions options = null); /// /// Modifies this user's properties in this guild. /// + /// The properties to modify the user with. + /// The options to be used when sending the request. Task ModifyAsync(Action func, RequestOptions options = null); /// /// Adds a to this user in this guild. /// /// The role to be added to the user. + /// The options to be used when sending the request. Task AddRoleAsync(IRole role, RequestOptions options = null); /// /// Adds to this user in this guild. /// /// The roles to be added to the user. + /// The options to be used when sending the request. Task AddRolesAsync(IEnumerable roles, RequestOptions options = null); /// /// Removes a from this user in this guild. /// /// The role to be removed from the user. + /// The options to be used when sending the request. Task RemoveRoleAsync(IRole role, RequestOptions options = null); /// /// Removes from this user in this guild. /// /// The roles to be removed from the user. + /// The options to be used when sending the request. Task RemoveRolesAsync(IEnumerable roles, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs index 9b62be362..f651a23f3 100644 --- a/src/Discord.Net.Core/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace Discord { /// - /// Represents a Discord user. + /// Represents a generic user. /// public interface IUser : ISnowflakeEntity, IMentionable, IPresence { @@ -41,8 +41,7 @@ namespace Discord string Username { get; } /// - /// Returns a private message channel to this user, creating one if it does not already - /// exist. + /// Returns a direct message channel to this user, or create one if it does not already exist. /// Task GetOrCreateDMChannelAsync(RequestOptions options = null); } diff --git a/src/Discord.Net.Core/Entities/Users/IVoiceState.cs b/src/Discord.Net.Core/Entities/Users/IVoiceState.cs index ee1f74bae..725ef2870 100644 --- a/src/Discord.Net.Core/Entities/Users/IVoiceState.cs +++ b/src/Discord.Net.Core/Entities/Users/IVoiceState.cs @@ -26,7 +26,7 @@ namespace Discord /// bool IsSuppressed { get; } /// - /// Gets the voice channel this user is currently in, if any. + /// Gets the voice channel this user is currently in, or if none. /// IVoiceChannel VoiceChannel { get; } /// diff --git a/src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs b/src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs index b650aa401..efa168be3 100644 --- a/src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs +++ b/src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs @@ -30,6 +30,7 @@ namespace Discord builder.WithAuthor($"{user.Nickname ?? user.Username}#{user.Discriminator}", user.GetAvatarUrl()); /// Converts a object to a . + /// The embed type is not . public static EmbedBuilder ToEmbedBuilder(this IEmbed embed) { if (embed.Type != EmbedType.Rich) diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index a383c37da..344dff6d5 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -5,6 +5,9 @@ using System.Threading.Tasks; namespace Discord { + /// + /// Represents a generic Discord client. + /// public interface IDiscordClient : IDisposable { ConnectionState ConnectionState { get; } @@ -14,11 +17,37 @@ namespace Discord Task StartAsync(); Task StopAsync(); + /// + /// Gets the application information associated with this account. + /// Task GetApplicationInfoAsync(RequestOptions options = null); + /// + /// Gets a generic channel with the provided ID. + /// + /// The ID of the channel. + /// The that determines whether the object should be fetched from cache. Task GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// + /// Gets a list of private channels. + /// + /// + /// The that determines whether the object should be fetched from cache. + /// Task> GetPrivateChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// + /// Gets a list of direct message channels. + /// + /// + /// The that determines whether the object should be fetched from cache. + /// Task> GetDMChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// + /// Gets a list of group channels. + /// + /// + /// The that determines whether the object should be fetched from cache. + /// Task> GetGroupChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task> GetConnectionsAsync(RequestOptions options = null); diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 995a5d96a..35727c33d 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord.Logging @@ -24,7 +24,10 @@ namespace Discord.Logging if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } public async Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null) { @@ -33,7 +36,10 @@ namespace Discord.Logging if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } #if FORMATSTR public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) diff --git a/src/Discord.Net.Core/RequestOptions.cs b/src/Discord.Net.Core/RequestOptions.cs index 2a03819cf..2318f3f98 100644 --- a/src/Discord.Net.Core/RequestOptions.cs +++ b/src/Discord.Net.Core/RequestOptions.cs @@ -44,7 +44,7 @@ namespace Discord /// /// Initializes a new class with the default request timeout set in - /// . + /// . /// public RequestOptions() { diff --git a/src/Discord.Net.Core/Utils/MentionUtils.cs b/src/Discord.Net.Core/Utils/MentionUtils.cs index 36779de6e..edfd3b12c 100644 --- a/src/Discord.Net.Core/Utils/MentionUtils.cs +++ b/src/Discord.Net.Core/Utils/MentionUtils.cs @@ -31,11 +31,12 @@ namespace Discord /// /// Parses a provided user mention string. /// + /// Invalid mention format. public static ulong ParseUser(string text) { if (TryParseUser(text, out ulong id)) return id; - throw new ArgumentException("Invalid mention format", nameof(text)); + throw new ArgumentException("Invalid mention format.", nameof(text)); } /// /// Tries to parse a provided user mention string. @@ -59,11 +60,12 @@ namespace Discord /// /// Parses a provided channel mention string. /// + /// Invalid mention format. public static ulong ParseChannel(string text) { if (TryParseChannel(text, out ulong id)) return id; - throw new ArgumentException("Invalid mention format", nameof(text)); + throw new ArgumentException("Invalid mention format.", nameof(text)); } /// /// Tries to parse a provided channel mention string. @@ -84,11 +86,12 @@ namespace Discord /// /// Parses a provided role mention string. /// + /// Invalid mention format. public static ulong ParseRole(string text) { if (TryParseRole(text, out ulong id)) return id; - throw new ArgumentException("Invalid mention format", nameof(text)); + throw new ArgumentException("Invalid mention format.", nameof(text)); } /// /// Tries to parse a provided role mention string. @@ -163,22 +166,22 @@ namespace Discord if (user != null) return $"@{guildUser?.Nickname ?? user?.Username}"; else - return $""; + return ""; case TagHandling.NameNoPrefix: if (user != null) return $"{guildUser?.Nickname ?? user?.Username}"; else - return $""; + return ""; case TagHandling.FullName: if (user != null) return $"@{user.Username}#{user.Discriminator}"; else - return $""; + return ""; case TagHandling.FullNameNoPrefix: if (user != null) return $"{user.Username}#{user.Discriminator}"; else - return $""; + return ""; case TagHandling.Sanitize: if (guildUser != null && guildUser.Nickname == null) return MentionUser($"{SanitizeChar}{tag.Key}", false); @@ -200,13 +203,13 @@ namespace Discord if (channel != null) return $"#{channel.Name}"; else - return $""; + return ""; case TagHandling.NameNoPrefix: case TagHandling.FullNameNoPrefix: if (channel != null) return $"{channel.Name}"; else - return $""; + return ""; case TagHandling.Sanitize: return MentionChannel($"{SanitizeChar}{tag.Key}"); } @@ -225,13 +228,13 @@ namespace Discord if (role != null) return $"@{role.Name}"; else - return $""; + return ""; case TagHandling.NameNoPrefix: case TagHandling.FullNameNoPrefix: if (role != null) return $"{role.Name}"; else - return $""; + return ""; case TagHandling.Sanitize: return MentionRole($"{SanitizeChar}{tag.Key}"); } diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 300f584e4..1af6dc35d 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Discord { @@ -86,7 +86,7 @@ namespace Discord private static ArgumentException CreateNotEqualException(string name, string msg, T value) { - if (msg == null) return new ArgumentException($"Value may not be equal to {value}", name); + if (msg == null) return new ArgumentException($"Value may not be equal to {value}.", name); else return new ArgumentException(msg, name); } @@ -109,7 +109,7 @@ namespace Discord private static ArgumentException CreateAtLeastException(string name, string msg, T value) { - if (msg == null) return new ArgumentException($"Value must be at least {value}", name); + if (msg == null) return new ArgumentException($"Value must be at least {value}.", name); else return new ArgumentException(msg, name); } @@ -132,7 +132,7 @@ namespace Discord private static ArgumentException CreateGreaterThanException(string name, string msg, T value) { - if (msg == null) return new ArgumentException($"Value must be greater than {value}", name); + if (msg == null) return new ArgumentException($"Value must be greater than {value}.", name); else return new ArgumentException(msg, name); } @@ -155,7 +155,7 @@ namespace Discord private static ArgumentException CreateAtMostException(string name, string msg, T value) { - if (msg == null) return new ArgumentException($"Value must be at most {value}", name); + if (msg == null) return new ArgumentException($"Value must be at most {value}.", name); else return new ArgumentException(msg, name); } @@ -178,11 +178,12 @@ namespace Discord private static ArgumentException CreateLessThanException(string name, string msg, T value) { - if (msg == null) return new ArgumentException($"Value must be less than {value}", name); + if (msg == null) return new ArgumentException($"Value must be less than {value}.", name); else return new ArgumentException(msg, name); } // Bulk Delete + /// Messages are younger than 2 weeks.. public static void YoungerThanTwoWeeks(ulong[] collection, string name) { var minimum = SnowflakeUtils.ToSnowflake(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(14))); @@ -193,12 +194,13 @@ namespace Discord throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks old."); } } + /// The everyone role cannot be assigned to a user public static void NotEveryoneRole(ulong[] roles, ulong guildId, string name) { for (var i = 0; i < roles.Length; i++) { if (roles[i] == guildId) - throw new ArgumentException($"The everyone role cannot be assigned to a user", name); + throw new ArgumentException("The everyone role cannot be assigned to a user.", name); } } } diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index f8642b96c..8ba7ea718 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -24,11 +24,20 @@ namespace Discord.Rest internal API.DiscordRestApiClient ApiClient { get; } internal LogManager LogManager { get; } + /// + /// Gets the login state of the client. + /// public LoginState LoginState { get; private set; } + /// + /// Gets the logged-in user. + /// public ISelfUser CurrentUser { get; protected set; } + /// + /// Gets the type of the authentication token. + /// public TokenType TokenType => ApiClient.AuthTokenType; - /// Creates a new REST-only discord client. + /// Creates a new REST-only Discord client. internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) { ApiClient = client; @@ -48,8 +57,7 @@ namespace Discord.Rest }; ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); } - - /// + public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true) { await _stateLock.WaitAsync().ConfigureAwait(false); @@ -87,8 +95,7 @@ namespace Discord.Rest } internal virtual Task OnLoginAsync(TokenType tokenType, string token) => Task.Delay(0); - - /// + public async Task LogoutAsync() { await _stateLock.WaitAsync().ConfigureAwait(false); @@ -130,49 +137,68 @@ namespace Discord.Rest => ClientHelper.GetRecommendShardCountAsync(this, options); //IDiscordClient + /// ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected; + /// ISelfUser IDiscordClient.CurrentUser => CurrentUser; + /// Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options) => throw new NotSupportedException(); + /// Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(null); + /// Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) => Task.FromResult(null); + /// Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(null); + /// Task> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options) => throw new NotSupportedException(); + /// Task IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(null); + /// Task IDiscordClient.GetUserAsync(string username, string discriminator, RequestOptions options) => Task.FromResult(null); + /// Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); + /// Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) => Task.FromResult(null); + /// Task IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options) => Task.FromResult(null); + /// Task IDiscordClient.StartAsync() => Task.Delay(0); + /// Task IDiscordClient.StopAsync() => Task.Delay(0); } diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 2dc3ebe05..471f8ee6c 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -33,65 +33,49 @@ namespace Discord.Rest _applicationInfo = null; return Task.Delay(0); } - - /// + public async Task GetApplicationInfoAsync(RequestOptions options = null) { return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, options).ConfigureAwait(false)); } - /// public Task GetChannelAsync(ulong id, RequestOptions options = null) => ClientHelper.GetChannelAsync(this, id, options); - /// public Task> GetPrivateChannelsAsync(RequestOptions options = null) => ClientHelper.GetPrivateChannelsAsync(this, options); public Task> GetDMChannelsAsync(RequestOptions options = null) => ClientHelper.GetDMChannelsAsync(this, options); public Task> GetGroupChannelsAsync(RequestOptions options = null) => ClientHelper.GetGroupChannelsAsync(this, options); - - /// + public Task> GetConnectionsAsync(RequestOptions options = null) => ClientHelper.GetConnectionsAsync(this, options); - - /// + public Task GetInviteAsync(string inviteId, RequestOptions options = null) => ClientHelper.GetInviteAsync(this, inviteId, options); - - /// + public Task GetGuildAsync(ulong id, RequestOptions options = null) => ClientHelper.GetGuildAsync(this, id, options); - /// public Task GetGuildEmbedAsync(ulong id, RequestOptions options = null) => ClientHelper.GetGuildEmbedAsync(this, id, options); - /// public IAsyncEnumerable> GetGuildSummariesAsync(RequestOptions options = null) => ClientHelper.GetGuildSummariesAsync(this, null, null, options); - /// public IAsyncEnumerable> GetGuildSummariesAsync(ulong fromGuildId, int limit, RequestOptions options = null) => ClientHelper.GetGuildSummariesAsync(this, fromGuildId, limit, options); - /// public Task> GetGuildsAsync(RequestOptions options = null) => ClientHelper.GetGuildsAsync(this, options); - /// public Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null, RequestOptions options = null) => ClientHelper.CreateGuildAsync(this, name, region, jpegIcon, options); - - /// + public Task GetUserAsync(ulong id, RequestOptions options = null) => ClientHelper.GetUserAsync(this, id, options); - /// public Task GetGuildUserAsync(ulong guildId, ulong id, RequestOptions options = null) => ClientHelper.GetGuildUserAsync(this, guildId, id, options); - - /// + public Task> GetVoiceRegionsAsync(RequestOptions options = null) => ClientHelper.GetVoiceRegionsAsync(this, options); - /// public Task GetVoiceRegionAsync(string id, RequestOptions options = null) => ClientHelper.GetVoiceRegionAsync(this, id, options); - /// public Task GetWebhookAsync(ulong id, RequestOptions options = null) => ClientHelper.GetWebhookAsync(this, id, options); diff --git a/src/Discord.Net.Rest/DiscordRestConfig.cs b/src/Discord.Net.Rest/DiscordRestConfig.cs index 4a7aae287..68fa68e03 100644 --- a/src/Discord.Net.Rest/DiscordRestConfig.cs +++ b/src/Discord.Net.Rest/DiscordRestConfig.cs @@ -1,7 +1,10 @@ -using Discord.Net.Rest; +using Discord.Net.Rest; namespace Discord.Rest { + /// + /// Represents a configuration class for . + /// public class DiscordRestConfig : DiscordConfig { /// Gets or sets the provider used to generate new REST connections. diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 6784f7f6a..7723861fa 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -288,7 +288,7 @@ namespace Discord.Rest } public static IDisposable EnterTypingState(IMessageChannel channel, BaseDiscordClient client, RequestOptions options) - => new TypingNotifier(client, channel, options); + => new TypingNotifier(channel, options); //Webhooks public static async Task CreateWebhookAsync(ITextChannel channel, BaseDiscordClient client, string name, Stream avatar, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs index 2bb5ed209..d253d562e 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs @@ -3,7 +3,7 @@ using Model = Discord.API.InviteMetadata; namespace Discord.Rest { - /// Represents additional information regarding the REST invite object. + /// Represents additional information regarding the REST-based invite object. public class RestInviteMetadata : RestInvite, IInviteMetadata { private long _createdAtTicks; @@ -48,6 +48,7 @@ namespace Discord.Rest _createdAtTicks = model.CreatedAt.UtcTicks; } + /// IUser IInviteMetadata.Inviter => Inviter; } } diff --git a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs b/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs index a5a440d8b..9bbcfb8a3 100644 --- a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Newtonsoft.Json; using Model = Discord.API.Image; @@ -13,6 +13,7 @@ namespace Discord.Net.Converters public override bool CanRead => true; public override bool CanWrite => true; + /// Cannot read from image. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new InvalidOperationException(); diff --git a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs b/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs index 0ed566a84..de2e379d7 100644 --- a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using System; namespace Discord.Net.Converters @@ -11,6 +11,7 @@ namespace Discord.Net.Converters public override bool CanRead => true; public override bool CanWrite => true; + /// Unknown permission target. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { switch ((string)reader.Value) @@ -20,10 +21,11 @@ namespace Discord.Net.Converters case "role": return PermissionTarget.Role; default: - throw new JsonSerializationException("Unknown permission target"); + throw new JsonSerializationException("Unknown permission target."); } } + /// Invalid permission target. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { switch ((PermissionTarget)value) @@ -35,7 +37,7 @@ namespace Discord.Net.Converters writer.WriteValue("role"); break; default: - throw new JsonSerializationException("Invalid permission target"); + throw new JsonSerializationException("Invalid permission target."); } } } diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs index 943b76359..e3b34eb8f 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; #if DEBUG_LIMITS using System.Diagnostics; @@ -117,7 +117,7 @@ namespace Discord.Net.Queue if ((now - bucket.LastAttemptAt).TotalMinutes > 1.0) _buckets.TryRemove(bucket.Id, out RequestBucket ignored); } - await Task.Delay(60000, _cancelToken.Token); //Runs each minute + await Task.Delay(60000, _cancelToken.Token).ConfigureAwait(false); //Runs each minute } } catch (OperationCanceledException) { } diff --git a/src/Discord.Net.Rest/Utils/TypingNotifier.cs b/src/Discord.Net.Rest/Utils/TypingNotifier.cs index b4bd2f44b..745dbd36d 100644 --- a/src/Discord.Net.Rest/Utils/TypingNotifier.cs +++ b/src/Discord.Net.Rest/Utils/TypingNotifier.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; @@ -6,21 +6,19 @@ namespace Discord.Rest { internal class TypingNotifier : IDisposable { - private readonly BaseDiscordClient _client; private readonly CancellationTokenSource _cancelToken; private readonly IMessageChannel _channel; private readonly RequestOptions _options; - public TypingNotifier(BaseDiscordClient discord, IMessageChannel channel, RequestOptions options) + public TypingNotifier(IMessageChannel channel, RequestOptions options) { - _client = discord; _cancelToken = new CancellationTokenSource(); _channel = channel; _options = options; - var _ = Run(); + _ = RunAsync(); } - private async Task Run() + private async Task RunAsync() { try { @@ -31,7 +29,11 @@ namespace Discord.Rest { await _channel.TriggerTypingAsync(_options).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } + await Task.Delay(9500, token).ConfigureAwait(false); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 1f33b3cc5..c3cbc9ca7 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -1,4 +1,4 @@ -using Discord.API.Voice; +using Discord.API.Voice; using Discord.Audio.Streams; using Discord.Logging; using Discord.Net.Converters; @@ -65,7 +65,7 @@ namespace Discord.Audio ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); - ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); + ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync("Sent Discovery").ConfigureAwait(false); //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedPacket += ProcessPacketAsync; @@ -291,7 +291,7 @@ namespace Discord.Audio { if (packet.Length != 70) { - await _audioLogger.DebugAsync($"Malformed Packet").ConfigureAwait(false); + await _audioLogger.DebugAsync("Malformed Packet").ConfigureAwait(false); return; } string ip; @@ -303,7 +303,7 @@ namespace Discord.Audio } catch (Exception ex) { - await _audioLogger.DebugAsync($"Malformed Packet", ex).ConfigureAwait(false); + await _audioLogger.DebugAsync("Malformed Packet", ex).ConfigureAwait(false); return; } @@ -343,7 +343,7 @@ namespace Discord.Audio { if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc)) { - await _audioLogger.DebugAsync($"Malformed Frame").ConfigureAwait(false); + await _audioLogger.DebugAsync("Malformed Frame").ConfigureAwait(false); return; } if (!_ssrcMap.TryGetValue(ssrc, out var userId)) @@ -362,7 +362,7 @@ namespace Discord.Audio } catch (Exception ex) { - await _audioLogger.DebugAsync($"Malformed Frame", ex).ConfigureAwait(false); + await _audioLogger.DebugAsync("Malformed Frame", ex).ConfigureAwait(false); return; } //await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false); @@ -371,7 +371,7 @@ namespace Discord.Audio } catch (Exception ex) { - await _audioLogger.WarningAsync($"Failed to process UDP packet", ex).ConfigureAwait(false); + await _audioLogger.WarningAsync("Failed to process UDP packet", ex).ConfigureAwait(false); return; } } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs index fb302f132..1365ddac2 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs @@ -116,7 +116,7 @@ namespace Discord.Audio.Streams timestamp += OpusEncoder.FrameSamplesPerChannel; } #if DEBUG - var _ = _logger?.DebugAsync($"Buffer underrun"); + var _ = _logger?.DebugAsync("Buffer underrun"); #endif } } @@ -140,7 +140,7 @@ namespace Discord.Audio.Streams if (!_bufferPool.TryDequeue(out byte[] buffer)) { #if DEBUG - var _ = _logger?.DebugAsync($"Buffer overflow"); //Should never happen because of the queueLock + var _ = _logger?.DebugAsync("Buffer overflow"); //Should never happen because of the queueLock #endif return; } @@ -149,7 +149,7 @@ namespace Discord.Audio.Streams if (!_isPreloaded && _queuedFrames.Count == _queueLength) { #if DEBUG - var _ = _logger?.DebugAsync($"Preloaded"); + var _ = _logger?.DebugAsync("Preloaded"); #endif _isPreloaded = true; } @@ -173,4 +173,4 @@ namespace Discord.Audio.Streams return Task.Delay(0); } } -} \ No newline at end of file +} diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index 923b2c23b..f628720ec 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -26,18 +26,12 @@ namespace Discord.WebSocket : base(config, client) => _baseconfig = config; private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); - - /// + public abstract Task GetApplicationInfoAsync(RequestOptions options = null); - /// public abstract SocketUser GetUser(ulong id); - /// public abstract SocketUser GetUser(string username, string discriminator); - /// public abstract SocketChannel GetChannel(ulong id); - /// public abstract SocketGuild GetGuild(ulong id); - /// public abstract RestVoiceRegion GetVoiceRegion(string id); /// public abstract Task StartAsync(); @@ -47,47 +41,56 @@ namespace Discord.WebSocket public abstract Task SetGameAsync(string name, string streamUrl = null, ActivityType type = ActivityType.Playing); public abstract Task SetActivityAsync(IActivity activity); public abstract Task DownloadUsersAsync(IEnumerable guilds); - - /// + public Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null, RequestOptions options = null) => ClientHelper.CreateGuildAsync(this, name, region, jpegIcon, options ?? RequestOptions.Default); - /// public Task> GetConnectionsAsync(RequestOptions options = null) => ClientHelper.GetConnectionsAsync(this, options ?? RequestOptions.Default); - /// public Task GetInviteAsync(string inviteId, RequestOptions options = null) => ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default); // IDiscordClient + /// async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options) => await GetApplicationInfoAsync(options).ConfigureAwait(false); + /// Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetChannel(id)); + /// Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(PrivateChannels); + /// async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync(options).ConfigureAwait(false); + /// async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) => await GetInviteAsync(inviteId, options).ConfigureAwait(false); + /// Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetGuild(id)); + /// Task> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(Guilds); + /// async Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options) => await CreateGuildAsync(name, region, jpegIcon, options).ConfigureAwait(false); + /// Task IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); + /// Task IDiscordClient.GetUserAsync(string username, string discriminator, RequestOptions options) => Task.FromResult(GetUser(username, discriminator)); + /// Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) => Task.FromResult(GetVoiceRegion(id)); + /// Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) => Task.FromResult>(VoiceRegions); } diff --git a/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs index 29d78ab45..0fb47ceff 100644 --- a/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs +++ b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs @@ -5,20 +5,37 @@ namespace Discord.Commands /// The WebSocket variant of , which may contain the client, user, guild, channel, and message. public class SocketCommandContext : ICommandContext { - /// Gets the that the command is executed with. + /// + /// Gets the that the command is executed with. + /// public DiscordSocketClient Client { get; } - /// Gets the that the command is executed in. + /// + /// Gets the that the command is executed in. + /// public SocketGuild Guild { get; } - /// Gets the that the command is executed in. + /// + /// Gets the that the command is executed in. + /// public ISocketMessageChannel Channel { get; } - /// Gets the who executed the command. + /// + /// Gets the who executed the command. + /// public SocketUser User { get; } - /// Gets the that the command is interpreted from. + /// + /// Gets the that the command is interpreted from. + /// public SocketUserMessage Message { get; } - /// Indicates whether the channel that the command is executed in is a private channel. + /// + /// Indicates whether the channel that the command is executed in is a private channel. + /// public bool IsPrivate => Channel is IPrivateChannel; + /// + /// Initializes a new class with the provided client and message. + /// + /// The underlying client. + /// The underlying message. public SocketCommandContext(DiscordSocketClient client, SocketUserMessage msg) { Client = client; diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index ad89067df..c65722e6f 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -19,24 +19,29 @@ namespace Discord.WebSocket private int _totalShards; private bool _automaticShards; - /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. + /// public override int Latency { get => GetLatency(); protected set { } } + /// public override UserStatus Status { get => _shards[0].Status; protected set { } } + /// public override IActivity Activity { get => _shards[0].Activity; protected set { } } internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; - public override IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount()); - public override IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(() => GetPrivateChannelCount()); + /// + public override IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount); + /// + public override IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount); public IReadOnlyCollection Shards => _shards; + /// public override IReadOnlyCollection VoiceRegions => _shards[0].VoiceRegions; - /// Creates a new REST/WebSocket discord client. + /// Creates a new REST/WebSocket Discord client. public DiscordShardedClient() : this(null, new DiscordSocketConfig()) { } - /// Creates a new REST/WebSocket discord client. + /// Creates a new REST/WebSocket Discord client. public DiscordShardedClient(DiscordSocketConfig config) : this(null, config, CreateApiClient(config)) { } - /// Creates a new REST/WebSocket discord client. + /// Creates a new REST/WebSocket Discord client. public DiscordShardedClient(int[] ids) : this(ids, new DiscordSocketConfig()) { } - /// Creates a new REST/WebSocket discord client. + /// Creates a new REST/WebSocket Discord client. public DiscordShardedClient(int[] ids, DiscordSocketConfig config) : this(ids, config, CreateApiClient(config)) { } private DiscordShardedClient(int[] ids, DiscordSocketConfig config, API.DiscordSocketApiClient client) : base(config, client) @@ -213,7 +218,9 @@ namespace Discord.WebSocket public override RestVoiceRegion GetVoiceRegion(string id) => _shards[0].GetVoiceRegion(id); - /// Downloads the users list for the provided guilds, if they don't have a complete list. + /// + /// Downloads the users list for the provided guilds if they don't have a complete list. + /// public override async Task DownloadUsersAsync(IEnumerable guilds) { for (int i = 0; i < _shards.Length; i++) @@ -233,11 +240,13 @@ namespace Discord.WebSocket return (int)Math.Round(total / (double)_shards.Length); } + /// public override async Task SetStatusAsync(UserStatus status) { for (int i = 0; i < _shards.Length; i++) await _shards[i].SetStatusAsync(status).ConfigureAwait(false); } + /// public override async Task SetGameAsync(string name, string streamUrl = null, ActivityType type = ActivityType.Playing) { IActivity activity = null; @@ -247,6 +256,7 @@ namespace Discord.WebSocket activity = new Game(name, type); await SetActivityAsync(activity).ConfigureAwait(false); } + /// public override async Task SetActivityAsync(IActivity activity) { for (int i = 0; i < _shards.Length; i++) @@ -316,34 +326,46 @@ namespace Discord.WebSocket } //IDiscordClient + /// async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options) => await GetApplicationInfoAsync().ConfigureAwait(false); + /// Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetChannel(id)); + /// Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(PrivateChannels); + /// async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync().ConfigureAwait(false); + /// async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) => await GetInviteAsync(inviteId).ConfigureAwait(false); + /// Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetGuild(id)); + /// Task> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(Guilds); + /// async Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon, RequestOptions options) => await CreateGuildAsync(name, region, jpegIcon).ConfigureAwait(false); + /// Task IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); + /// Task IDiscordClient.GetUserAsync(string username, string discriminator, RequestOptions options) => Task.FromResult(GetUser(username, discriminator)); + /// Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) => Task.FromResult>(VoiceRegions); + /// Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) => Task.FromResult(GetVoiceRegion(id)); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 80c16ea79..c9f8fa5f5 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -508,7 +508,7 @@ namespace Discord.WebSocket { type = "GUILD_AVAILABLE"; _lastGuildAvailableTime = Environment.TickCount; - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false); var guild = State.GetGuild(data.Id); if (guild != null) @@ -533,7 +533,7 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); var guild = AddGuild(data, State); if (guild != null) @@ -614,7 +614,7 @@ namespace Discord.WebSocket if (data.Unavailable == true) { type = "GUILD_UNAVAILABLE"; - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); var guild = State.GetGuild(data.Id); if (guild != null) @@ -630,7 +630,7 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); var guild = RemoveGuild(data.Id); if (guild != null) diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 3f9c18863..17f200c08 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -1,41 +1,72 @@ -using Discord.Net.Udp; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; namespace Discord.WebSocket { + /// + /// Represents a configuration class for . + /// public class DiscordSocketConfig : DiscordRestConfig { + /// + /// Gets or sets the encoding gateway should use. + /// public const string GatewayEncoding = "json"; - /// Gets or sets the websocket host to connect to. If null, the client will use the /gateway endpoint. + /// + /// Gets or sets the WebSocket host to connect to. If , the client will use the + /// /gateway endpoint. + /// public string GatewayHost { get; set; } = null; - /// Gets or sets the time, in milliseconds, to wait for a connection to complete before aborting. + /// + /// Gets or sets the time, in milliseconds, to wait for a connection to complete before aborting. + /// public int ConnectionTimeout { get; set; } = 30000; - /// Gets or sets the id for this shard. Must be less than TotalShards. + /// + /// Gets or sets the ID for this shard. Must be less than . + /// public int? ShardId { get; set; } = null; - /// Gets or sets the total number of shards for this application. + /// + /// Gets or sets the total number of shards for this application. + /// public int? TotalShards { get; set; } = null; - /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. + /// + /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero + /// disables the message cache entirely. + /// public int MessageCacheSize { get; set; } = 0; - /// - /// Gets or sets the max number of users a guild may have for offline users to be included in the READY packet. Max is 250. + /// + /// Gets or sets the max number of users a guild may have for offline users to be included in the READY + /// packet. Max is 250. /// public int LargeThreshold { get; set; } = 250; - /// Gets or sets the provider used to generate new websocket connections. + /// + /// Gets or sets the provider used to generate new WebSocket connections. + /// public WebSocketProvider WebSocketProvider { get; set; } - /// Gets or sets the provider used to generate new udp sockets. + /// + /// Gets or sets the provider used to generate new UDP sockets. + /// public UdpSocketProvider UdpSocketProvider { get; set; } - /// Gets or sets whether or not all users should be downloaded as guilds come available. + /// + /// Gets or sets whether or not all users should be downloaded as guilds come available. + /// public bool AlwaysDownloadUsers { get; set; } = false; - /// Gets or sets the timeout for event handlers, in milliseconds, after which a warning will be logged. Null disables this check. + /// + /// Gets or sets the timeout for event handlers, in milliseconds, after which a warning will be logged. Null + /// disables this check. + /// public int? HandlerTimeout { get; set; } = 3000; + /// + /// Initializes a default configuration. + /// public DiscordSocketConfig() { WebSocketProvider = DefaultWebSocketProvider.Instance; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs index 7056a4df5..3cb978abf 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs @@ -1,5 +1,8 @@ -namespace Discord.WebSocket +namespace Discord.WebSocket { + /// + /// Represents a generic WebSocket-based audio channel. + /// public interface ISocketAudioChannel : IAudioChannel { } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index 026bd8378..6d769b9c4 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -5,18 +5,52 @@ using System.Threading.Tasks; namespace Discord.WebSocket { + /// + /// Represents a generic WebSocket-based channel that can send and receive messages. + /// public interface ISocketMessageChannel : IMessageChannel { /// Gets all messages in this channel's cache. IReadOnlyCollection CachedMessages { get; } - /// Sends a message to this message channel. + /// + /// Sends a message to this message channel. + /// + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. new Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if FILESYSTEM - /// Sends a file to this text channel, with an optional caption. + /// + /// Sends a file to this message channel, with an optional caption. + /// + /// The file path of the file. + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. + /// + /// If you wish to upload an image and have it embedded in a embed, you may + /// upload the file and refer to the file with "attachment://filename.ext" in the + /// . + /// new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); #endif - /// Sends a file to this text channel, with an optional caption. + /// + /// Sends a file to this message channel, with an optional caption. + /// + /// The of the file to be sent. + /// The name of the attachment. + /// The message to be sent. + /// Whether the message should be read aloud by Discord or not. + /// The to be sent. + /// The options to be used when sending the request. + /// + /// If you wish to upload an image and have it embedded in a embed, you may + /// upload the file and refer to the file with "attachment://filename.ext" in the + /// . + /// new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); SocketMessage GetCachedMessage(ulong id); @@ -26,7 +60,13 @@ namespace Discord.WebSocket IReadOnlyCollection GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); /// Gets a collection of messages in this channel. IReadOnlyCollection GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); - /// Gets a collection of pinned messages in this channel. + /// + /// Gets a collection of pinned messages in this channel. + /// + /// The options to be used when sending the request. + /// + /// A collection of messages. + /// new Task> GetPinnedMessagesAsync(RequestOptions options = null); } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketPrivateChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketPrivateChannel.cs index 4e91673dd..08da2237c 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketPrivateChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketPrivateChannel.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Discord.WebSocket { + /// + /// Represents a generic WebSocket-based channel that is private to select recipients. + /// public interface ISocketPrivateChannel : IPrivateChannel { new IReadOnlyCollection Recipients { get; } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs index e7a165c2f..1d05b4974 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs @@ -3,14 +3,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading.Tasks; -using Discord.Audio; -using Discord.Rest; using Model = Discord.API.Channel; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based category channel. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketCategoryChannel : SocketGuildChannel, ICategoryChannel { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs index 502e61d15..ec842c8a3 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs @@ -1,4 +1,3 @@ -using Discord.Rest; using System; using System.Collections.Generic; using System.Diagnostics; @@ -8,16 +7,27 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based channel. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public abstract class SocketChannel : SocketEntity, IChannel { + /// + /// Gets when the channel is created. + /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); + /// + /// Gets a collection of users from the WebSocket cache. + /// public IReadOnlyCollection Users => GetUsersInternal(); internal SocketChannel(DiscordSocketClient discord, ulong id) : base(discord, id) { } + + /// Unexpected channel type is created. internal static ISocketPrivateChannel CreatePrivate(DiscordSocketClient discord, ClientState state, Model model) { switch (model.Type) @@ -33,6 +43,17 @@ namespace Discord.WebSocket internal abstract void Update(ClientState state, Model model); //User + /// + /// Gets the user from the WebSocket cache. + /// + /// + /// This method does NOT attempt to fetch the user if they don't exist in the cache. To guarantee a return + /// from an existing user that doesn't exist in cache, use . + /// + /// The ID of the user. + /// + /// The user. + /// public SocketUser GetUser(ulong id) => GetUserInternal(id); internal abstract SocketUser GetUserInternal(ulong id); internal abstract IReadOnlyCollection GetUsersInternal(); @@ -40,10 +61,13 @@ namespace Discord.WebSocket internal SocketChannel Clone() => MemberwiseClone() as SocketChannel; //IChannel + /// string IChannel.Name => null; + /// Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(null); //Overridden + /// IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => AsyncEnumerable.Empty>(); //Overridden } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 451240e66..8008d434a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -10,13 +10,17 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based direct-message channel. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketDMChannel : SocketChannel, IDMChannel, ISocketPrivateChannel, ISocketMessageChannel { private readonly MessageCache _messages; - public SocketUser Recipient { get; private set; } + public SocketUser Recipient { get; } + /// public IReadOnlyCollection CachedMessages => _messages?.Messages ?? ImmutableArray.Create(); public new IReadOnlyCollection Users => ImmutableArray.Create(Discord.CurrentUser, Recipient); @@ -39,10 +43,12 @@ namespace Discord.WebSocket Recipient.Update(state, model.Recipients.Value[0]); } + /// public Task CloseAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); //Messages + /// public SocketMessage GetCachedMessage(ulong id) => _messages?.Get(id); public async Task GetMessageAsync(ulong id, RequestOptions options = null) @@ -58,26 +64,35 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); + /// public IReadOnlyCollection GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); + /// public IReadOnlyCollection GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessageId, dir, limit); + /// public IReadOnlyCollection GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); + /// 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) => 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 TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); + /// public IDisposable EnterTypingState(RequestOptions options = null) => ChannelHelper.EnterTypingState(this, Discord, options); @@ -97,24 +112,33 @@ namespace Discord.WebSocket return null; } + /// + /// Returns the recipient user. + /// public override string ToString() => $"@{Recipient}"; private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; internal new SocketDMChannel Clone() => MemberwiseClone() as SocketDMChannel; //SocketChannel + /// internal override IReadOnlyCollection GetUsersInternal() => Users; + /// internal override SocketUser GetUserInternal(ulong id) => GetUser(id); - //IDMChannel + //IDMChannel + /// IUser IDMChannel.Recipient => Recipient; //ISocketPrivateChannel + /// IReadOnlyCollection ISocketPrivateChannel.Recipients => ImmutableArray.Create(Recipient); //IPrivateChannel + /// IReadOnlyCollection IPrivateChannel.Recipients => ImmutableArray.Create(Recipient); //IMessageChannel + /// async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -122,30 +146,41 @@ namespace Discord.WebSocket else return GetCachedMessage(id); } + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); + /// 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); - //IChannel + //IChannel + /// string IChannel.Name => $"@{Recipient}"; + /// Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); + /// IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => ImmutableArray.Create>(Users).ToAsyncEnumerable(); } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 27713609c..1d131876a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -15,7 +15,7 @@ using VoiceStateModel = Discord.API.VoiceState; namespace Discord.WebSocket { /// - /// Represents a private WebSocket group channel. + /// Represents a WebSocket-based private group channel. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGroupChannel : SocketChannel, IGroupChannel, ISocketPrivateChannel, ISocketMessageChannel, ISocketAudioChannel @@ -24,10 +24,12 @@ namespace Discord.WebSocket private string _iconId; private ConcurrentDictionary _users; - private ConcurrentDictionary _voiceStates; + private readonly ConcurrentDictionary _voiceStates; + /// public string Name { get; private set; } + /// public IReadOnlyCollection CachedMessages => _messages?.Messages ?? ImmutableArray.Create(); public new IReadOnlyCollection Users => _users.ToReadOnlyCollection(); public IReadOnlyCollection Recipients @@ -65,15 +67,18 @@ namespace Discord.WebSocket _users = users; } + /// public Task LeaveAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); + /// Voice is not yet supported for group channels. public Task ConnectAsync() { throw new NotSupportedException("Voice is not yet supported for group channels."); } //Messages + /// public SocketMessage GetCachedMessage(ulong id) => _messages?.Get(id); public async Task GetMessageAsync(ulong id, RequestOptions options = null) @@ -89,26 +94,35 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); + /// public IReadOnlyCollection GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); + /// public IReadOnlyCollection GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessageId, dir, limit); + /// public IReadOnlyCollection GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); + /// 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) => 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 TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); + /// public IDisposable EnterTypingState(RequestOptions options = null) => ChannelHelper.EnterTypingState(this, Discord, options); @@ -118,6 +132,17 @@ namespace Discord.WebSocket => _messages?.Remove(id); //Users + /// + /// Gets the group user from the WebSocket cache. + /// + /// + /// This method does NOT attempt to fetch the user if they don't exist in the cache. To guarantee a return + /// from an existing user that doesn't exist in cache, use . + /// + /// The ID of the user. + /// + /// The user in the group. + /// public new SocketGroupUser GetUser(ulong id) { if (_users.TryGetValue(id, out SocketGroupUser user)) @@ -167,21 +192,29 @@ namespace Discord.WebSocket return null; } + /// + /// Returns the name of the group. + /// public override string ToString() => Name; private string DebuggerDisplay => $"{Name} ({Id}, Group)"; internal new SocketGroupChannel Clone() => MemberwiseClone() as SocketGroupChannel; //SocketChannel + /// internal override IReadOnlyCollection GetUsersInternal() => Users; + /// internal override SocketUser GetUserInternal(ulong id) => GetUser(id); //ISocketPrivateChannel + /// IReadOnlyCollection ISocketPrivateChannel.Recipients => Recipients; //IPrivateChannel + /// IReadOnlyCollection IPrivateChannel.Recipients => Recipients; //IMessageChannel + /// async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -189,31 +222,42 @@ namespace Discord.WebSocket else return GetCachedMessage(id); } + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); + /// IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); + /// 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); //IAudioChannel + /// Task IAudioChannel.ConnectAsync(Action configAction) { throw new NotSupportedException(); } //IChannel + /// Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); + /// IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => ImmutableArray.Create>(Users).ToAsyncEnumerable(); } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 84072a2ab..8d6f22133 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -9,12 +9,20 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { - /// The WebSocket variant of . Represents a guild channel (text, voice, category). + /// + /// Represents a WebSocket-based guild channel. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGuildChannel : SocketChannel, IGuildChannel { private ImmutableArray _overwrites; + /// + /// Gets the guild associated with this channel. + /// + /// + /// The guild that this channel belongs to. + /// public SocketGuild Guild { get; } /// public string Name { get; private set; } @@ -22,11 +30,23 @@ namespace Discord.WebSocket public int Position { get; private set; } /// public ulong? CategoryId { get; private set; } + /// + /// Gets the parent category of this channel. + /// + /// + /// The parent category ID associated with this channel, or if none is set. + /// public ICategoryChannel Category => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; /// public IReadOnlyCollection PermissionOverwrites => _overwrites; + /// + /// Returns a collection of users that are able to view the channel. + /// + /// + /// A collection of users that can access the channel (i.e. the users seen in the user list). + /// public new virtual IReadOnlyCollection Users => ImmutableArray.Create(); internal SocketGuildChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) @@ -131,7 +151,11 @@ namespace Discord.WebSocket public new virtual SocketGuildUser GetUser(ulong id) => null; + /// + /// Gets the name of the channel. + /// public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id}, Guild)"; internal new SocketGuildChannel Clone() => MemberwiseClone() as SocketGuildChannel; //SocketChannel diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 368a2a730..5cc35cf14 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -10,6 +10,9 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based channel in a guild that can send and receive messages. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketTextChannel : SocketGuildChannel, ITextChannel, ISocketMessageChannel { @@ -73,12 +76,16 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); + /// public IReadOnlyCollection GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); + /// public IReadOnlyCollection GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessageId, dir, limit); + /// public IReadOnlyCollection GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); + /// public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); @@ -104,6 +111,7 @@ namespace Discord.WebSocket /// public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); + /// public IDisposable EnterTypingState(RequestOptions options = null) => ChannelHelper.EnterTypingState(this, Discord, options); @@ -128,10 +136,34 @@ namespace Discord.WebSocket } //Webhooks + /// + /// Creates a webhook in this text channel. + /// + /// The name of the webhook. + /// The avatar of the webhook. + /// The options to be used when sending the request. + /// + /// The created webhook. + /// public Task CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null) => ChannelHelper.CreateWebhookAsync(this, Discord, name, avatar, options); + /// + /// Gets the webhook in this text channel with the provided ID. + /// + /// The ID of the webhook. + /// The options to be used when sending the request. + /// + /// A webhook associated with the , or if not found. + /// public Task GetWebhookAsync(ulong id, RequestOptions options = null) => ChannelHelper.GetWebhookAsync(this, Discord, id, options); + /// + /// Gets the webhooks for this text channel. + /// + /// The options to be used when sending the request. + /// + /// A collection of webhooks. + /// public Task> GetWebhooksAsync(RequestOptions options = null) => ChannelHelper.GetWebhooksAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index e8a669845..568c5ad7b 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using Discord.Rest; using System; using System.Collections.Generic; @@ -10,12 +10,18 @@ using Model = Discord.API.Channel; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based voice channel in a guild. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudioChannel { + /// public int Bitrate { get; private set; } + /// public int? UserLimit { get; private set; } + /// public override IReadOnlyCollection Users => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); @@ -37,14 +43,17 @@ namespace Discord.WebSocket UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } + /// public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); + /// public async Task ConnectAsync(Action configAction = null) { return await Guild.ConnectAudioAsync(Id, false, false, configAction).ConfigureAwait(false); } + /// public override SocketGuildUser GetUser(ulong id) { var user = Guild.GetUser(id); @@ -57,8 +66,10 @@ namespace Discord.WebSocket internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel; //IGuildChannel + /// Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); + /// IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => ImmutableArray.Create>(Users).ToAsyncEnumerable(); } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 5acb359c3..c4c223e64 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -21,6 +21,10 @@ using VoiceStateModel = Discord.API.VoiceState; namespace Discord.WebSocket { + /// + /// Represents a WebSocket-based guild object. + /// + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGuild : SocketEntity, IGuild { private readonly SemaphoreSlim _audioLock; @@ -46,11 +50,13 @@ namespace Discord.WebSocket public MfaLevel MfaLevel { get; private set; } /// public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } - /// Gets the number of members. - /// - /// The number of members is returned by Discord and is the most accurate. - /// You may see discrepancy between the Users collection and this. - /// + /// + /// Gets the number of members. + /// + /// + /// The number of members is returned by Discord and is the most accurate. You may see discrepancy between + /// the collection and this. + /// public int MemberCount { get; internal set; } /// Gets the number of members downloaded to the local guild cache. public int DownloadedMemberCount { get; private set; } @@ -84,11 +90,23 @@ namespace Discord.WebSocket public bool IsSynced => _syncPromise.Task.IsCompleted; public Task SyncPromise => _syncPromise.Task; public Task DownloaderPromise => _downloaderPromise.Task; + /// + /// Returns the associated with this guild. + /// public IAudioClient AudioClient => _audioClient; + /// + /// Returns the first viewable text channel. + /// + /// + /// This property does not guarantee the user can send message to it. + /// public SocketTextChannel DefaultChannel => TextChannels .Where(c => CurrentUser.GetPermissions(c).ViewChannel) .OrderBy(c => c.Position) .FirstOrDefault(); + /// + /// Returns the AFK voice channel, or if none is set. + /// public SocketVoiceChannel AFKChannel { get @@ -97,6 +115,9 @@ namespace Discord.WebSocket return id.HasValue ? GetVoiceChannel(id.Value) : null; } } + /// + /// Gets the embed channel set in the widget settings of this guild, or if none is set. + /// public SocketGuildChannel EmbedChannel { get @@ -105,6 +126,9 @@ namespace Discord.WebSocket return id.HasValue ? GetChannel(id.Value) : null; } } + /// + /// Gets the channel where randomized welcome messages are sent, or if none is set. + /// public SocketTextChannel SystemChannel { get @@ -113,14 +137,32 @@ namespace Discord.WebSocket return id.HasValue ? GetTextChannel(id.Value) : null; } } + /// + /// Returns a collection of text channels present in this guild. + /// public IReadOnlyCollection TextChannels => Channels.Select(x => x as SocketTextChannel).Where(x => x != null).ToImmutableArray(); + /// + /// Returns a collection of voice channels present in this guild. + /// public IReadOnlyCollection VoiceChannels => Channels.Select(x => x as SocketVoiceChannel).Where(x => x != null).ToImmutableArray(); + /// + /// Returns a collection of category channels present in this guild. + /// public IReadOnlyCollection CategoryChannels => Channels.Select(x => x as SocketCategoryChannel).Where(x => x != null).ToImmutableArray(); + /// + /// Returns the current logged-in user. + /// public SocketGuildUser CurrentUser => _members.TryGetValue(Discord.CurrentUser.Id, out SocketGuildUser member) ? member : null; + /// + /// Returns the @everyone role in this guild. + /// public SocketRole EveryoneRole => GetRole(Id); + /// + /// Returns a collection of channels present in this guild. + /// public IReadOnlyCollection Channels { get @@ -130,9 +172,26 @@ namespace Discord.WebSocket return channels.Select(x => state.GetChannel(x) as SocketGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels); } } + /// + /// Gets a collection of emotes created in this guild. + /// public IReadOnlyCollection Emotes => _emotes; + /// + /// Gets a collection of features enabled in this guild. + /// public IReadOnlyCollection Features => _features; + /// + /// Gets a collection of users in this guild. + /// + /// + /// This property may not always return all the members for large guilds (i.e. guilds containing 100+ users). + /// You may need to enable to fetch the full user list + /// upon startup, or use to manually download the users. + /// public IReadOnlyCollection Users => _members.ToReadOnlyCollection(); + /// + /// Gets a collection of roles in this guild. + /// public IReadOnlyCollection Roles => _roles.ToReadOnlyCollection(); internal SocketGuild(DiscordSocketClient client, ulong id) @@ -315,7 +374,13 @@ namespace Discord.WebSocket => GuildHelper.LeaveAsync(this, Discord, options); //Bans - /// Gets a collection of the banned users in this guild. + /// + /// Gets a collection of the banned users in this guild. + /// + /// The options to be used when sending the request. + /// + /// A collection of bans. + /// public Task> GetBansAsync(RequestOptions options = null) => GuildHelper.GetBansAsync(this, Discord, options); @@ -334,6 +399,13 @@ namespace Discord.WebSocket => GuildHelper.RemoveBanAsync(this, Discord, userId, options); //Channels + /// + /// Returns a guild channel with the provided ID. + /// + /// The channel ID. + /// + /// The guild channel associated with the ID. + /// public SocketGuildChannel GetChannel(ulong id) { var channel = Discord.State.GetChannel(id) as SocketGuildChannel; @@ -341,14 +413,52 @@ namespace Discord.WebSocket return channel; return null; } + /// + /// Returns a text channel with the provided ID. + /// + /// The channel ID. + /// + /// The text channel associated with the ID. + /// public SocketTextChannel GetTextChannel(ulong id) => GetChannel(id) as SocketTextChannel; + /// + /// Returns a voice channel with the provided ID. + /// + /// The channel ID. + /// + /// The voice channel associated with the ID. + /// public SocketVoiceChannel GetVoiceChannel(ulong id) => GetChannel(id) as SocketVoiceChannel; + /// + /// Creates a text channel with the provided name. + /// + /// The name of the new channel. + /// The options to be used when sending the request. + /// + /// The created text channel. + /// public Task CreateTextChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); + /// + /// Creates a voice channel with the provided name. + /// + /// The name of the new channel. + /// The options to be used when sending the request. + /// + /// The created voice channel. + /// public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); + /// + /// Creates a category channel with the provided name. + /// + /// The name of the new channel. + /// The options to be used when sending the request. + /// + /// The created category channel. + /// public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -373,16 +483,43 @@ namespace Discord.WebSocket => GuildHelper.CreateIntegrationAsync(this, Discord, id, type, options); //Invites + /// + /// Returns a collection of invites associated with this channel. + /// + /// The options to be used when sending the request. + /// + /// A collection of invites. + /// public Task> GetInvitesAsync(RequestOptions options = null) => GuildHelper.GetInvitesAsync(this, Discord, options); //Roles + /// + /// Returns a role with the provided role ID. + /// + /// The ID of the role. + /// + /// The role associated with the ID. + /// public SocketRole GetRole(ulong id) { if (_roles.TryGetValue(id, out SocketRole value)) return value; return null; } + /// + /// Creates a role. + /// + /// The name of the new role. + /// + /// The permissions that the new role possesses. Set to to use the default permissions. + /// + /// The color of the role. Set to to use the default color. + /// Used to determine if users of this role are separated in the user list. + /// The options to be used when sending the request. + /// + /// The created role. + /// public Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false, RequestOptions options = null) => GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted, options); @@ -400,12 +537,20 @@ namespace Discord.WebSocket } //Users + /// + /// Gets the user with the provided ID. + /// + /// The ID of the user. + /// + /// The user associated with the ID. + /// public SocketGuildUser GetUser(ulong id) { if (_members.TryGetValue(id, out SocketGuildUser member)) return member; return null; } + /// public Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null) => GuildHelper.PruneUsersAsync(this, Discord, days, simulate, options); @@ -459,6 +604,9 @@ namespace Discord.WebSocket return null; } + /// + /// Downloads the users of this guild to the WebSocket cache. + /// public async Task DownloadUsersAsync() { await Discord.DownloadUsersAsync(new[] { this }).ConfigureAwait(false); @@ -469,8 +617,23 @@ namespace Discord.WebSocket } //Webhooks + /// + /// Returns the webhook with the provided ID. + /// + /// The ID of the webhook. + /// The options to be used when sending the request. + /// + /// A webhook associated with the ID. + /// public Task GetWebhookAsync(ulong id, RequestOptions options = null) => GuildHelper.GetWebhookAsync(this, Discord, id, options); + /// + /// Gets a collection of webhooks that exist in the guild. + /// + /// The options to be used when sending the request. + /// + /// A collection of webhooks. + /// public Task> GetWebhooksAsync(RequestOptions options = null) => GuildHelper.GetWebhooksAsync(this, Discord, options); @@ -592,7 +755,7 @@ namespace Discord.WebSocket try { var timeoutTask = Task.Delay(15000); - if (await Task.WhenAny(promise.Task, timeoutTask) == timeoutTask) + if (await Task.WhenAny(promise.Task, timeoutTask).ConfigureAwait(false) == timeoutTask) throw new TimeoutException(); return await promise.Task.ConfigureAwait(false); } @@ -663,6 +826,9 @@ namespace Discord.WebSocket } } + /// + /// Gets the name of the guild. + /// public override string ToString() => Name; private string DebuggerDisplay => $"{Name} ({Id})"; internal SocketGuild Clone() => MemberwiseClone() as SocketGuild; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index cad354dae..356ba63ac 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -12,7 +12,9 @@ using PresenceModel = Discord.API.Presence; namespace Discord.WebSocket { - /// The WebSocket variant of . Represents a Discord user that is in a guild. + /// + /// Represents a WebSocket guild user. + /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGuildUser : SocketUser, IGuildUser { @@ -20,6 +22,9 @@ namespace Discord.WebSocket private ImmutableArray _roleIds; internal override SocketGlobalUser GlobalUser { get; } + /// + /// Gets the guild the user is in. + /// public SocketGuild Guild { get; } /// public string Nickname { get; private set; } @@ -50,17 +55,27 @@ namespace Discord.WebSocket public bool IsMuted => VoiceState?.IsMuted ?? false; /// public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); + /// + /// Returns a collection of roles that the user possesses. + /// public IReadOnlyCollection Roles => _roleIds.Select(id => Guild.GetRole(id)).Where(x => x != null).ToReadOnlyCollection(() => _roleIds.Length); + /// + /// Returns the voice channel the user is in, or if none. + /// public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; /// public string VoiceSessionId => VoiceState?.VoiceSessionId ?? ""; public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id); public AudioInStream AudioStream => Guild.GetAudioStream(Id); - /// The position of the user within the role hierarchy. - /// The returned value equal to the position of the highest role the user has, - /// or if user is the server owner. + /// + /// Returns the position of the user within the role hierarchy. + /// + /// + /// The returned value equal to the position of the highest role the user has, or + /// if user is the server owner. + /// public int Hierarchy { get