| @@ -1,5 +1,5 @@ | |||||
| steps: | steps: | ||||
| - script: dotnet restore -v minimal Discord.Net.sln | |||||
| - script: dotnet restore --no-cache Discord.Net.sln | |||||
| displayName: Restore packages | displayName: Restore packages | ||||
| - script: dotnet build "Discord.Net.sln" --no-restore -v minimal -c $(buildConfiguration) /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag) | - script: dotnet build "Discord.Net.sln" --no-restore -v minimal -c $(buildConfiguration) /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag) | ||||
| @@ -135,7 +135,8 @@ namespace Discord.Commands | |||||
| if (builder.Name == null) | if (builder.Name == null) | ||||
| builder.Name = typeInfo.Name; | builder.Name = typeInfo.Name; | ||||
| var validCommands = typeInfo.DeclaredMethods.Where(IsValidCommandDefinition); | |||||
| // Get all methods (including from inherited members), that are valid commands | |||||
| var validCommands = typeInfo.GetMethods().Where(IsValidCommandDefinition); | |||||
| foreach (var method in validCommands) | foreach (var method in validCommands) | ||||
| { | { | ||||
| @@ -59,11 +59,15 @@ namespace Discord | |||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | ||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -88,11 +92,15 @@ namespace Discord | |||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | ||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a message from this message channel. | /// Gets a message from this message channel. | ||||
| @@ -8,17 +8,27 @@ namespace Discord | |||||
| [Flags] | [Flags] | ||||
| public enum AllowedMentionTypes | public enum AllowedMentionTypes | ||||
| { | { | ||||
| /// <summary> | |||||
| /// No flag is set. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This flag is not used to control mentions. | |||||
| /// <note type="warning"> | |||||
| /// It will always be present and does not mean mentions will not be allowed. | |||||
| /// </note> | |||||
| /// </remarks> | |||||
| None = 0, | |||||
| /// <summary> | /// <summary> | ||||
| /// Controls role mentions. | /// Controls role mentions. | ||||
| /// </summary> | /// </summary> | ||||
| Roles, | |||||
| Roles = 1, | |||||
| /// <summary> | /// <summary> | ||||
| /// Controls user mentions. | /// Controls user mentions. | ||||
| /// </summary> | /// </summary> | ||||
| Users, | |||||
| Users = 2, | |||||
| /// <summary> | /// <summary> | ||||
| /// Controls <code>@everyone</code> and <code>@here</code> mentions. | /// Controls <code>@everyone</code> and <code>@here</code> mentions. | ||||
| /// </summary> | /// </summary> | ||||
| Everyone, | |||||
| Everyone = 4, | |||||
| } | } | ||||
| } | } | ||||
| @@ -39,7 +39,7 @@ namespace Discord | |||||
| /// flag of the <see cref="AllowedTypes"/> property. If the flag is set, the value of this property | /// flag of the <see cref="AllowedTypes"/> property. If the flag is set, the value of this property | ||||
| /// must be <c>null</c> or empty. | /// must be <c>null</c> or empty. | ||||
| /// </summary> | /// </summary> | ||||
| public List<ulong> RoleIds { get; set; } | |||||
| public List<ulong> RoleIds { get; set; } = new List<ulong>(); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the list of all user ids that will be mentioned. | /// Gets or sets the list of all user ids that will be mentioned. | ||||
| @@ -47,7 +47,7 @@ namespace Discord | |||||
| /// flag of the <see cref="AllowedTypes"/> property. If the flag is set, the value of this property | /// flag of the <see cref="AllowedTypes"/> property. If the flag is set, the value of this property | ||||
| /// must be <c>null</c> or empty. | /// must be <c>null</c> or empty. | ||||
| /// </summary> | /// </summary> | ||||
| public List<ulong> UserIds { get; set; } | |||||
| public List<ulong> UserIds { get; set; } = new List<ulong>(); | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a new instance of the <see cref="AllowedMentions"/> class. | /// Initializes a new instance of the <see cref="AllowedMentions"/> class. | ||||
| @@ -73,7 +73,7 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| /// <example> | /// <example> | ||||
| /// <para>The following example checks if the current user has the ability to send a message with attachment in | /// <para>The following example checks if the current user has the ability to send a message with attachment in | ||||
| /// this channel; if so, uploads a file via <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool)"/>.</para> | |||||
| /// this channel; if so, uploads a file via <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool, AllowedMentions)"/>.</para> | |||||
| /// <code language="cs"> | /// <code language="cs"> | ||||
| /// if (currentUser?.GetPermissions(targetChannel)?.AttachFiles) | /// if (currentUser?.GetPermissions(targetChannel)?.AttachFiles) | ||||
| /// await targetChannel.SendFileAsync("fortnite.png"); | /// await targetChannel.SendFileAsync("fortnite.png"); | ||||
| @@ -0,0 +1,41 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| [Flags] | |||||
| public enum GatewayIntents | |||||
| { | |||||
| /// <summary> This intent includes no events </summary> | |||||
| None = 0, | |||||
| /// <summary> This intent includes GUILD_CREATE, GUILD_UPDATE, GUILD_DELETE, GUILD_ROLE_CREATE, GUILD_ROLE_UPDATE, GUILD_ROLE_DELETE, CHANNEL_CREATE, CHANNEL_UPDATE, CHANNEL_DELETE, CHANNEL_PINS_UPDATE </summary> | |||||
| Guilds = 1 << 0, | |||||
| /// <summary> This intent includes GUILD_MEMBER_ADD, GUILD_MEMBER_UPDATE, GUILD_MEMBER_REMOVE </summary> | |||||
| GuildMembers = 1 << 1, | |||||
| /// <summary> This intent includes GUILD_BAN_ADD, GUILD_BAN_REMOVE </summary> | |||||
| GuildBans = 1 << 2, | |||||
| /// <summary> This intent includes GUILD_EMOJIS_UPDATE </summary> | |||||
| GuildEmojis = 1 << 3, | |||||
| /// <summary> This intent includes GUILD_INTEGRATIONS_UPDATE </summary> | |||||
| GuildIntegrations = 1 << 4, | |||||
| /// <summary> This intent includes WEBHOOKS_UPDATE </summary> | |||||
| GuildWebhooks = 1 << 5, | |||||
| /// <summary> This intent includes INVITE_CREATE, INVITE_DELETE </summary> | |||||
| GuildInvites = 1 << 6, | |||||
| /// <summary> This intent includes VOICE_STATE_UPDATE </summary> | |||||
| GuildVoiceStates = 1 << 7, | |||||
| /// <summary> This intent includes PRESENCE_UPDATE </summary> | |||||
| GuildPresences = 1 << 8, | |||||
| /// <summary> This intent includes MESSAGE_CREATE, MESSAGE_UPDATE, MESSAGE_DELETE, MESSAGE_DELETE_BULK </summary> | |||||
| GuildMessages = 1 << 9, | |||||
| /// <summary> This intent includes MESSAGE_REACTION_ADD, MESSAGE_REACTION_REMOVE, MESSAGE_REACTION_REMOVE_ALL, MESSAGE_REACTION_REMOVE_EMOJI </summary> | |||||
| GuildMessageReactions = 1 << 10, | |||||
| /// <summary> This intent includes TYPING_START </summary> | |||||
| GuildMessageTyping = 1 << 11, | |||||
| /// <summary> This intent includes CHANNEL_CREATE, MESSAGE_CREATE, MESSAGE_UPDATE, MESSAGE_DELETE, CHANNEL_PINS_UPDATE </summary> | |||||
| DirectMessages = 1 << 12, | |||||
| /// <summary> This intent includes MESSAGE_REACTION_ADD, MESSAGE_REACTION_REMOVE, MESSAGE_REACTION_REMOVE_ALL, MESSAGE_REACTION_REMOVE_EMOJI </summary> | |||||
| DirectMessageReactions = 1 << 13, | |||||
| /// <summary> This intent includes TYPING_START </summary> | |||||
| DirectMessageTyping = 1 << 14, | |||||
| } | |||||
| } | |||||
| @@ -19,6 +19,7 @@ namespace Discord.API.Rest | |||||
| public Optional<string> Nonce { get; set; } | public Optional<string> Nonce { get; set; } | ||||
| public Optional<bool> IsTTS { get; set; } | public Optional<bool> IsTTS { get; set; } | ||||
| public Optional<Embed> Embed { get; set; } | public Optional<Embed> Embed { get; set; } | ||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||||
| public bool IsSpoiler { get; set; } = false; | public bool IsSpoiler { get; set; } = false; | ||||
| public UploadFileParams(Stream file) | public UploadFileParams(Stream file) | ||||
| @@ -43,6 +44,8 @@ namespace Discord.API.Rest | |||||
| payload["nonce"] = Nonce.Value; | payload["nonce"] = Nonce.Value; | ||||
| if (Embed.IsSpecified) | if (Embed.IsSpecified) | ||||
| payload["embed"] = Embed.Value; | payload["embed"] = Embed.Value; | ||||
| if (AllowedMentions.IsSpecified) | |||||
| payload["allowed_mentions"] = AllowedMentions.Value; | |||||
| if (IsSpoiler) | if (IsSpoiler) | ||||
| payload["hasSpoiler"] = IsSpoiler.ToString(); | payload["hasSpoiler"] = IsSpoiler.ToString(); | ||||
| @@ -109,12 +109,19 @@ namespace Discord.Rest | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, | public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, | ||||
| ulong? fromMessageId, Direction dir, int limit, RequestOptions options) | ulong? fromMessageId, Direction dir, int limit, RequestOptions options) | ||||
| { | { | ||||
| if (dir == Direction.Around) | |||||
| throw new NotImplementedException(); //TODO: Impl | |||||
| var guildId = (channel as IGuildChannel)?.GuildId; | var guildId = (channel as IGuildChannel)?.GuildId; | ||||
| var guild = guildId != null ? (client as IDiscordClient).GetGuildAsync(guildId.Value, CacheMode.CacheOnly).Result : null; | var guild = guildId != null ? (client as IDiscordClient).GetGuildAsync(guildId.Value, CacheMode.CacheOnly).Result : null; | ||||
| if (dir == Direction.Around && limit > DiscordConfig.MaxMessagesPerBatch) | |||||
| { | |||||
| int around = limit / 2; | |||||
| if (fromMessageId.HasValue) | |||||
| return GetMessagesAsync(channel, client, fromMessageId.Value + 1, Direction.Before, around + 1, options) //Need to include the message itself | |||||
| .Concat(GetMessagesAsync(channel, client, fromMessageId, Direction.After, around, options)); | |||||
| else //Shouldn't happen since there's no public overload for ulong? and Direction | |||||
| return GetMessagesAsync(channel, client, null, Direction.Before, around + 1, options); | |||||
| } | |||||
| return new PagedAsyncEnumerable<RestMessage>( | return new PagedAsyncEnumerable<RestMessage>( | ||||
| DiscordConfig.MaxMessagesPerBatch, | DiscordConfig.MaxMessagesPerBatch, | ||||
| async (info, ct) => | async (info, ct) => | ||||
| @@ -218,18 +225,37 @@ namespace Discord.Rest | |||||
| /// <exception cref="IOException">An I/O error occurred while opening the file.</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> | /// <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, bool isSpoiler) | |||||
| string filePath, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler) | |||||
| { | { | ||||
| string filename = Path.GetFileName(filePath); | string filename = Path.GetFileName(filePath); | ||||
| using (var file = File.OpenRead(filePath)) | using (var file = File.OpenRead(filePath)) | ||||
| return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, allowedMentions, options, isSpoiler).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</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, | ||||
| Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| Stream stream, string filename, string text, bool isTTS, Embed embed, AllowedMentions allowedMentions, RequestOptions options, bool isSpoiler) | |||||
| { | { | ||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed != null ? embed.ToModel() : Optional<API.Embed>.Unspecified, IsSpoiler = isSpoiler }; | |||||
| Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed."); | |||||
| Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed."); | |||||
| // check that user flag and user Id list are exclusive, same with role flag and role Id list | |||||
| if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue) | |||||
| { | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) && | |||||
| allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) && | |||||
| allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0) | |||||
| { | |||||
| throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions)); | |||||
| } | |||||
| } | |||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional<API.Embed>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, IsSpoiler = isSpoiler }; | |||||
| var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
| } | } | ||||
| @@ -34,7 +34,7 @@ namespace Discord.Rest | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method follows the same behavior as described in | /// This method follows the same behavior as described in | ||||
| /// <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool)"/>. Please visit | |||||
| /// <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool, AllowedMentions)"/>. Please visit | |||||
| /// its documentation for more details on this method. | /// its documentation for more details on this method. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <param name="filePath">The file path of the file.</param> | /// <param name="filePath">The file path of the file.</param> | ||||
| @@ -42,16 +42,21 @@ namespace Discord.Rest | |||||
| /// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param> | /// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param> | ||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | |||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions, bool)"/>. | |||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions, bool, AllowedMentions)"/>. | |||||
| /// Please visit its documentation for more details on this method. | /// Please visit its documentation for more details on this method. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param> | /// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param> | ||||
| @@ -60,11 +65,16 @@ namespace Discord.Rest | |||||
| /// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param> | /// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param> | ||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | |||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a message from this message channel. | /// Gets a message from this message channel. | ||||
| @@ -121,12 +121,12 @@ namespace Discord.Rest | |||||
| /// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</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="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> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | ||||
| @@ -200,11 +200,11 @@ namespace Discord.Rest | |||||
| 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); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -123,12 +123,12 @@ namespace Discord.Rest | |||||
| /// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</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="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> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| @@ -178,11 +178,11 @@ namespace Discord.Rest | |||||
| 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); | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -129,13 +129,13 @@ namespace Discord.Rest | |||||
| /// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</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="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> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | ||||
| @@ -266,12 +266,12 @@ namespace Discord.Rest | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -17,5 +17,7 @@ namespace Discord.API.Gateway | |||||
| public Optional<int[]> ShardingParams { get; set; } | public Optional<int[]> ShardingParams { get; set; } | ||||
| [JsonProperty("guild_subscriptions")] | [JsonProperty("guild_subscriptions")] | ||||
| public Optional<bool> GuildSubscriptions { get; set; } | public Optional<bool> GuildSubscriptions { get; set; } | ||||
| [JsonProperty("intents")] | |||||
| public Optional<int> Intents { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -209,7 +209,7 @@ namespace Discord.API | |||||
| await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, bool guildSubscriptions = true, RequestOptions options = null) | |||||
| public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, bool guildSubscriptions = true, GatewayIntents? gatewayIntents = null, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var props = new Dictionary<string, string> | var props = new Dictionary<string, string> | ||||
| @@ -220,12 +220,16 @@ namespace Discord.API | |||||
| { | { | ||||
| Token = AuthToken, | Token = AuthToken, | ||||
| Properties = props, | Properties = props, | ||||
| LargeThreshold = largeThreshold, | |||||
| GuildSubscriptions = guildSubscriptions | |||||
| LargeThreshold = largeThreshold | |||||
| }; | }; | ||||
| if (totalShards > 1) | if (totalShards > 1) | ||||
| msg.ShardingParams = new int[] { shardID, totalShards }; | msg.ShardingParams = new int[] { shardID, totalShards }; | ||||
| if (gatewayIntents.HasValue) | |||||
| msg.Intents = (int)gatewayIntents.Value; | |||||
| else | |||||
| msg.GuildSubscriptions = guildSubscriptions; | |||||
| await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) | public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) | ||||
| @@ -21,7 +21,13 @@ namespace Discord.WebSocket | |||||
| remove { _disconnectedEvent.Remove(value); } | remove { _disconnectedEvent.Remove(value); } | ||||
| } | } | ||||
| private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>(); | private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>(); | ||||
| /// <summary> Fired when guild data has finished downloading. </summary> | |||||
| /// <summary> | |||||
| /// Fired when guild data has finished downloading. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// It is possible that some guilds might be unsynced if <see cref="DiscordSocketConfig.MaxWaitBetweenGuildAvailablesBeforeReady" /> | |||||
| /// was not long enough to receive all GUILD_AVAILABLEs before READY. | |||||
| /// </remarks> | |||||
| public event Func<Task> Ready | public event Func<Task> Ready | ||||
| { | { | ||||
| add { _readyEvent.Add(value); } | add { _readyEvent.Add(value); } | ||||
| @@ -44,6 +44,7 @@ namespace Discord.WebSocket | |||||
| private RestApplication _applicationInfo; | private RestApplication _applicationInfo; | ||||
| private bool _isDisposed; | private bool _isDisposed; | ||||
| private bool _guildSubscriptions; | private bool _guildSubscriptions; | ||||
| private GatewayIntents? _gatewayIntents; | |||||
| /// <summary> | /// <summary> | ||||
| /// Provides access to a REST-only client with a shared state from this client. | /// Provides access to a REST-only client with a shared state from this client. | ||||
| @@ -137,6 +138,7 @@ namespace Discord.WebSocket | |||||
| Rest = new DiscordSocketRestClient(config, ApiClient); | Rest = new DiscordSocketRestClient(config, ApiClient); | ||||
| _heartbeatTimes = new ConcurrentQueue<long>(); | _heartbeatTimes = new ConcurrentQueue<long>(); | ||||
| _guildSubscriptions = config.GuildSubscriptions; | _guildSubscriptions = config.GuildSubscriptions; | ||||
| _gatewayIntents = config.GatewayIntents; | |||||
| _stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
| _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | ||||
| @@ -167,7 +169,7 @@ namespace Discord.WebSocket | |||||
| GuildAvailable += g => | GuildAvailable += g => | ||||
| { | { | ||||
| if (ConnectionState == ConnectionState.Connected && AlwaysDownloadUsers && !g.HasAllMembers) | |||||
| if (_guildDownloadTask?.IsCompleted == true && ConnectionState == ConnectionState.Connected && AlwaysDownloadUsers && !g.HasAllMembers) | |||||
| { | { | ||||
| var _ = g.DownloadUsersAsync(); | var _ = g.DownloadUsersAsync(); | ||||
| } | } | ||||
| @@ -242,7 +244,7 @@ namespace Discord.WebSocket | |||||
| else | else | ||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); | ||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions).ConfigureAwait(false); | |||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents).ConfigureAwait(false); | |||||
| } | } | ||||
| //Wait for READY | //Wait for READY | ||||
| @@ -368,7 +370,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var cachedGuilds = guilds.ToImmutableArray(); | var cachedGuilds = guilds.ToImmutableArray(); | ||||
| const short batchSize = 50; | |||||
| const short batchSize = 100; //TODO: Gateway Intents will limit to a maximum of 1 guild_id | |||||
| ulong[] batchIds = new ulong[Math.Min(batchSize, cachedGuilds.Length)]; | ulong[] batchIds = new ulong[Math.Min(batchSize, cachedGuilds.Length)]; | ||||
| Task[] batchTasks = new Task[batchIds.Length]; | Task[] batchTasks = new Task[batchIds.Length]; | ||||
| int batchCount = (cachedGuilds.Length + (batchSize - 1)) / batchSize; | int batchCount = (cachedGuilds.Length + (batchSize - 1)) / batchSize; | ||||
| @@ -376,7 +378,7 @@ namespace Discord.WebSocket | |||||
| for (int i = 0, k = 0; i < batchCount; i++) | for (int i = 0, k = 0; i < batchCount; i++) | ||||
| { | { | ||||
| bool isLast = i == batchCount - 1; | bool isLast = i == batchCount - 1; | ||||
| int count = isLast ? (batchIds.Length - (batchCount - 1) * batchSize) : batchSize; | |||||
| int count = isLast ? (cachedGuilds.Length - (batchCount - 1) * batchSize) : batchSize; | |||||
| for (int j = 0; j < count; j++, k++) | for (int j = 0; j < count; j++, k++) | ||||
| { | { | ||||
| @@ -517,7 +519,7 @@ namespace Discord.WebSocket | |||||
| _sessionId = null; | _sessionId = null; | ||||
| _lastSeq = 0; | _lastSeq = 0; | ||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); | |||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case GatewayOpCode.Reconnect: | case GatewayOpCode.Reconnect: | ||||
| @@ -576,6 +578,9 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| else if (_connection.CancelToken.IsCancellationRequested) | else if (_connection.CancelToken.IsCancellationRequested) | ||||
| return; | return; | ||||
| if (BaseConfig.AlwaysDownloadUsers) | |||||
| _ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers)); | |||||
| 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); | ||||
| @@ -1770,7 +1775,7 @@ namespace Discord.WebSocket | |||||
| try | try | ||||
| { | { | ||||
| await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false); | await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false); | ||||
| while ((_unavailableGuildCount != 0) && (Environment.TickCount - _lastGuildAvailableTime < 2000)) | |||||
| while ((_unavailableGuildCount != 0) && (Environment.TickCount - _lastGuildAvailableTime < BaseConfig.MaxWaitBetweenGuildAvailablesBeforeReady)) | |||||
| await Task.Delay(500, cancelToken).ConfigureAwait(false); | await Task.Delay(500, cancelToken).ConfigureAwait(false); | ||||
| await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false); | await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -121,9 +121,45 @@ namespace Discord.WebSocket | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets enabling dispatching of guild subscription events e.g. presence and typing events. | /// Gets or sets enabling dispatching of guild subscription events e.g. presence and typing events. | ||||
| /// This is not used if <see cref="GatewayIntents"/> are provided. | |||||
| /// </summary> | /// </summary> | ||||
| public bool GuildSubscriptions { get; set; } = true; | public bool GuildSubscriptions { get; set; } = true; | ||||
| /// <summary> | |||||
| /// Gets or sets the maximum wait time in milliseconds between GUILD_AVAILABLE events before firing READY. | |||||
| /// | |||||
| /// If zero, READY will fire as soon as it is received and all guilds will be unavailable. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// <para>This property is measured in milliseconds, negative values will throw an exception.</para> | |||||
| /// <para>If a guild is not received before READY, it will be unavailable.</para> | |||||
| /// </remarks> | |||||
| /// <returns> | |||||
| /// The maximum wait time in milliseconds between GUILD_AVAILABLE events before firing READY. | |||||
| /// </returns> | |||||
| /// <exception cref="System.ArgumentException">Value must be at least 0.</exception> | |||||
| public int MaxWaitBetweenGuildAvailablesBeforeReady { | |||||
| get | |||||
| { | |||||
| return _maxWaitForGuildAvailable; | |||||
| } | |||||
| set | |||||
| { | |||||
| Preconditions.AtLeast(value, 0, nameof(MaxWaitBetweenGuildAvailablesBeforeReady)); | |||||
| _maxWaitForGuildAvailable = value; | |||||
| } | |||||
| } | |||||
| private int _maxWaitForGuildAvailable = 10000; | |||||
| /// Gets or sets gateway intents to limit what events are sent from Discord. Allows for more granular control than the <see cref="GuildSubscriptions"/> property. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// For more information, please see | |||||
| /// <see href="https://discord.com/developers/docs/topics/gateway#gateway-intents">GatewayIntents</see> | |||||
| /// on the official Discord API documentation. | |||||
| /// </remarks> | |||||
| public GatewayIntents? GatewayIntents { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a default configuration. | /// Initializes a default configuration. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -42,7 +42,7 @@ namespace Discord.WebSocket | |||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool)"/>. | |||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions, bool, AllowedMentions)"/>. | |||||
| /// Please visit its documentation for more details on this method. | /// Please visit its documentation for more details on this method. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <param name="filePath">The file path of the file.</param> | /// <param name="filePath">The file path of the file.</param> | ||||
| @@ -51,16 +51,20 @@ namespace Discord.WebSocket | |||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | ||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Sends a file to this message channel with an optional caption. | /// Sends a file to this message channel with an optional caption. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions, bool)"/>. | |||||
| /// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions, bool, AllowedMentions)"/>. | |||||
| /// Please visit its documentation for more details on this method. | /// Please visit its documentation for more details on this method. | ||||
| /// </remarks> | /// </remarks> | ||||
| /// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param> | /// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param> | ||||
| @@ -70,11 +74,15 @@ namespace Discord.WebSocket | |||||
| /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | /// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</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> | ||||
| /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | /// <param name="isSpoiler">Whether the message attachment should be hidden as a spoiler.</param> | ||||
| /// <param name="allowedMentions"> | |||||
| /// Specifies if notifications are sent for mentioned users and roles in the message <paramref name="text"/>. | |||||
| /// If <c>null</c>, all mentioned roles and users will be notified. | |||||
| /// </param> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents an asynchronous send operation for delivering the message. The task result | /// A task that represents an asynchronous send operation for delivering the message. The task result | ||||
| /// contains the sent message. | /// contains the sent message. | ||||
| /// </returns> | /// </returns> | ||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false); | |||||
| new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a cached message from this channel. | /// Gets a cached message from this channel. | ||||
| @@ -11,23 +11,11 @@ namespace Discord.WebSocket | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ISocketMessageChannel channel, DiscordSocketClient discord, MessageCache messages, | public static IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ISocketMessageChannel channel, DiscordSocketClient discord, MessageCache messages, | ||||
| ulong? fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ulong? fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (dir == Direction.Around) | |||||
| throw new NotImplementedException(); //TODO: Impl | |||||
| IReadOnlyCollection<SocketMessage> cachedMessages = null; | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> result = null; | |||||
| if (dir == Direction.After && fromMessageId == null) | if (dir == Direction.After && fromMessageId == null) | ||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | ||||
| if (dir == Direction.Before || mode == CacheMode.CacheOnly) | |||||
| { | |||||
| if (messages != null) //Cache enabled | |||||
| cachedMessages = messages.GetMany(fromMessageId, dir, limit); | |||||
| else | |||||
| cachedMessages = ImmutableArray.Create<SocketMessage>(); | |||||
| result = ImmutableArray.Create(cachedMessages).ToAsyncEnumerable<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| var cachedMessages = GetCachedMessages(channel, discord, messages, fromMessageId, dir, limit); | |||||
| var result = ImmutableArray.Create(cachedMessages).ToAsyncEnumerable<IReadOnlyCollection<IMessage>>(); | |||||
| if (dir == Direction.Before) | if (dir == Direction.Before) | ||||
| { | { | ||||
| @@ -38,18 +26,35 @@ namespace Discord.WebSocket | |||||
| //Download remaining messages | //Download remaining messages | ||||
| ulong? minId = cachedMessages.Count > 0 ? cachedMessages.Min(x => x.Id) : fromMessageId; | ulong? minId = cachedMessages.Count > 0 ? cachedMessages.Min(x => x.Id) : fromMessageId; | ||||
| var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, minId, dir, limit, options); | var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, minId, dir, limit, options); | ||||
| return result.Concat(downloadedMessages); | |||||
| if (cachedMessages.Count != 0) | |||||
| return result.Concat(downloadedMessages); | |||||
| else | |||||
| return downloadedMessages; | |||||
| } | } | ||||
| else | |||||
| else if (dir == Direction.After) | |||||
| { | |||||
| limit -= cachedMessages.Count; | |||||
| if (mode == CacheMode.CacheOnly || limit <= 0) | |||||
| return result; | |||||
| //Download remaining messages | |||||
| ulong maxId = cachedMessages.Count > 0 ? cachedMessages.Max(x => x.Id) : fromMessageId.Value; | |||||
| var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, maxId, dir, limit, options); | |||||
| if (cachedMessages.Count != 0) | |||||
| return result.Concat(downloadedMessages); | |||||
| else | |||||
| return downloadedMessages; | |||||
| } | |||||
| else //Direction.Around | |||||
| { | { | ||||
| if (mode == CacheMode.CacheOnly) | |||||
| if (mode == CacheMode.CacheOnly || limit <= cachedMessages.Count) | |||||
| return result; | return result; | ||||
| //Dont use cache in this case | |||||
| //Cache isn't useful here since Discord will send them anyways | |||||
| return ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit, options); | return ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit, options); | ||||
| } | } | ||||
| } | } | ||||
| public static IReadOnlyCollection<SocketMessage> GetCachedMessages(SocketChannel channel, DiscordSocketClient discord, MessageCache messages, | |||||
| public static IReadOnlyCollection<SocketMessage> GetCachedMessages(ISocketMessageChannel channel, DiscordSocketClient discord, MessageCache messages, | |||||
| ulong? fromMessageId, Direction dir, int limit) | ulong? fromMessageId, Direction dir, int limit) | ||||
| { | { | ||||
| if (messages != null) //Cache enabled | if (messages != null) //Cache enabled | ||||
| @@ -139,12 +139,12 @@ namespace Discord.WebSocket | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); | => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); | ||||
| @@ -229,11 +229,11 @@ namespace Discord.WebSocket | |||||
| 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); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -167,11 +167,11 @@ namespace Discord.WebSocket | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | ||||
| @@ -293,11 +293,11 @@ namespace Discord.WebSocket | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -165,13 +165,13 @@ namespace Discord.WebSocket | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options, isSpoiler); | |||||
| public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, options, isSpoiler); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) | ||||
| @@ -302,11 +302,11 @@ namespace Discord.WebSocket | |||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler).ConfigureAwait(false); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions) | ||||
| => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | => await SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | ||||
| @@ -56,11 +56,23 @@ namespace Discord.WebSocket | |||||
| cachedMessageIds = _orderedMessages; | cachedMessageIds = _orderedMessages; | ||||
| else if (dir == Direction.Before) | else if (dir == Direction.Before) | ||||
| cachedMessageIds = _orderedMessages.Where(x => x < fromMessageId.Value); | cachedMessageIds = _orderedMessages.Where(x => x < fromMessageId.Value); | ||||
| else | |||||
| else if (dir == Direction.After) | |||||
| cachedMessageIds = _orderedMessages.Where(x => x > fromMessageId.Value); | cachedMessageIds = _orderedMessages.Where(x => x > fromMessageId.Value); | ||||
| else //Direction.Around | |||||
| { | |||||
| if (!_messages.TryGetValue(fromMessageId.Value, out SocketMessage msg)) | |||||
| return ImmutableArray<SocketMessage>.Empty; | |||||
| int around = limit / 2; | |||||
| var before = GetMany(fromMessageId, Direction.Before, around); | |||||
| var after = GetMany(fromMessageId, Direction.After, around).Reverse(); | |||||
| return after.Concat(new SocketMessage[] { msg }).Concat(before).ToImmutableArray(); | |||||
| } | |||||
| if (dir == Direction.Before) | if (dir == Direction.Before) | ||||
| cachedMessageIds = cachedMessageIds.Reverse(); | cachedMessageIds = cachedMessageIds.Reverse(); | ||||
| if (dir == Direction.Around) //Only happens if fromMessageId is null, should only get "around" and itself (+1) | |||||
| limit = limit / 2 + 1; | |||||
| return cachedMessageIds | return cachedMessageIds | ||||
| .Select(x => | .Select(x => | ||||
| @@ -73,12 +73,12 @@ namespace Discord | |||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| @@ -81,12 +81,12 @@ namespace Discord | |||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| @@ -167,12 +167,12 @@ namespace Discord | |||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false) | |||||
| public Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null) | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||