| @@ -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} | |||
| @@ -101,9 +101,8 @@ namespace Discord | |||
| /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | |||
| /// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any | |||
| /// messages to the channel. | |||
| /// <code lang="cs" | |||
| /// source="../Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs" | |||
| /// region="AddPermissionOverwriteAsyncRole"/> | |||
| /// <code language="cs" region="AddPermissionOverwriteAsyncRole" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/> | |||
| /// </example> | |||
| /// <param name="role">The role to add the overwrite to.</param> | |||
| /// <param name="permissions">The overwrite to add to the role.</param> | |||
| @@ -121,9 +120,8 @@ namespace Discord | |||
| /// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via | |||
| /// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any | |||
| /// messages to the channel. | |||
| /// <code lang="cs" | |||
| /// source="../Discord.Net.Examples/Core/Entities/Channels/IGuildChannel.Examples.cs" | |||
| /// region="AddPermissionOverwriteAsyncUser"/> | |||
| /// <code language="cs" region="AddPermissionOverwriteAsyncUser" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IGuildChannel.Examples.cs"/> | |||
| /// </example> | |||
| /// <param name="user">The user to add the overwrite to.</param> | |||
| /// <param name="permissions">The overwrite to add to the user.</param> | |||
| @@ -16,11 +16,8 @@ namespace Discord | |||
| /// <example> | |||
| /// The following example sends a message with the current system time in RFC 1123 format to the channel and | |||
| /// deletes itself after 5 seconds. | |||
| /// <code language="cs"> | |||
| /// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R")); | |||
| /// await Task.Delay(TimeSpan.FromSeconds(5)) | |||
| /// .ContinueWith(x => message.DeleteAsync()); | |||
| /// </code> | |||
| /// <code language="cs" region="SendMessageAsync" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <param name="text">The message to be sent.</param> | |||
| /// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param> | |||
| @@ -35,18 +32,14 @@ namespace Discord | |||
| /// Sends a file to this message channel with an optional caption. | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example uploads a local file called <c>wumpus.txt</c> along with the text | |||
| /// <c>good discord boi</c> to the channel. | |||
| /// <code language="cs"> | |||
| /// await channel.SendFileAsync("wumpus.txt", "good discord boi"); | |||
| /// </code> | |||
| /// | |||
| /// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the | |||
| /// channel. | |||
| /// <code language="cs"> | |||
| /// await channel.SendFileAsync("b1nzy.jpg", | |||
| /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); | |||
| /// </code> | |||
| /// <para>The following example uploads a local file called <c>wumpus.txt</c> along with the text | |||
| /// <c>good discord boi</c> to the channel.</para> | |||
| /// <code language="cs" region="SendFileAsync.FilePath" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// <para>The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the | |||
| /// channel.</para> | |||
| /// <code language="cs" region="SendFileAsync.FilePath.EmbeddedImage" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <remarks> | |||
| /// This method sends a file as if you are uploading an attachment directly from your Discord client. | |||
| @@ -72,10 +65,8 @@ namespace Discord | |||
| /// <example> | |||
| /// The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a | |||
| /// rich embed to the channel. | |||
| /// <code language="cs"> | |||
| /// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg", | |||
| /// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build()); | |||
| /// </code> | |||
| /// <code language="cs" region="SendFileAsync.FileStream.EmbeddedImage" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <remarks> | |||
| /// This method sends a file as if you are uploading an attachment directly from your Discord client. | |||
| @@ -132,10 +123,8 @@ namespace Discord | |||
| /// <example> | |||
| /// The following example downloads 300 messages and gets messages that belong to the user | |||
| /// <c>53905483156684800</c>. | |||
| /// <code lang="cs"> | |||
| /// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync(); | |||
| /// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800); | |||
| /// </code> | |||
| /// <code language="cs" region="GetMessagesAsync.FromLimit.Standard" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <param name="limit">The numbers of message to be gotten from.</param> | |||
| /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from | |||
| @@ -168,10 +157,13 @@ namespace Discord | |||
| /// of flattening. | |||
| /// </remarks> | |||
| /// <example> | |||
| /// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>. | |||
| /// <code lang="cs"> | |||
| /// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync(); | |||
| /// </code> | |||
| /// <para>The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.</para> | |||
| /// <code language="cs" region="GetMessagesAsync.FromId.FromMessage" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// <para>The following example attempts to retrieve <c>messageCount</c> number of messages from the | |||
| /// beginning of the channel and prints them to the console.</para> | |||
| /// <code language="cs" region="GetMessagesAsync.FromId.BeginningMessages" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <param name="fromMessageId">The ID of the starting message to get the messages from.</param> | |||
| /// <param name="dir">The direction of the messages to be gotten from.</param> | |||
| @@ -207,9 +199,8 @@ namespace Discord | |||
| /// </remarks> | |||
| /// <example> | |||
| /// The following example gets 5 message prior to a specific message, <c>oldMessage</c>. | |||
| /// <code lang="cs"> | |||
| /// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync(); | |||
| /// </code> | |||
| /// <code language="cs" region="GetMessagesAsync.FromMessage" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <param name="fromMessage">The starting message to get the messages from.</param> | |||
| /// <param name="dir">The direction of the messages to be gotten from.</param> | |||
| @@ -263,12 +254,8 @@ namespace Discord | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished. | |||
| /// <code lang="cs"> | |||
| /// using (messageChannel.EnterTypingState()) | |||
| /// { | |||
| /// await LongRunningAsync(); | |||
| /// } | |||
| /// </code> | |||
| /// <code language="cs" region="EnterTypingState" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Channels\IMessageChannel.Examples.cs" /> | |||
| /// </example> | |||
| /// <param name="options">The options to be used when sending the request.</param> | |||
| /// <returns> | |||
| @@ -40,7 +40,7 @@ namespace Discord | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example gets 250 messages from the channel and deletes them. | |||
| /// <code lang="cs"> | |||
| /// <code language="cs"> | |||
| /// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync(); | |||
| /// await textChannel.DeleteMessagesAsync(messages); | |||
| /// </code> | |||
| @@ -440,16 +440,8 @@ namespace Discord | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. | |||
| /// <code lang="cs"> | |||
| /// 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}."; | |||
| /// }); | |||
| /// </code> | |||
| /// <code language="cs" region="CreateTextChannelAsync" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Guilds\IGuild.Examples.cs"/> | |||
| /// </example> | |||
| /// <param name="name">The new name for the text channel.</param> | |||
| /// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param> | |||
| @@ -23,7 +23,8 @@ namespace Discord | |||
| /// <example> | |||
| /// 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. | |||
| /// <code source="../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs" region="GetAvatarUrl"/> | |||
| /// <code language="cs" region="GetAvatarUrl" | |||
| /// source="..\..\..\Discord.Net.Examples\Core\Entities\Users\IUser.Examples.cs"/> | |||
| /// </example> | |||
| /// <param name="format">The format to return.</param> | |||
| /// <param name="size">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 | |||
| /// <example> | |||
| /// The following example attempts to send a direct message to the target user and logs the incident should | |||
| /// it fail. | |||
| /// <code source="../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs" | |||
| /// region="GetOrCreateDMChannelAsync"/> | |||
| /// <code region="GetOrCreateDMChannelAsync" language="cs" | |||
| /// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/> | |||
| /// </example> | |||
| /// <param name="options">The options to be used when sending the request.</param> | |||
| /// <returns> | |||
| @@ -63,7 +63,7 @@ namespace Discord | |||
| /// Gets a generic channel. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code lang="cs" title="Example method"> | |||
| /// <code language="cs" title="Example method"> | |||
| /// var channel = await _client.GetChannelAsync(381889909113225237); | |||
| /// if (channel != null && channel is IMessageChannel msgChannel) | |||
| /// { | |||
| @@ -194,7 +194,7 @@ namespace Discord | |||
| /// Gets a user. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code lang="cs" title="Example method"> | |||
| /// <code language="cs" title="Example method"> | |||
| /// 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. | |||
| /// </summary> | |||
| /// <example> | |||
| /// <code lang="cs" title="Example method"> | |||
| /// <code language="cs" title="Example method"> | |||
| /// var user = await _client.GetUserAsync("Still", "2876"); | |||
| /// if (user != null) | |||
| /// Console.WriteLine($"{user} is created at {user.CreatedAt}."; | |||
| @@ -232,7 +232,7 @@ namespace Discord | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example gets the most optimal voice region from the collection. | |||
| /// <code lang="cs"> | |||
| /// <code language="cs"> | |||
| /// var regions = await client.GetVoiceRegionsAsync(); | |||
| /// var optimalRegion = regions.FirstOrDefault(x => x.IsOptimal); | |||
| /// </code> | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,21 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | |||
| <PropertyGroup> | |||
| <TargetFramework>netstandard2.0</TargetFramework> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Compile Remove="Core\Entities\Guilds\IGuild.Examples.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="Core\Entities\Guilds\IGuild.Examples.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | |||
| <ProjectReference Include="..\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" /> | |||
| <PackageReference Include="JetBrains.Annotations" Version="2018.3.0" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -1,90 +1,117 @@ | |||
| #region ReactionAdded | |||
| public void HookReactionAdded(BaseSocketClient client) | |||
| { | |||
| client.ReactionAdded += HandleReactionAddedAsync; | |||
| } | |||
| public async Task HandleReactionAddedAsync(Cacheable<IUserMessage, ulong> 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<IUserMessage, ulong> 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<IMessage, ulong> 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<IMessage, ulong> 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 | |||
| @@ -405,7 +405,7 @@ namespace Discord.Rest | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. | |||
| /// <code lang="cs"> | |||
| /// <code language="cs"> | |||
| /// var categories = await guild.GetCategoriesAsync(); | |||
| /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); | |||
| /// if (targetCategory == null) return; | |||
| @@ -19,7 +19,8 @@ namespace Discord.WebSocket | |||
| /// </para> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="ChannelCreated"></code> | |||
| /// <code language="cs" region="ChannelCreated" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/> | |||
| /// </example> | |||
| public event Func<SocketChannel, Task> ChannelCreated | |||
| { | |||
| @@ -40,7 +41,8 @@ namespace Discord.WebSocket | |||
| /// </para> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="ChannelDestroyed"></code> | |||
| /// <code language="cs" region="ChannelDestroyed" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/> | |||
| /// </example> | |||
| public event Func<SocketChannel, Task> ChannelDestroyed { | |||
| add { _channelDestroyedEvent.Add(value); } | |||
| @@ -61,7 +63,8 @@ namespace Discord.WebSocket | |||
| /// </para> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="ChannelUpdated"></code> | |||
| /// <code language="cs" region="ChannelUpdated" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/> | |||
| /// </example> | |||
| public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated { | |||
| add { _channelUpdatedEvent.Add(value); } | |||
| @@ -84,8 +87,9 @@ namespace Discord.WebSocket | |||
| /// </para> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// The example below checks if the newly received message contains the target user. | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="MessageReceived"></code> | |||
| /// <para>The example below checks if the newly received message contains the target user.</para> | |||
| /// <code language="cs" region="MessageReceived" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/> | |||
| /// </example> | |||
| public event Func<SocketMessage, Task> MessageReceived { | |||
| add { _messageReceivedEvent.Add(value); } | |||
| @@ -116,7 +120,8 @@ namespace Discord.WebSocket | |||
| /// </para> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="MessageDeleted"></code> | |||
| /// <code language="cs" region="MessageDeleted" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" /> | |||
| /// </example> | |||
| public event Func<Cacheable<IMessage, ulong>, ISocketMessageChannel, Task> MessageDeleted { | |||
| add { _messageDeletedEvent.Add(value); } | |||
| @@ -176,7 +181,8 @@ namespace Discord.WebSocket | |||
| /// </note> | |||
| /// </remarks> | |||
| /// <example> | |||
| /// <code source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs" region="ReactionAdded"></code> | |||
| /// <code language="cs" region="ReactionAdded" | |||
| /// source="..\Discord.Net.Examples\WebSocket\BaseSocketClient.Events.Examples.cs"/> | |||
| /// </example> | |||
| public event Func<Cacheable<IUserMessage, ulong>, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { | |||
| add { _reactionAddedEvent.Add(value); } | |||
| @@ -97,15 +97,15 @@ namespace Discord.WebSocket | |||
| /// <remarks> | |||
| /// This method gets the user present in the WebSocket cache with the given condition. | |||
| /// <note type="warning"> | |||
| /// Sometimes a user may return <c>null</c> 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 <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>. | |||
| /// Sometimes a user may return <c>null</c> 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 <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />. | |||
| /// </note> | |||
| /// <note> | |||
| /// 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 | |||
| /// <see cref="DiscordSocketRestClient.GetUserAsync"/>. | |||
| /// 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 | |||
| /// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />. | |||
| /// </note> | |||
| /// </remarks> | |||
| /// <returns> | |||
| @@ -116,22 +116,22 @@ namespace Discord.WebSocket | |||
| /// <summary> | |||
| /// Gets a user. | |||
| /// </summary> | |||
| /// <param name="username">The name of the user.</param> | |||
| /// <param name="discriminator">The discriminator value of the user.</param> | |||
| /// <remarks> | |||
| /// This method gets the user present in the WebSocket cache with the given condition. | |||
| /// <note type="warning"> | |||
| /// Sometimes a user may return <c>null</c> 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 <see cref="DiscordSocketConfig.AlwaysDownloadUsers"/>. | |||
| /// Sometimes a user may return <c>null</c> 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 <see cref="Discord.WebSocket.DiscordSocketConfig.AlwaysDownloadUsers" />. | |||
| /// </note> | |||
| /// <note> | |||
| /// 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 | |||
| /// <see cref="DiscordSocketRestClient.GetUserAsync"/>. | |||
| /// 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 | |||
| /// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)" />. | |||
| /// </note> | |||
| /// </remarks> | |||
| /// <param name="username">The name of the user.</param> | |||
| /// <param name="discriminator">The discriminator value of the user.</param> | |||
| /// <returns> | |||
| /// A generic WebSocket-based user; <c>null</c> when the user cannot be found. | |||
| /// </returns> | |||
| @@ -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 <see cref="DiscordSocketRestClient.GetUserAsync"/> | |||
| /// or <see cref="DiscordSocketRestClient.GetGuildUserAsync"/> as a backup. | |||
| /// overriding the TypeReader and use | |||
| /// <see cref="DiscordRestClient.GetUserAsync(System.UInt64,Discord.RequestOptions)"/> | |||
| /// or <see cref="DiscordSocketRestClient.GetGuildUserAsync(ulong, ulong, RequestOptions)"/> as a backup. | |||
| /// </note> | |||
| /// </remarks> | |||
| public bool AlwaysDownloadUsers { get; set; } = false; | |||
| @@ -535,7 +535,7 @@ namespace Discord.WebSocket | |||
| /// </summary> | |||
| /// <example> | |||
| /// The following example creates a new text channel under an existing category named <c>Wumpus</c> with a set topic. | |||
| /// <code lang="cs"> | |||
| /// <code language="cs"> | |||
| /// var categories = await guild.GetCategoriesAsync(); | |||
| /// var targetCategory = categories.FirstOrDefault(x => x.Name == "wumpus"); | |||
| /// if (targetCategory == null) return; | |||