| @@ -36,7 +36,7 @@ class Program | |||||
| // you must set the MessageCacheSize. You may adjust the number as needed. | // you must set the MessageCacheSize. You may adjust the number as needed. | ||||
| //MessageCacheSize = 50, | //MessageCacheSize = 50, | ||||
| // If your platform doesn't have native websockets, | |||||
| // If your platform doesn't have native WebSockets, | |||||
| // add Discord.Net.Providers.WS4Net from NuGet, | // add Discord.Net.Providers.WS4Net from NuGet, | ||||
| // add the `using` at the top, and uncomment this line: | // add the `using` at the top, and uncomment this line: | ||||
| //WebSocketProvider = WS4NetProvider.Instance | //WebSocketProvider = WS4NetProvider.Instance | ||||
| @@ -192,7 +192,7 @@ namespace Discord.API | |||||
| internal override async Task DisconnectInternalAsync() | internal override async Task DisconnectInternalAsync() | ||||
| { | { | ||||
| if (_webSocketClient == null) | if (_webSocketClient == null) | ||||
| throw new NotSupportedException("This client is not configured with websocket support."); | |||||
| throw new NotSupportedException("This client is not configured with WebSocket support."); | |||||
| if (ConnectionState == ConnectionState.Disconnected) return; | if (ConnectionState == ConnectionState.Disconnected) return; | ||||
| ConnectionState = ConnectionState.Disconnecting; | ConnectionState = ConnectionState.Disconnecting; | ||||
| @@ -14,7 +14,7 @@ namespace Discord.Rpc | |||||
| /// <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 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; } | ||||
| public DiscordRpcConfig() | public DiscordRpcConfig() | ||||
| @@ -24,7 +24,7 @@ namespace Discord.Rpc | |||||
| #else | #else | ||||
| WebSocketProvider = () => | WebSocketProvider = () => | ||||
| { | { | ||||
| throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + | |||||
| throw new InvalidOperationException("The default WebSocket provider is not supported on this platform.\n" + | |||||
| "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); | "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); | ||||
| }; | }; | ||||
| #endif | #endif | ||||
| @@ -117,6 +117,7 @@ namespace Discord.Commands.Builders | |||||
| return this; | return this; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Only the last parameter in a command may have the Remainder or Multiple flag.</exception> | |||||
| internal CommandInfo Build(ModuleInfo info, CommandService service) | internal CommandInfo Build(ModuleInfo info, CommandService service) | ||||
| { | { | ||||
| //Default name to primary alias | //Default name to primary alias | ||||
| @@ -40,17 +40,17 @@ namespace Discord.Commands | |||||
| internal readonly LogManager _logManager; | internal readonly LogManager _logManager; | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents all modules loaded within <see cref="CommandService" /> . | |||||
| /// Represents all modules loaded within <see cref="CommandService" />. | |||||
| /// </summary> | /// </summary> | ||||
| public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x); | public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x); | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents all commands loaded within <see cref="CommandService" /> . | |||||
| /// Represents all commands loaded within <see cref="CommandService" />. | |||||
| /// </summary> | /// </summary> | ||||
| public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands); | public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands); | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents all <see cref="TypeReader" /> loaded within <see cref="CommandService" /> . | |||||
| /// Represents all <see cref="TypeReader" /> loaded within <see cref="CommandService" />. | |||||
| /// </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); | ||||
| @@ -122,12 +122,12 @@ namespace Discord.Commands | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Add a command module from a <see cref="Type" /> . | |||||
| /// Add a command module from a <see cref="Type" />. | |||||
| /// </summary> | /// </summary> | ||||
| /// <typeparam name="T">The type of module.</typeparam> | /// <typeparam name="T">The type of module.</typeparam> | ||||
| /// <param name="services"> | /// <param name="services"> | ||||
| /// The <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | /// The <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | ||||
| /// <see langword="null" /> . | |||||
| /// <see langword="null" />. | |||||
| /// </param> | /// </param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A built module. | /// A built module. | ||||
| @@ -135,12 +135,12 @@ namespace Discord.Commands | |||||
| 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> | ||||
| /// <param name="type">The type of module.</param> | /// <param name="type">The type of module.</param> | ||||
| /// <param name="services"> | /// <param name="services"> | ||||
| /// The <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | /// The <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | ||||
| /// <see langword="null" /> . | |||||
| /// <see langword="null" />. | |||||
| /// </param> | /// </param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A built module. | /// A built module. | ||||
| @@ -174,12 +174,12 @@ namespace Discord.Commands | |||||
| } | } | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Add command modules from an <see cref="Assembly" /> . | |||||
| /// Add command modules from an <see cref="Assembly" />. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="assembly">The <see cref="Assembly" /> containing command modules.</param> | /// <param name="assembly">The <see cref="Assembly" /> containing command modules.</param> | ||||
| /// <param name="services"> | /// <param name="services"> | ||||
| /// An <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | /// An <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | ||||
| /// <see langword="null" /> . | |||||
| /// <see langword="null" />. | |||||
| /// </param> | /// </param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A collection of built modules. | /// A collection of built modules. | ||||
| @@ -16,7 +16,7 @@ namespace Discord.Commands | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This object contains the information of a command. This can include the module of the command, various | /// This object contains the information of a command. This can include the module of the command, various | ||||
| /// descriptions regarding the command, and its <see cref="RunMode" /> . | |||||
| /// descriptions regarding the command, and its <see cref="RunMode" />. | |||||
| /// </remarks> | /// </remarks> | ||||
| [DebuggerDisplay("{Name,nq}")] | [DebuggerDisplay("{Name,nq}")] | ||||
| public class CommandInfo | public class CommandInfo | ||||
| @@ -23,6 +23,7 @@ namespace Discord.Commands | |||||
| _commands = ImmutableArray.Create<CommandInfo>(); | _commands = ImmutableArray.Create<CommandInfo>(); | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Cannot add commands to the root node.</exception> | |||||
| public void AddCommand(CommandService service, string text, int index, CommandInfo command) | public void AddCommand(CommandService service, string text, int index, CommandInfo command) | ||||
| { | { | ||||
| int nextSegment = NextSegment(text, index, service._separatorChar); | int nextSegment = NextSegment(text, index, service._separatorChar); | ||||
| @@ -17,10 +17,12 @@ namespace Discord.Commands | |||||
| private readonly TryParseDelegate<T> _tryParse; | private readonly TryParseDelegate<T> _tryParse; | ||||
| private readonly float _score; | private readonly float _score; | ||||
| /// <exception cref="ArgumentOutOfRangeException"><typeparamref name="T"/> must be within the range [0, 1].</exception> | |||||
| public PrimitiveTypeReader() | public PrimitiveTypeReader() | ||||
| : this(PrimitiveParsers.Get<T>(), 1) | : this(PrimitiveParsers.Get<T>(), 1) | ||||
| { } | { } | ||||
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="score"/> must be within the range [0, 1].</exception> | |||||
| public PrimitiveTypeReader(TryParseDelegate<T> tryParse, float score) | public PrimitiveTypeReader(TryParseDelegate<T> tryParse, float score) | ||||
| { | { | ||||
| if (score < 0 || score > 1) | if (score < 0 || score > 1) | ||||
| @@ -15,7 +15,7 @@ namespace Discord.Audio | |||||
| /// <summary> Gets the current connection state of this client. </summary> | /// <summary> Gets the current connection state of this client. </summary> | ||||
| ConnectionState ConnectionState { get; } | ConnectionState ConnectionState { get; } | ||||
| /// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice websocket server. </summary> | |||||
| /// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice WebSocket server. </summary> | |||||
| int Latency { get; } | int Latency { get; } | ||||
| /// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice UDP server. </summary> | /// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice UDP server. </summary> | ||||
| int UdpLatency { get; } | int UdpLatency { get; } | ||||
| @@ -13,7 +13,7 @@ namespace Discord | |||||
| public static string GetApplicationIconUrl(ulong appId, string iconId) | public static string GetApplicationIconUrl(ulong appId, string iconId) | ||||
| => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; | => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; | ||||
| /// <summary> | /// <summary> | ||||
| /// Returns the user avatar URL based on the <paramref name="size"/> and <see cref="ImageFormat" /> . | |||||
| /// Returns the user avatar URL based on the <paramref name="size"/> and <see cref="ImageFormat" />. | |||||
| /// </summary> | /// </summary> | ||||
| public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size, ImageFormat format) | public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size, ImageFormat format) | ||||
| { | { | ||||
| @@ -52,7 +52,7 @@ namespace Discord | |||||
| => $"{DiscordConfig.CDNUrl}emojis/{emojiId}.{(animated ? "gif" : "png")}"; | => $"{DiscordConfig.CDNUrl}emojis/{emojiId}.{(animated ? "gif" : "png")}"; | ||||
| /// <summary> | /// <summary> | ||||
| /// Returns the rich presence asset URL based on the asset ID and <see cref="ImageFormat" /> . | |||||
| /// Returns the rich presence asset URL based on the asset ID and <see cref="ImageFormat" />. | |||||
| /// </summary> | /// </summary> | ||||
| public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format) | public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format) | ||||
| { | { | ||||
| @@ -1,7 +1,7 @@ | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents the context of a command. This may include the client, guild, channel, user, and message. | |||||
| /// Represents a context of a command. This may include the client, guild, channel, user, and message. | |||||
| /// </summary> | /// </summary> | ||||
| public interface ICommandContext | public interface ICommandContext | ||||
| { | { | ||||
| @@ -18,7 +18,7 @@ namespace Discord | |||||
| /// Creates a <see cref="Game"/> with the provided <paramref name="name"/> and <see cref="ActivityType"/>. | /// Creates a <see cref="Game"/> with the provided <paramref name="name"/> and <see cref="ActivityType"/>. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="name">The name of the game.</param> | /// <param name="name">The name of the game.</param> | ||||
| /// <param name="type">The type of activity. Default is <see cref="Discord.ActivityType.Playing" /> .</param> | |||||
| /// <param name="type">The type of activity. Default is <see cref="Discord.ActivityType.Playing" />.</param> | |||||
| public Game(string name, ActivityType type = ActivityType.Playing) | public Game(string name, ActivityType type = ActivityType.Playing) | ||||
| { | { | ||||
| Name = name; | Name = name; | ||||
| @@ -1,7 +1,7 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Properties that are used to reorder an <see cref="IGuildChannel" /> . | |||||
| /// Properties that are used to reorder an <see cref="IGuildChannel" />. | |||||
| /// </summary> | /// </summary> | ||||
| public class ReorderChannelProperties | public class ReorderChannelProperties | ||||
| { | { | ||||
| @@ -8,11 +8,11 @@ namespace Discord | |||||
| public class EmoteProperties | public class EmoteProperties | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the name of the <see cref="Emote" /> . | |||||
| /// Gets or sets the name of the <see cref="Emote" />. | |||||
| /// </summary> | /// </summary> | ||||
| public Optional<string> Name { get; set; } | public Optional<string> Name { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the roles that can access this <see cref="Emote" /> . | |||||
| /// Gets or sets the roles that can access this <see cref="Emote" />. | |||||
| /// </summary> | /// </summary> | ||||
| public Optional<IEnumerable<IRole>> Roles { get; set; } | public Optional<IEnumerable<IRole>> Roles { get; set; } | ||||
| } | } | ||||
| @@ -6,7 +6,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a generic guild object. | |||||
| /// Represents a generic guild/server. | |||||
| /// </summary> | /// </summary> | ||||
| public interface IGuild : IDeletable, ISnowflakeEntity | public interface IGuild : IDeletable, ISnowflakeEntity | ||||
| { | { | ||||
| @@ -23,7 +23,7 @@ namespace Discord | |||||
| /// Determines if this guild is embeddable (i.e. can use widget). | /// Determines if this guild is embeddable (i.e. can use widget). | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| /// Returns <see langword="true"/> if this guild can be embedded via widgets. | |||||
| /// <see langword="true"/> if this guild can be embedded via widgets; otherwise <see langword="false"/>. | |||||
| /// </returns> | /// </returns> | ||||
| bool IsEmbeddable { get; } | bool IsEmbeddable { get; } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -91,58 +91,91 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the <see cref="IAudioClient" /> currently associated with this guild. | /// Gets the <see cref="IAudioClient" /> currently associated with this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// <see cref="IAudioClient" /> currently associated with this guild. | |||||
| /// </returns> | |||||
| IAudioClient AudioClient { get; } | IAudioClient AudioClient { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the built-in role containing all users in this guild. | /// Gets the built-in role containing all users in this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// Built-in role that represents an @everyone role in this guild. | |||||
| /// </returns> | |||||
| IRole EveryoneRole { get; } | IRole EveryoneRole { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of all custom emotes for this guild. | /// Gets a collection of all custom emotes for this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// A collection of all custom emotes for this guild. | |||||
| /// </returns> | |||||
| IReadOnlyCollection<GuildEmote> Emotes { get; } | IReadOnlyCollection<GuildEmote> Emotes { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of all extra features added to this guild. | /// Gets a collection of all extra features added to this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// A collection of enabled features in this guild. | |||||
| /// </returns> | |||||
| IReadOnlyCollection<string> Features { get; } | IReadOnlyCollection<string> Features { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of all roles in this guild. | /// Gets a collection of all roles in this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// A collection of roles found within this guild. | |||||
| /// </returns> | |||||
| IReadOnlyCollection<IRole> Roles { get; } | IReadOnlyCollection<IRole> Roles { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Modifies this guild. | /// Modifies this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="func">The properties to modify the guild with.</param> | |||||
| /// <param name="func">The delegate containing the properties to modify the guild with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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="func">The delegate containing the properties to modify the guild widget with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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="args">The properties used to modify the channel positions with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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="args">The properties used to modify the role positions with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</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); | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| /// <summary> | /// <summary> | ||||
| /// Leaves this guild. If you are the owner, use <see cref="IDeletable.DeleteAsync" /> 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of banned users with reasons. | |||||
| /// </returns> | |||||
| Task<IReadOnlyCollection<IBan>> GetBansAsync(RequestOptions options = null); | Task<IReadOnlyCollection<IBan>> GetBansAsync(RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Bans the provided user from this guild and optionally prunes their recent messages. | /// Bans the provided user from this guild and optionally prunes their recent messages. | ||||
| @@ -154,6 +187,9 @@ namespace Discord | |||||
| /// <param name="reason">The reason of the ban to be written in the audit log.</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> | /// <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> | /// <exception cref="ArgumentException"><paramref name="pruneDays" /> is not between 0 to 7.</exception> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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. | ||||
| @@ -165,14 +201,27 @@ namespace Discord | |||||
| /// <param name="reason">The reason of the ban to be written in the audit log.</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> | /// <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> | /// <exception cref="ArgumentException"><paramref name="pruneDays" /> is not between 0 to 7.</exception> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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 user if they are currently banned. | /// Unbans the provided user if they are currently banned. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="user">The user to be unbanned.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| Task RemoveBanAsync(IUser user, RequestOptions options = null); | Task RemoveBanAsync(IUser user, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Unbans the provided user ID if it is currently banned. | /// Unbans the provided user ID if it is currently banned. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="userId">The snowflake ID of the user to be unbanned.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| Task RemoveBanAsync(ulong userId, RequestOptions options = null); | Task RemoveBanAsync(ulong userId, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -182,15 +231,22 @@ namespace Discord | |||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of generic channels found within this guild. | |||||
| /// </returns> | |||||
| 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. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="id">The channel ID.</param> | /// <param name="id">The channel ID.</param> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the generic channel with the specified ID, or | |||||
| /// <see langword="null"/> if none is found. | |||||
| /// </returns> | |||||
| 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. | ||||
| @@ -199,6 +255,9 @@ namespace Discord | |||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of text channels found within this guild. | |||||
| /// </returns> | |||||
| 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. | ||||
| @@ -208,6 +267,10 @@ namespace Discord | |||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the text channel with the specified ID, or | |||||
| /// <see langword="null"/> if none is found. | |||||
| /// </returns> | |||||
| 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. | ||||
| @@ -216,6 +279,9 @@ namespace Discord | |||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of voice channels found within this guild. | |||||
| /// </returns> | |||||
| 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. | ||||
| @@ -224,68 +290,97 @@ namespace Discord | |||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of category channels found within this guild. | |||||
| /// </returns> | |||||
| 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. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="id">The text channel ID.</param> | /// <param name="id">The text channel ID.</param> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the voice channel with the specified ID, or | |||||
| /// <see langword="null"/> if none is found. | |||||
| /// </returns> | |||||
| 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 AFK voice channel in this guild. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the AFK voice channel set within this guild, or | |||||
| /// <see langword="null"/> if none is set. | |||||
| /// </returns> | |||||
| 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 | |||||
| /// none is set. | |||||
| /// Gets the default system text channel in this guild with the provided ID. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the system channel within this guild, or | |||||
| /// <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| 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 | |||||
| /// found. | |||||
| /// Gets the top viewable text channel in this guild with the provided ID. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the first viewable text channel in this guild, or | |||||
| /// <see langword="null"/> if none is found. | |||||
| /// </returns> | |||||
| 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 (i.e. the channel set in the guild's widget settings) in this guild, or | |||||
| /// <see langword="null" /> if none is set. | |||||
| /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="mode"> | /// <param name="mode"> | ||||
| /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | ||||
| /// </param> | /// </param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the embed channel set within the server's widget settings, or | |||||
| /// <see langword="null"/> if none is set. | |||||
| /// </returns> | |||||
| 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly created text channel. | |||||
| /// </returns> | |||||
| 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly created voice channel. | |||||
| /// </returns> | |||||
| 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly created category channel. | |||||
| /// </returns> | |||||
| 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); | ||||
| @@ -309,13 +404,24 @@ namespace Discord | |||||
| /// <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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly crated role. | |||||
| /// </returns> | |||||
| 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> | ||||
| /// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of users found within this guild. | |||||
| /// </returns> | |||||
| /// <remarks> | |||||
| /// This may return an incomplete list on the WebSocket implementation because Discord only sends offline | |||||
| /// users on large guilds. | |||||
| /// </remarks> | |||||
| Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | 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. | ||||
| @@ -323,22 +429,34 @@ namespace Discord | |||||
| /// <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="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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the guild user with the specified ID, otherwise <see langword="null"/>. | |||||
| /// </returns> | |||||
| 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="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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the currently logged-in user within this guild. | |||||
| /// </returns> | |||||
| 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="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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the owner of this guild. | |||||
| /// </returns> | |||||
| 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. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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 | ||||
| @@ -349,7 +467,7 @@ namespace Discord | |||||
| /// <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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | /// <returns> | ||||
| /// The number of users removed from this guild. | |||||
| /// An awaitable <see cref="Task"/> containing the number of users to be or has been removed from this guild. | |||||
| /// </returns> | /// </returns> | ||||
| Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); | Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); | ||||
| @@ -358,11 +476,17 @@ namespace Discord | |||||
| /// </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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the webhook with the specified ID, otherwise <see langword="null"/>. | |||||
| /// </returns> | |||||
| Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of all webhook 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of webhooks found within the guild. | |||||
| /// </returns> | |||||
| Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null); | Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -370,6 +494,9 @@ namespace Discord | |||||
| /// </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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the emote found with the specified ID, or <see langword="null"/> if not found. | |||||
| /// </returns> | |||||
| Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null); | Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Creates a new <see cref="GuildEmote"/> in this guild. | /// Creates a new <see cref="GuildEmote"/> in this guild. | ||||
| @@ -378,20 +505,29 @@ namespace Discord | |||||
| /// <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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the created emote. | |||||
| /// </returns> | |||||
| 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 <see cref="GuildEmote"/> 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="emote">The emote to be modified.</param> | ||||
| /// <param name="func">The properties to modify the emote with.</param> | |||||
| /// <param name="func">The delegate containing the properties to modify the emote with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly modified emote. | |||||
| /// </returns> | |||||
| 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 <see cref="GuildEmote"/> from this guild. | /// Deletes an existing <see cref="GuildEmote"/> from this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="emote">The 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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); | Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); | ||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents whether the object is updatable or not. | |||||
| /// Defines whether the object is updateable or not. | |||||
| /// </summary> | /// </summary> | ||||
| public interface IUpdateable | public interface IUpdateable | ||||
| { | { | ||||
| @@ -33,9 +33,9 @@ namespace Discord | |||||
| /// <param name="path">The path to the file.</param> | /// <param name="path">The path to the file.</param> | ||||
| /// <exception cref="ArgumentException"> | /// <exception cref="ArgumentException"> | ||||
| /// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid | /// <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" /> . | |||||
| /// characters as defined by <see cref="Path.GetInvalidPathChars" />. | |||||
| /// </exception> | /// </exception> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="path" /> is <see langword="null" /> .</exception> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="path" /> is <see langword="null" />.</exception> | |||||
| /// <exception cref="PathTooLongException"> | /// <exception cref="PathTooLongException"> | ||||
| /// The specified path, file name, or both exceed the system-defined maximum length. For example, on | /// 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 | /// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 | ||||
| @@ -172,7 +172,7 @@ 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> | /// <returns> | ||||
| @@ -3,7 +3,7 @@ using System.Diagnostics; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a field for an <see cref="Embed" /> . | |||||
| /// Represents a field for an <see cref="Embed" />. | |||||
| /// </summary> | /// </summary> | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | [DebuggerDisplay("{DebuggerDisplay,nq}")] | ||||
| public struct EmbedField | public struct EmbedField | ||||
| @@ -3,7 +3,7 @@ using System.Diagnostics; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// A video featured in an <see cref="Embed" /> . | |||||
| /// A video featured in an <see cref="Embed" />. | |||||
| /// </summary> | /// </summary> | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | [DebuggerDisplay("{DebuggerDisplay,nq}")] | ||||
| public struct EmbedVideo | public struct EmbedVideo | ||||
| @@ -4,7 +4,7 @@ using System.Collections.Generic; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a Discord message object. | |||||
| /// Represents a message object. | |||||
| /// </summary> | /// </summary> | ||||
| public interface IMessage : ISnowflakeEntity, IDeletable | public interface IMessage : ISnowflakeEntity, IDeletable | ||||
| { | { | ||||
| @@ -4,20 +4,21 @@ namespace Discord | |||||
| /// Properties that are used to modify an <see cref="IUserMessage" /> with the specified changes. | /// Properties that are used to modify an <see cref="IUserMessage" /> with the specified changes. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// The content of a message can be cleared with String.Empty; if and only if an Embed is present. | |||||
| /// The content of a message can be cleared with <see cref="string.Empty"/> if and only if an <see cref="Discord.Embed"> is present. | |||||
| /// </remarks> | /// </remarks> | ||||
| /// <example> | /// <example> | ||||
| /// <code lang="c#"> | /// <code lang="c#"> | ||||
| /// var message = await ReplyAsync("abc"); | |||||
| /// await message.ModifyAsync(x => | |||||
| /// { | |||||
| /// x.Content = ""; | |||||
| /// x.Embed = new EmbedBuilder() | |||||
| /// .WithColor(new Color(40, 40, 120)) | |||||
| /// .WithAuthor(a => a.Name = "foxbot") | |||||
| /// .WithTitle("Embed!") | |||||
| /// .WithDescription("This is an embed."); | |||||
| /// }); | |||||
| /// var message = await ReplyAsync("abc"); | |||||
| /// await message.ModifyAsync(x => | |||||
| /// { | |||||
| /// x.Content = ""; | |||||
| /// x.Embed = new EmbedBuilder() | |||||
| /// .WithColor(new Color(40, 40, 120)) | |||||
| /// .WithAuthor(a => a.Name = "foxbot") | |||||
| /// .WithTitle("Embed!") | |||||
| /// .WithDescription("This is an embed.") | |||||
| /// .Build(); | |||||
| /// }); | |||||
| /// </code> | /// </code> | ||||
| /// </example> | /// </example> | ||||
| public class MessageProperties | public class MessageProperties | ||||
| @@ -26,7 +27,7 @@ namespace Discord | |||||
| /// Gets or sets the content of the message. | /// Gets or sets the content of the message. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This must be less than 2000 characters. | |||||
| /// This must be less than the constant defined by <see cref="DiscordConfig.MaxMessageSize"/>. | |||||
| /// </remarks> | /// </remarks> | ||||
| public Optional<string> Content { get; set; } | public Optional<string> Content { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -19,7 +19,8 @@ namespace Discord | |||||
| public static readonly ChannelPermissions DM = new ChannelPermissions(0b00000_1000110_1011100110000_000000); | public static readonly ChannelPermissions DM = new ChannelPermissions(0b00000_1000110_1011100110000_000000); | ||||
| /// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for group channels. </summary> | /// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for group channels. </summary> | ||||
| public static readonly ChannelPermissions Group = new ChannelPermissions(0b00000_1000110_0001101100000_000000); | public static readonly ChannelPermissions Group = new ChannelPermissions(0b00000_1000110_0001101100000_000000); | ||||
| /// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for a given channelType. </summary> | |||||
| /// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for a given channel type. </summary> | |||||
| /// <exception cref="ArgumentException">Unknown channel type.</exception> | |||||
| public static ChannelPermissions All(IChannel channel) | public static ChannelPermissions All(IChannel channel) | ||||
| { | { | ||||
| switch (channel) | switch (channel) | ||||
| @@ -1,7 +1,7 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Properties that are used to reorder an <see cref="IRole" /> . | |||||
| /// Properties that are used to reorder an <see cref="IRole" />. | |||||
| /// </summary> | /// </summary> | ||||
| public class ReorderRoleProperties | public class ReorderRoleProperties | ||||
| { | { | ||||
| @@ -23,7 +23,7 @@ namespace Discord | |||||
| /// </remarks> | /// </remarks> | ||||
| public Optional<string> Name { get; set; } | public Optional<string> Name { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the role's <see cref="GuildPermission" /> . | |||||
| /// Gets or sets the role's <see cref="GuildPermission" />. | |||||
| /// </summary> | /// </summary> | ||||
| public Optional<GuildPermissions> Permissions { get; set; } | public Optional<GuildPermissions> Permissions { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -7,10 +7,10 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// <code lang="c#"> | /// <code lang="c#"> | ||||
| /// await (Context.User as IGuildUser)?.ModifyAsync(x => | |||||
| /// { | |||||
| /// x.Nickname = $"festive {Context.User.Username}"; | |||||
| /// }); | |||||
| /// await guildUser.ModifyAsync(x => | |||||
| /// { | |||||
| /// x.Nickname = $"festive {guildUser.Username}"; | |||||
| /// }); | |||||
| /// </code> | /// </code> | ||||
| /// </example> | /// </example> | ||||
| /// <seealso cref="T:Discord.IGuildUser" /> | /// <seealso cref="T:Discord.IGuildUser" /> | ||||
| @@ -35,7 +35,7 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// To clear the user's nickname, this value can be set to <see langword="null" /> or | /// To clear the user's nickname, this value can be set to <see langword="null" /> or | ||||
| /// <see cref="string.Empty" /> . | |||||
| /// <see cref="string.Empty" />. | |||||
| /// </remarks> | /// </remarks> | ||||
| public Optional<string> Nickname { get; set; } | public Optional<string> Nickname { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -27,14 +27,14 @@ namespace Discord | |||||
| /// Gets or sets the channel for this webhook. | /// Gets or sets the channel for this webhook. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This field is not used when authenticated with <see cref="Discord.TokenType.Webhook" /> . | |||||
| /// This field is not used when authenticated with <see cref="Discord.TokenType.Webhook" />. | |||||
| /// </remarks> | /// </remarks> | ||||
| public Optional<ITextChannel> Channel { get; set; } | public Optional<ITextChannel> Channel { get; set; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the channel ID for this webhook. | /// Gets or sets the channel ID for this webhook. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This field is not used when authenticated with <see cref="Discord.TokenType.Webhook" /> . | |||||
| /// This field is not used when authenticated with <see cref="Discord.TokenType.Webhook" />. | |||||
| /// </remarks> | /// </remarks> | ||||
| public Optional<ulong> ChannelId { get; set; } | public Optional<ulong> ChannelId { get; set; } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections; | using System.Collections; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.ObjectModel; | using System.Collections.ObjectModel; | ||||
| @@ -157,12 +157,16 @@ namespace Discord | |||||
| : this(collection, EqualityComparer<T>.Default) { } | : this(collection, EqualityComparer<T>.Default) { } | ||||
| public ConcurrentHashSet(IEqualityComparer<T> comparer) | public ConcurrentHashSet(IEqualityComparer<T> comparer) | ||||
| : this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { } | : this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="collection"/> is <see langword="null"/></exception> | |||||
| public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) | public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) | ||||
| : this(comparer) | : this(comparer) | ||||
| { | { | ||||
| if (collection == null) throw new ArgumentNullException(nameof(collection)); | if (collection == null) throw new ArgumentNullException(nameof(collection)); | ||||
| InitializeFromCollection(collection); | InitializeFromCollection(collection); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"> | |||||
| /// <paramref name="collection" /> or <paramref name="comparer" /> is <see langword="null" /> | |||||
| /// </exception> | |||||
| public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer) | public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer) | ||||
| : this(concurrencyLevel, DefaultCapacity, false, comparer) | : this(concurrencyLevel, DefaultCapacity, false, comparer) | ||||
| { | { | ||||
| @@ -197,7 +201,7 @@ namespace Discord | |||||
| { | { | ||||
| foreach (var value in collection) | foreach (var value in collection) | ||||
| { | { | ||||
| if (value == null) throw new ArgumentNullException("key"); | |||||
| if (value == null) throw new ArgumentNullException(nameof(value)); | |||||
| if (!TryAddInternal(value, _comparer.GetHashCode(value), false)) | if (!TryAddInternal(value, _comparer.GetHashCode(value), false)) | ||||
| throw new ArgumentException(); | throw new ArgumentException(); | ||||
| @@ -206,10 +210,10 @@ namespace Discord | |||||
| if (_budget == 0) | if (_budget == 0) | ||||
| _budget = _tables._buckets.Length / _tables._locks.Length; | _budget = _tables._buckets.Length / _tables._locks.Length; | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||||
| public bool ContainsKey(T value) | public bool ContainsKey(T value) | ||||
| { | { | ||||
| if (value == null) throw new ArgumentNullException("key"); | |||||
| if (value == null) throw new ArgumentNullException(nameof(value)); | |||||
| return ContainsKeyInternal(value, _comparer.GetHashCode(value)); | return ContainsKeyInternal(value, _comparer.GetHashCode(value)); | ||||
| } | } | ||||
| private bool ContainsKeyInternal(T value, int hashcode) | private bool ContainsKeyInternal(T value, int hashcode) | ||||
| @@ -230,9 +234,10 @@ namespace Discord | |||||
| return false; | return false; | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||||
| public bool TryAdd(T value) | public bool TryAdd(T value) | ||||
| { | { | ||||
| if (value == null) throw new ArgumentNullException("key"); | |||||
| if (value == null) throw new ArgumentNullException(nameof(value)); | |||||
| return TryAddInternal(value, _comparer.GetHashCode(value), true); | return TryAddInternal(value, _comparer.GetHashCode(value), true); | ||||
| } | } | ||||
| private bool TryAddInternal(T value, int hashcode, bool acquireLock) | private bool TryAddInternal(T value, int hashcode, bool acquireLock) | ||||
| @@ -279,9 +284,10 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||||
| public bool TryRemove(T value) | public bool TryRemove(T value) | ||||
| { | { | ||||
| if (value == null) throw new ArgumentNullException("key"); | |||||
| if (value == null) throw new ArgumentNullException(nameof(value)); | |||||
| return TryRemoveInternal(value); | return TryRemoveInternal(value); | ||||
| } | } | ||||
| private bool TryRemoveInternal(T value) | private bool TryRemoveInternal(T value) | ||||
| @@ -467,4 +473,4 @@ namespace Discord | |||||
| Monitor.Exit(_tables._locks[i]); | Monitor.Exit(_tables._locks[i]); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -11,6 +11,7 @@ namespace Discord | |||||
| private readonly T _value; | private readonly T _value; | ||||
| /// <summary> Gets the value for this parameter. </summary> | /// <summary> Gets the value for this parameter. </summary> | ||||
| /// <exception cref="InvalidOperationException" accessor="get">This property has no value set.</exception> | |||||
| public T Value | public T Value | ||||
| { | { | ||||
| get | get | ||||
| @@ -183,7 +183,7 @@ namespace Discord | |||||
| } | } | ||||
| // Bulk Delete | // Bulk Delete | ||||
| /// <exception cref="ArgumentOutOfRangeException">Messages are younger than 2 weeks..</exception> | |||||
| /// <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))); | ||||
| @@ -194,7 +194,7 @@ 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> | |||||
| /// <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++) | ||||
| @@ -1,3 +1,4 @@ | |||||
| using System; | |||||
| using Discord.API.Rest; | using Discord.API.Rest; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -24,6 +25,7 @@ namespace Discord.Rest | |||||
| return RestChannel.Create(client, model); | return RestChannel.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unexpected channel type.</exception> | |||||
| public static async Task<IReadOnlyCollection<IRestPrivateChannel>> GetPrivateChannelsAsync(BaseDiscordClient client, RequestOptions options) | public static async Task<IReadOnlyCollection<IRestPrivateChannel>> GetPrivateChannelsAsync(BaseDiscordClient client, RequestOptions options) | ||||
| { | { | ||||
| var models = await client.ApiClient.GetMyPrivateChannelsAsync(options).ConfigureAwait(false); | var models = await client.ApiClient.GetMyPrivateChannelsAsync(options).ConfigureAwait(false); | ||||
| @@ -58,6 +58,9 @@ namespace Discord.API | |||||
| SetBaseUrl(DiscordConfig.APIUrl); | SetBaseUrl(DiscordConfig.APIUrl); | ||||
| } | } | ||||
| /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | |||||
| /// <exception cref="Exception">A delegate callback throws an exception.</exception> | |||||
| internal void SetBaseUrl(string baseUrl) | internal void SetBaseUrl(string baseUrl) | ||||
| { | { | ||||
| RestClient = _restClientProvider(baseUrl); | RestClient = _restClientProvider(baseUrl); | ||||
| @@ -65,6 +68,7 @@ namespace Discord.API | |||||
| RestClient.SetHeader("user-agent", UserAgent); | RestClient.SetHeader("user-agent", UserAgent); | ||||
| RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); | RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); | ||||
| } | } | ||||
| /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | |||||
| internal static string GetPrefixedToken(TokenType tokenType, string token) | internal static string GetPrefixedToken(TokenType tokenType, string token) | ||||
| { | { | ||||
| switch (tokenType) | switch (tokenType) | ||||
| @@ -76,7 +80,7 @@ namespace Discord.API | |||||
| case TokenType.Bearer: | case TokenType.Bearer: | ||||
| return $"Bearer {token}"; | return $"Bearer {token}"; | ||||
| default: | default: | ||||
| throw new ArgumentException("Unknown OAuth token type", nameof(tokenType)); | |||||
| throw new ArgumentException("Unknown OAuth token type.", nameof(tokenType)); | |||||
| } | } | ||||
| } | } | ||||
| internal virtual void Dispose(bool disposing) | internal virtual void Dispose(bool disposing) | ||||
| @@ -463,6 +467,7 @@ namespace Discord.API | |||||
| endpoint = () => $"channels/{channelId}/messages?limit={limit}"; | endpoint = () => $"channels/{channelId}/messages?limit={limit}"; | ||||
| return await SendAsync<IReadOnlyCollection<Message>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | return await SendAsync<IReadOnlyCollection<Message>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public async Task<Message> CreateMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) | public async Task<Message> CreateMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| @@ -471,12 +476,14 @@ namespace Discord.API | |||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content?.Length > DiscordConfig.MaxMessageSize) | if (args.Content?.Length > DiscordConfig.MaxMessageSize) | ||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var ids = new BucketIds(channelId: channelId); | var ids = new BucketIds(channelId: channelId); | ||||
| return await SendJsonAsync<Message>("POST", () => $"channels/{channelId}/messages", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendJsonAsync<Message>("POST", () => $"channels/{channelId}/messages", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task<Message> CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null) | public async Task<Message> CreateWebhookMessageAsync(ulong webhookId, CreateWebhookMessageParams args, RequestOptions options = null) | ||||
| { | { | ||||
| if (AuthTokenType != TokenType.Webhook) | if (AuthTokenType != TokenType.Webhook) | ||||
| @@ -488,11 +495,12 @@ namespace Discord.API | |||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | ||||
| if (args.Content?.Length > DiscordConfig.MaxMessageSize) | if (args.Content?.Length > DiscordConfig.MaxMessageSize) | ||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, new BucketIds(), clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, new BucketIds(), clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| @@ -507,6 +515,9 @@ namespace Discord.API | |||||
| var ids = new BucketIds(channelId: channelId); | var ids = new BucketIds(channelId: channelId); | ||||
| return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task<Message> UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null) | public async Task<Message> UploadWebhookFileAsync(ulong webhookId, UploadWebhookFileParams args, RequestOptions options = null) | ||||
| { | { | ||||
| if (AuthTokenType != TokenType.Webhook) | if (AuthTokenType != TokenType.Webhook) | ||||
| @@ -559,6 +570,7 @@ namespace Discord.API | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, Rest.ModifyMessageParams args, RequestOptions options = null) | public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, Rest.ModifyMessageParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| @@ -1267,6 +1279,7 @@ namespace Discord.API | |||||
| } | } | ||||
| //Helpers | //Helpers | ||||
| /// <exception cref="InvalidOperationException">Client is not logged in.</exception> | |||||
| protected void CheckState() | protected void CheckState() | ||||
| { | { | ||||
| if (LoginState != LoginState.LoggedIn) | if (LoginState != LoginState.LoggedIn) | ||||
| @@ -22,12 +22,14 @@ namespace Discord.Rest | |||||
| ApiClient.Dispose(); | ApiClient.Dispose(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
| { | { | ||||
| var user = await ApiClient.GetMyUserAsync(new RequestOptions { RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); | var user = await ApiClient.GetMyUserAsync(new RequestOptions { RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); | ||||
| ApiClient.CurrentUserId = user.Id; | ApiClient.CurrentUserId = user.Id; | ||||
| base.CurrentUser = RestSelfUser.Create(this, user); | base.CurrentUser = RestSelfUser.Create(this, user); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| internal override Task OnLogoutAsync() | internal override Task OnLogoutAsync() | ||||
| { | { | ||||
| _applicationInfo = null; | _applicationInfo = null; | ||||
| @@ -80,9 +82,11 @@ namespace Discord.Rest | |||||
| => ClientHelper.GetWebhookAsync(this, id, options); | => ClientHelper.GetWebhookAsync(this, id, options); | ||||
| //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 /> | |||||
| async Task<IChannel> IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IChannel> IDiscordClient.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -90,6 +94,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IPrivateChannel>> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IPrivateChannel>> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -97,6 +102,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<IPrivateChannel>(); | return ImmutableArray.Create<IPrivateChannel>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IDMChannel>> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IDMChannel>> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -104,6 +110,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<IDMChannel>(); | return ImmutableArray.Create<IDMChannel>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IGroupChannel>> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IGroupChannel>> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -112,12 +119,15 @@ namespace Discord.Rest | |||||
| return ImmutableArray.Create<IGroupChannel>(); | return ImmutableArray.Create<IGroupChannel>(); | ||||
| } | } | ||||
| /// <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 /> | |||||
| async Task<IGuild> IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IGuild> IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -125,6 +135,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IGuild>> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IGuild>> IDiscordClient.GetGuildsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -132,9 +143,11 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<IGuild>(); | return ImmutableArray.Create<IGuild>(); | ||||
| } | } | ||||
| /// <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 /> | |||||
| async Task<IUser> IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IUser> IDiscordClient.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -143,11 +156,14 @@ namespace Discord.Rest | |||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | async Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | ||||
| => await GetVoiceRegionsAsync(options).ConfigureAwait(false); | => await GetVoiceRegionsAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | ||||
| => await GetVoiceRegionAsync(id, options).ConfigureAwait(false); | => await GetVoiceRegionAsync(id, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options) | async Task<IWebhook> IDiscordClient.GetWebhookAsync(ulong id, RequestOptions options) | ||||
| => await GetWebhookAsync(id, options).ConfigureAwait(false); | => await GetWebhookAsync(id, options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -7,7 +7,6 @@ using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Channel; | using Model = Discord.API.Channel; | ||||
| using UserModel = Discord.API.User; | using UserModel = Discord.API.User; | ||||
| using WebhookModel = Discord.API.Webhook; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| @@ -77,14 +76,8 @@ namespace Discord.Rest | |||||
| int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | ||||
| { | { | ||||
| var args = new CreateChannelInviteParams { IsTemporary = isTemporary, IsUnique = isUnique }; | var args = new CreateChannelInviteParams { IsTemporary = isTemporary, IsUnique = isUnique }; | ||||
| if (maxAge.HasValue) | |||||
| args.MaxAge = maxAge.Value; | |||||
| else | |||||
| args.MaxAge = 0; | |||||
| if (maxUses.HasValue) | |||||
| args.MaxUses = maxUses.Value; | |||||
| else | |||||
| args.MaxUses = 0; | |||||
| args.MaxAge = maxAge.GetValueOrDefault(0); | |||||
| args.MaxUses = maxUses.GetValueOrDefault(0); | |||||
| var model = await client.ApiClient.CreateChannelInviteAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateChannelInviteAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestInviteMetadata.Create(client, null, channel, model); | return RestInviteMetadata.Create(client, null, channel, model); | ||||
| } | } | ||||
| @@ -160,6 +153,7 @@ namespace Discord.Rest | |||||
| return builder.ToImmutable(); | return builder.ToImmutable(); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| string text, bool isTTS, Embed embed, RequestOptions options) | string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| { | { | ||||
| @@ -169,6 +163,30 @@ namespace Discord.Rest | |||||
| } | } | ||||
| #if FILESYSTEM | #if FILESYSTEM | ||||
| /// <exception cref="ArgumentException"> | |||||
| /// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more | |||||
| /// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />. | |||||
| /// </exception> | |||||
| /// <exception cref="ArgumentNullException"> | |||||
| /// <paramref name="filePath" /> 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="DirectoryNotFoundException"> | |||||
| /// The specified path is invalid, (for example, it is on an unmapped drive). | |||||
| /// </exception> | |||||
| /// <exception cref="UnauthorizedAccessException"> | |||||
| /// <paramref name="filePath" /> specified a directory.-or- The caller does not have the required permission. | |||||
| /// </exception> | |||||
| /// <exception cref="FileNotFoundException"> | |||||
| /// The file specified in <paramref name="filePath" /> was not found. | |||||
| /// </exception> | |||||
| /// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</exception> | |||||
| /// <exception cref="IOException">An I/O error occurred while opening the file.</exception> | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | string filePath, string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| { | { | ||||
| @@ -177,6 +195,7 @@ namespace Discord.Rest | |||||
| return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); | return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); | ||||
| } | } | ||||
| #endif | #endif | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) | ||||
| { | { | ||||
| @@ -249,6 +268,7 @@ namespace Discord.Rest | |||||
| return user; | return user; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Resolving permissions requires the parent guild to be downloaded.</exception> | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, IGuild guild, BaseDiscordClient client, | public static IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(IGuildChannel channel, IGuild guild, BaseDiscordClient client, | ||||
| ulong? fromUserId, int? limit, RequestOptions options) | ulong? fromUserId, int? limit, RequestOptions options) | ||||
| { | { | ||||
| @@ -5,7 +5,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a REST channel that can send and receive messages. | |||||
| /// Represents a REST-based channel that can send and receive messages. | |||||
| /// </summary> | /// </summary> | ||||
| public interface IRestMessageChannel : IMessageChannel | public interface IRestMessageChannel : IMessageChannel | ||||
| { | { | ||||
| @@ -3,7 +3,7 @@ using System.Collections.Generic; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a REST channel that is private to select recipients. | |||||
| /// Represents a REST-based channel that is private to select recipients. | |||||
| /// </summary> | /// </summary> | ||||
| public interface IRestPrivateChannel : IPrivateChannel | public interface IRestPrivateChannel : IPrivateChannel | ||||
| { | { | ||||
| @@ -7,7 +7,7 @@ using Model = Discord.API.Channel; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a REST category channel. | |||||
| /// Represents a REST-based category channel. | |||||
| /// </summary> | /// </summary> | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestCategoryChannel : RestGuildChannel, ICategoryChannel | public class RestCategoryChannel : RestGuildChannel, ICategoryChannel | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -8,12 +8,14 @@ namespace Discord.Rest | |||||
| { | { | ||||
| public class RestChannel : RestEntity<ulong>, IChannel, IUpdateable | public class RestChannel : RestEntity<ulong>, IChannel, IUpdateable | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| internal RestChannel(BaseDiscordClient discord, ulong id) | internal RestChannel(BaseDiscordClient discord, ulong id) | ||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unexpected channel type.</exception> | |||||
| internal static RestChannel Create(BaseDiscordClient discord, Model model) | internal static RestChannel Create(BaseDiscordClient discord, Model model) | ||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| @@ -28,6 +30,7 @@ namespace Discord.Rest | |||||
| return new RestChannel(discord, model.Id); | return new RestChannel(discord, model.Id); | ||||
| } | } | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unexpected channel type.</exception> | |||||
| internal static IRestPrivateChannel CreatePrivate(BaseDiscordClient discord, Model model) | internal static IRestPrivateChannel CreatePrivate(BaseDiscordClient discord, Model model) | ||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| @@ -42,13 +45,17 @@ namespace Discord.Rest | |||||
| } | } | ||||
| internal virtual void Update(Model model) { } | internal virtual void Update(Model model) { } | ||||
| /// <inheritdoc /> | |||||
| public virtual Task UpdateAsync(RequestOptions options = null) => Task.Delay(0); | public virtual Task UpdateAsync(RequestOptions options = null) => Task.Delay(0); | ||||
| //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,7 +10,7 @@ using Model = Discord.API.Channel; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a REST DM channel. | |||||
| /// Represents a REST-based DM channel. | |||||
| /// </summary> | /// </summary> | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel | public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel | ||||
| @@ -37,6 +37,7 @@ namespace Discord.Rest | |||||
| Recipient.Update(model.Recipients.Value[0]); | Recipient.Update(model.Recipients.Value[0]); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override async Task UpdateAsync(RequestOptions options = null) | public override async Task UpdateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| var model = await Discord.ApiClient.GetChannelAsync(Id, options).ConfigureAwait(false); | var model = await Discord.ApiClient.GetChannelAsync(Id, options).ConfigureAwait(false); | ||||
| @@ -93,16 +94,20 @@ namespace Discord.Rest | |||||
| public override string ToString() => $"@{Recipient}"; | public override string ToString() => $"@{Recipient}"; | ||||
| private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; | private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)"; | ||||
| //IDMChannel | |||||
| //IDMChannel | |||||
| /// <inheritdoc /> | |||||
| IUser IDMChannel.Recipient => Recipient; | IUser IDMChannel.Recipient => Recipient; | ||||
| //IRestPrivateChannel | //IRestPrivateChannel | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<RestUser> IRestPrivateChannel.Recipients => ImmutableArray.Create(Recipient); | IReadOnlyCollection<RestUser> IRestPrivateChannel.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) | ||||
| @@ -110,6 +115,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -117,6 +123,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | ||||
| } | } | ||||
| /// <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) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -124,6 +131,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | ||||
| } | } | ||||
| /// <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) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -131,25 +139,33 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | ||||
| } | } | ||||
| /// <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(); | ||||
| } | } | ||||
| @@ -146,6 +146,7 @@ namespace Discord.Rest | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| //IGuildChannel | //IGuildChannel | ||||
| /// <inheritdoc /> | |||||
| IGuild IGuildChannel.Guild | IGuild IGuildChannel.Guild | ||||
| { | { | ||||
| get | get | ||||
| @@ -156,32 +157,44 @@ namespace Discord.Rest | |||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | ||||
| => await GetInvitesAsync(options).ConfigureAwait(false); | => await GetInvitesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | ||||
| => await CreateInviteAsync(maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); | => await CreateInviteAsync(maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) | OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) | ||||
| => GetPermissionOverwrite(role); | => GetPermissionOverwrite(role); | ||||
| /// <inheritdoc /> | |||||
| OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) | OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) | ||||
| => GetPermissionOverwrite(user); | => GetPermissionOverwrite(user); | ||||
| /// <inheritdoc /> | |||||
| async Task IGuildChannel.AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options) | async Task IGuildChannel.AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options) | ||||
| => await AddPermissionOverwriteAsync(role, permissions, options).ConfigureAwait(false); | => await AddPermissionOverwriteAsync(role, permissions, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task IGuildChannel.AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options) | async Task IGuildChannel.AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options) | ||||
| => await AddPermissionOverwriteAsync(user, permissions, options).ConfigureAwait(false); | => await AddPermissionOverwriteAsync(user, permissions, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task IGuildChannel.RemovePermissionOverwriteAsync(IRole role, RequestOptions options) | async Task IGuildChannel.RemovePermissionOverwriteAsync(IRole role, RequestOptions options) | ||||
| => await RemovePermissionOverwriteAsync(role, options).ConfigureAwait(false); | => await RemovePermissionOverwriteAsync(role, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user, RequestOptions options) | async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user, RequestOptions options) | ||||
| => await RemovePermissionOverwriteAsync(user, options).ConfigureAwait(false); | => await RemovePermissionOverwriteAsync(user, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| => AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>(); //Overridden //Overridden in Text/Voice | => AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>(); //Overridden //Overridden in Text/Voice | ||||
| /// <inheritdoc /> | |||||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IGuildUser>(null); //Overridden in Text/Voice | => Task.FromResult<IGuildUser>(null); //Overridden in Text/Voice | ||||
| //IChannel | //IChannel | ||||
| /// <inheritdoc /> | |||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| => AsyncEnumerable.Empty<IReadOnlyCollection<IUser>>(); //Overridden in Text/Voice | => AsyncEnumerable.Empty<IReadOnlyCollection<IUser>>(); //Overridden in Text/Voice | ||||
| /// <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 in Text/Voice | => Task.FromResult<IUser>(null); //Overridden in Text/Voice | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.API.Rest; | |||||
| using Discord.API.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -14,10 +14,11 @@ namespace Discord.Rest | |||||
| internal static class GuildHelper | internal static class GuildHelper | ||||
| { | { | ||||
| //General | //General | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | public static async Task<Model> ModifyAsync(IGuild guild, BaseDiscordClient client, | ||||
| Action<GuildProperties> func, RequestOptions options) | Action<GuildProperties> func, RequestOptions options) | ||||
| { | { | ||||
| if (func == null) throw new NullReferenceException(nameof(func)); | |||||
| if (func == null) throw new ArgumentNullException(nameof(func)); | |||||
| var args = new GuildProperties(); | var args = new GuildProperties(); | ||||
| func(args); | func(args); | ||||
| @@ -62,10 +63,11 @@ namespace Discord.Rest | |||||
| return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, | public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, | ||||
| Action<GuildEmbedProperties> func, RequestOptions options) | Action<GuildEmbedProperties> func, RequestOptions options) | ||||
| { | { | ||||
| if (func == null) throw new NullReferenceException(nameof(func)); | |||||
| if (func == null) throw new ArgumentNullException(nameof(func)); | |||||
| var args = new GuildEmbedProperties(); | var args = new GuildEmbedProperties(); | ||||
| func(args); | func(args); | ||||
| @@ -139,6 +141,7 @@ namespace Discord.Rest | |||||
| var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id, options).ConfigureAwait(false); | var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id, options).ConfigureAwait(false); | ||||
| return models.Select(x => RestGuildChannel.Create(client, guild, x)).ToImmutableArray(); | return models.Select(x => RestGuildChannel.Create(client, guild, x)).ToImmutableArray(); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options) | string name, RequestOptions options) | ||||
| { | { | ||||
| @@ -148,6 +151,7 @@ namespace Discord.Rest | |||||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | ||||
| return RestTextChannel.Create(client, guild, model); | return RestTextChannel.Create(client, guild, model); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options) | string name, RequestOptions options) | ||||
| { | { | ||||
| @@ -157,6 +161,7 @@ namespace Discord.Rest | |||||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | ||||
| return RestVoiceChannel.Create(client, guild, model); | return RestVoiceChannel.Create(client, guild, model); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, RequestOptions options) | string name, RequestOptions options) | ||||
| { | { | ||||
| @@ -191,6 +196,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Roles | //Roles | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | public static async Task<RestRole> CreateRoleAsync(IGuild guild, BaseDiscordClient client, | ||||
| string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) | string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) | ||||
| { | { | ||||
| @@ -292,11 +298,12 @@ namespace Discord.Rest | |||||
| Image = image.ToModel() | Image = image.ToModel() | ||||
| }; | }; | ||||
| if (roles.IsSpecified) | if (roles.IsSpecified) | ||||
| apiargs.RoleIds = roles.Value?.Select(xr => xr.Id)?.ToArray(); | |||||
| apiargs.RoleIds = roles.Value?.Select(xr => xr.Id).ToArray(); | |||||
| var emote = await client.ApiClient.CreateGuildEmoteAsync(guild.Id, apiargs, options).ConfigureAwait(false); | var emote = await client.ApiClient.CreateGuildEmoteAsync(guild.Id, apiargs, options).ConfigureAwait(false); | ||||
| return emote.ToEntity(); | return emote.ToEntity(); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public static async Task<GuildEmote> ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action<EmoteProperties> func, | public static async Task<GuildEmote> ModifyEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, Action<EmoteProperties> func, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| @@ -310,7 +317,7 @@ namespace Discord.Rest | |||||
| Name = props.Name | Name = props.Name | ||||
| }; | }; | ||||
| if (props.Roles.IsSpecified) | if (props.Roles.IsSpecified) | ||||
| apiargs.RoleIds = props.Roles.Value?.Select(xr => xr.Id)?.ToArray(); | |||||
| apiargs.RoleIds = props.Roles.Value?.Select(xr => xr.Id).ToArray(); | |||||
| var emote = await client.ApiClient.ModifyGuildEmoteAsync(guild.Id, id, apiargs, options).ConfigureAwait(false); | var emote = await client.ApiClient.ModifyGuildEmoteAsync(guild.Id, id, apiargs, options).ConfigureAwait(false); | ||||
| return emote.ToEntity(); | return emote.ToEntity(); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.Ban; | using Model = Discord.API.Ban; | ||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| @@ -7,6 +7,7 @@ namespace Discord.Rest | |||||
| public class RestBan : IBan | public class RestBan : IBan | ||||
| { | { | ||||
| public RestUser User { get; } | public RestUser User { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Reason { get; } | public string Reason { get; } | ||||
| internal RestBan(RestUser user, string reason) | internal RestBan(RestUser user, string reason) | ||||
| @@ -23,6 +24,7 @@ namespace Discord.Rest | |||||
| private string DebuggerDisplay => $"{User}: {Reason}"; | private string DebuggerDisplay => $"{User}: {Reason}"; | ||||
| //IBan | //IBan | ||||
| /// <inheritdoc /> | |||||
| IUser IBan.User => User; | IUser IBan.User => User; | ||||
| } | } | ||||
| } | } | ||||
| @@ -10,6 +10,9 @@ using Model = Discord.API.Guild; | |||||
| namespace Discord.Rest | namespace Discord.Rest | ||||
| { | { | ||||
| /// <summary> | |||||
| /// Represents a REST-based guild/server. | |||||
| /// </summary> | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestGuild : RestEntity<ulong>, IGuild, IUpdateable | public class RestGuild : RestEntity<ulong>, IGuild, IUpdateable | ||||
| { | { | ||||
| @@ -17,32 +20,50 @@ namespace Discord.Rest | |||||
| private ImmutableArray<GuildEmote> _emotes; | private ImmutableArray<GuildEmote> _emotes; | ||||
| private ImmutableArray<string> _features; | private ImmutableArray<string> _features; | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public int AFKTimeout { get; private set; } | public int AFKTimeout { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsEmbeddable { get; private set; } | public bool IsEmbeddable { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public VerificationLevel VerificationLevel { get; private set; } | public VerificationLevel VerificationLevel { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public MfaLevel MfaLevel { get; private set; } | public MfaLevel MfaLevel { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } | public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong? AFKChannelId { get; private set; } | public ulong? AFKChannelId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong? EmbedChannelId { get; private set; } | public ulong? EmbedChannelId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong? SystemChannelId { get; private set; } | public ulong? SystemChannelId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong OwnerId { get; private set; } | public ulong OwnerId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string VoiceRegionId { get; private set; } | public string VoiceRegionId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string IconId { get; private set; } | public string IconId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string SplashId { get; private set; } | public string SplashId { get; private set; } | ||||
| internal bool Available { get; private set; } | internal bool Available { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| [Obsolete("DefaultChannelId is deprecated, use GetDefaultChannelAsync")] | [Obsolete("DefaultChannelId is deprecated, use GetDefaultChannelAsync")] | ||||
| public ulong DefaultChannelId => Id; | public ulong DefaultChannelId => Id; | ||||
| /// <inheritdoc /> | |||||
| public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); | public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); | ||||
| /// <inheritdoc /> | |||||
| public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); | public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); | ||||
| public RestRole EveryoneRole => GetRole(Id); | public RestRole EveryoneRole => GetRole(Id); | ||||
| public IReadOnlyCollection<RestRole> Roles => _roles.ToReadOnlyCollection(); | public IReadOnlyCollection<RestRole> Roles => _roles.ToReadOnlyCollection(); | ||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<GuildEmote> Emotes => _emotes; | public IReadOnlyCollection<GuildEmote> Emotes => _emotes; | ||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<string> Features => _features; | public IReadOnlyCollection<string> Features => _features; | ||||
| internal RestGuild(BaseDiscordClient client, ulong id) | internal RestGuild(BaseDiscordClient client, ulong id) | ||||
| @@ -103,37 +124,48 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //General | //General | ||||
| /// <inheritdoc /> | |||||
| public async Task UpdateAsync(RequestOptions options = null) | public async Task UpdateAsync(RequestOptions options = null) | ||||
| => Update(await Discord.ApiClient.GetGuildAsync(Id, options).ConfigureAwait(false)); | => Update(await Discord.ApiClient.GetGuildAsync(Id, options).ConfigureAwait(false)); | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| => GuildHelper.DeleteAsync(this, Discord, options); | => GuildHelper.DeleteAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public async Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null) | public async Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null) | ||||
| { | { | ||||
| var model = await GuildHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | var model = await GuildHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | ||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public async Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | public async Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | ||||
| { | { | ||||
| var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); | var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); | ||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="args" /> is <see langword="null" />.</exception> | |||||
| public async Task ReorderChannelsAsync(IEnumerable<ReorderChannelProperties> args, RequestOptions options = null) | public async Task ReorderChannelsAsync(IEnumerable<ReorderChannelProperties> args, RequestOptions options = null) | ||||
| { | { | ||||
| var arr = args.ToArray(); | var arr = args.ToArray(); | ||||
| await GuildHelper.ReorderChannelsAsync(this, Discord, arr, options).ConfigureAwait(false); | await GuildHelper.ReorderChannelsAsync(this, Discord, arr, options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public async Task ReorderRolesAsync(IEnumerable<ReorderRoleProperties> args, RequestOptions options = null) | public async Task ReorderRolesAsync(IEnumerable<ReorderRoleProperties> args, RequestOptions options = null) | ||||
| { | { | ||||
| var models = await GuildHelper.ReorderRolesAsync(this, Discord, args, options).ConfigureAwait(false); | var models = await GuildHelper.ReorderRolesAsync(this, Discord, args, options).ConfigureAwait(false); | ||||
| foreach (var model in models) | foreach (var model in models) | ||||
| { | { | ||||
| var role = GetRole(model.Id); | var role = GetRole(model.Id); | ||||
| if (role != null) | |||||
| role.Update(model); | |||||
| role?.Update(model); | |||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task LeaveAsync(RequestOptions options = null) | public Task LeaveAsync(RequestOptions options = null) | ||||
| => GuildHelper.LeaveAsync(this, Discord, options); | => GuildHelper.LeaveAsync(this, Discord, options); | ||||
| @@ -141,13 +173,17 @@ namespace Discord.Rest | |||||
| 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); | ||||
| /// <inheritdoc /> | |||||
| public Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) | public Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) | ||||
| => GuildHelper.AddBanAsync(this, Discord, user.Id, pruneDays, reason, options); | => GuildHelper.AddBanAsync(this, Discord, user.Id, pruneDays, reason, options); | ||||
| /// <inheritdoc /> | |||||
| public Task AddBanAsync(ulong userId, int pruneDays = 0, string reason = null, RequestOptions options = null) | public Task AddBanAsync(ulong userId, int pruneDays = 0, string reason = null, RequestOptions options = null) | ||||
| => GuildHelper.AddBanAsync(this, Discord, userId, pruneDays, reason, options); | => GuildHelper.AddBanAsync(this, Discord, userId, pruneDays, reason, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveBanAsync(IUser user, RequestOptions options = null) | public Task RemoveBanAsync(IUser user, RequestOptions options = null) | ||||
| => GuildHelper.RemoveBanAsync(this, Discord, user.Id, options); | => GuildHelper.RemoveBanAsync(this, Discord, user.Id, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveBanAsync(ulong userId, RequestOptions options = null) | public Task RemoveBanAsync(ulong userId, RequestOptions options = null) | ||||
| => GuildHelper.RemoveBanAsync(this, Discord, userId, options); | => GuildHelper.RemoveBanAsync(this, Discord, userId, options); | ||||
| @@ -261,6 +297,7 @@ namespace Discord.Rest | |||||
| public Task<RestGuildUser> GetOwnerAsync(RequestOptions options = null) | public Task<RestGuildUser> GetOwnerAsync(RequestOptions options = null) | ||||
| => GuildHelper.GetUserAsync(this, Discord, OwnerId, options); | => GuildHelper.GetUserAsync(this, Discord, OwnerId, options); | ||||
| /// <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); | ||||
| @@ -270,28 +307,45 @@ namespace Discord.Rest | |||||
| 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); | ||||
| /// <summary> | |||||
| /// Returns the name of the guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The name of the guild. | |||||
| /// </returns> | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} ({Id})"; | private string DebuggerDisplay => $"{Name} ({Id})"; | ||||
| //Emotes | //Emotes | ||||
| /// <inheritdoc /> | |||||
| public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | ||||
| => GuildHelper.GetEmoteAsync(this, Discord, id, options); | => GuildHelper.GetEmoteAsync(this, Discord, id, options); | ||||
| /// <inheritdoc /> | |||||
| public Task<GuildEmote> CreateEmoteAsync(string name, Image image, Optional<IEnumerable<IRole>> roles = default(Optional<IEnumerable<IRole>>), RequestOptions options = null) | public Task<GuildEmote> CreateEmoteAsync(string name, Image image, Optional<IEnumerable<IRole>> roles = default(Optional<IEnumerable<IRole>>), RequestOptions options = null) | ||||
| => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); | => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null" />.</exception> | |||||
| public Task<GuildEmote> ModifyEmoteAsync(GuildEmote emote, Action<EmoteProperties> func, RequestOptions options = null) | public Task<GuildEmote> ModifyEmoteAsync(GuildEmote emote, Action<EmoteProperties> func, RequestOptions options = null) | ||||
| => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); | => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null) | public Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null) | ||||
| => GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options); | => GuildHelper.DeleteEmoteAsync(this, Discord, emote.Id, options); | ||||
| //IGuild | //IGuild | ||||
| /// <inheritdoc /> | |||||
| bool IGuild.Available => Available; | bool IGuild.Available => Available; | ||||
| /// <inheritdoc /> | |||||
| IAudioClient IGuild.AudioClient => null; | IAudioClient IGuild.AudioClient => null; | ||||
| /// <inheritdoc /> | |||||
| IRole IGuild.EveryoneRole => EveryoneRole; | IRole IGuild.EveryoneRole => EveryoneRole; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<IRole> IGuild.Roles => Roles; | IReadOnlyCollection<IRole> IGuild.Roles => Roles; | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options) | async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync(RequestOptions options) | ||||
| => await GetBansAsync(options).ConfigureAwait(false); | => await GetBansAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -299,6 +353,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<IGuildChannel>(); | return ImmutableArray.Create<IGuildChannel>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -306,6 +361,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<ITextChannel>> IGuild.GetTextChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<ITextChannel>> IGuild.GetTextChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -313,6 +369,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<ITextChannel>(); | return ImmutableArray.Create<ITextChannel>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<ITextChannel> IGuild.GetTextChannelAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<ITextChannel> IGuild.GetTextChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -320,6 +377,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IVoiceChannel>> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IVoiceChannel>> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -327,6 +385,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return ImmutableArray.Create<IVoiceChannel>(); | return ImmutableArray.Create<IVoiceChannel>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<ICategoryChannel>> IGuild.GetCategoriesAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<ICategoryChannel>> IGuild.GetCategoriesAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -334,6 +393,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -341,6 +401,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IVoiceChannel> IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) | async Task<IVoiceChannel> IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -348,6 +409,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<ITextChannel> IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) | async Task<ITextChannel> IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -355,6 +417,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildChannel> IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) | async Task<IGuildChannel> IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -362,6 +425,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<ITextChannel> IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) | async Task<ITextChannel> IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -369,26 +433,35 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, RequestOptions options) | async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, RequestOptions options) | ||||
| => await CreateTextChannelAsync(name, options).ConfigureAwait(false); | => await CreateTextChannelAsync(name, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) | async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) | ||||
| => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); | => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) | async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) | ||||
| => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); | => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options) | async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options) | ||||
| => await GetIntegrationsAsync(options).ConfigureAwait(false); | => await GetIntegrationsAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildIntegration> IGuild.CreateIntegrationAsync(ulong id, string type, RequestOptions options) | async Task<IGuildIntegration> IGuild.CreateIntegrationAsync(ulong id, string type, RequestOptions options) | ||||
| => await CreateIntegrationAsync(id, type, options).ConfigureAwait(false); | => await CreateIntegrationAsync(id, type, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options) | ||||
| => await GetInvitesAsync(options).ConfigureAwait(false); | => await GetInvitesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| IRole IGuild.GetRole(ulong id) | IRole IGuild.GetRole(ulong id) | ||||
| => GetRole(id); | => GetRole(id); | ||||
| /// <inheritdoc /> | |||||
| async Task<IRole> IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) | async Task<IRole> IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) | ||||
| => await CreateRoleAsync(name, permissions, color, isHoisted, options).ConfigureAwait(false); | => await CreateRoleAsync(name, permissions, color, isHoisted, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildUser> IGuild.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IGuildUser> IGuild.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -396,6 +469,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildUser> IGuild.GetCurrentUserAsync(CacheMode mode, RequestOptions options) | async Task<IGuildUser> IGuild.GetCurrentUserAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -403,6 +477,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) | async Task<IGuildUser> IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -410,6 +485,7 @@ namespace Discord.Rest | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync(CacheMode mode, RequestOptions options) | async Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -418,12 +494,14 @@ namespace Discord.Rest | |||||
| return ImmutableArray.Create<IGuildUser>(); | return ImmutableArray.Create<IGuildUser>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Downloading users is not supported with a REST-based guild.</exception> | |||||
| /// <exception cref="NotSupportedException">Downloading users is not supported for a REST-based guild.</exception> | |||||
| Task IGuild.DownloadUsersAsync() => | Task IGuild.DownloadUsersAsync() => | ||||
| throw new NotSupportedException(); | throw new NotSupportedException(); | ||||
| /// <inheritdoc /> | |||||
| async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | ||||
| => await GetWebhookAsync(id, options).ConfigureAwait(false); | => await GetWebhookAsync(id, options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | ||||
| => await GetWebhooksAsync(options).ConfigureAwait(false); | => await GetWebhooksAsync(options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.GuildEmbed; | using Model = Discord.API.GuildEmbed; | ||||
| namespace Discord | namespace Discord | ||||
| @@ -19,7 +19,7 @@ namespace Discord | |||||
| return new RestGuildEmbed(model.Enabled, model.ChannelId); | return new RestGuildEmbed(model.Enabled, model.ChannelId); | ||||
| } | } | ||||
| public override string ToString() => ChannelId?.ToString(); | |||||
| public override string ToString() => ChannelId?.ToString() ?? "Unknown"; | |||||
| private string DebuggerDisplay => $"{ChannelId} ({(IsEnabled ? "Enabled" : "Disabled")})"; | private string DebuggerDisplay => $"{ChannelId} ({(IsEnabled ? "Enabled" : "Disabled")})"; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Integration; | using Model = Discord.API.Integration; | ||||
| @@ -10,18 +10,28 @@ namespace Discord.Rest | |||||
| { | { | ||||
| private long _syncedAtTicks; | private long _syncedAtTicks; | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string Type { get; private set; } | public string Type { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsEnabled { get; private set; } | public bool IsEnabled { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsSyncing { get; private set; } | public bool IsSyncing { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong ExpireBehavior { get; private set; } | public ulong ExpireBehavior { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong ExpireGracePeriod { get; private set; } | public ulong ExpireGracePeriod { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong GuildId { get; private set; } | public ulong GuildId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong RoleId { get; private set; } | public ulong RoleId { get; private set; } | ||||
| public RestUser User { get; private set; } | public RestUser User { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public IntegrationAccount Account { get; private set; } | public IntegrationAccount Account { get; private set; } | ||||
| internal IGuild Guild { get; private set; } | internal IGuild Guild { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | ||||
| internal RestGuildIntegration(BaseDiscordClient discord, IGuild guild, ulong id) | internal RestGuildIntegration(BaseDiscordClient discord, IGuild guild, ulong id) | ||||
| @@ -78,6 +88,7 @@ namespace Discord.Rest | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} ({Id}{(IsEnabled ? ", Enabled" : "")})"; | private string DebuggerDisplay => $"{Name} ({Id}{(IsEnabled ? ", Enabled" : "")})"; | ||||
| /// <inheritdoc /> | |||||
| IGuild IGuildIntegration.Guild | IGuild IGuildIntegration.Guild | ||||
| { | { | ||||
| get | get | ||||
| @@ -87,6 +98,7 @@ namespace Discord.Rest | |||||
| throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | ||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| IUser IGuildIntegration.User => User; | IUser IGuildIntegration.User => User; | ||||
| } | } | ||||
| } | } | ||||
| @@ -9,12 +9,17 @@ namespace Discord.Rest | |||||
| public class RestUserGuild : RestEntity<ulong>, IUserGuild | public class RestUserGuild : RestEntity<ulong>, IUserGuild | ||||
| { | { | ||||
| private string _iconId; | private string _iconId; | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsOwner { get; private set; } | public bool IsOwner { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public GuildPermissions Permissions { get; private set; } | public GuildPermissions Permissions { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| /// <inheritdoc /> | |||||
| public string IconUrl => CDN.GetGuildIconUrl(Id, _iconId); | public string IconUrl => CDN.GetGuildIconUrl(Id, _iconId); | ||||
| internal RestUserGuild(BaseDiscordClient discord, ulong id) | internal RestUserGuild(BaseDiscordClient discord, ulong id) | ||||
| @@ -40,6 +45,7 @@ namespace Discord.Rest | |||||
| { | { | ||||
| await Discord.ApiClient.LeaveGuildAsync(Id, options).ConfigureAwait(false); | await Discord.ApiClient.LeaveGuildAsync(Id, options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public async Task DeleteAsync(RequestOptions options = null) | public async Task DeleteAsync(RequestOptions options = null) | ||||
| { | { | ||||
| await Discord.ApiClient.DeleteGuildAsync(Id, options).ConfigureAwait(false); | await Discord.ApiClient.DeleteGuildAsync(Id, options).ConfigureAwait(false); | ||||
| @@ -59,7 +59,8 @@ namespace Discord.Rest | |||||
| public override string ToString() => Url; | public override string ToString() => Url; | ||||
| private string DebuggerDisplay => $"{Url} ({GuildName} / {ChannelName})"; | private string DebuggerDisplay => $"{Url} ({GuildName} / {ChannelName})"; | ||||
| /// <inheritdoc /> | |||||
| IGuild IInvite.Guild | IGuild IInvite.Guild | ||||
| { | { | ||||
| get | get | ||||
| @@ -71,6 +72,7 @@ namespace Discord.Rest | |||||
| throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | ||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| IChannel IInvite.Channel | IChannel IInvite.Channel | ||||
| { | { | ||||
| get | get | ||||
| @@ -10,11 +10,13 @@ namespace Discord.Rest | |||||
| { | { | ||||
| internal static class MessageHelper | internal static class MessageHelper | ||||
| { | { | ||||
| /// <exception cref="InvalidOperationException">Only the author of a message may modify the message.</exception> | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func, | public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| { | { | ||||
| if (msg.Author.Id != client.CurrentUser.Id) | if (msg.Author.Id != client.CurrentUser.Id) | ||||
| throw new InvalidOperationException("Only the author of a message may change it."); | |||||
| throw new InvalidOperationException("Only the author of a message may modify the message."); | |||||
| var args = new MessageProperties(); | var args = new MessageProperties(); | ||||
| func(args); | func(args); | ||||
| @@ -35,6 +35,7 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public virtual IReadOnlyCollection<ulong> MentionedRoleIds => ImmutableArray.Create<ulong>(); | public virtual IReadOnlyCollection<ulong> MentionedRoleIds => ImmutableArray.Create<ulong>(); | ||||
| public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | ||||
| /// <inheritdoc /> | |||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -75,10 +76,14 @@ namespace Discord.Rest | |||||
| public override string ToString() => Content; | public override string ToString() => Content; | ||||
| /// <inheritdoc /> | |||||
| MessageType IMessage.Type => MessageType.Default; | MessageType IMessage.Type => MessageType.Default; | ||||
| IUser IMessage.Author => Author; | IUser IMessage.Author => Author; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<IAttachment> IMessage.Attachments => Attachments; | IReadOnlyCollection<IAttachment> IMessage.Attachments => Attachments; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -17,23 +17,33 @@ namespace Discord.Rest | |||||
| private ImmutableArray<Embed> _embeds; | private ImmutableArray<Embed> _embeds; | ||||
| private ImmutableArray<ITag> _tags; | private ImmutableArray<ITag> _tags; | ||||
| private ImmutableArray<RestReaction> _reactions; | private ImmutableArray<RestReaction> _reactions; | ||||
| /// <inheritdoc /> | |||||
| public override bool IsTTS => _isTTS; | public override bool IsTTS => _isTTS; | ||||
| /// <inheritdoc /> | |||||
| public override bool IsPinned => _isPinned; | public override bool IsPinned => _isPinned; | ||||
| /// <inheritdoc /> | |||||
| public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<Attachment> Attachments => _attachments; | public override IReadOnlyCollection<Attachment> Attachments => _attachments; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<Embed> Embeds => _embeds; | public override IReadOnlyCollection<Embed> Embeds => _embeds; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<ulong> MentionedChannelIds => MessageHelper.FilterTagsByKey(TagType.ChannelMention, _tags); | public override IReadOnlyCollection<ulong> MentionedChannelIds => MessageHelper.FilterTagsByKey(TagType.ChannelMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); | public override IReadOnlyCollection<ulong> MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<RestUser> MentionedUsers => MessageHelper.FilterTagsByValue<RestUser>(TagType.UserMention, _tags); | public override IReadOnlyCollection<RestUser> MentionedUsers => MessageHelper.FilterTagsByValue<RestUser>(TagType.UserMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<ITag> Tags => _tags; | public override IReadOnlyCollection<ITag> Tags => _tags; | ||||
| /// <inheritdoc /> | |||||
| public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); | public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); | ||||
| internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) | internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) | ||||
| : base(discord, id, channel, author, source) | : base(discord, id, channel, author, source) | ||||
| { | { | ||||
| } | } | ||||
| internal static new RestUserMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model) | |||||
| internal new static RestUserMessage Create(BaseDiscordClient discord, IMessageChannel channel, IUser author, Model model) | |||||
| { | { | ||||
| var entity = new RestUserMessage(discord, model.Id, channel, author, MessageHelper.GetSource(model)); | var entity = new RestUserMessage(discord, model.Id, channel, author, MessageHelper.GetSource(model)); | ||||
| entity.Update(model); | entity.Update(model); | ||||
| @@ -124,30 +134,37 @@ namespace Discord.Rest | |||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public async Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null) | public async Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null) | ||||
| { | { | ||||
| var model = await MessageHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | var model = await MessageHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | ||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | ||||
| => MessageHelper.AddReactionAsync(this, emote, Discord, options); | => MessageHelper.AddReactionAsync(this, emote, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) | public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) | ||||
| => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); | => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveAllReactionsAsync(RequestOptions options = null) | public Task RemoveAllReactionsAsync(RequestOptions options = null) | ||||
| => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); | => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) | public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) | ||||
| => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options); | => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task PinAsync(RequestOptions options = null) | public Task PinAsync(RequestOptions options = null) | ||||
| => MessageHelper.PinAsync(this, Discord, options); | => MessageHelper.PinAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task UnpinAsync(RequestOptions options = null) | public Task UnpinAsync(RequestOptions options = null) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| => MentionUtils.Resolve(this, startIndex, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | => MentionUtils.Resolve(this, startIndex, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | ||||
| /// <inheritdoc /> | |||||
| public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | ||||
| @@ -19,6 +19,7 @@ namespace Discord.Rest | |||||
| public string Description { get; private set; } | public string Description { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string[] RPCOrigins { get; private set; } | public string[] RPCOrigins { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong Flags { get; private set; } | public ulong Flags { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -52,6 +53,7 @@ namespace Discord.Rest | |||||
| Owner = RestUser.Create(Discord, model.Owner.Value); | Owner = RestUser.Create(Discord, model.Owner.Value); | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unable to update this object from a different application token.</exception> | |||||
| public async Task UpdateAsync() | public async Task UpdateAsync() | ||||
| { | { | ||||
| var response = await Discord.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); | var response = await Discord.ApiClient.GetMyApplicationAsync().ConfigureAwait(false); | ||||
| @@ -60,6 +62,12 @@ namespace Discord.Rest | |||||
| Update(response); | Update(response); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gets the name of the application. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// Name of the application. | |||||
| /// </returns> | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} ({Id})"; | private string DebuggerDisplay => $"{Name} ({Id})"; | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Role; | using Model = Discord.API.Role; | ||||
| @@ -9,16 +9,25 @@ namespace Discord.Rest | |||||
| public class RestRole : RestEntity<ulong>, IRole | public class RestRole : RestEntity<ulong>, IRole | ||||
| { | { | ||||
| internal IGuild Guild { get; } | internal IGuild Guild { get; } | ||||
| /// <inheritdoc /> | |||||
| public Color Color { get; private set; } | public Color Color { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsHoisted { get; private set; } | public bool IsHoisted { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsManaged { get; private set; } | public bool IsManaged { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsMentionable { get; private set; } | public bool IsMentionable { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public GuildPermissions Permissions { get; private set; } | public GuildPermissions Permissions { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public int Position { get; private set; } | public int Position { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| public bool IsEveryone => Id == Guild.Id; | public bool IsEveryone => Id == Guild.Id; | ||||
| /// <inheritdoc /> | |||||
| public string Mention => IsEveryone ? "@everyone" : MentionUtils.MentionRole(Id); | public string Mention => IsEveryone ? "@everyone" : MentionUtils.MentionRole(Id); | ||||
| internal RestRole(BaseDiscordClient discord, IGuild guild, ulong id) | internal RestRole(BaseDiscordClient discord, IGuild guild, ulong id) | ||||
| @@ -43,20 +52,24 @@ namespace Discord.Rest | |||||
| Permissions = new GuildPermissions(model.Permissions); | Permissions = new GuildPermissions(model.Permissions); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public async Task ModifyAsync(Action<RoleProperties> func, RequestOptions options = null) | public async Task ModifyAsync(Action<RoleProperties> func, RequestOptions options = null) | ||||
| { | { | ||||
| var model = await RoleHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | var model = await RoleHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); | ||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| => RoleHelper.DeleteAsync(this, Discord, options); | => RoleHelper.DeleteAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public int CompareTo(IRole role) => RoleUtils.Compare(this, role); | public int CompareTo(IRole role) => RoleUtils.Compare(this, role); | ||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} ({Id})"; | private string DebuggerDisplay => $"{Name} ({Id})"; | ||||
| //IRole | //IRole | ||||
| /// <inheritdoc /> | |||||
| IGuild IRole.Guild | IGuild IRole.Guild | ||||
| { | { | ||||
| get | get | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.Connection; | using Model = Discord.API.Connection; | ||||
| @@ -8,10 +8,15 @@ namespace Discord | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestConnection : IConnection | public class RestConnection : IConnection | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public string Id { get; } | public string Id { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Type { get; } | public string Type { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; } | public string Name { get; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsRevoked { get; } | public bool IsRevoked { get; } | ||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<ulong> IntegrationIds { get; } | public IReadOnlyCollection<ulong> IntegrationIds { get; } | ||||
| internal RestConnection(string id, string type, string name, bool isRevoked, IReadOnlyCollection<ulong> integrationIds) | internal RestConnection(string id, string type, string name, bool isRevoked, IReadOnlyCollection<ulong> integrationIds) | ||||
| @@ -28,6 +33,12 @@ namespace Discord | |||||
| return new RestConnection(model.Id, model.Type, model.Name, model.Revoked, model.Integrations.ToImmutableArray()); | return new RestConnection(model.Id, model.Type, model.Name, model.Revoked, model.Integrations.ToImmutableArray()); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gets the name of the connection. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// Name of the connection. | |||||
| /// </returns> | |||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| private string DebuggerDisplay => $"{Name} ({Id}, {Type}{(IsRevoked ? ", Revoked" : "")})"; | private string DebuggerDisplay => $"{Name} ({Id}, {Type}{(IsRevoked ? ", Revoked" : "")})"; | ||||
| } | } | ||||
| @@ -24,7 +24,9 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public ulong GuildId => Guild.Id; | public ulong GuildId => Guild.Id; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="InvalidOperationException" accessor="get">Resolving permissions requires the parent guild to be downloaded.</exception> | |||||
| public GuildPermissions GuildPermissions | public GuildPermissions GuildPermissions | ||||
| { | { | ||||
| get | get | ||||
| @@ -112,6 +114,7 @@ namespace Discord.Rest | |||||
| => UserHelper.RemoveRolesAsync(this, Discord, roles, options); | => UserHelper.RemoveRolesAsync(this, Discord, roles, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="InvalidOperationException">Resolving permissions requires the parent guild to be downloaded.</exception> | |||||
| public ChannelPermissions GetPermissions(IGuildChannel channel) | public ChannelPermissions GetPermissions(IGuildChannel channel) | ||||
| { | { | ||||
| var guildPerms = GuildPermissions; | var guildPerms = GuildPermissions; | ||||
| @@ -119,6 +122,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //IGuildUser | //IGuildUser | ||||
| /// <inheritdoc /> | |||||
| IGuild IGuildUser.Guild | IGuild IGuildUser.Guild | ||||
| { | { | ||||
| get | get | ||||
| @@ -130,10 +134,15 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //IVoiceState | //IVoiceState | ||||
| /// <inheritdoc /> | |||||
| bool IVoiceState.IsSelfDeafened => false; | bool IVoiceState.IsSelfDeafened => false; | ||||
| /// <inheritdoc /> | |||||
| bool IVoiceState.IsSelfMuted => false; | bool IVoiceState.IsSelfMuted => false; | ||||
| /// <inheritdoc /> | |||||
| bool IVoiceState.IsSuppressed => false; | bool IVoiceState.IsSuppressed => false; | ||||
| /// <inheritdoc /> | |||||
| IVoiceChannel IVoiceState.VoiceChannel => null; | IVoiceChannel IVoiceState.VoiceChannel => null; | ||||
| /// <inheritdoc /> | |||||
| string IVoiceState.VoiceSessionId => null; | string IVoiceState.VoiceSessionId => null; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| @@ -8,8 +8,11 @@ namespace Discord.Rest | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestSelfUser : RestUser, ISelfUser | public class RestSelfUser : RestUser, ISelfUser | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public string Email { get; private set; } | public string Email { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsVerified { get; private set; } | public bool IsVerified { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsMfaEnabled { get; private set; } | public bool IsMfaEnabled { get; private set; } | ||||
| internal RestSelfUser(BaseDiscordClient discord, ulong id) | internal RestSelfUser(BaseDiscordClient discord, ulong id) | ||||
| @@ -22,6 +25,7 @@ namespace Discord.Rest | |||||
| entity.Update(model); | entity.Update(model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| internal override void Update(Model model) | internal override void Update(Model model) | ||||
| { | { | ||||
| base.Update(model); | base.Update(model); | ||||
| @@ -34,6 +38,8 @@ namespace Discord.Rest | |||||
| IsMfaEnabled = model.MfaEnabled.Value; | IsMfaEnabled = model.MfaEnabled.Value; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="InvalidOperationException">Unable to update this object using a different token.</exception> | |||||
| public override async Task UpdateAsync(RequestOptions options = null) | public override async Task UpdateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| var model = await Discord.ApiClient.GetMyUserAsync(options).ConfigureAwait(false); | var model = await Discord.ApiClient.GetMyUserAsync(options).ConfigureAwait(false); | ||||
| @@ -42,6 +48,8 @@ namespace Discord.Rest | |||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="InvalidOperationException">Unable to modify this object using a different token.</exception> | |||||
| public async Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | public async Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | ||||
| { | { | ||||
| if (Id != Discord.CurrentUser.Id) | if (Id != Discord.CurrentUser.Id) | ||||
| @@ -8,16 +8,26 @@ namespace Discord.Rest | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RestUser : RestEntity<ulong>, IUser, IUpdateable | public class RestUser : RestEntity<ulong>, IUser, IUpdateable | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public bool IsBot { get; private set; } | public bool IsBot { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string Username { get; private set; } | public string Username { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ushort DiscriminatorValue { get; private set; } | public ushort DiscriminatorValue { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string AvatarId { get; private set; } | public string AvatarId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| /// <inheritdoc /> | |||||
| public string Discriminator => DiscriminatorValue.ToString("D4"); | public string Discriminator => DiscriminatorValue.ToString("D4"); | ||||
| /// <inheritdoc /> | |||||
| public string Mention => MentionUtils.MentionUser(Id); | public string Mention => MentionUtils.MentionUser(Id); | ||||
| /// <inheritdoc /> | |||||
| public virtual IActivity Activity => null; | public virtual IActivity Activity => null; | ||||
| /// <inheritdoc /> | |||||
| public virtual UserStatus Status => UserStatus.Offline; | public virtual UserStatus Status => UserStatus.Offline; | ||||
| /// <inheritdoc /> | |||||
| public virtual bool IsWebhook => false; | public virtual bool IsWebhook => false; | ||||
| internal RestUser(BaseDiscordClient discord, ulong id) | internal RestUser(BaseDiscordClient discord, ulong id) | ||||
| @@ -48,6 +58,7 @@ namespace Discord.Rest | |||||
| Username = model.Username.Value; | Username = model.Username.Value; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public virtual async Task UpdateAsync(RequestOptions options = null) | public virtual async Task UpdateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| var model = await Discord.ApiClient.GetUserAsync(Id, options).ConfigureAwait(false); | var model = await Discord.ApiClient.GetUserAsync(Id, options).ConfigureAwait(false); | ||||
| @@ -57,9 +68,11 @@ namespace Discord.Rest | |||||
| public Task<RestDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | public Task<RestDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | ||||
| => UserHelper.CreateDMChannelAsync(this, Discord, options); | => UserHelper.CreateDMChannelAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | ||||
| => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); | => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); | ||||
| /// <inheritdoc /> | |||||
| public string GetDefaultAvatarUrl() | public string GetDefaultAvatarUrl() | ||||
| => CDN.GetDefaultUserAvatarUrl(DiscriminatorValue); | => CDN.GetDefaultUserAvatarUrl(DiscriminatorValue); | ||||
| @@ -67,6 +80,7 @@ namespace Discord.Rest | |||||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; | private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; | ||||
| //IUser | //IUser | ||||
| /// <inheritdoc /> | |||||
| async Task<IDMChannel> IUser.GetOrCreateDMChannelAsync(RequestOptions options) | async Task<IDMChannel> IUser.GetOrCreateDMChannelAsync(RequestOptions options) | ||||
| => await GetOrCreateDMChannelAsync(options).ConfigureAwait(false); | => await GetOrCreateDMChannelAsync(options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Webhook; | using Model = Discord.API.Webhook; | ||||
| @@ -11,14 +11,21 @@ namespace Discord.Rest | |||||
| internal IGuild Guild { get; private set; } | internal IGuild Guild { get; private set; } | ||||
| internal ITextChannel Channel { get; private set; } | internal ITextChannel Channel { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong ChannelId { get; } | public ulong ChannelId { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Token { get; } | public string Token { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string AvatarId { get; private set; } | public string AvatarId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong? GuildId { get; private set; } | public ulong? GuildId { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public IUser Creator { get; private set; } | public IUser Creator { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| internal RestWebhook(BaseDiscordClient discord, IGuild guild, ulong id, string token, ulong channelId) | internal RestWebhook(BaseDiscordClient discord, IGuild guild, ulong id, string token, ulong channelId) | ||||
| @@ -59,12 +66,14 @@ namespace Discord.Rest | |||||
| Name = model.Name.Value; | Name = model.Name.Value; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public async Task UpdateAsync(RequestOptions options = null) | public async Task UpdateAsync(RequestOptions options = null) | ||||
| { | { | ||||
| var model = await Discord.ApiClient.GetWebhookAsync(Id, options).ConfigureAwait(false); | var model = await Discord.ApiClient.GetWebhookAsync(Id, options).ConfigureAwait(false); | ||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | ||||
| => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); | => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); | ||||
| @@ -74,6 +83,7 @@ namespace Discord.Rest | |||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| => WebhookHelper.DeleteAsync(this, Discord, options); | => WebhookHelper.DeleteAsync(this, Discord, options); | ||||
| @@ -81,10 +91,13 @@ namespace Discord.Rest | |||||
| private string DebuggerDisplay => $"Webhook: {Name} ({Id})"; | private string DebuggerDisplay => $"Webhook: {Name} ({Id})"; | ||||
| //IWebhook | //IWebhook | ||||
| /// <inheritdoc /> | |||||
| IGuild IWebhook.Guild | IGuild IWebhook.Guild | ||||
| => Guild ?? throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | => Guild ?? throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | ||||
| /// <inheritdoc /> | |||||
| ITextChannel IWebhook.Channel | ITextChannel IWebhook.Channel | ||||
| => Channel ?? throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | => Channel ?? throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | ||||
| /// <inheritdoc /> | |||||
| Task IWebhook.ModifyAsync(Action<WebhookProperties> func, RequestOptions options) | Task IWebhook.ModifyAsync(Action<WebhookProperties> func, RequestOptions options) | ||||
| => ModifyAsync(func, options); | => ModifyAsync(func, options); | ||||
| } | } | ||||
| @@ -82,6 +82,8 @@ namespace Discord.Net.Rest | |||||
| return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false); | return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false); | ||||
| } | } | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unsupported param type.</exception> | |||||
| public async Task<RestResponse> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, CancellationToken cancelToken, bool headerOnly, string reason = null) | public async Task<RestResponse> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, CancellationToken cancelToken, bool headerOnly, string reason = null) | ||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| @@ -111,7 +113,7 @@ namespace Discord.Net.Rest | |||||
| content.Add(new StreamContent(stream), p.Key, fileValue.Filename); | content.Add(new StreamContent(stream), p.Key, fileValue.Filename); | ||||
| continue; | continue; | ||||
| } | } | ||||
| default: throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\""); | |||||
| default: throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\"."); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -6,6 +6,7 @@ namespace Discord.Net.Rest | |||||
| { | { | ||||
| public static readonly RestClientProvider Instance = Create(); | public static readonly RestClientProvider Instance = Create(); | ||||
| /// <exception cref="PlatformNotSupportedException">The default RestClientProvider is not supported on this platform.</exception> | |||||
| public static RestClientProvider Create(bool useProxy = false) | public static RestClientProvider Create(bool useProxy = false) | ||||
| { | { | ||||
| return url => | return url => | ||||
| @@ -126,7 +126,7 @@ namespace Discord.Net.Queue | |||||
| if ((request.Options.RetryMode & RetryMode.RetryTimeouts) == 0) | if ((request.Options.RetryMode & RetryMode.RetryTimeouts) == 0) | ||||
| throw; | throw; | ||||
| await Task.Delay(500); | |||||
| await Task.Delay(500).ConfigureAwait(false); | |||||
| continue; //Retry | continue; //Retry | ||||
| } | } | ||||
| /*catch (Exception) | /*catch (Exception) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| namespace Discord.Net | namespace Discord.Net | ||||
| @@ -15,7 +15,7 @@ namespace Discord.Net | |||||
| internal RateLimitInfo(Dictionary<string, string> headers) | internal RateLimitInfo(Dictionary<string, string> headers) | ||||
| { | { | ||||
| IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && | IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && | ||||
| bool.TryParse(temp, out var isGlobal) ? isGlobal : false; | |||||
| bool.TryParse(temp, out var isGlobal) && isGlobal; | |||||
| Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && | Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && | ||||
| int.TryParse(temp, out var limit) ? limit : (int?)null; | int.TryParse(temp, out var limit) ? limit : (int?)null; | ||||
| Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && | Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -22,19 +22,22 @@ namespace Discord.Audio.Streams | |||||
| _decoder = new OpusDecoder(); | _decoder = new OpusDecoder(); | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Header received with no payload.</exception> | |||||
| public override void WriteHeader(ushort seq, uint timestamp, bool missed) | public override void WriteHeader(ushort seq, uint timestamp, bool missed) | ||||
| { | { | ||||
| if (_hasHeader) | if (_hasHeader) | ||||
| throw new InvalidOperationException("Header received with no payload"); | |||||
| throw new InvalidOperationException("Header received with no payload."); | |||||
| _hasHeader = true; | _hasHeader = true; | ||||
| _nextMissed = missed; | _nextMissed = missed; | ||||
| _next.WriteHeader(seq, timestamp, missed); | _next.WriteHeader(seq, timestamp, missed); | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Received payload without an RTP header.</exception> | |||||
| public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | ||||
| { | { | ||||
| if (!_hasHeader) | if (!_hasHeader) | ||||
| throw new InvalidOperationException("Received payload without an RTP header"); | |||||
| throw new InvalidOperationException("Received payload without an RTP header."); | |||||
| _hasHeader = false; | _hasHeader = false; | ||||
| if (!_nextMissed) | if (!_nextMissed) | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System.Threading; | |||||
| using System; | |||||
| using System.Threading; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Audio.Streams | namespace Discord.Audio.Streams | ||||
| @@ -20,6 +21,8 @@ namespace Discord.Audio.Streams | |||||
| _nonce = new byte[24]; | _nonce = new byte[24]; | ||||
| } | } | ||||
| /// <exception cref="OperationCanceledException">The token has had cancellation requested.</exception> | |||||
| /// <exception cref="ObjectDisposedException">The associated <see cref="T:System.Threading.CancellationTokenSource" /> has been disposed.</exception> | |||||
| public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | ||||
| { | { | ||||
| cancelToken.ThrowIfCancellationRequested(); | cancelToken.ThrowIfCancellationRequested(); | ||||
| @@ -20,21 +20,25 @@ namespace Discord.Audio.Streams | |||||
| _client = (AudioClient)client; | _client = (AudioClient)client; | ||||
| _nonce = new byte[24]; | _nonce = new byte[24]; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Header received with no payload.</exception> | |||||
| public override void WriteHeader(ushort seq, uint timestamp, bool missed) | public override void WriteHeader(ushort seq, uint timestamp, bool missed) | ||||
| { | { | ||||
| if (_hasHeader) | if (_hasHeader) | ||||
| throw new InvalidOperationException("Header received with no payload"); | |||||
| throw new InvalidOperationException("Header received with no payload."); | |||||
| _nextSeq = seq; | _nextSeq = seq; | ||||
| _nextTimestamp = timestamp; | _nextTimestamp = timestamp; | ||||
| _hasHeader = true; | _hasHeader = true; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Received payload without an RTP header.</exception> | |||||
| /// <exception cref="OperationCanceledException">The token has had cancellation requested.</exception> | |||||
| /// <exception cref="ObjectDisposedException">The associated <see cref="T:System.Threading.CancellationTokenSource" /> has been disposed.</exception> | |||||
| public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) | ||||
| { | { | ||||
| cancelToken.ThrowIfCancellationRequested(); | cancelToken.ThrowIfCancellationRequested(); | ||||
| if (!_hasHeader) | if (!_hasHeader) | ||||
| throw new InvalidOperationException("Received payload without an RTP header"); | |||||
| throw new InvalidOperationException("Received payload without an RTP header."); | |||||
| _hasHeader = false; | _hasHeader = false; | ||||
| if (_client.SecretKey == null) | if (_client.SecretKey == null) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| @@ -10,42 +10,174 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| protected readonly DiscordSocketConfig BaseConfig; | protected readonly DiscordSocketConfig BaseConfig; | ||||
| /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> | |||||
| /// <summary> | |||||
| /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. | |||||
| /// </summary> | |||||
| public abstract int Latency { get; protected set; } | public abstract int Latency { get; protected set; } | ||||
| public abstract UserStatus Status { get; protected set; } | |||||
| /// <summary> | |||||
| /// Gets the status for the logged-in user. | |||||
| /// </summary> | |||||
| public abstract UserStatus Status { get; protected set; } | |||||
| /// <summary> | |||||
| /// Gets the activity for the logged-in user. | |||||
| /// </summary> | |||||
| public abstract IActivity Activity { get; protected set; } | public abstract IActivity Activity { get; protected set; } | ||||
| internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | ||||
| /// <summary> | |||||
| /// Gets the current logged-in user. | |||||
| /// </summary> | |||||
| public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; protected set => base.CurrentUser = value; } | public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; protected set => base.CurrentUser = value; } | ||||
| /// <summary> | |||||
| /// Gets a collection of guilds that the logged-in user is currently in. | |||||
| /// </summary> | |||||
| public abstract IReadOnlyCollection<SocketGuild> Guilds { get; } | public abstract IReadOnlyCollection<SocketGuild> Guilds { get; } | ||||
| /// <summary> | |||||
| /// Gets a collection of private channels that are currently open for the logged-in user. | |||||
| /// </summary> | |||||
| public abstract IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels { get; } | public abstract IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels { get; } | ||||
| /// <summary> | |||||
| /// Gets a collection of available voice regions for the logged-in user. | |||||
| /// </summary> | |||||
| public abstract IReadOnlyCollection<RestVoiceRegion> VoiceRegions { get; } | public abstract IReadOnlyCollection<RestVoiceRegion> VoiceRegions { get; } | ||||
| internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | ||||
| : 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); | ||||
| /// <summary> | |||||
| /// Gets a Discord application information for the logged-in user. | |||||
| /// </summary> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// Application information. This reflects your application information you submitted when creating a | |||||
| /// Discord application via the Developer Portal. | |||||
| /// </returns> | |||||
| public abstract Task<RestApplication> GetApplicationInfoAsync(RequestOptions options = null); | public abstract Task<RestApplication> GetApplicationInfoAsync(RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Gets a user who shares a mutual guild with logged-in user with the provided snowflake ID. | |||||
| /// </summary> | |||||
| /// <param name="id">The user snowflake ID.</param> | |||||
| /// <returns> | |||||
| /// A user who shares a mutual guild with the logged-in user and who is also present in the WebSocket cache; | |||||
| /// or <see langword="null"/> when the user cannot be found. | |||||
| /// </returns> | |||||
| public abstract SocketUser GetUser(ulong id); | public abstract SocketUser GetUser(ulong id); | ||||
| /// <summary> | |||||
| /// Gets a user who shares a mutual guild with the logged-in user with the provided username and discriminator value combo. | |||||
| /// </summary> | |||||
| /// <param name="username">The name of the user.</param> | |||||
| /// <param name="discriminator">The discriminator value of the user.</param> | |||||
| /// <returns> | |||||
| /// A user who shares a mutual guild with the logged-in user and who is also present in the WebSocket cache; | |||||
| /// or <see langword="null"/> when the user cannot be found. | |||||
| /// </returns> | |||||
| public abstract SocketUser GetUser(string username, string discriminator); | public abstract SocketUser GetUser(string username, string discriminator); | ||||
| /// <summary> | |||||
| /// Gets a channel that the logged-in user is accessible to with the provided ID. | |||||
| /// </summary> | |||||
| /// <param name="id">The channel snowflake ID.</param> | |||||
| /// <returns> | |||||
| /// A generic channel object (voice, text, category, etc.); or <see langword="null" /> when the channel | |||||
| /// cannot be found. | |||||
| /// </returns> | |||||
| public abstract SocketChannel GetChannel(ulong id); | public abstract SocketChannel GetChannel(ulong id); | ||||
| /// <summary> | |||||
| /// Gets a guild that the logged-in user is accessible to with the provided ID. | |||||
| /// </summary> | |||||
| /// <param name="id">The guild snowflake ID.</param> | |||||
| /// <returns> | |||||
| /// A guild; or <see langword="null"/> when the guild cannot be found. | |||||
| /// </returns> | |||||
| public abstract SocketGuild GetGuild(ulong id); | public abstract SocketGuild GetGuild(ulong id); | ||||
| /// <summary> | |||||
| /// Gets a voice region with the provided ID. | |||||
| /// </summary> | |||||
| /// <param name="id">The unique identifier of the voice region.</param> | |||||
| /// <returns> | |||||
| /// A voice region; or <see langword="null"/> if none can be found. | |||||
| /// </returns> | |||||
| public abstract RestVoiceRegion GetVoiceRegion(string id); | public abstract RestVoiceRegion GetVoiceRegion(string id); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public abstract Task StartAsync(); | public abstract Task StartAsync(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public abstract Task StopAsync(); | public abstract Task StopAsync(); | ||||
| /// <summary> | |||||
| /// Sets the current status of the logged-in user (e.g. Online, Do not Disturb). | |||||
| /// </summary> | |||||
| /// <param name="status">The new status to be set.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| public abstract Task SetStatusAsync(UserStatus status); | public abstract Task SetStatusAsync(UserStatus status); | ||||
| /// <summary> | |||||
| /// Sets the game of the logged-in user. | |||||
| /// </summary> | |||||
| /// <param name="name">The name of the game.</param> | |||||
| /// <param name="streamUrl">If streaming, the URL of the stream. Must be a valid Twitch URL.</param> | |||||
| /// <param name="type">The type of the game.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| 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); | ||||
| /// <summary> | |||||
| /// Sets the <paramref name="activity"/> of the logged-in user. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method sets the <paramref name="activity"/> of the user. Please note that Rich Presence cannot be | |||||
| /// set via this method or client. Rich Presence is strictly limited to RPC clients only. Furthermore, | |||||
| /// Discord will only accept setting of name and the type of activity. | |||||
| /// </remarks> | |||||
| /// <param name="activity">The activty to be set.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| public abstract Task SetActivityAsync(IActivity activity); | public abstract Task SetActivityAsync(IActivity activity); | ||||
| public abstract Task DownloadUsersAsync(IEnumerable<IGuild> guilds); | |||||
| /// <summary> | |||||
| /// Attempts to download users into the user cache for the selected guilds. | |||||
| /// </summary> | |||||
| /// <param name="guilds">The guilds to download the members from.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/>. | |||||
| /// </returns> | |||||
| public abstract Task DownloadUsersAsync(IEnumerable<IGuild> guilds); | |||||
| /// <summary> | |||||
| /// Creates a guild for the logged-in user who is in less than 10 active guilds. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method creates a new guild on behalf of the logged-in user. Note that due to Discord's limitation, | |||||
| /// this method will only work for users that are in less than 10 guilds. | |||||
| /// </remarks> | |||||
| /// <param name="name">The name of the new guild.</param> | |||||
| /// <param name="region">The voice region to create the guild with.</param> | |||||
| /// <param name="jpegIcon">The icon of the guild.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the newly created guild. | |||||
| /// </returns> | |||||
| 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); | ||||
| /// <summary> | |||||
| /// Gets the connections that the logged-in user has set up. | |||||
| /// </summary> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing a collection of connections. | |||||
| /// </returns> | |||||
| 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); | ||||
| /// <summary> | |||||
| /// Gets an invite with the provided invite identifier. | |||||
| /// </summary> | |||||
| /// <param name="inviteId">The invitation identifier.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// An awaitable <see cref="Task"/> containing the invite information. | |||||
| /// </returns> | |||||
| 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); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| @@ -72,9 +72,9 @@ namespace Discord.WebSocket | |||||
| switch (channel) | switch (channel) | ||||
| { | { | ||||
| case SocketDMChannel dmChannel: | case SocketDMChannel dmChannel: | ||||
| _dmChannels.TryRemove(dmChannel.Recipient.Id, out var ignored); | |||||
| _dmChannels.TryRemove(dmChannel.Recipient.Id, out var _); | |||||
| break; | break; | ||||
| case SocketGroupChannel groupChannel: | |||||
| case SocketGroupChannel _: | |||||
| _groupChannels.TryRemove(id); | _groupChannels.TryRemove(id); | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -2,7 +2,9 @@ using Discord.WebSocket; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| /// <summary> The WebSocket variant of <see cref="ICommandContext"/>, which may contain the client, user, guild, channel, and message. </summary> | |||||
| /// <summary> | |||||
| /// Represents a WebSocket-based context of a command. This may include the client, guild, channel, user, and message. | |||||
| /// </summary> | |||||
| public class SocketCommandContext : ICommandContext | public class SocketCommandContext : ICommandContext | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| @@ -120,12 +120,14 @@ namespace Discord.API | |||||
| } | } | ||||
| finally { _stateLock.Release(); } | finally { _stateLock.Release(); } | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">The client must be logged in before connecting.</exception> | |||||
| /// <exception cref="NotSupportedException">This client is not configured with WebSocket support.</exception> | |||||
| internal override async Task ConnectInternalAsync() | internal override async Task ConnectInternalAsync() | ||||
| { | { | ||||
| if (LoginState != LoginState.LoggedIn) | if (LoginState != LoginState.LoggedIn) | ||||
| throw new InvalidOperationException("You must log in before connecting."); | |||||
| throw new InvalidOperationException("The client must be logged in before connecting."); | |||||
| if (WebSocketClient == null) | if (WebSocketClient == null) | ||||
| throw new NotSupportedException("This client is not configured with websocket support."); | |||||
| throw new NotSupportedException("This client is not configured with WebSocket support."); | |||||
| //Re-create streams to reset the zlib state | //Re-create streams to reset the zlib state | ||||
| _compressed?.Dispose(); | _compressed?.Dispose(); | ||||
| @@ -176,10 +178,11 @@ namespace Discord.API | |||||
| } | } | ||||
| finally { _stateLock.Release(); } | finally { _stateLock.Release(); } | ||||
| } | } | ||||
| /// <exception cref="NotSupportedException">This client is not configured with WebSocket support.</exception> | |||||
| internal override async Task DisconnectInternalAsync() | internal override async Task DisconnectInternalAsync() | ||||
| { | { | ||||
| if (WebSocketClient == null) | if (WebSocketClient == null) | ||||
| throw new NotSupportedException("This client is not configured with websocket support."); | |||||
| throw new NotSupportedException("This client is not configured with WebSocket support."); | |||||
| if (ConnectionState == ConnectionState.Disconnected) return; | if (ConnectionState == ConnectionState.Disconnected) return; | ||||
| ConnectionState = ConnectionState.Disconnecting; | ConnectionState = ConnectionState.Disconnecting; | ||||
| @@ -46,7 +46,9 @@ namespace Discord.WebSocket | |||||
| public ConnectionState ConnectionState => _connection.State; | public ConnectionState ConnectionState => _connection.State; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override int Latency { get; protected set; } | public override int Latency { get; protected set; } | ||||
| /// <inheritdoc /> | |||||
| public override UserStatus Status { get; protected set; } = UserStatus.Online; | public override UserStatus Status { get; protected set; } = UserStatus.Online; | ||||
| /// <inheritdoc /> | |||||
| public override IActivity Activity { get; protected set; } | public override IActivity Activity { get; protected set; } | ||||
| //From DiscordSocketConfig | //From DiscordSocketConfig | ||||
| @@ -58,14 +60,17 @@ namespace Discord.WebSocket | |||||
| internal WebSocketProvider WebSocketProvider { get; private set; } | internal WebSocketProvider WebSocketProvider { get; private set; } | ||||
| internal bool AlwaysDownloadUsers { get; private set; } | internal bool AlwaysDownloadUsers { get; private set; } | ||||
| internal int? HandlerTimeout { get; private set; } | internal int? HandlerTimeout { get; private set; } | ||||
| internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | public override IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; | ||||
| public IReadOnlyCollection<SocketDMChannel> DMChannels | public IReadOnlyCollection<SocketDMChannel> DMChannels | ||||
| => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); | ||||
| public IReadOnlyCollection<SocketGroupChannel> GroupChannels | public IReadOnlyCollection<SocketGroupChannel> GroupChannels | ||||
| => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | ||||
| /// <summary> Creates a new REST/WebSocket Discord client. </summary> | /// <summary> Creates a new REST/WebSocket Discord client. </summary> | ||||
| @@ -128,6 +133,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | ||||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); | => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); | ||||
| /// <inheritdoc /> | |||||
| internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
| { | { | ||||
| if (disposing) | if (disposing) | ||||
| @@ -136,7 +142,8 @@ namespace Discord.WebSocket | |||||
| ApiClient.Dispose(); | ApiClient.Dispose(); | ||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
| { | { | ||||
| if (_parentClient == null) | if (_parentClient == null) | ||||
| @@ -147,6 +154,7 @@ namespace Discord.WebSocket | |||||
| else | else | ||||
| _voiceRegions = _parentClient._voiceRegions; | _voiceRegions = _parentClient._voiceRegions; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| internal override async Task OnLogoutAsync() | internal override async Task OnLogoutAsync() | ||||
| { | { | ||||
| await StopAsync().ConfigureAwait(false); | await StopAsync().ConfigureAwait(false); | ||||
| @@ -154,8 +162,10 @@ namespace Discord.WebSocket | |||||
| _voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | _voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override async Task StartAsync() | public override async Task StartAsync() | ||||
| => await _connection.StartAsync().ConfigureAwait(false); | => await _connection.StartAsync().ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| public override async Task StopAsync() | public override async Task StopAsync() | ||||
| => await _connection.StopAsync().ConfigureAwait(false); | => await _connection.StopAsync().ConfigureAwait(false); | ||||
| @@ -277,7 +287,7 @@ namespace Discord.WebSocket | |||||
| return null; | return null; | ||||
| } | } | ||||
| /// <summary> Downloads the users list for the provided guilds, if they don't have a complete list. </summary> | |||||
| /// <inheritdoc /> | |||||
| public override async Task DownloadUsersAsync(IEnumerable<IGuild> guilds) | public override async Task DownloadUsersAsync(IEnumerable<IGuild> guilds) | ||||
| { | { | ||||
| if (ConnectionState == ConnectionState.Connected) | if (ConnectionState == ConnectionState.Connected) | ||||
| @@ -316,6 +326,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override async Task SetStatusAsync(UserStatus status) | public override async Task SetStatusAsync(UserStatus status) | ||||
| { | { | ||||
| Status = status; | Status = status; | ||||
| @@ -325,6 +336,7 @@ namespace Discord.WebSocket | |||||
| _statusSince = null; | _statusSince = null; | ||||
| await SendStatusAsync().ConfigureAwait(false); | await SendStatusAsync().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) | ||||
| { | { | ||||
| if (!string.IsNullOrEmpty(streamUrl)) | if (!string.IsNullOrEmpty(streamUrl)) | ||||
| @@ -335,6 +347,7 @@ namespace Discord.WebSocket | |||||
| Activity = null; | Activity = null; | ||||
| await SendStatusAsync().ConfigureAwait(false); | await SendStatusAsync().ConfigureAwait(false); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override async Task SetActivityAsync(IActivity activity) | public override async Task SetActivityAsync(IActivity activity) | ||||
| { | { | ||||
| Activity = activity; | Activity = activity; | ||||
| @@ -351,8 +364,8 @@ namespace Discord.WebSocket | |||||
| var gameModel = new GameModel(); | var gameModel = new GameModel(); | ||||
| // Discord only accepts rich presence over RPC, don't even bother building a payload | // Discord only accepts rich presence over RPC, don't even bother building a payload | ||||
| if (Activity is RichGame game) | |||||
| throw new NotSupportedException("Outgoing Rich Presences are not supported"); | |||||
| if (Activity is RichGame) | |||||
| throw new NotSupportedException("Outgoing Rich Presences are not supported via WebSocket."); | |||||
| if (Activity != null) | if (Activity != null) | ||||
| { | { | ||||
| @@ -479,7 +492,7 @@ namespace Discord.WebSocket | |||||
| await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); | ||||
| await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); | ||||
| }); | }); | ||||
| var _ = _connection.CompleteAsync(); | |||||
| _ = _connection.CompleteAsync(); | |||||
| } | } | ||||
| break; | break; | ||||
| case "RESUMED": | case "RESUMED": | ||||
| @@ -1173,7 +1186,7 @@ namespace Discord.WebSocket | |||||
| var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); | var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); | ||||
| bool isCached = msg != null; | bool isCached = msg != null; | ||||
| var cacheable = new Cacheable<IMessage, ulong>(msg, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id)); | |||||
| var cacheable = new Cacheable<IMessage, ulong>(msg, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id).ConfigureAwait(false)); | |||||
| await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false); | await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1609,6 +1622,7 @@ namespace Discord.WebSocket | |||||
| return guild; | return guild; | ||||
| } | } | ||||
| /// <exception cref="InvalidOperationException">Unexpected channel type is created.</exception> | |||||
| internal ISocketPrivateChannel AddPrivateChannel(API.Channel model, ClientState state) | internal ISocketPrivateChannel AddPrivateChannel(API.Channel model, ClientState state) | ||||
| { | { | ||||
| var channel = SocketChannel.CreatePrivate(this, state, model); | var channel = SocketChannel.CreatePrivate(this, state, model); | ||||
| @@ -1781,43 +1795,59 @@ namespace Discord.WebSocket | |||||
| internal int GetAudioId() => _nextAudioId++; | internal int GetAudioId() => _nextAudioId++; | ||||
| //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 /> | |||||
| Task<IReadOnlyCollection<IDMChannel>> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options) | Task<IReadOnlyCollection<IDMChannel>> IDiscordClient.GetDMChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IReadOnlyCollection<IDMChannel>>(DMChannels); | => Task.FromResult<IReadOnlyCollection<IDMChannel>>(DMChannels); | ||||
| /// <inheritdoc /> | |||||
| Task<IReadOnlyCollection<IGroupChannel>> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options) | Task<IReadOnlyCollection<IGroupChannel>> IDiscordClient.GetGroupChannelsAsync(CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IReadOnlyCollection<IGroupChannel>>(GroupChannels); | => Task.FromResult<IReadOnlyCollection<IGroupChannel>>(GroupChannels); | ||||
| /// <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)); | ||||
| /// <inheritdoc /> | |||||
| async Task IDiscordClient.StartAsync() | async Task IDiscordClient.StartAsync() | ||||
| => await StartAsync().ConfigureAwait(false); | => await StartAsync().ConfigureAwait(false); | ||||
| /// <inheritdoc /> | |||||
| async Task IDiscordClient.StopAsync() | async Task IDiscordClient.StopAsync() | ||||
| => await StopAsync().ConfigureAwait(false); | => await StopAsync().ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -58,6 +58,7 @@ namespace Discord.WebSocket | |||||
| internal abstract SocketUser GetUserInternal(ulong id); | internal abstract SocketUser GetUserInternal(ulong id); | ||||
| internal abstract IReadOnlyCollection<SocketUser> GetUsersInternal(); | internal abstract IReadOnlyCollection<SocketUser> GetUsersInternal(); | ||||
| private string DebuggerDisplay => $"Unknown ({Id}, Channel)"; | |||||
| internal SocketChannel Clone() => MemberwiseClone() as SocketChannel; | internal SocketChannel Clone() => MemberwiseClone() as SocketChannel; | ||||
| //IChannel | //IChannel | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -57,7 +57,7 @@ namespace Discord.WebSocket | |||||
| else | else | ||||
| return ImmutableArray.Create<SocketMessage>(); | return ImmutableArray.Create<SocketMessage>(); | ||||
| } | } | ||||
| /// <exception cref="NotSupportedException">Unexpected <see cref="ISocketMessageChannel"/> type.</exception> | |||||
| public static void AddMessage(ISocketMessageChannel channel, DiscordSocketClient discord, | public static void AddMessage(ISocketMessageChannel channel, DiscordSocketClient discord, | ||||
| SocketMessage msg) | SocketMessage msg) | ||||
| { | { | ||||
| @@ -66,9 +66,10 @@ namespace Discord.WebSocket | |||||
| case SocketDMChannel dmChannel: dmChannel.AddMessage(msg); break; | case SocketDMChannel dmChannel: dmChannel.AddMessage(msg); break; | ||||
| case SocketGroupChannel groupChannel: groupChannel.AddMessage(msg); break; | case SocketGroupChannel groupChannel: groupChannel.AddMessage(msg); break; | ||||
| case SocketTextChannel textChannel: textChannel.AddMessage(msg); break; | case SocketTextChannel textChannel: textChannel.AddMessage(msg); break; | ||||
| default: throw new NotSupportedException("Unexpected ISocketMessageChannel type"); | |||||
| default: throw new NotSupportedException($"Unexpected {nameof(ISocketMessageChannel)} type."); | |||||
| } | } | ||||
| } | } | ||||
| /// <exception cref="NotSupportedException">Unexpected <see cref="ISocketMessageChannel"/> type.</exception> | |||||
| public static SocketMessage RemoveMessage(ISocketMessageChannel channel, DiscordSocketClient discord, | public static SocketMessage RemoveMessage(ISocketMessageChannel channel, DiscordSocketClient discord, | ||||
| ulong id) | ulong id) | ||||
| { | { | ||||
| @@ -77,7 +78,7 @@ namespace Discord.WebSocket | |||||
| case SocketDMChannel dmChannel: return dmChannel.RemoveMessage(id); | case SocketDMChannel dmChannel: return dmChannel.RemoveMessage(id); | ||||
| case SocketGroupChannel groupChannel: return groupChannel.RemoveMessage(id); | case SocketGroupChannel groupChannel: return groupChannel.RemoveMessage(id); | ||||
| case SocketTextChannel textChannel: return textChannel.RemoveMessage(id); | case SocketTextChannel textChannel: return textChannel.RemoveMessage(id); | ||||
| default: throw new NotSupportedException("Unexpected ISocketMessageChannel type"); | |||||
| default: throw new NotSupportedException($"Unexpected {nameof(ISocketMessageChannel)} type."); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -111,7 +111,6 @@ 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); | ||||
| @@ -357,9 +357,12 @@ namespace Discord.WebSocket | |||||
| => GuildHelper.DeleteAsync(this, Discord, options); | => GuildHelper.DeleteAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null) | public Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null) | ||||
| => GuildHelper.ModifyAsync(this, Discord, func, options); | => GuildHelper.ModifyAsync(this, Discord, func, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | public Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | ||||
| => GuildHelper.ModifyEmbedAsync(this, Discord, func, options); | => GuildHelper.ModifyEmbedAsync(this, Discord, func, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -431,31 +434,37 @@ namespace Discord.WebSocket | |||||
| /// </returns> | /// </returns> | ||||
| public SocketVoiceChannel GetVoiceChannel(ulong id) | public SocketVoiceChannel GetVoiceChannel(ulong id) | ||||
| => GetChannel(id) as SocketVoiceChannel; | => GetChannel(id) as SocketVoiceChannel; | ||||
| /// <summary> | /// <summary> | ||||
| /// Creates a text channel with the provided name. | /// Creates a text channel with the provided name. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="name">The name of the new channel.</param> | /// <param name="name">The name of the new channel.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null" />.</exception> | |||||
| /// <returns> | /// <returns> | ||||
| /// The created text channel. | /// The created text channel. | ||||
| /// </returns> | /// </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> | /// <summary> | ||||
| /// Creates a voice channel with the provided name. | /// Creates a voice channel with the provided name. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="name">The name of the new channel.</param> | /// <param name="name">The name of the new channel.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null" />.</exception> | |||||
| /// <returns> | /// <returns> | ||||
| /// The created voice channel. | /// The created voice channel. | ||||
| /// </returns> | /// </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> | /// <summary> | ||||
| /// Creates a category channel with the provided name. | /// Creates a category channel with the provided name. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="name">The name of the new channel.</param> | /// <param name="name">The name of the new channel.</param> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null" />.</exception> | |||||
| /// <returns> | /// <returns> | ||||
| /// The created category channel. | /// The created category channel. | ||||
| /// </returns> | /// </returns> | ||||
| @@ -507,6 +516,7 @@ namespace Discord.WebSocket | |||||
| return value; | return value; | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Creates a role. | /// Creates a role. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -517,6 +527,7 @@ namespace Discord.WebSocket | |||||
| /// <param name="color">The color of the role. Set to <see langword="null" /> to use the default color.</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="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> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null" />.</exception> | |||||
| /// <returns> | /// <returns> | ||||
| /// The created role. | /// The created role. | ||||
| /// </returns> | /// </returns> | ||||
| @@ -645,6 +656,7 @@ namespace Discord.WebSocket | |||||
| public Task<GuildEmote> CreateEmoteAsync(string name, Image image, Optional<IEnumerable<IRole>> roles = default(Optional<IEnumerable<IRole>>), RequestOptions options = null) | public Task<GuildEmote> CreateEmoteAsync(string name, Image image, Optional<IEnumerable<IRole>> roles = default(Optional<IEnumerable<IRole>>), RequestOptions options = null) | ||||
| => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); | => GuildHelper.CreateEmoteAsync(this, Discord, name, image, roles, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public Task<GuildEmote> ModifyEmoteAsync(GuildEmote emote, Action<EmoteProperties> func, RequestOptions options = null) | public Task<GuildEmote> ModifyEmoteAsync(GuildEmote emote, Action<EmoteProperties> func, RequestOptions options = null) | ||||
| => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); | => GuildHelper.ModifyEmoteAsync(this, Discord, emote.Id, func, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -931,9 +943,9 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | async Task<IWebhook> IGuild.GetWebhookAsync(ulong id, RequestOptions options) | ||||
| => await GetWebhookAsync(id, options); | |||||
| => await GetWebhookAsync(id, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | async Task<IReadOnlyCollection<IWebhook>> IGuild.GetWebhooksAsync(RequestOptions options) | ||||
| => await GetWebhooksAsync(options); | |||||
| => await GetWebhooksAsync(options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| @@ -8,27 +8,38 @@ using Model = Discord.API.Message; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| /// <summary> | |||||
| /// Represents a WebSocket-based message. | |||||
| /// </summary> | |||||
| public abstract class SocketMessage : SocketEntity<ulong>, IMessage | public abstract class SocketMessage : SocketEntity<ulong>, IMessage | ||||
| { | { | ||||
| private long _timestampTicks; | private long _timestampTicks; | ||||
| public SocketUser Author { get; } | public SocketUser Author { get; } | ||||
| public ISocketMessageChannel Channel { get; } | public ISocketMessageChannel Channel { get; } | ||||
| /// <inheritdoc /> | |||||
| public MessageSource Source { get; } | public MessageSource Source { get; } | ||||
| /// <inheritdoc /> | |||||
| public string Content { get; private set; } | public string Content { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| /// <inheritdoc /> | |||||
| public virtual bool IsTTS => false; | public virtual bool IsTTS => false; | ||||
| /// <inheritdoc /> | |||||
| public virtual bool IsPinned => false; | public virtual bool IsPinned => false; | ||||
| /// <inheritdoc /> | |||||
| public virtual DateTimeOffset? EditedTimestamp => null; | public virtual DateTimeOffset? EditedTimestamp => null; | ||||
| public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | public virtual IReadOnlyCollection<Attachment> Attachments => ImmutableArray.Create<Attachment>(); | ||||
| public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | public virtual IReadOnlyCollection<Embed> Embeds => ImmutableArray.Create<Embed>(); | ||||
| public virtual IReadOnlyCollection<SocketGuildChannel> MentionedChannels => ImmutableArray.Create<SocketGuildChannel>(); | public virtual IReadOnlyCollection<SocketGuildChannel> MentionedChannels => ImmutableArray.Create<SocketGuildChannel>(); | ||||
| public virtual IReadOnlyCollection<SocketRole> MentionedRoles => ImmutableArray.Create<SocketRole>(); | public virtual IReadOnlyCollection<SocketRole> MentionedRoles => ImmutableArray.Create<SocketRole>(); | ||||
| public virtual IReadOnlyCollection<SocketUser> MentionedUsers => ImmutableArray.Create<SocketUser>(); | public virtual IReadOnlyCollection<SocketUser> MentionedUsers => ImmutableArray.Create<SocketUser>(); | ||||
| /// <inheritdoc /> | |||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| internal SocketMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | internal SocketMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | ||||
| @@ -54,6 +65,7 @@ namespace Discord.WebSocket | |||||
| Content = model.Content.Value; | Content = model.Content.Value; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| => MessageHelper.DeleteAsync(this, Discord, options); | => MessageHelper.DeleteAsync(this, Discord, options); | ||||
| @@ -61,13 +73,21 @@ namespace Discord.WebSocket | |||||
| internal SocketMessage Clone() => MemberwiseClone() as SocketMessage; | internal SocketMessage Clone() => MemberwiseClone() as SocketMessage; | ||||
| //IMessage | //IMessage | ||||
| /// <inheritdoc /> | |||||
| IUser IMessage.Author => Author; | IUser IMessage.Author => Author; | ||||
| /// <inheritdoc /> | |||||
| IMessageChannel IMessage.Channel => Channel; | IMessageChannel IMessage.Channel => Channel; | ||||
| /// <inheritdoc /> | |||||
| MessageType IMessage.Type => MessageType.Default; | MessageType IMessage.Type => MessageType.Default; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<IAttachment> IMessage.Attachments => Attachments; | IReadOnlyCollection<IAttachment> IMessage.Attachments => Attachments; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedChannelIds => MentionedChannels.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedChannelIds => MentionedChannels.Select(x => x.Id).ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Model = Discord.API.Gateway.Reaction; | |||||
| using Model = Discord.API.Gateway.Reaction; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| @@ -9,6 +9,7 @@ namespace Discord.WebSocket | |||||
| public ulong MessageId { get; } | public ulong MessageId { get; } | ||||
| public Optional<SocketUserMessage> Message { get; } | public Optional<SocketUserMessage> Message { get; } | ||||
| public ISocketMessageChannel Channel { get; } | public ISocketMessageChannel Channel { get; } | ||||
| /// <inheritdoc /> | |||||
| public IEmote Emote { get; } | public IEmote Emote { get; } | ||||
| internal SocketReaction(ISocketMessageChannel channel, ulong messageId, Optional<SocketUserMessage> message, ulong userId, Optional<IUser> user, IEmote emoji) | internal SocketReaction(ISocketMessageChannel channel, ulong messageId, Optional<SocketUserMessage> message, ulong userId, Optional<IUser> user, IEmote emoji) | ||||
| @@ -30,6 +31,7 @@ namespace Discord.WebSocket | |||||
| return new SocketReaction(channel, model.MessageId, message, model.UserId, user, emote); | return new SocketReaction(channel, model.MessageId, message, model.UserId, user, emote); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override bool Equals(object other) | public override bool Equals(object other) | ||||
| { | { | ||||
| if (other == null) return false; | if (other == null) return false; | ||||
| @@ -41,6 +43,7 @@ namespace Discord.WebSocket | |||||
| return UserId == otherReaction.UserId && MessageId == otherReaction.MessageId && Emote.Equals(otherReaction.Emote); | return UserId == otherReaction.UserId && MessageId == otherReaction.MessageId && Emote.Equals(otherReaction.Emote); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public override int GetHashCode() | public override int GetHashCode() | ||||
| { | { | ||||
| unchecked | unchecked | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.Message; | using Model = Discord.API.Message; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| @@ -6,6 +6,7 @@ namespace Discord.WebSocket | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class SocketSystemMessage : SocketMessage, ISystemMessage | public class SocketSystemMessage : SocketMessage, ISystemMessage | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public MessageType Type { get; private set; } | public MessageType Type { get; private set; } | ||||
| internal SocketSystemMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) | internal SocketSystemMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) | ||||
| @@ -17,24 +17,34 @@ namespace Discord.WebSocket | |||||
| private ImmutableArray<Attachment> _attachments; | private ImmutableArray<Attachment> _attachments; | ||||
| private ImmutableArray<Embed> _embeds; | private ImmutableArray<Embed> _embeds; | ||||
| private ImmutableArray<ITag> _tags; | private ImmutableArray<ITag> _tags; | ||||
| private List<SocketReaction> _reactions = new List<SocketReaction>(); | |||||
| private readonly List<SocketReaction> _reactions = new List<SocketReaction>(); | |||||
| /// <inheritdoc /> | |||||
| public override bool IsTTS => _isTTS; | public override bool IsTTS => _isTTS; | ||||
| /// <inheritdoc /> | |||||
| public override bool IsPinned => _isPinned; | public override bool IsPinned => _isPinned; | ||||
| /// <inheritdoc /> | |||||
| public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<Attachment> Attachments => _attachments; | public override IReadOnlyCollection<Attachment> Attachments => _attachments; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<Embed> Embeds => _embeds; | public override IReadOnlyCollection<Embed> Embeds => _embeds; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<ITag> Tags => _tags; | public override IReadOnlyCollection<ITag> Tags => _tags; | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => MessageHelper.FilterTagsByValue<SocketGuildChannel>(TagType.ChannelMention, _tags); | public override IReadOnlyCollection<SocketGuildChannel> MentionedChannels => MessageHelper.FilterTagsByValue<SocketGuildChannel>(TagType.ChannelMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<SocketRole> MentionedRoles => MessageHelper.FilterTagsByValue<SocketRole>(TagType.RoleMention, _tags); | public override IReadOnlyCollection<SocketRole> MentionedRoles => MessageHelper.FilterTagsByValue<SocketRole>(TagType.RoleMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public override IReadOnlyCollection<SocketUser> MentionedUsers => MessageHelper.FilterTagsByValue<SocketUser>(TagType.UserMention, _tags); | public override IReadOnlyCollection<SocketUser> MentionedUsers => MessageHelper.FilterTagsByValue<SocketUser>(TagType.UserMention, _tags); | ||||
| /// <inheritdoc /> | |||||
| public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); | public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); | ||||
| internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | ||||
| : base(discord, id, channel, author, source) | : base(discord, id, channel, author, source) | ||||
| { | { | ||||
| } | } | ||||
| internal static new SocketUserMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model) | |||||
| internal new static SocketUserMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model) | |||||
| { | { | ||||
| var entity = new SocketUserMessage(discord, model.Id, channel, author, MessageHelper.GetSource(model)); | var entity = new SocketUserMessage(discord, model.Id, channel, author, MessageHelper.GetSource(model)); | ||||
| entity.Update(state, model); | entity.Update(state, model); | ||||
| @@ -121,30 +131,40 @@ namespace Discord.WebSocket | |||||
| _reactions.Clear(); | _reactions.Clear(); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="InvalidOperationException">Only the author of a message may modify the message.</exception> | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| public Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null) | public Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null) | ||||
| => MessageHelper.ModifyAsync(this, Discord, func, options); | => MessageHelper.ModifyAsync(this, Discord, func, options); | ||||
| /// <inheritdoc /> | |||||
| public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | public Task AddReactionAsync(IEmote emote, RequestOptions options = null) | ||||
| => MessageHelper.AddReactionAsync(this, emote, Discord, options); | => MessageHelper.AddReactionAsync(this, emote, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) | public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) | ||||
| => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); | => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task RemoveAllReactionsAsync(RequestOptions options = null) | public Task RemoveAllReactionsAsync(RequestOptions options = null) | ||||
| => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); | => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) | public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) | ||||
| => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options); | => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task PinAsync(RequestOptions options = null) | public Task PinAsync(RequestOptions options = null) | ||||
| => MessageHelper.PinAsync(this, Discord, options); | => MessageHelper.PinAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task UnpinAsync(RequestOptions options = null) | public Task UnpinAsync(RequestOptions options = null) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| => MentionUtils.Resolve(this, startIndex, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | => MentionUtils.Resolve(this, startIndex, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | ||||
| /// <inheritdoc /> | |||||
| public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | ||||
| private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | ||||
| internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage; | internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage; | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -13,16 +13,25 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| public SocketGuild Guild { get; } | public SocketGuild Guild { get; } | ||||
| /// <inheritdoc /> | |||||
| public Color Color { get; private set; } | public Color Color { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsHoisted { get; private set; } | public bool IsHoisted { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsManaged { get; private set; } | public bool IsManaged { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsMentionable { get; private set; } | public bool IsMentionable { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public GuildPermissions Permissions { get; private set; } | public GuildPermissions Permissions { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public int Position { get; private set; } | public int Position { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| public bool IsEveryone => Id == Guild.Id; | public bool IsEveryone => Id == Guild.Id; | ||||
| /// <inheritdoc /> | |||||
| public string Mention => IsEveryone ? "@everyone" : MentionUtils.MentionRole(Id); | public string Mention => IsEveryone ? "@everyone" : MentionUtils.MentionRole(Id); | ||||
| public IEnumerable<SocketGuildUser> Members | public IEnumerable<SocketGuildUser> Members | ||||
| => Guild.Users.Where(x => x.Roles.Any(r => r.Id == Id)); | => Guild.Users.Where(x => x.Roles.Any(r => r.Id == Id)); | ||||
| @@ -49,8 +58,10 @@ namespace Discord.WebSocket | |||||
| Permissions = new GuildPermissions(model.Permissions); | Permissions = new GuildPermissions(model.Permissions); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task ModifyAsync(Action<RoleProperties> func, RequestOptions options = null) | public Task ModifyAsync(Action<RoleProperties> func, RequestOptions options = null) | ||||
| => RoleHelper.ModifyAsync(this, Discord, func, options); | => RoleHelper.ModifyAsync(this, Discord, func, options); | ||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| => RoleHelper.DeleteAsync(this, Discord, options); | => RoleHelper.DeleteAsync(this, Discord, options); | ||||
| @@ -58,9 +69,11 @@ namespace Discord.WebSocket | |||||
| private string DebuggerDisplay => $"{Name} ({Id})"; | private string DebuggerDisplay => $"{Name} ({Id})"; | ||||
| internal SocketRole Clone() => MemberwiseClone() as SocketRole; | internal SocketRole Clone() => MemberwiseClone() as SocketRole; | ||||
| /// <inheritdoc /> | |||||
| public int CompareTo(IRole role) => RoleUtils.Compare(this, role); | public int CompareTo(IRole role) => RoleUtils.Compare(this, role); | ||||
| //IRole | //IRole | ||||
| /// <inheritdoc /> | |||||
| IGuild IRole.Guild => Guild; | IGuild IRole.Guild => Guild; | ||||
| } | } | ||||
| } | } | ||||
| @@ -13,7 +13,7 @@ using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| /// <summary> | /// <summary> | ||||
| /// Represents a WebSocket guild user. | |||||
| /// Represents a WebSocket-based guild user. | |||||
| /// </summary> | /// </summary> | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class SocketGuildUser : SocketUser, IGuildUser | public class SocketGuildUser : SocketUser, IGuildUser | ||||
| @@ -1,13 +1,14 @@ | |||||
| using System.Diagnostics; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.Presence; | using Model = Discord.API.Presence; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| //TODO: C#7 Candidate for record type | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public struct SocketPresence : IPresence | public struct SocketPresence : IPresence | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public UserStatus Status { get; } | public UserStatus Status { get; } | ||||
| /// <inheritdoc /> | |||||
| public IActivity Activity { get; } | public IActivity Activity { get; } | ||||
| internal SocketPresence(UserStatus status, IActivity activity) | internal SocketPresence(UserStatus status, IActivity activity) | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Discord.Rest; | |||||
| using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -9,17 +9,26 @@ namespace Discord.WebSocket | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class SocketSelfUser : SocketUser, ISelfUser | public class SocketSelfUser : SocketUser, ISelfUser | ||||
| { | { | ||||
| /// <inheritdoc /> | |||||
| public string Email { get; private set; } | public string Email { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsVerified { get; private set; } | public bool IsVerified { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsMfaEnabled { get; private set; } | public bool IsMfaEnabled { get; private set; } | ||||
| internal override SocketGlobalUser GlobalUser { get; } | internal override SocketGlobalUser GlobalUser { get; } | ||||
| /// <inheritdoc /> | |||||
| public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } } | public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } } | ||||
| /// <inheritdoc /> | |||||
| public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } } | public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } } | ||||
| /// <inheritdoc /> | |||||
| public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } } | public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } } | ||||
| /// <inheritdoc /> | |||||
| public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } } | public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } } | ||||
| /// <inheritdoc /> | |||||
| internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } } | internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } } | ||||
| /// <inheritdoc /> | |||||
| public override bool IsWebhook => false; | public override bool IsWebhook => false; | ||||
| internal SocketSelfUser(DiscordSocketClient discord, SocketGlobalUser globalUser) | internal SocketSelfUser(DiscordSocketClient discord, SocketGlobalUser globalUser) | ||||
| @@ -53,7 +62,8 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| return hasGlobalChanges; | return hasGlobalChanges; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| public Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | public Task ModifyAsync(Action<SelfUserProperties> func, RequestOptions options = null) | ||||
| => UserHelper.ModifyAsync(this, Discord, func, options); | => UserHelper.ModifyAsync(this, Discord, func, options); | ||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| @@ -15,7 +15,8 @@ namespace Discord.WebSocket | |||||
| public override bool IsWebhook => false; | public override bool IsWebhook => false; | ||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null); } set { } } | internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null); } set { } } | ||||
| internal override SocketGlobalUser GlobalUser { get { throw new NotSupportedException(); } } | |||||
| internal override SocketGlobalUser GlobalUser => | |||||
| throw new NotSupportedException(); | |||||
| internal SocketUnknownUser(DiscordSocketClient discord, ulong id) | internal SocketUnknownUser(DiscordSocketClient discord, ulong id) | ||||
| : base(discord, id) | : base(discord, id) | ||||
| @@ -1,11 +1,15 @@ | |||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using System; | using System; | ||||
| using System.Diagnostics; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| /// <summary> The WebSocket variant of <see cref="IUser"/>. Represents a Discord user. </summary> | |||||
| /// <summary> | |||||
| /// Represents a WebSocket-based user. | |||||
| /// </summary> | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public abstract class SocketUser : SocketEntity<ulong>, IUser | public abstract class SocketUser : SocketEntity<ulong>, IUser | ||||
| { | { | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -68,7 +72,7 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public async Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | public async Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | ||||
| => GlobalUser.DMChannel ?? await UserHelper.CreateDMChannelAsync(this, Discord, options) as IDMChannel; | |||||
| => GlobalUser.DMChannel ?? await UserHelper.CreateDMChannelAsync(this, Discord, options).ConfigureAwait(false) as IDMChannel; | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | ||||
| @@ -78,6 +82,12 @@ namespace Discord.WebSocket | |||||
| public string GetDefaultAvatarUrl() | public string GetDefaultAvatarUrl() | ||||
| => CDN.GetDefaultUserAvatarUrl(DiscriminatorValue); | => CDN.GetDefaultUserAvatarUrl(DiscriminatorValue); | ||||
| /// <summary> | |||||
| /// Gets the full name of the user (e.g. Example#0001). | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The full name of the user. | |||||
| /// </returns> | |||||
| public override string ToString() => $"{Username}#{Discriminator}"; | public override string ToString() => $"{Username}#{Discriminator}"; | ||||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; | private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; | ||||
| internal SocketUser Clone() => MemberwiseClone() as SocketUser; | internal SocketUser Clone() => MemberwiseClone() as SocketUser; | ||||
| @@ -1,10 +1,9 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.VoiceState; | using Model = Discord.API.VoiceState; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| //TODO: C#7 Candidate for record type | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public struct SocketVoiceState : IVoiceState | public struct SocketVoiceState : IVoiceState | ||||
| { | { | ||||
| @@ -22,14 +21,23 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| private readonly Flags _voiceStates; | private readonly Flags _voiceStates; | ||||
| /// <summary> | |||||
| /// Gets the voice channel that the user is currently in; or <see langword="null"/> if none. | |||||
| /// </summary> | |||||
| public SocketVoiceChannel VoiceChannel { get; } | public SocketVoiceChannel VoiceChannel { get; } | ||||
| /// <inheritdoc /> | |||||
| public string VoiceSessionId { get; } | public string VoiceSessionId { get; } | ||||
| /// <inheritdoc /> | |||||
| public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | ||||
| /// <inheritdoc /> | |||||
| public bool IsDeafened => (_voiceStates & Flags.Deafened) != 0; | public bool IsDeafened => (_voiceStates & Flags.Deafened) != 0; | ||||
| /// <inheritdoc /> | |||||
| public bool IsSuppressed => (_voiceStates & Flags.Suppressed) != 0; | public bool IsSuppressed => (_voiceStates & Flags.Suppressed) != 0; | ||||
| /// <inheritdoc /> | |||||
| public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; | public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; | ||||
| /// <inheritdoc /> | |||||
| public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | ||||
| internal SocketVoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isMuted, bool isDeafened, bool isSuppressed) | internal SocketVoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isMuted, bool isDeafened, bool isSuppressed) | ||||
| @@ -55,6 +63,12 @@ namespace Discord.WebSocket | |||||
| return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Mute, model.Deaf, model.Suppress); | return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Mute, model.Deaf, model.Suppress); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gets the name of the voice channel. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The name of the voice channel. | |||||
| /// </returns> | |||||
| public override string ToString() => VoiceChannel?.Name ?? "Unknown"; | public override string ToString() => VoiceChannel?.Name ?? "Unknown"; | ||||
| private string DebuggerDisplay => $"{VoiceChannel?.Name ?? "Unknown"} ({_voiceStates})"; | private string DebuggerDisplay => $"{VoiceChannel?.Name ?? "Unknown"} ({_voiceStates})"; | ||||
| internal SocketVoiceState Clone() => this; | internal SocketVoiceState Clone() => this; | ||||
| @@ -63,26 +63,32 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Webhook users cannot be kicked.</exception> | |||||
| Task IGuildUser.KickAsync(string reason, RequestOptions options) => | Task IGuildUser.KickAsync(string reason, RequestOptions options) => | ||||
| throw new NotSupportedException("Webhook users cannot be kicked."); | throw new NotSupportedException("Webhook users cannot be kicked."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Webhook users cannot be modified.</exception> | |||||
| Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | ||||
| throw new NotSupportedException("Webhook users cannot be modified."); | throw new NotSupportedException("Webhook users cannot be modified."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | ||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | ||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | ||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | ||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| @@ -1,4 +1,4 @@ | |||||
| #if DEFAULTWEBSOCKET | |||||
| #if DEFAULTWEBSOCKET | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.ComponentModel; | using System.ComponentModel; | ||||
| @@ -248,4 +248,4 @@ namespace Discord.Net.WebSockets | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| #endif | |||||
| @@ -8,6 +8,7 @@ namespace Discord.Net.WebSockets | |||||
| #if DEFAULTWEBSOCKET | #if DEFAULTWEBSOCKET | ||||
| public static readonly WebSocketProvider Instance = Create(); | public static readonly WebSocketProvider Instance = Create(); | ||||
| /// <exception cref="PlatformNotSupportedException">The default WebSocketProvider is not supported on this platform.</exception> | |||||
| public static WebSocketProvider Create(IWebProxy proxy = null) | public static WebSocketProvider Create(IWebProxy proxy = null) | ||||
| { | { | ||||
| return () => | return () => | ||||
| @@ -30,4 +31,4 @@ namespace Discord.Net.WebSockets | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Linq; | using System.Linq; | ||||
| @@ -12,11 +12,12 @@ namespace Discord.Webhook | |||||
| { | { | ||||
| internal static class WebhookClientHelper | internal static class WebhookClientHelper | ||||
| { | { | ||||
| /// <exception cref="InvalidOperationException">Could not find a webhook with the supplied credentials.</exception> | |||||
| public static async Task<RestInternalWebhook> GetWebhookAsync(DiscordWebhookClient client, ulong webhookId) | public static async Task<RestInternalWebhook> GetWebhookAsync(DiscordWebhookClient client, ulong webhookId) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetWebhookAsync(webhookId).ConfigureAwait(false); | var model = await client.ApiClient.GetWebhookAsync(webhookId).ConfigureAwait(false); | ||||
| if (model == null) | if (model == null) | ||||
| throw new InvalidOperationException("Could not find a webhook for the supplied credentials."); | |||||
| throw new InvalidOperationException("Could not find a webhook with the supplied credentials."); | |||||
| return RestInternalWebhook.Create(client, model); | return RestInternalWebhook.Create(client, model); | ||||
| } | } | ||||
| public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client, | public static async Task<ulong> SendMessageAsync(DiscordWebhookClient client, | ||||