@@ -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, | ||||