From 456ee4b15017a39ea964b49702b9eb027663ba75 Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 10 Feb 2019 03:12:48 +0800 Subject: [PATCH] Add/migrate code examples --- Discord.Net.sln | 19 +- .../Entities/Channels/IGuildChannel.cs | 10 +- .../Entities/Channels/IMessageChannel.cs | 63 +++--- .../Entities/Channels/ITextChannel.cs | 2 +- .../Entities/Guilds/IGuild.cs | 12 +- src/Discord.Net.Core/Entities/Users/IUser.cs | 7 +- src/Discord.Net.Core/IDiscordClient.cs | 8 +- .../Channels/IGuildChannel.Examples.cs | 63 +++--- .../Channels/IMessageChannel.Examples.cs | 114 +++++++++++ .../Core/Entities/Guilds/IGuild.Examples.cs | 25 +++ .../Core/Entities/Users/IUser.Examples.cs | 50 +++-- .../Discord.Net.Examples.csproj | 21 ++ .../BaseSocketClient.Events.Examples.cs | 193 ++++++++++-------- .../Entities/Guilds/RestGuild.cs | 2 +- .../BaseSocketClient.Events.cs | 20 +- src/Discord.Net.WebSocket/BaseSocketClient.cs | 28 +-- .../DiscordSocketConfig.cs | 5 +- .../Entities/Guilds/SocketGuild.cs | 2 +- 18 files changed, 432 insertions(+), 212 deletions(-) create mode 100644 src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs create mode 100644 src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs create mode 100644 src/Discord.Net.Examples/Discord.Net.Examples.csproj diff --git a/Discord.Net.sln b/Discord.Net.sln index 245515c7c..75f0a4391 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -30,7 +30,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01_basic_ping_bot", "sample EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03_sharded_client", "samples\03_sharded_client\03_sharded_client.csproj", "{9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Examples", "src\Discord.Net.Examples\Discord.Net.Examples.csproj", "{7EA96B2B-4D71-458D-9423-839362DC38BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -174,6 +178,18 @@ Global {9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x64.Build.0 = Release|Any CPU {9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.ActiveCfg = Release|Any CPU {9B4C4AFB-3D15-44C6-9E36-12ED625AAA26}.Release|x86.Build.0 = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.ActiveCfg = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x64.Build.0 = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.ActiveCfg = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Debug|x86.Build.0 = Debug|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|Any CPU.Build.0 = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.ActiveCfg = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x64.Build.0 = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.ActiveCfg = Release|Any CPU + {7EA96B2B-4D71-458D-9423-839362DC38BE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -188,6 +204,7 @@ Global {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} {9B4C4AFB-3D15-44C6-9E36-12ED625AAA26} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} + {7EA96B2B-4D71-458D-9423-839362DC38BE} = {D1F0271E-0EE2-4B66-AC3D-9871B7E1C4CF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 652507492..b964c0896 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -101,9 +101,8 @@ namespace Discord /// . Next, it checks if an overwrite had already been set via /// ; if not, it denies the role from sending any /// messages to the channel. - /// + /// /// /// The role to add the overwrite to. /// The overwrite to add to the role. @@ -121,9 +120,8 @@ namespace Discord /// . Next, it checks if an overwrite had already been set via /// ; if not, it denies the user from sending any /// messages to the channel. - /// + /// /// /// The user to add the overwrite to. /// The overwrite to add to the user. diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 919b3f60b..322150e92 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -16,11 +16,8 @@ namespace Discord /// /// The following example sends a message with the current system time in RFC 1123 format to the channel and /// deletes itself after 5 seconds. - /// - /// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R")); - /// await Task.Delay(TimeSpan.FromSeconds(5)) - /// .ContinueWith(x => message.DeleteAsync()); - /// + /// /// /// The message to be sent. /// Determines whether the message should be read aloud by Discord or not. @@ -35,18 +32,14 @@ namespace Discord /// Sends a file to this message channel with an optional caption. /// /// - /// The following example uploads a local file called wumpus.txt along with the text - /// good discord boi to the channel. - /// - /// await channel.SendFileAsync("wumpus.txt", "good discord boi"); - /// - /// - /// The following example uploads a local image called b1nzy.jpg embedded inside a rich embed to the - /// channel. - /// - /// await channel.SendFileAsync("b1nzy.jpg", - /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); - /// + /// The following example uploads a local file called wumpus.txt along with the text + /// good discord boi to the channel. + /// + /// The following example uploads a local image called b1nzy.jpg embedded inside a rich embed to the + /// channel. + /// /// /// /// This method sends a file as if you are uploading an attachment directly from your Discord client. @@ -72,10 +65,8 @@ namespace Discord /// /// The following example uploads a streamed image that will be called b1nzy.jpg embedded inside a /// rich embed to the channel. - /// - /// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg", - /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); - /// + /// /// /// /// This method sends a file as if you are uploading an attachment directly from your Discord client. @@ -132,10 +123,8 @@ namespace Discord /// /// The following example downloads 300 messages and gets messages that belong to the user /// 53905483156684800. - /// - /// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync(); - /// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800); - /// + /// /// /// The numbers of message to be gotten from. /// The that determines whether the object should be fetched from @@ -168,10 +157,13 @@ namespace Discord /// of flattening. /// /// - /// The following example gets 5 message prior to the message identifier 442012544660537354. - /// - /// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync(); - /// + /// The following example gets 5 message prior to the message identifier 442012544660537354. + /// + /// The following example attempts to retrieve messageCount number of messages from the + /// beginning of the channel and prints them to the console. + /// /// /// The ID of the starting message to get the messages from. /// The direction of the messages to be gotten from. @@ -207,9 +199,8 @@ namespace Discord /// /// /// The following example gets 5 message prior to a specific message, oldMessage. - /// - /// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync(); - /// + /// /// /// The starting message to get the messages from. /// The direction of the messages to be gotten from. @@ -263,12 +254,8 @@ namespace Discord /// /// /// The following example keeps the client in the typing state until LongRunningAsync has finished. - /// - /// using (messageChannel.EnterTypingState()) - /// { - /// await LongRunningAsync(); - /// } - /// + /// /// /// The options to be used when sending the request. /// diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 440349eca..29c764e3f 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -40,7 +40,7 @@ namespace Discord /// /// /// The following example gets 250 messages from the channel and deletes them. - /// + /// /// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync(); /// await textChannel.DeleteMessagesAsync(messages); /// diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 3b35796b9..571afef15 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -440,16 +440,8 @@ namespace Discord /// /// /// The following example creates a new text channel under an existing category named Wumpus with a set topic. - /// - /// var categories = await guild.GetCategoriesAsync(); - /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); - /// if (targetCategory == null) return; - /// await Context.Guild.CreateTextChannelAsync(name, x => - /// { - /// x.CategoryId = targetCategory.Id; - /// x.Topic = $"This channel was created at {DateTimeOffset.UtcNow} by {user}."; - /// }); - /// + /// /// /// The new name for the text channel. /// The delegate containing the properties to be applied to the channel upon its creation. diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs index 66fdcf5ba..c59a75de1 100644 --- a/src/Discord.Net.Core/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -23,7 +23,8 @@ namespace Discord /// /// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is /// not set, a default avatar for this user will be returned instead. - /// + /// /// /// The format to return. /// The size of the image to return in. This can be any power of two between 16 and 2048. @@ -91,8 +92,8 @@ namespace Discord /// /// The following example attempts to send a direct message to the target user and logs the incident should /// it fail. - /// + /// /// /// The options to be used when sending the request. /// diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index d6a863556..e1c900680 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -63,7 +63,7 @@ namespace Discord /// Gets a generic channel. /// /// - /// + /// /// var channel = await _client.GetChannelAsync(381889909113225237); /// if (channel != null && channel is IMessageChannel msgChannel) /// { @@ -194,7 +194,7 @@ namespace Discord /// Gets a user. /// /// - /// + /// /// var user = await _client.GetUserAsync(168693960628371456); /// if (user != null) /// Console.WriteLine($"{user} is created at {user.CreatedAt}."; @@ -212,7 +212,7 @@ namespace Discord /// Gets a user. /// /// - /// + /// /// var user = await _client.GetUserAsync("Still", "2876"); /// if (user != null) /// Console.WriteLine($"{user} is created at {user.CreatedAt}."; @@ -232,7 +232,7 @@ namespace Discord /// /// /// The following example gets the most optimal voice region from the collection. - /// + /// /// var regions = await client.GetVoiceRegionsAsync(); /// var optimalRegion = regions.FirstOrDefault(x => x.IsOptimal); /// diff --git a/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs index c059f4681..d382ddbf3 100644 --- a/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs +++ b/src/Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs @@ -1,31 +1,46 @@ -#region AddPermissionOverwriteAsyncRole -public async Task MuteRoleAsync(IRole role, IGuildChannel channel) +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Discord.Net.Examples.Core.Entities.Channels { - if (role == null) throw new ArgumentNullException(nameof(role)); - if (channel == null) throw new ArgumentNullException(nameof(channel)); + [PublicAPI] + internal class GuildChannelExamples + { + #region AddPermissionOverwriteAsyncRole - // Fetches the previous overwrite and bail if one is found - var previousOverwrite = channel.GetPermissionOverwrite(role); - if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny) - throw new InvalidOperationException($"Role {role.Name} had already been muted in this channel."); + public async Task MuteRoleAsync(IRole role, IGuildChannel channel) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + if (channel == null) throw new ArgumentNullException(nameof(channel)); - // Creates a new OverwritePermissions with send message set to deny and pass it into the method - await channel.AddPermissionOverwriteAsync(role, new OverwritePermissions(sendMessages: PermValue.Deny)); -} -#endregion + // Fetches the previous overwrite and bail if one is found + var previousOverwrite = channel.GetPermissionOverwrite(role); + if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny) + throw new InvalidOperationException($"Role {role.Name} had already been muted in this channel."); -#region AddPermissionOverwriteAsyncUser -public async Task MuteUserAsync(IGuildUser user, IGuildChannel channel) -{ - if (role == null) throw new ArgumentNullException(nameof(user)); - if (channel == null) throw new ArgumentNullException(nameof(channel)); + // Creates a new OverwritePermissions with send message set to deny and pass it into the method + await channel.AddPermissionOverwriteAsync(role, new OverwritePermissions(sendMessages: PermValue.Deny)); + } + + #endregion + + #region AddPermissionOverwriteAsyncUser + + public async Task MuteUserAsync(IGuildUser user, IGuildChannel channel) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + if (channel == null) throw new ArgumentNullException(nameof(channel)); + + // Fetches the previous overwrite and bail if one is found + var previousOverwrite = channel.GetPermissionOverwrite(user); + if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny) + throw new InvalidOperationException($"User {user.Username} had already been muted in this channel."); - // Fetches the previous overwrite and bail if one is found - var previousOverwrite = channel.GetPermissionOverwrite(user); - if (previousOverwrite.HasValue && previousOverwrite.Value.SendMessages == PermValue.Deny) - throw new InvalidOperationException($"User {user.Name} had already been muted in this channel."); + // Creates a new OverwritePermissions with send message set to deny and pass it into the method + await channel.AddPermissionOverwriteAsync(user, new OverwritePermissions(sendMessages: PermValue.Deny)); + } - // Creates a new OverwritePermissions with send message set to deny and pass it into the method - await channel.AddPermissionOverwriteAsync(user, new OverwritePermissions(sendMessages: PermValue.Deny)); + #endregion + } } -#endregion \ No newline at end of file diff --git a/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs new file mode 100644 index 000000000..5f9d4b5d6 --- /dev/null +++ b/src/Discord.Net.Examples/Core/Entities/Channels/IMessageChannel.Examples.cs @@ -0,0 +1,114 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Discord.Net.Examples.Core.Entities.Channels +{ + [PublicAPI] + internal class MessageChannelExamples + { + #region GetMessagesAsync.FromId.BeginningMessages + + public async Task PrintFirstMessages(IMessageChannel channel, int messageCount) + { + // Although the library does attempt to divide the messageCount by 100 + // to comply to Discord's maximum message limit per request, sending + // too many could still cause the queue to clog up. + // The purpose of this exception is to discourage users from sending + // too many requests at once. + if (messageCount > 1000) + throw new InvalidOperationException("Too many messages requested."); + + // Setting fromMessageId to 0 will make Discord + // default to the first message in channel. + var messages = await channel.GetMessagesAsync( + 0, Direction.After, messageCount) + .FlattenAsync(); + + // Print message content + foreach (var message in messages) + Console.WriteLine($"{message.Author} posted '{message.Content}' at {message.CreatedAt}."); + } + + #endregion + + public async Task GetMessagesExampleBody(IMessageChannel channel) + { +#pragma warning disable IDISP001 +#pragma warning disable IDISP014 + // We're just declaring this for the sample below. + // Ideally, you want to get or create your HttpClient + // from IHttpClientFactory. + // You get a bonus for reading the example source though! + var httpClient = new HttpClient(); +#pragma warning restore IDISP014 +#pragma warning restore IDISP001 + + // Another dummy method + Task LongRunningAsync() + { + return Task.Delay(0); + } + + #region GetMessagesAsync.FromLimit.Standard + + var messages = await channel.GetMessagesAsync(300).FlattenAsync(); + var userMessages = messages.Where(x => x.Author.Id == 53905483156684800); + + #endregion + + #region GetMessagesAsync.FromMessage + + var oldMessage = await channel.SendMessageAsync("boi"); + var messagesFromMsg = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync(); + + #endregion + + + #region GetMessagesAsync.FromId.FromMessage + + await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync(); + + #endregion + + #region SendMessageAsync + + var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R")); + await Task.Delay(TimeSpan.FromSeconds(5)) + .ContinueWith(x => message.DeleteAsync()); + + #endregion + + #region SendFileAsync.FilePath + + await channel.SendFileAsync("wumpus.txt", "good discord boi"); + + #endregion + + #region SendFileAsync.FilePath.EmbeddedImage + + await channel.SendFileAsync("b1nzy.jpg", + embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); + + #endregion + + + #region SendFileAsync.FileStream.EmbeddedImage + + using (var b1nzyStream = await httpClient.GetStreamAsync("https://example.com/b1nzy")) + await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg", + embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); + + #endregion + + #region EnterTypingState + + using (channel.EnterTypingState()) await LongRunningAsync(); + + #endregion + + } + } +} diff --git a/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs new file mode 100644 index 000000000..03144b232 --- /dev/null +++ b/src/Discord.Net.Examples/Core/Entities/Guilds/IGuild.Examples.cs @@ -0,0 +1,25 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Discord.Net.Examples.Core.Entities.Guilds +{ + [PublicAPI] + internal class GuildExamples + { + #region CreateTextChannelAsync + public async Task CreateTextChannelUnderWumpus(IGuild guild, string name) + { + var categories = await guild.GetCategoriesAsync(); + var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); + if (targetCategory == null) return; + await guild.CreateTextChannelAsync(name, x => + { + x.CategoryId = targetCategory.Id; + x.Topic = $"This channel was created at {DateTimeOffset.UtcNow}."; + }); + } + #endregion + } +} diff --git a/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs index 145e406ba..79a90b46d 100644 --- a/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs +++ b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs @@ -1,22 +1,38 @@ -#region GetAvatarUrl -public async Task GetAvatarAsync(IUser user) -{ - var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl(); - await textChannel.SendMessageAsync(userAvatarUrl); -} -#endregion +using System; +using System.Net; +using System.Threading.Tasks; +using JetBrains.Annotations; -#region GetOrCreateDMChannelAsync -public async Task MessageUserAsync(IUser user) +namespace Discord.Net.Examples.Core.Entities.Users { - var channel = await user.GetOrCreateDMChannelAsync(); - try + [PublicAPI] + internal class UserExamples { - await channel.SendMessageAsync("Awesome stuff!"); - } - catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403) - { - Console.WriteLine($"Boo, I cannot message {user}"); + #region GetAvatarUrl + + public async Task GetAvatarAsync(IUser user, ITextChannel textChannel) + { + var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl(); + await textChannel.SendMessageAsync(userAvatarUrl); + } + + #endregion + + #region GetOrCreateDMChannelAsync + + public async Task MessageUserAsync(IUser user) + { + var channel = await user.GetOrCreateDMChannelAsync(); + try + { + await channel.SendMessageAsync("Awesome stuff!"); + } + catch (Discord.Net.HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden) + { + Console.WriteLine($"Boo, I cannot message {user}."); + } + } + + #endregion } } -#endregion \ No newline at end of file diff --git a/src/Discord.Net.Examples/Discord.Net.Examples.csproj b/src/Discord.Net.Examples/Discord.Net.Examples.csproj new file mode 100644 index 000000000..b02d2e6d8 --- /dev/null +++ b/src/Discord.Net.Examples/Discord.Net.Examples.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + + + diff --git a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs index 0041929c2..387584877 100644 --- a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs +++ b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs @@ -1,90 +1,117 @@ -#region ReactionAdded -public void HookReactionAdded(BaseSocketClient client) -{ - client.ReactionAdded += HandleReactionAddedAsync; -} -public async Task HandleReactionAddedAsync(Cacheable cachedMessage, - ISocketMessageChannel originChannel, SocketReaction reaction) -{ - var message = await cachedMessage.GetOrDownloadAsync(); - if (message != null && reaction.User.IsSpecified) - Console.WriteLine($"{message.User.Value} just added a reaction '{reaction.Emote}' " + - $"to {message.Author}'s message ({message.Id})."); -} -#endregion +using System; +using System.Linq; +using System.Threading.Tasks; +using Discord.WebSocket; +using JetBrains.Annotations; -#region ChannelCreated -public void HookChannelCreated(BaseSocketClient client) -{ - client.ChannelCreated += HandleChannelCreated; -} -public Task HandleChannelCreated(SocketChannel channel) +namespace Discord.Net.Examples.WebSocket { - Console.WriteLine($"A new channel '{channel.Name}'({channel.Id}, {channel.GetType()})" - + $"has been created at {channel.CreatedAt}."); - return Task.CompletedTask; -} -#endregion + [PublicAPI] + internal class BaseSocketClientExamples + { + #region ReactionAdded -#region ChannelDestroyed -public void HookChannelDestroyed(BaseSocketClient client) -{ - client.ChannelDestroyed += HandleChannelDestroyed; -} -public Task HandleChannelDestroyed(SocketChannel channel) -{ - Console.WriteLine($"A new channel '{channel.Name}'({channel.Id}, {channel.GetType()}) has been deleted."); - return Task.CompletedTask; -} -#endregion + public void HookReactionAdded(BaseSocketClient client) + => client.ReactionAdded += HandleReactionAddedAsync; -#region ChannelUpdated -public void HookChannelUpdated(BaseSocketClient client) -{ - client.ChannelUpdated += HandleChannelRename; -} -public Task HandleChannelRename(SocketChannel beforeChannel, SocketChannel afterChannel) -{ - if (beforeChannel.Name != afterChannel.Name) - Console.WriteLine($"A channel ({beforeChannel.Id}) is renamed from {beforeChannel.Name} to {afterChannel.Name}."); - return Task.CompletedTask; -} -#endregion + public async Task HandleReactionAddedAsync(Cacheable cachedMessage, + ISocketMessageChannel originChannel, SocketReaction reaction) + { + var message = await cachedMessage.GetOrDownloadAsync(); + if (message != null && reaction.User.IsSpecified) + Console.WriteLine($"{reaction.User.Value} just added a reaction '{reaction.Emote}' " + + $"to {message.Author}'s message ({message.Id})."); + } -#region MessageReceived -private readonly ulong[] _targetUserIds = new {168693960628371456, 53905483156684800}; -public void HookMessageReceived(BaseSocketClient client) -{ - client.MessageReceived += HandleMessageReceived; -} -public Task HandleMessageReceived(SocketMessage message) -{ - // check if the message is a user message as opposed to a system message (e.g. Clyde, pins, etc.) - if (!(message is SocketUserMessage userMessage)) return; - // check if the message origin is a guild message channel - if (!(userMessage.Channel is SocketTextChannel textChannel)) return; - // check if the target user was mentioned - var targetUsers = userMessage.MentionedUsers.Where(x => _targetUserIds.Contains(x)); - if (targetUsers == null) return; - foreach (var targetUser in targetUsers) - Console.WriteLine($"{targetUser} was mentioned in the message '{message.Content}' by {message.Author}."); - return Task.CompletedTask; -} -#endregion + #endregion -#region MessageDeleted -public void HookMessageDeleted(BaseSocketClient client) -{ - client.MessageDeleted += HandleMessageDelete; -} -public Task HandleMessageDelete(Cacheable cachedMessage, ISocketMessageChannel channel) -{ - // check if the message exists in cache; if not, we cannot report what was removed - if (!cachedMessage.HasValue) return; - var message = cachedMessage.Value; - Console.WriteLine($"A message ({message.Id}) from {message.Author} was removed from the channel {channel.Name} ({channel.Id}):" - + Environment.NewLine - + message.Content); - return Task.CompletedTask; + #region ChannelCreated + + public void HookChannelCreated(BaseSocketClient client) + => client.ChannelCreated += HandleChannelCreated; + + public Task HandleChannelCreated(SocketChannel channel) + { + if (channel is SocketGuildChannel guildChannel) + Console.WriteLine($"A new channel '{guildChannel.Name}'({guildChannel.Id}, {guildChannel.GetType()})" + + $"has been created at {guildChannel.CreatedAt}."); + return Task.CompletedTask; + } + + #endregion + + #region ChannelDestroyed + + public void HookChannelDestroyed(BaseSocketClient client) + => client.ChannelDestroyed += HandleChannelDestroyed; + + public Task HandleChannelDestroyed(SocketChannel channel) + { + if (channel is SocketGuildChannel guildChannel) + Console.WriteLine( + $"A new channel '{guildChannel.Name}'({guildChannel.Id}, {guildChannel.GetType()}) has been deleted."); + return Task.CompletedTask; + } + + #endregion + + #region ChannelUpdated + + public void HookChannelUpdated(BaseSocketClient client) + => client.ChannelUpdated += HandleChannelRename; + + public Task HandleChannelRename(SocketChannel beforeChannel, SocketChannel afterChannel) + { + if (beforeChannel is SocketGuildChannel beforeGuildChannel && + afterChannel is SocketGuildChannel afterGuildChannel) + if (beforeGuildChannel.Name != afterGuildChannel.Name) + Console.WriteLine( + $"A channel ({beforeChannel.Id}) is renamed from {beforeGuildChannel.Name} to {afterGuildChannel.Name}."); + return Task.CompletedTask; + } + + #endregion + + #region MessageReceived + + private readonly ulong[] _targetUserIds = {168693960628371456, 53905483156684800}; + + public void HookMessageReceived(BaseSocketClient client) + => client.MessageReceived += HandleMessageReceived; + + public Task HandleMessageReceived(SocketMessage message) + { + // check if the message is a user message as opposed to a system message (e.g. Clyde, pins, etc.) + if (!(message is SocketUserMessage userMessage)) return Task.CompletedTask; + // check if the message origin is a guild message channel + if (!(userMessage.Channel is SocketTextChannel textChannel)) return Task.CompletedTask; + // check if the target user was mentioned + var targetUsers = userMessage.MentionedUsers.Where(x => _targetUserIds.Contains(x.Id)); + foreach (var targetUser in targetUsers) + Console.WriteLine( + $"{targetUser} was mentioned in the message '{message.Content}' by {message.Author} in {textChannel.Name}."); + return Task.CompletedTask; + } + + #endregion + + #region MessageDeleted + + public void HookMessageDeleted(BaseSocketClient client) + => client.MessageDeleted += HandleMessageDelete; + + public Task HandleMessageDelete(Cacheable cachedMessage, ISocketMessageChannel channel) + { + // check if the message exists in cache; if not, we cannot report what was removed + if (!cachedMessage.HasValue) return Task.CompletedTask; + var message = cachedMessage.Value; + Console.WriteLine( + $"A message ({message.Id}) from {message.Author} was removed from the channel {channel.Name} ({channel.Id}):" + + Environment.NewLine + + message.Content); + return Task.CompletedTask; + } + + #endregion + } } -#endregion \ No newline at end of file diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index a847998b5..902d2c9a8 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -405,7 +405,7 @@ namespace Discord.Rest /// /// /// The following example creates a new text channel under an existing category named Wumpus with a set topic. - /// + /// /// var categories = await guild.GetCategoriesAsync(); /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); /// if (targetCategory == null) return; diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index 8d759dbcb..4f94f702d 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -19,7 +19,8 @@ namespace Discord.WebSocket /// /// /// - /// + /// /// public event Func ChannelCreated { @@ -40,7 +41,8 @@ namespace Discord.WebSocket /// /// /// - /// + /// /// public event Func ChannelDestroyed { add { _channelDestroyedEvent.Add(value); } @@ -61,7 +63,8 @@ namespace Discord.WebSocket /// /// /// - /// + /// /// public event Func ChannelUpdated { add { _channelUpdatedEvent.Add(value); } @@ -84,8 +87,9 @@ namespace Discord.WebSocket /// /// /// - /// The example below checks if the newly received message contains the target user. - /// + /// The example below checks if the newly received message contains the target user. + /// /// public event Func MessageReceived { add { _messageReceivedEvent.Add(value); } @@ -116,7 +120,8 @@ namespace Discord.WebSocket /// /// /// - /// + /// /// public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } @@ -176,7 +181,8 @@ namespace Discord.WebSocket /// /// /// - /// + /// /// public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index a7ebf72d8..4ab149832 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -97,15 +97,15 @@ namespace Discord.WebSocket /// /// This method gets the user present in the WebSocket cache with the given condition. /// - /// Sometimes a user may return null due to Discord not sending offline users in large - /// guilds (i.e. guild with 100+ members) actively. To download users on startup and to see - /// more information about this subject, see . + /// Sometimes a user may return null due to Discord not sending offline users in large guilds + /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information + /// about this subject, see . /// /// /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. - /// users who don't share mutual guild(s) with the current user). If you wish to get a user that - /// you do not have access to, consider using the REST implementation of - /// . + /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do + /// not have access to, consider using the REST implementation of + /// . /// /// /// @@ -116,22 +116,22 @@ namespace Discord.WebSocket /// /// Gets a user. /// - /// The name of the user. - /// The discriminator value of the user. /// /// This method gets the user present in the WebSocket cache with the given condition. /// - /// Sometimes a user may return null due to Discord not sending offline users in large - /// guilds (i.e. guild with 100+ members) actively. To download users on startup and to see - /// more information about this subject, see . + /// Sometimes a user may return null due to Discord not sending offline users in large guilds + /// (i.e. guild with 100+ members) actively. To download users on startup and to see more information + /// about this subject, see . /// /// /// This method does not attempt to fetch users that the logged-in user does not have access to (i.e. - /// users who don't share mutual guild(s) with the current user). If you wish to get a user that - /// you do not have access to, consider using the REST implementation of - /// . + /// users who don't share mutual guild(s) with the current user). If you wish to get a user that you do + /// not have access to, consider using the REST implementation of + /// . /// /// + /// The name of the user. + /// The discriminator value of the user. /// /// A generic WebSocket-based user; null when the user cannot be found. /// diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 6ff83b152..8571cd5c9 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -94,8 +94,9 @@ namespace Discord.WebSocket /// Please note that it can be difficult to fill the cache completely on large guilds depending on the /// traffic. If you are using the command system, the default user TypeReader may fail to find the user /// due to this issue. This may be resolved at v3 of the library. Until then, you may want to consider - /// overriding the TypeReader and use - /// or as a backup. + /// overriding the TypeReader and use + /// + /// or as a backup. /// /// public bool AlwaysDownloadUsers { get; set; } = false; diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 955e219af..d952b3d92 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -535,7 +535,7 @@ namespace Discord.WebSocket /// /// /// The following example creates a new text channel under an existing category named Wumpus with a set topic. - /// + /// /// var categories = await guild.GetCategoriesAsync(); /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); /// if (targetCategory == null) return;