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