| @@ -1,5 +1,36 @@ | |||||
| # Changelog | # Changelog | ||||
| ## [2.4.0] - 2021-05-22 | |||||
| ### Added | |||||
| - #1726 Add stickers (91a9063) | |||||
| - #1753 Webhook message edit & delete functionality (f67cd8e) | |||||
| - #1757 Add ability to add/remove roles by id (4c9910c) | |||||
| - #1781 Add GetEmotesAsync to IGuild (df23d57) | |||||
| - #1801 Add missing property to MESSAGE_REACTION_ADD event (0715d7d) | |||||
| - #1828 Add methods to interact with reactions without a message object (5b244f2) | |||||
| - #1830 Add ModifyMessageAsync to IMessageChannel (365a848) | |||||
| - #1844 Add Discord Certified Moderator user flag (4b8d444) | |||||
| ### Fixed | |||||
| - #1486 Add type reader when entity type reader exists (c46daaa) | |||||
| - #1835 Cached message emoji cleanup at MESSAGE_REACTION_REMOVE_EMOJI (8afef82) | |||||
| ### Misc | |||||
| - #1778 Remove URI check from EmbedBuilder (25b04c4) | |||||
| - #1800 Fix spelling in SnowflakeUtils.FromSnowflake (6aff419) | |||||
| ## [2.3.1] - 2021-03-10 | |||||
| ### Fixed | |||||
| - #1761 Deadlock in DiscordShardedClient when Ready is never received (73e5cc2) | |||||
| - #1773 Private methods aren't added as commands (0fc713a) | |||||
| - #1780 NullReferenceException in pin/unpin audit logs (f794163) | |||||
| - #1786 Add ChannelType property to ChannelInfo audit log (6ac5ea1) | |||||
| - #1791 Update Webhook ChannelId from model change (d2518db) | |||||
| - #1794 Audit log UserId can be null (d41aeee) | |||||
| ### Misc | |||||
| - #1774 Add remark regarding CustomStatus as the activity (51b7afe) | |||||
| ## [2.3.0] - 2021-01-28 | ## [2.3.0] - 2021-01-28 | ||||
| ### Added | ### Added | ||||
| - #1491 Add INVITE_CREATE and INVITE_DELETE events (1ab670b) | - #1491 Add INVITE_CREATE and INVITE_DELETE events (1ab670b) | ||||
| @@ -1,6 +1,6 @@ | |||||
| <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
| <PropertyGroup> | <PropertyGroup> | ||||
| <VersionPrefix>2.3.1</VersionPrefix> | |||||
| <VersionPrefix>3.0.0</VersionPrefix> | |||||
| <VersionSuffix>dev</VersionSuffix> | <VersionSuffix>dev</VersionSuffix> | ||||
| <LangVersion>latest</LangVersion> | <LangVersion>latest</LangVersion> | ||||
| <Authors>Discord.Net Contributors</Authors> | <Authors>Discord.Net Contributors</Authors> | ||||
| @@ -8,8 +8,6 @@ An unofficial .NET API Wrapper for the Discord client (https://discord.com). | |||||
| ## Documentation | ## Documentation | ||||
| - [Stable](https://discord.foxbot.me/) | |||||
| - Hosted by @foxbot | |||||
| - [Nightly](https://docs.stillu.cc/) | - [Nightly](https://docs.stillu.cc/) | ||||
| - [Latest CI repo](https://github.com/discord-net/docs-static) | - [Latest CI repo](https://github.com/discord-net/docs-static) | ||||
| @@ -28,7 +28,7 @@ public async Task SendRichEmbedAsync() | |||||
| var embed = new EmbedBuilder | var embed = new EmbedBuilder | ||||
| { | { | ||||
| // Embed property can be set within object initializer | // Embed property can be set within object initializer | ||||
| Title = "Hello world!" | |||||
| Title = "Hello world!", | |||||
| Description = "I am a description set by initializer." | Description = "I am a description set by initializer." | ||||
| }; | }; | ||||
| // Or with methods | // Or with methods | ||||
| @@ -134,7 +134,7 @@ If, for whatever reason, you have two commands which are ambiguous to | |||||
| each other, you may use the @Discord.Commands.PriorityAttribute to | each other, you may use the @Discord.Commands.PriorityAttribute to | ||||
| specify which should be tested before the other. | specify which should be tested before the other. | ||||
| The `Priority` attributes are sorted in ascending order; the higher | |||||
| The `Priority` attributes are sorted in descending order; the higher | |||||
| priority will be called first. | priority will be called first. | ||||
| ### Command Context | ### Command Context | ||||
| @@ -9,7 +9,7 @@ namespace _03_sharded_client.Modules | |||||
| [Command("info")] | [Command("info")] | ||||
| public async Task InfoAsync() | public async Task InfoAsync() | ||||
| { | { | ||||
| var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards} shards! | |||||
| var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards! | |||||
| This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}"; | This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}"; | ||||
| await ReplyAsync(msg); | await ReplyAsync(msg); | ||||
| } | } | ||||
| @@ -136,7 +136,7 @@ namespace Discord.Commands | |||||
| builder.Name = typeInfo.Name; | builder.Name = typeInfo.Name; | ||||
| // Get all methods (including from inherited members), that are valid commands | // Get all methods (including from inherited members), that are valid commands | ||||
| var validCommands = typeInfo.GetMethods().Where(IsValidCommandDefinition); | |||||
| var validCommands = typeInfo.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsValidCommandDefinition); | |||||
| foreach (var method in validCommands) | foreach (var method in validCommands) | ||||
| { | { | ||||
| @@ -408,7 +408,7 @@ namespace Discord.Commands | |||||
| var typeInfo = type.GetTypeInfo(); | var typeInfo = type.GetTypeInfo(); | ||||
| if (typeInfo.IsEnum) | if (typeInfo.IsEnum) | ||||
| return true; | return true; | ||||
| return _entityTypeReaders.Any(x => type == x.EntityType || typeInfo.ImplementedInterfaces.Contains(x.TypeReaderType)); | |||||
| return _entityTypeReaders.Any(x => type == x.EntityType || typeInfo.ImplementedInterfaces.Contains(x.EntityType)); | |||||
| } | } | ||||
| internal void AddNullableTypeReader(Type valueType, TypeReader valueTypeReader) | internal void AddNullableTypeReader(Type valueType, TypeReader valueTypeReader) | ||||
| { | { | ||||
| @@ -511,7 +511,7 @@ namespace Discord.Commands | |||||
| await _commandExecutedEvent.InvokeAsync(Optional.Create<CommandInfo>(), context, searchResult).ConfigureAwait(false); | await _commandExecutedEvent.InvokeAsync(Optional.Create<CommandInfo>(), context, searchResult).ConfigureAwait(false); | ||||
| return searchResult; | return searchResult; | ||||
| } | } | ||||
| var commands = searchResult.Commands; | var commands = searchResult.Commands; | ||||
| var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>(); | var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>(); | ||||
| @@ -16,7 +16,7 @@ namespace Discord | |||||
| /// <see href="https://discord.com/developers/docs/reference#api-versioning">Discord API documentation</see> | /// <see href="https://discord.com/developers/docs/reference#api-versioning">Discord API documentation</see> | ||||
| /// .</para> | /// .</para> | ||||
| /// </returns> | /// </returns> | ||||
| public const int APIVersion = 6; | |||||
| public const int APIVersion = 9; | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns the Voice API version Discord.Net uses. | /// Returns the Voice API version Discord.Net uses. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -43,7 +43,7 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// The user agent used in each Discord.Net request. | /// The user agent used in each Discord.Net request. | ||||
| /// </returns> | /// </returns> | ||||
| public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; | |||||
| public static string UserAgent { get; } = $"DiscordBot (https://github.com/discord-net/Discord.Net, v{Version})"; | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns the base Discord API URL. | /// Returns the base Discord API URL. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -141,18 +141,6 @@ namespace Discord | |||||
| /// </remarks> | /// </remarks> | ||||
| internal bool DisplayInitialLog { get; set; } = true; | internal bool DisplayInitialLog { get; set; } = true; | ||||
| /// <summary> | |||||
| /// Gets or sets the level of precision of the rate limit reset response. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// If set to <see cref="RateLimitPrecision.Second"/>, this value will be rounded up to the | |||||
| /// nearest second. | |||||
| /// </remarks> | |||||
| /// <returns> | |||||
| /// The currently set <see cref="RateLimitPrecision"/>. | |||||
| /// </returns> | |||||
| public RateLimitPrecision RateLimitPrecision { get; set; } = RateLimitPrecision.Millisecond; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets whether or not rate-limits should use the system clock. | /// Gets or sets whether or not rate-limits should use the system clock. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -257,6 +257,21 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task DeleteMessageAsync(IMessage message, RequestOptions options = null); | Task DeleteMessageAsync(IMessage message, RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Modifies a message. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method modifies this message with the specified properties. To see an example of this | |||||
| /// method and what properties are available, please refer to <see cref="MessageProperties"/>. | |||||
| /// </remarks> | |||||
| /// <param name="messageId">The snowflake identifier of the message that would be changed.</param> | |||||
| /// <param name="func">A delegate containing the properties to modify the message with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous modification operation. | |||||
| /// </returns> | |||||
| Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. | /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -1,21 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Provides properties that are used to modify the widget of an <see cref="IGuild" /> with the specified changes. | |||||
| /// </summary> | |||||
| public class GuildEmbedProperties | |||||
| { | |||||
| /// <summary> | |||||
| /// Sets whether the widget should be enabled. | |||||
| /// </summary> | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| /// <summary> | |||||
| /// Sets the channel that the invite should place its users in, if not <c>null</c>. | |||||
| /// </summary> | |||||
| public Optional<IChannel> Channel { get; set; } | |||||
| /// <summary> | |||||
| /// Sets the channel the invite should place its users in, if not <c>null</c>. | |||||
| /// </summary> | |||||
| public Optional<ulong?> ChannelId { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -28,13 +28,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| int AFKTimeout { get; } | int AFKTimeout { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a value that indicates whether this guild is embeddable (i.e. can use widget). | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// <see langword="true" /> if this guild has a widget enabled; otherwise <see langword="false" />. | |||||
| /// </returns> | |||||
| bool IsEmbeddable { get; } | |||||
| /// <summary> | |||||
| /// Gets a value that indicates whether this guild has the widget enabled. | /// Gets a value that indicates whether this guild has the widget enabled. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -132,29 +125,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| ulong? AFKChannelId { get; } | ulong? AFKChannelId { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the ID of the default channel for this guild. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property retrieves the snowflake identifier of the first viewable text channel for this guild. | |||||
| /// <note type="warning"> | |||||
| /// This channel does not guarantee the user can send message to it, as it only looks for the first viewable | |||||
| /// text channel. | |||||
| /// </note> | |||||
| /// </remarks> | |||||
| /// <returns> | |||||
| /// A <see langword="ulong"/> representing the snowflake identifier of the default text channel; <c>0</c> if | |||||
| /// none can be found. | |||||
| /// </returns> | |||||
| ulong DefaultChannelId { get; } | |||||
| /// <summary> | |||||
| /// Gets the ID of the widget embed channel of this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see langword="ulong"/> representing the snowflake identifier of the embedded channel found within the | |||||
| /// widget settings of this guild; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| ulong? EmbedChannelId { get; } | |||||
| /// <summary> | |||||
| /// Gets the ID of the channel assigned to the widget of this guild. | /// Gets the ID of the channel assigned to the widget of this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -364,16 +334,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null); | Task ModifyAsync(Action<GuildProperties> func, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Modifies this guild's embed channel. | |||||
| /// </summary> | |||||
| /// <param name="func">The delegate containing the properties to modify the guild widget with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous modification operation. | |||||
| /// </returns> | |||||
| [Obsolete("This endpoint is deprecated, use ModifyWidgetAsync instead.")] | |||||
| Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Modifies this guild's widget. | /// Modifies this guild's widget. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="func">The delegate containing the properties to modify the guild widget with.</param> | /// <param name="func">The delegate containing the properties to modify the guild widget with.</param> | ||||
| @@ -592,17 +552,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task<ITextChannel> GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | Task<ITextChannel> GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild. | |||||
| /// </summary> | |||||
| /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous get operation. The task result contains the embed channel set | |||||
| /// within the server's widget settings; <see langword="null" /> if none is set. | |||||
| /// </returns> | |||||
| [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")] | |||||
| Task<IGuildChannel> GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param> | /// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param> | ||||
| @@ -892,6 +841,15 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null); | Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Gets a collection of emotes from this guild. | |||||
| /// </summary> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous get operation. The task result contains a read-only collection | |||||
| /// of emotes found within the guild. | |||||
| /// </returns> | |||||
| Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(RequestOptions options = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a specific emote from this guild. | /// Gets a specific emote from this guild. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -8,10 +8,10 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// The target of the permission is a role. | /// The target of the permission is a role. | ||||
| /// </summary> | /// </summary> | ||||
| Role, | |||||
| Role = 0, | |||||
| /// <summary> | /// <summary> | ||||
| /// The target of the permission is a user. | /// The target of the permission is a user. | ||||
| /// </summary> | /// </summary> | ||||
| User | |||||
| User = 1, | |||||
| } | } | ||||
| } | } | ||||
| @@ -16,14 +16,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| bool IsTemporary { get; } | bool IsTemporary { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a value that indicates whether the invite has been revoked. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// <c>true</c> if this invite was revoked; otherwise <c>false</c>. | |||||
| /// </returns> | |||||
| [Obsolete("This property doesn't exist anymore and shouldn't be used.")] | |||||
| bool IsRevoked { get; } | |||||
| /// <summary> | |||||
| /// Gets the time (in seconds) until the invite expires. | /// Gets the time (in seconds) until the invite expires. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -12,7 +12,6 @@ namespace Discord | |||||
| { | { | ||||
| private string _title; | private string _title; | ||||
| private string _description; | private string _description; | ||||
| private string _url; | |||||
| private EmbedImage? _image; | private EmbedImage? _image; | ||||
| private EmbedThumbnail? _thumbnail; | private EmbedThumbnail? _thumbnail; | ||||
| private List<EmbedFieldBuilder> _fields; | private List<EmbedFieldBuilder> _fields; | ||||
| @@ -70,26 +69,14 @@ namespace Discord | |||||
| /// <summary> Gets or sets the URL of an <see cref="Embed"/>. </summary> | /// <summary> Gets or sets the URL of an <see cref="Embed"/>. </summary> | ||||
| /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | ||||
| /// <returns> The URL of the embed.</returns> | /// <returns> The URL of the embed.</returns> | ||||
| public string Url | |||||
| { | |||||
| get => _url; | |||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(Url)); | |||||
| _url = value; | |||||
| } | |||||
| } | |||||
| public string Url { get; set; } | |||||
| /// <summary> Gets or sets the thumbnail URL of an <see cref="Embed"/>. </summary> | /// <summary> Gets or sets the thumbnail URL of an <see cref="Embed"/>. </summary> | ||||
| /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | ||||
| /// <returns> The thumbnail URL of the embed.</returns> | /// <returns> The thumbnail URL of the embed.</returns> | ||||
| public string ThumbnailUrl | public string ThumbnailUrl | ||||
| { | { | ||||
| get => _thumbnail?.Url; | get => _thumbnail?.Url; | ||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(ThumbnailUrl)); | |||||
| _thumbnail = new EmbedThumbnail(value, null, null, null); | |||||
| } | |||||
| set => _thumbnail = new EmbedThumbnail(value, null, null, null); | |||||
| } | } | ||||
| /// <summary> Gets or sets the image URL of an <see cref="Embed"/>. </summary> | /// <summary> Gets or sets the image URL of an <see cref="Embed"/>. </summary> | ||||
| /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | /// <exception cref="ArgumentException" accessor="set">Url is not a well-formed <see cref="Uri"/>.</exception> | ||||
| @@ -97,11 +84,7 @@ namespace Discord | |||||
| public string ImageUrl | public string ImageUrl | ||||
| { | { | ||||
| get => _image?.Url; | get => _image?.Url; | ||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(ImageUrl)); | |||||
| _image = new EmbedImage(value, null, null, null); | |||||
| } | |||||
| set => _image = new EmbedImage(value, null, null, null); | |||||
| } | } | ||||
| /// <summary> Gets or sets the list of <see cref="EmbedFieldBuilder"/> of an <see cref="Embed"/>. </summary> | /// <summary> Gets or sets the list of <see cref="EmbedFieldBuilder"/> of an <see cref="Embed"/>. </summary> | ||||
| @@ -553,8 +536,6 @@ namespace Discord | |||||
| public class EmbedAuthorBuilder | public class EmbedAuthorBuilder | ||||
| { | { | ||||
| private string _name; | private string _name; | ||||
| private string _url; | |||||
| private string _iconUrl; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the maximum author name length allowed by Discord. | /// Gets the maximum author name length allowed by Discord. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -585,15 +566,7 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// The URL of the author field. | /// The URL of the author field. | ||||
| /// </returns> | /// </returns> | ||||
| public string Url | |||||
| { | |||||
| get => _url; | |||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(Url)); | |||||
| _url = value; | |||||
| } | |||||
| } | |||||
| public string Url { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the icon URL of the author field. | /// Gets or sets the icon URL of the author field. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -601,15 +574,7 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// The icon URL of the author field. | /// The icon URL of the author field. | ||||
| /// </returns> | /// </returns> | ||||
| public string IconUrl | |||||
| { | |||||
| get => _iconUrl; | |||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(IconUrl)); | |||||
| _iconUrl = value; | |||||
| } | |||||
| } | |||||
| public string IconUrl { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Sets the name of the author field. | /// Sets the name of the author field. | ||||
| @@ -671,7 +636,6 @@ namespace Discord | |||||
| public class EmbedFooterBuilder | public class EmbedFooterBuilder | ||||
| { | { | ||||
| private string _text; | private string _text; | ||||
| private string _iconUrl; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the maximum footer length allowed by Discord. | /// Gets the maximum footer length allowed by Discord. | ||||
| @@ -703,15 +667,7 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// The icon URL of the footer field. | /// The icon URL of the footer field. | ||||
| /// </returns> | /// </returns> | ||||
| public string IconUrl | |||||
| { | |||||
| get => _iconUrl; | |||||
| set | |||||
| { | |||||
| if (!value.IsNullOrUri()) throw new ArgumentException(message: "Url must be a well-formed URI.", paramName: nameof(IconUrl)); | |||||
| _iconUrl = value; | |||||
| } | |||||
| } | |||||
| public string IconUrl { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Sets the name of the footer field. | /// Sets the name of the footer field. | ||||
| @@ -164,6 +164,14 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions { get; } | IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions { get; } | ||||
| /// <summary> | |||||
| /// Gets all stickers included in this message. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A read-only collection of sticker objects. | |||||
| /// </returns> | |||||
| IReadOnlyCollection<ISticker> Stickers { get; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the flags related to this message. | /// Gets the flags related to this message. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -0,0 +1,67 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a discord sticker. | |||||
| /// </summary> | |||||
| public interface ISticker | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the ID of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A snowflake ID associated with this sticker. | |||||
| /// </returns> | |||||
| ulong Id { get; } | |||||
| /// <summary> | |||||
| /// Gets the ID of the pack of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A snowflake ID associated with the pack of this sticker. | |||||
| /// </returns> | |||||
| ulong PackId { get; } | |||||
| /// <summary> | |||||
| /// Gets the name of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see langword="string"/> with the name of this sticker. | |||||
| /// </returns> | |||||
| string Name { get; } | |||||
| /// <summary> | |||||
| /// Gets the description of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see langword="string"/> with the description of this sticker. | |||||
| /// </returns> | |||||
| string Description { get; } | |||||
| /// <summary> | |||||
| /// Gets the list of tags of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A read-only list with the tags of this sticker. | |||||
| /// </returns> | |||||
| IReadOnlyCollection<string> Tags { get; } | |||||
| /// <summary> | |||||
| /// Gets the asset hash of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see langword="string"/> with the asset hash of this sticker. | |||||
| /// </returns> | |||||
| string Asset { get; } | |||||
| /// <summary> | |||||
| /// Gets the preview asset hash of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see langword="string"/> with the preview asset hash of this sticker. | |||||
| /// </returns> | |||||
| string PreviewAsset { get; } | |||||
| /// <summary> | |||||
| /// Gets the format type of this sticker. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A <see cref="StickerFormatType"/> with the format type of this sticker. | |||||
| /// </returns> | |||||
| StickerFormatType FormatType { get; } | |||||
| } | |||||
| } | |||||
| @@ -36,18 +36,6 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null); | Task ModifyAsync(Action<MessageProperties> func, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Modifies the suppression of this message. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method modifies whether or not embeds in this message are suppressed (hidden). | |||||
| /// </remarks> | |||||
| /// <param name="suppressEmbeds">Whether or not embeds in this message should be suppressed.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous modification operation. | |||||
| /// </returns> | |||||
| Task ModifySuppressionAsync(bool suppressEmbeds, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Adds this message to its channel's pinned messages. | /// Adds this message to its channel's pinned messages. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| @@ -60,9 +60,6 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// The message is an inline reply. | /// The message is an inline reply. | ||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | |||||
| /// Only available in API v8. | |||||
| /// </remarks> | |||||
| Reply = 19, | Reply = 19, | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,15 @@ | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> Defines the types of formats for stickers. </summary> | |||||
| public enum StickerFormatType | |||||
| { | |||||
| /// <summary> Default value for a sticker format type. </summary> | |||||
| None = 0, | |||||
| /// <summary> The sticker format type is png. </summary> | |||||
| Png = 1, | |||||
| /// <summary> The sticker format type is apng. </summary> | |||||
| Apng = 2, | |||||
| /// <summary> The sticker format type is lottie. </summary> | |||||
| Lottie = 3, | |||||
| } | |||||
| } | |||||
| @@ -22,11 +22,6 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| AddReactions = 0x00_00_00_40, | AddReactions = 0x00_00_00_40, | ||||
| /// <summary> | /// <summary> | ||||
| /// Allows for reading of messages. This flag is obsolete, use <see cref = "ViewChannel" /> instead. | |||||
| /// </summary> | |||||
| [Obsolete("Use ViewChannel instead.")] | |||||
| ReadMessages = ViewChannel, | |||||
| /// <summary> | |||||
| /// Allows guild members to view a channel, which includes reading messages in text channels. | /// Allows guild members to view a channel, which includes reading messages in text channels. | ||||
| /// </summary> | /// </summary> | ||||
| ViewChannel = 0x00_00_04_00, | ViewChannel = 0x00_00_04_00, | ||||
| @@ -45,9 +45,6 @@ namespace Discord | |||||
| /// <summary> If <c>true</c>, a user may add reactions. </summary> | /// <summary> If <c>true</c>, a user may add reactions. </summary> | ||||
| public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions); | public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions); | ||||
| /// <summary> If <c>true</c>, a user may join channels. </summary> | |||||
| [Obsolete("Use ViewChannel instead.")] | |||||
| public bool ReadMessages => ViewChannel; | |||||
| /// <summary> If <c>true</c>, a user may view channels. </summary> | /// <summary> If <c>true</c>, a user may view channels. </summary> | ||||
| public bool ViewChannel => Permissions.GetValue(RawValue, ChannelPermission.ViewChannel); | public bool ViewChannel => Permissions.GetValue(RawValue, ChannelPermission.ViewChannel); | ||||
| @@ -65,8 +65,6 @@ namespace Discord | |||||
| /// Allows for viewing of audit logs. | /// Allows for viewing of audit logs. | ||||
| /// </summary> | /// </summary> | ||||
| ViewAuditLog = 0x00_00_00_80, | ViewAuditLog = 0x00_00_00_80, | ||||
| [Obsolete("Use ViewChannel instead.")] | |||||
| ReadMessages = ViewChannel, | |||||
| ViewChannel = 0x00_00_04_00, | ViewChannel = 0x00_00_04_00, | ||||
| SendMessages = 0x00_00_08_00, | SendMessages = 0x00_00_08_00, | ||||
| /// <summary> | /// <summary> | ||||
| @@ -37,9 +37,6 @@ namespace Discord | |||||
| /// <summary> If <c>true</c>, a user may view the guild insights. </summary> | /// <summary> If <c>true</c>, a user may view the guild insights. </summary> | ||||
| public bool ViewGuildInsights => Permissions.GetValue(RawValue, GuildPermission.ViewGuildInsights); | public bool ViewGuildInsights => Permissions.GetValue(RawValue, GuildPermission.ViewGuildInsights); | ||||
| /// <summary> If True, a user may join channels. </summary> | |||||
| [Obsolete("Use ViewChannel instead.")] | |||||
| public bool ReadMessages => ViewChannel; | |||||
| /// <summary> If True, a user may view channels. </summary> | /// <summary> If True, a user may view channels. </summary> | ||||
| public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel); | public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel); | ||||
| /// <summary> If True, a user may send messages. </summary> | /// <summary> If True, a user may send messages. </summary> | ||||
| @@ -90,6 +87,9 @@ namespace Discord | |||||
| /// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary> | /// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary> | ||||
| public GuildPermissions(ulong rawValue) { RawValue = rawValue; } | public GuildPermissions(ulong rawValue) { RawValue = rawValue; } | ||||
| /// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value after converting to ulong. </summary> | |||||
| public GuildPermissions(string rawValue) { RawValue = ulong.Parse(rawValue); } | |||||
| private GuildPermissions(ulong initialValue, | private GuildPermissions(ulong initialValue, | ||||
| bool? createInstantInvite = null, | bool? createInstantInvite = null, | ||||
| bool? kickMembers = null, | bool? kickMembers = null, | ||||
| @@ -43,9 +43,6 @@ namespace Discord | |||||
| /// <summary> If Allowed, a user may add reactions. </summary> | /// <summary> If Allowed, a user may add reactions. </summary> | ||||
| public PermValue AddReactions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AddReactions); | public PermValue AddReactions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AddReactions); | ||||
| /// <summary> If Allowed, a user may join channels. </summary> | /// <summary> If Allowed, a user may join channels. </summary> | ||||
| [Obsolete("Use ViewChannel instead.")] | |||||
| public PermValue ReadMessages => ViewChannel; | |||||
| /// <summary> If Allowed, a user may join channels. </summary> | |||||
| public PermValue ViewChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ViewChannel); | public PermValue ViewChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ViewChannel); | ||||
| /// <summary> If Allowed, a user may send messages. </summary> | /// <summary> If Allowed, a user may send messages. </summary> | ||||
| public PermValue SendMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.SendMessages); | public PermValue SendMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.SendMessages); | ||||
| @@ -93,6 +90,13 @@ namespace Discord | |||||
| DenyValue = denyValue; | DenyValue = denyValue; | ||||
| } | } | ||||
| /// <summary> Creates a new OverwritePermissions with the provided allow and deny packed values after converting to ulong. </summary> | |||||
| public OverwritePermissions(string allowValue, string denyValue) | |||||
| { | |||||
| AllowValue = ulong.Parse(allowValue); | |||||
| DenyValue = ulong.Parse(denyValue); | |||||
| } | |||||
| private OverwritePermissions(ulong allowValue, ulong denyValue, | private OverwritePermissions(ulong allowValue, ulong denyValue, | ||||
| PermValue? createInstantInvite = null, | PermValue? createInstantInvite = null, | ||||
| PermValue? manageChannel = null, | PermValue? manageChannel = null, | ||||
| @@ -113,7 +113,15 @@ namespace Discord | |||||
| /// A task that represents the asynchronous modification operation. | /// A task that represents the asynchronous modification operation. | ||||
| /// </returns> | /// </returns> | ||||
| Task ModifyAsync(Action<GuildUserProperties> func, RequestOptions options = null); | Task ModifyAsync(Action<GuildUserProperties> func, RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Adds the specified role to this user in the guild. | |||||
| /// </summary> | |||||
| /// <param name="roleId">The role to be added to the user.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous role addition operation. | |||||
| /// </returns> | |||||
| Task AddRoleAsync(ulong roleId, RequestOptions options = null); | |||||
| /// <summary> | /// <summary> | ||||
| /// Adds the specified role to this user in the guild. | /// Adds the specified role to this user in the guild. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -124,6 +132,15 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task AddRoleAsync(IRole role, RequestOptions options = null); | Task AddRoleAsync(IRole role, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Adds the specified <paramref name="roleIds"/> to this user in the guild. | |||||
| /// </summary> | |||||
| /// <param name="roleIds">The roles to be added to the user.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous role addition operation. | |||||
| /// </returns> | |||||
| Task AddRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Adds the specified <paramref name="roles"/> to this user in the guild. | /// Adds the specified <paramref name="roles"/> to this user in the guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="roles">The roles to be added to the user.</param> | /// <param name="roles">The roles to be added to the user.</param> | ||||
| @@ -133,6 +150,15 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null); | Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Removes the specified <paramref name="roleId"/> from this user in the guild. | |||||
| /// </summary> | |||||
| /// <param name="roleId">The role to be removed from the user.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous role removal operation. | |||||
| /// </returns> | |||||
| Task RemoveRoleAsync(ulong roleId, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Removes the specified <paramref name="role"/> from this user in the guild. | /// Removes the specified <paramref name="role"/> from this user in the guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="role">The role to be removed from the user.</param> | /// <param name="role">The role to be removed from the user.</param> | ||||
| @@ -142,6 +168,15 @@ namespace Discord | |||||
| /// </returns> | /// </returns> | ||||
| Task RemoveRoleAsync(IRole role, RequestOptions options = null); | Task RemoveRoleAsync(IRole role, RequestOptions options = null); | ||||
| /// <summary> | /// <summary> | ||||
| /// Removes the specified <paramref name="roleIds"/> from this user in the guild. | |||||
| /// </summary> | |||||
| /// <param name="roleIds">The roles to be removed from the user.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous role removal operation. | |||||
| /// </returns> | |||||
| Task RemoveRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Removes the specified <paramref name="roles"/> from this user in the guild. | /// Removes the specified <paramref name="roles"/> from this user in the guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="roles">The roles to be removed from the user.</param> | /// <param name="roles">The roles to be removed from the user.</param> | ||||
| @@ -7,10 +7,6 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| public interface IPresence | public interface IPresence | ||||
| { | { | ||||
| /// <summary> | |||||
| /// Gets the activity this user is currently doing. | |||||
| /// </summary> | |||||
| IActivity Activity { get; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the current status of this user. | /// Gets the current status of this user. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -87,7 +87,7 @@ namespace Discord | |||||
| UserProperties? PublicFlags { get; } | UserProperties? PublicFlags { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the direct message channel of this user, or create one if it does not already exist. | |||||
| /// Creates the direct message channel of this user. | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// This method is used to obtain or create a channel used to send a direct message. | /// This method is used to obtain or create a channel used to send a direct message. | ||||
| @@ -102,7 +102,7 @@ namespace Discord | |||||
| /// <example> | /// <example> | ||||
| /// <para>The following example attempts to send a direct message to the target user and logs the incident should | /// <para>The following example attempts to send a direct message to the target user and logs the incident should | ||||
| /// it fail.</para> | /// it fail.</para> | ||||
| /// <code region="GetOrCreateDMChannelAsync" language="cs" | |||||
| /// <code region="CreateDMChannelAsync" language="cs" | |||||
| /// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/> | /// source="../../../Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs"/> | ||||
| /// </example> | /// </example> | ||||
| /// <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> | ||||
| @@ -110,6 +110,6 @@ namespace Discord | |||||
| /// A task that represents the asynchronous operation for getting or creating a DM channel. The task result | /// A task that represents the asynchronous operation for getting or creating a DM channel. The task result | ||||
| /// contains the DM channel associated with this user. | /// contains the DM channel associated with this user. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null); | |||||
| Task<IDMChannel> CreateDMChannelAsync(RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -22,12 +22,6 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| HypeSquadEvents = 1 << 2, | HypeSquadEvents = 1 << 2, | ||||
| /// <summary> | /// <summary> | ||||
| /// Flag given to users who have participated in the bug report program. | |||||
| /// This flag is obsolete, use <see cref="BugHunterLevel1"/> instead. | |||||
| /// </summary> | |||||
| [Obsolete("Use BugHunterLevel1 instead.")] | |||||
| BugHunter = 1 << 3, | |||||
| /// <summary> | |||||
| /// Flag given to users who have participated in the bug report program and are level 1. | /// Flag given to users who have participated in the bug report program and are level 1. | ||||
| /// </summary> | /// </summary> | ||||
| BugHunterLevel1 = 1 << 3, | BugHunterLevel1 = 1 << 3, | ||||
| @@ -67,5 +61,9 @@ namespace Discord | |||||
| /// Flag given to users that developed bots and early verified their accounts. | /// Flag given to users that developed bots and early verified their accounts. | ||||
| /// </summary> | /// </summary> | ||||
| EarlyVerifiedBotDeveloper = 1 << 17, | EarlyVerifiedBotDeveloper = 1 << 17, | ||||
| /// <summary> | |||||
| /// Flag given to users that are discord certified moderators who has give discord's exam. | |||||
| /// </summary> | |||||
| DiscordCertifiedModerator = 1 << 18, | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +0,0 @@ | |||||
| using System; | |||||
| namespace Discord | |||||
| { | |||||
| internal static class StringExtensions | |||||
| { | |||||
| public static bool IsNullOrUri(this string url) => | |||||
| string.IsNullOrEmpty(url) || Uri.IsWellFormedUriString(url, UriKind.Absolute); | |||||
| } | |||||
| } | |||||
| @@ -42,7 +42,7 @@ namespace Discord | |||||
| RequestOptions options = null, | RequestOptions options = null, | ||||
| AllowedMentions allowedMentions = null) | AllowedMentions allowedMentions = null) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | |||||
| return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -94,7 +94,7 @@ namespace Discord | |||||
| RequestOptions options = null | RequestOptions options = null | ||||
| ) | ) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -149,7 +149,7 @@ namespace Discord | |||||
| Embed embed = null, | Embed embed = null, | ||||
| RequestOptions options = null) | RequestOptions options = null) | ||||
| { | { | ||||
| return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -39,5 +39,16 @@ namespace Discord | |||||
| DirectMessageReactions = 1 << 13, | DirectMessageReactions = 1 << 13, | ||||
| /// <summary> This intent includes TYPING_START </summary> | /// <summary> This intent includes TYPING_START </summary> | ||||
| DirectMessageTyping = 1 << 14, | DirectMessageTyping = 1 << 14, | ||||
| /// <summary> | |||||
| /// This intent includes all but <see cref="GuildMembers"/> and <see cref="GuildMembers"/> | |||||
| /// that are privileged must be enabled for the application. | |||||
| /// </summary> | |||||
| AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites | | |||||
| GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages | | |||||
| DirectMessageReactions | DirectMessageTyping, | |||||
| /// <summary> | |||||
| /// This intent includes all of them, including privileged ones. | |||||
| /// </summary> | |||||
| All = AllUnprivileged | GuildMembers | GuildPresences | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,18 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Specifies the level of precision to request in the rate limit | |||||
| /// response header. | |||||
| /// </summary> | |||||
| public enum RateLimitPrecision | |||||
| { | |||||
| /// <summary> | |||||
| /// Specifies precision rounded up to the nearest whole second | |||||
| /// </summary> | |||||
| Second, | |||||
| /// <summary> | |||||
| /// Specifies precision rounded to the nearest millisecond. | |||||
| /// </summary> | |||||
| Millisecond | |||||
| } | |||||
| } | |||||
| @@ -5,8 +5,6 @@ namespace Discord | |||||
| /// <summary> Specifies the type of token to use with the client. </summary> | /// <summary> Specifies the type of token to use with the client. </summary> | ||||
| public enum TokenType | public enum TokenType | ||||
| { | { | ||||
| [Obsolete("User logins are deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827", error: true)] | |||||
| User, | |||||
| /// <summary> | /// <summary> | ||||
| /// An OAuth2 token type. | /// An OAuth2 token type. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -12,7 +12,7 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="value">The snowflake identifier to resolve.</param> | /// <param name="value">The snowflake identifier to resolve.</param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A <see cref="DateTimeOffset" /> representing the time for when the object is geenrated. | |||||
| /// A <see cref="DateTimeOffset" /> representing the time for when the object is generated. | |||||
| /// </returns> | /// </returns> | ||||
| public static DateTimeOffset FromSnowflake(ulong value) | public static DateTimeOffset FromSnowflake(ulong value) | ||||
| => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); | => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); | ||||
| @@ -18,11 +18,11 @@ namespace Discord.Net.Examples.Core.Entities.Users | |||||
| #endregion | #endregion | ||||
| #region GetOrCreateDMChannelAsync | |||||
| #region CreateDMChannelAsync | |||||
| public async Task MessageUserAsync(IUser user) | public async Task MessageUserAsync(IUser user) | ||||
| { | { | ||||
| var channel = await user.GetOrCreateDMChannelAsync(); | |||||
| var channel = await user.CreateDMChannelAsync(); | |||||
| try | try | ||||
| { | { | ||||
| await channel.SendMessageAsync("Awesome stuff!"); | await channel.SendMessageAsync("Awesome stuff!"); | ||||
| @@ -7,7 +7,7 @@ namespace Discord.API | |||||
| [JsonProperty("target_id")] | [JsonProperty("target_id")] | ||||
| public ulong? TargetId { get; set; } | public ulong? TargetId { get; set; } | ||||
| [JsonProperty("user_id")] | [JsonProperty("user_id")] | ||||
| public ulong UserId { get; set; } | |||||
| public ulong? UserId { get; set; } | |||||
| [JsonProperty("changes")] | [JsonProperty("changes")] | ||||
| public AuditLogChange[] Changes { get; set; } | public AuditLogChange[] Changes { get; set; } | ||||
| @@ -23,10 +23,6 @@ namespace Discord.API | |||||
| public ulong? AFKChannelId { get; set; } | public ulong? AFKChannelId { get; set; } | ||||
| [JsonProperty("afk_timeout")] | [JsonProperty("afk_timeout")] | ||||
| public int AFKTimeout { get; set; } | public int AFKTimeout { get; set; } | ||||
| [JsonProperty("embed_enabled")] | |||||
| public Optional<bool> EmbedEnabled { get; set; } | |||||
| [JsonProperty("embed_channel_id")] | |||||
| public Optional<ulong?> EmbedChannelId { get; set; } | |||||
| [JsonProperty("verification_level")] | [JsonProperty("verification_level")] | ||||
| public VerificationLevel VerificationLevel { get; set; } | public VerificationLevel VerificationLevel { get; set; } | ||||
| [JsonProperty("default_message_notifications")] | [JsonProperty("default_message_notifications")] | ||||
| @@ -1,13 +0,0 @@ | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class GuildEmbed | |||||
| { | |||||
| [JsonProperty("enabled")] | |||||
| public bool Enabled { get; set; } | |||||
| [JsonProperty("channel_id")] | |||||
| public ulong? ChannelId { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -6,5 +6,7 @@ namespace Discord.API | |||||
| { | { | ||||
| [JsonProperty("code")] | [JsonProperty("code")] | ||||
| public string Code { get; set; } | public string Code { get; set; } | ||||
| [JsonProperty("uses")] | |||||
| public int Uses { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -58,5 +58,7 @@ namespace Discord.API | |||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | public Optional<AllowedMentions> AllowedMentions { get; set; } | ||||
| [JsonProperty("referenced_message")] | [JsonProperty("referenced_message")] | ||||
| public Optional<Message> ReferencedMessage { get; set; } | public Optional<Message> ReferencedMessage { get; set; } | ||||
| [JsonProperty("stickers")] | |||||
| public Optional<Sticker[]> Stickers { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,8 +10,8 @@ namespace Discord.API | |||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public PermissionTarget TargetType { get; set; } | public PermissionTarget TargetType { get; set; } | ||||
| [JsonProperty("deny"), Int53] | [JsonProperty("deny"), Int53] | ||||
| public ulong Deny { get; set; } | |||||
| public string Deny { get; set; } | |||||
| [JsonProperty("allow"), Int53] | [JsonProperty("allow"), Int53] | ||||
| public ulong Allow { get; set; } | |||||
| public string Allow { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -13,8 +13,6 @@ namespace Discord.API | |||||
| public Optional<ulong> GuildId { get; set; } | public Optional<ulong> GuildId { get; set; } | ||||
| [JsonProperty("status")] | [JsonProperty("status")] | ||||
| public UserStatus Status { get; set; } | public UserStatus Status { get; set; } | ||||
| [JsonProperty("game")] | |||||
| public Game Game { get; set; } | |||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public Optional<ulong[]> Roles { get; set; } | public Optional<ulong[]> Roles { get; set; } | ||||
| @@ -18,7 +18,7 @@ namespace Discord.API | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public int Position { get; set; } | public int Position { get; set; } | ||||
| [JsonProperty("permissions"), Int53] | [JsonProperty("permissions"), Int53] | ||||
| public ulong Permissions { get; set; } | |||||
| public string Permissions { get; set; } | |||||
| [JsonProperty("managed")] | [JsonProperty("managed")] | ||||
| public bool Managed { get; set; } | public bool Managed { get; set; } | ||||
| [JsonProperty("tags")] | [JsonProperty("tags")] | ||||
| @@ -0,0 +1,25 @@ | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class Sticker | |||||
| { | |||||
| [JsonProperty("id")] | |||||
| public ulong Id { get; set; } | |||||
| [JsonProperty("pack_id")] | |||||
| public ulong PackId { get; set; } | |||||
| [JsonProperty("name")] | |||||
| public string Name { get; set; } | |||||
| [JsonProperty("description")] | |||||
| public string Desription { get; set; } | |||||
| [JsonProperty("tags")] | |||||
| public Optional<string> Tags { get; set; } | |||||
| [JsonProperty("asset")] | |||||
| public string Asset { get; set; } | |||||
| [JsonProperty("preview_asset")] | |||||
| public string PreviewAsset { get; set; } | |||||
| [JsonProperty("format_type")] | |||||
| public StickerFormatType FormatType { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API | namespace Discord.API | ||||
| @@ -14,6 +14,6 @@ namespace Discord.API | |||||
| [JsonProperty("owner")] | [JsonProperty("owner")] | ||||
| public bool Owner { get; set; } | public bool Owner { get; set; } | ||||
| [JsonProperty("permissions"), Int53] | [JsonProperty("permissions"), Int53] | ||||
| public ulong Permissions { get; set; } | |||||
| public string Permissions { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| @@ -7,13 +7,13 @@ namespace Discord.API.Rest | |||||
| internal class ModifyChannelPermissionsParams | internal class ModifyChannelPermissionsParams | ||||
| { | { | ||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public string Type { get; } | |||||
| public int Type { get; } | |||||
| [JsonProperty("allow")] | [JsonProperty("allow")] | ||||
| public ulong Allow { get; } | |||||
| public string Allow { get; } | |||||
| [JsonProperty("deny")] | [JsonProperty("deny")] | ||||
| public ulong Deny { get; } | |||||
| public string Deny { get; } | |||||
| public ModifyChannelPermissionsParams(string type, ulong allow, ulong deny) | |||||
| public ModifyChannelPermissionsParams(int type, string allow, string deny) | |||||
| { | { | ||||
| Type = type; | Type = type; | ||||
| Allow = allow; | Allow = allow; | ||||
| @@ -1,4 +1,4 @@ | |||||
| #pragma warning disable CS1591 | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| @@ -9,7 +9,7 @@ namespace Discord.API.Rest | |||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public Optional<string> Name { get; set; } | public Optional<string> Name { get; set; } | ||||
| [JsonProperty("permissions")] | [JsonProperty("permissions")] | ||||
| public Optional<ulong> Permissions { get; set; } | |||||
| public Optional<string> Permissions { get; set; } | |||||
| [JsonProperty("color")] | [JsonProperty("color")] | ||||
| public Optional<uint> Color { get; set; } | public Optional<uint> Color { get; set; } | ||||
| [JsonProperty("hoist")] | [JsonProperty("hoist")] | ||||
| @@ -0,0 +1,16 @@ | |||||
| #pragma warning disable CS1591 | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| internal class ModifyWebhookMessageParams | |||||
| { | |||||
| [JsonProperty("content")] | |||||
| public Optional<string> Content { get; set; } | |||||
| [JsonProperty("embeds")] | |||||
| public Optional<Embed[]> Embeds { get; set; } | |||||
| [JsonProperty("allowed_mentions")] | |||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -1,11 +0,0 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| internal class SuppressEmbedParams | |||||
| { | |||||
| [JsonProperty("suppress")] | |||||
| public bool Suppressed { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -17,7 +17,7 @@ namespace Discord.Rest | |||||
| return RestApplication.Create(client, model); | return RestApplication.Create(client, model); | ||||
| } | } | ||||
| public static async Task<RestChannel> GetChannelAsync(BaseDiscordClient client, | |||||
| public static async Task<RestChannel> GetChannelAsync(BaseDiscordClient client, | |||||
| ulong id, RequestOptions options) | ulong id, RequestOptions options) | ||||
| { | { | ||||
| var model = await client.ApiClient.GetChannelAsync(id, options).ConfigureAwait(false); | var model = await client.ApiClient.GetChannelAsync(id, options).ConfigureAwait(false); | ||||
| @@ -45,13 +45,13 @@ namespace Discord.Rest | |||||
| .Where(x => x.Type == ChannelType.Group) | .Where(x => x.Type == ChannelType.Group) | ||||
| .Select(x => RestGroupChannel.Create(client, x)).ToImmutableArray(); | .Select(x => RestGroupChannel.Create(client, x)).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client, RequestOptions options) | public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client, RequestOptions options) | ||||
| { | { | ||||
| var models = await client.ApiClient.GetMyConnectionsAsync(options).ConfigureAwait(false); | var models = await client.ApiClient.GetMyConnectionsAsync(options).ConfigureAwait(false); | ||||
| return models.Select(RestConnection.Create).ToImmutableArray(); | return models.Select(RestConnection.Create).ToImmutableArray(); | ||||
| } | } | ||||
| public static async Task<RestInviteMetadata> GetInviteAsync(BaseDiscordClient client, | public static async Task<RestInviteMetadata> GetInviteAsync(BaseDiscordClient client, | ||||
| string inviteId, RequestOptions options) | string inviteId, RequestOptions options) | ||||
| { | { | ||||
| @@ -60,7 +60,7 @@ namespace Discord.Rest | |||||
| return RestInviteMetadata.Create(client, null, null, model); | return RestInviteMetadata.Create(client, null, null, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestGuild> GetGuildAsync(BaseDiscordClient client, | public static async Task<RestGuild> GetGuildAsync(BaseDiscordClient client, | ||||
| ulong id, bool withCounts, RequestOptions options) | ulong id, bool withCounts, RequestOptions options) | ||||
| { | { | ||||
| @@ -69,14 +69,6 @@ namespace Discord.Rest | |||||
| return RestGuild.Create(client, model); | return RestGuild.Create(client, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static async Task<RestGuildEmbed?> GetGuildEmbedAsync(BaseDiscordClient client, | |||||
| ulong id, RequestOptions options) | |||||
| { | |||||
| var model = await client.ApiClient.GetGuildEmbedAsync(id, options).ConfigureAwait(false); | |||||
| if (model != null) | |||||
| return RestGuildEmbed.Create(model); | |||||
| return null; | |||||
| } | |||||
| public static async Task<RestGuildWidget?> GetGuildWidgetAsync(BaseDiscordClient client, | public static async Task<RestGuildWidget?> GetGuildWidgetAsync(BaseDiscordClient client, | ||||
| ulong id, RequestOptions options) | ulong id, RequestOptions options) | ||||
| { | { | ||||
| @@ -85,7 +77,7 @@ namespace Discord.Rest | |||||
| return RestGuildWidget.Create(model); | return RestGuildWidget.Create(model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(BaseDiscordClient client, | |||||
| public static IAsyncEnumerable<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(BaseDiscordClient client, | |||||
| ulong? fromGuildId, int? limit, RequestOptions options) | ulong? fromGuildId, int? limit, RequestOptions options) | ||||
| { | { | ||||
| return new PagedAsyncEnumerable<RestUserGuild>( | return new PagedAsyncEnumerable<RestUserGuild>( | ||||
| @@ -136,7 +128,7 @@ namespace Discord.Rest | |||||
| var model = await client.ApiClient.CreateGuildAsync(args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateGuildAsync(args, options).ConfigureAwait(false); | ||||
| return RestGuild.Create(client, model); | return RestGuild.Create(client, model); | ||||
| } | } | ||||
| public static async Task<RestUser> GetUserAsync(BaseDiscordClient client, | public static async Task<RestUser> GetUserAsync(BaseDiscordClient client, | ||||
| ulong id, RequestOptions options) | ulong id, RequestOptions options) | ||||
| { | { | ||||
| @@ -201,5 +193,9 @@ namespace Discord.Rest | |||||
| } | } | ||||
| }; | }; | ||||
| } | } | ||||
| public static Task AddRoleAsync(BaseDiscordClient client, ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | |||||
| => client.ApiClient.AddRoleAsync(guildId, userId, roleId, options); | |||||
| public static Task RemoveRoleAsync(BaseDiscordClient client, ulong guildId, ulong userId, ulong roleId, RequestOptions options = null) | |||||
| => client.ApiClient.RemoveRoleAsync(guildId, userId, roleId, options); | |||||
| } | } | ||||
| } | } | ||||
| @@ -45,20 +45,18 @@ namespace Discord.API | |||||
| internal string AuthToken { get; private set; } | internal string AuthToken { get; private set; } | ||||
| internal IRestClient RestClient { get; private set; } | internal IRestClient RestClient { get; private set; } | ||||
| internal ulong? CurrentUserId { get; set; } | internal ulong? CurrentUserId { get; set; } | ||||
| public RateLimitPrecision RateLimitPrecision { get; private set; } | |||||
| internal bool UseSystemClock { get; set; } | internal bool UseSystemClock { get; set; } | ||||
| internal JsonSerializer Serializer => _serializer; | internal JsonSerializer Serializer => _serializer; | ||||
| /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | ||||
| public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, | public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, | ||||
| JsonSerializer serializer = null, RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, bool useSystemClock = true) | |||||
| JsonSerializer serializer = null, bool useSystemClock = true) | |||||
| { | { | ||||
| _restClientProvider = restClientProvider; | _restClientProvider = restClientProvider; | ||||
| UserAgent = userAgent; | UserAgent = userAgent; | ||||
| DefaultRetryMode = defaultRetryMode; | DefaultRetryMode = defaultRetryMode; | ||||
| _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; | ||||
| RateLimitPrecision = rateLimitPrecision; | |||||
| UseSystemClock = useSystemClock; | UseSystemClock = useSystemClock; | ||||
| RequestQueue = new RequestQueue(); | RequestQueue = new RequestQueue(); | ||||
| @@ -75,14 +73,12 @@ namespace Discord.API | |||||
| RestClient.SetHeader("accept", "*/*"); | RestClient.SetHeader("accept", "*/*"); | ||||
| RestClient.SetHeader("user-agent", UserAgent); | RestClient.SetHeader("user-agent", UserAgent); | ||||
| RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); | RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); | ||||
| RestClient.SetHeader("X-RateLimit-Precision", RateLimitPrecision.ToString().ToLower()); | |||||
| } | } | ||||
| /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | /// <exception cref="ArgumentException">Unknown OAuth token type.</exception> | ||||
| internal static string GetPrefixedToken(TokenType tokenType, string token) | internal static string GetPrefixedToken(TokenType tokenType, string token) | ||||
| { | { | ||||
| return tokenType switch | return tokenType switch | ||||
| { | { | ||||
| default(TokenType) => token, | |||||
| TokenType.Bot => $"Bot {token}", | TokenType.Bot => $"Bot {token}", | ||||
| TokenType.Bearer => $"Bearer {token}", | TokenType.Bearer => $"Bearer {token}", | ||||
| _ => throw new ArgumentException(message: "Unknown OAuth token type.", paramName: nameof(tokenType)), | _ => throw new ArgumentException(message: "Unknown OAuth token type.", paramName: nameof(tokenType)), | ||||
| @@ -523,6 +519,43 @@ namespace Discord.API | |||||
| var ids = new BucketIds(webhookId: webhookId); | var ids = new BucketIds(webhookId: webhookId); | ||||
| return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task ModifyWebhookMessageAsync(ulong webhookId, ulong messageId, ModifyWebhookMessageParams args, RequestOptions options = null) | |||||
| { | |||||
| if (AuthTokenType != TokenType.Webhook) | |||||
| throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); | |||||
| Preconditions.NotNull(args, nameof(args)); | |||||
| Preconditions.NotEqual(webhookId, 0, nameof(webhookId)); | |||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
| if (args.Embeds.IsSpecified) | |||||
| Preconditions.AtMost(args.Embeds.Value.Length, 10, nameof(args.Embeds), "A max of 10 Embeds are allowed."); | |||||
| if (args.Content.IsSpecified && args.Content.Value.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(webhookId: webhookId); | |||||
| await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | |||||
| } | |||||
| /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> | |||||
| public async Task DeleteWebhookMessageAsync(ulong webhookId, ulong messageId, RequestOptions options = null) | |||||
| { | |||||
| if (AuthTokenType != TokenType.Webhook) | |||||
| throw new InvalidOperationException($"This operation may only be called with a {nameof(TokenType.Webhook)} token."); | |||||
| Preconditions.NotEqual(webhookId, 0, nameof(webhookId)); | |||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(webhookId: webhookId); | |||||
| await SendAsync("DELETE", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}", ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | ||||
| public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -608,16 +641,6 @@ namespace Discord.API | |||||
| return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SuppressEmbedAsync(ulong channelId, ulong messageId, Rest.SuppressEmbedParams args, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(channelId: channelId); | |||||
| await SendJsonAsync("POST", () => $"channels/{channelId}/messages/{messageId}/suppress-embeds", args, ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| public async Task AddReactionAsync(ulong channelId, ulong messageId, string emoji, RequestOptions options = null) | public async Task AddReactionAsync(ulong channelId, ulong messageId, string emoji, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| @@ -899,7 +922,7 @@ namespace Discord.API | |||||
| var ids = new BucketIds(guildId: guildId); | var ids = new BucketIds(guildId: guildId); | ||||
| string reason = string.IsNullOrWhiteSpace(args.Reason) ? "" : $"&reason={Uri.EscapeDataString(args.Reason)}"; | string reason = string.IsNullOrWhiteSpace(args.Reason) ? "" : $"&reason={Uri.EscapeDataString(args.Reason)}"; | ||||
| await SendAsync("PUT", () => $"guilds/{guildId}/bans/{userId}?delete-message-days={args.DeleteMessageDays}{reason}", ids, options: options).ConfigureAwait(false); | |||||
| await SendAsync("PUT", () => $"guilds/{guildId}/bans/{userId}?delete_message_days={args.DeleteMessageDays}{reason}", ids, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.</exception> | /// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.</exception> | ||||
| public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null) | public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null) | ||||
| @@ -912,32 +935,6 @@ namespace Discord.API | |||||
| await SendAsync("DELETE", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false); | await SendAsync("DELETE", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| //Guild Embeds | |||||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | |||||
| public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| try | |||||
| { | |||||
| var ids = new BucketIds(guildId: guildId); | |||||
| return await SendAsync<GuildEmbed>("GET", () => $"guilds/{guildId}/embed", ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } | |||||
| } | |||||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception> | |||||
| public async Task<GuildEmbed> ModifyGuildEmbedAsync(ulong guildId, Rest.ModifyGuildEmbedParams args, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotNull(args, nameof(args)); | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(guildId: guildId); | |||||
| return await SendJsonAsync<GuildEmbed>("PATCH", () => $"guilds/{guildId}/embed", args, ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| //Guild Widget | //Guild Widget | ||||
| /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | /// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception> | ||||
| public async Task<GuildWidget> GetGuildWidgetAsync(ulong guildId, RequestOptions options = null) | public async Task<GuildWidget> GetGuildWidgetAsync(ulong guildId, RequestOptions options = null) | ||||
| @@ -1243,6 +1240,15 @@ namespace Discord.API | |||||
| } | } | ||||
| //Guild emoji | //Guild emoji | ||||
| public async Task<IReadOnlyCollection<Emoji>> GetGuildEmotesAsync(ulong guildId, RequestOptions options = null) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| var ids = new BucketIds(guildId: guildId); | |||||
| return await SendAsync<IReadOnlyCollection<Emoji>>("GET", () => $"guilds/{guildId}/emojis", ids, options: options).ConfigureAwait(false); | |||||
| } | |||||
| public async Task<Emoji> GetGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null) | public async Task<Emoji> GetGuildEmoteAsync(ulong guildId, ulong emoteId, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| @@ -29,10 +29,7 @@ namespace Discord.Rest | |||||
| internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { } | internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { } | ||||
| private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | ||||
| => new API.DiscordRestApiClient(config.RestClientProvider, | |||||
| DiscordRestConfig.UserAgent, | |||||
| rateLimitPrecision: config.RateLimitPrecision, | |||||
| useSystemClock: config.UseSystemClock); | |||||
| => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, useSystemClock: config.UseSystemClock); | |||||
| internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
| { | { | ||||
| @@ -80,9 +77,6 @@ namespace Discord.Rest | |||||
| => ClientHelper.GetGuildAsync(this, id, false, options); | => ClientHelper.GetGuildAsync(this, id, false, options); | ||||
| public Task<RestGuild> GetGuildAsync(ulong id, bool withCounts, RequestOptions options = null) | public Task<RestGuild> GetGuildAsync(ulong id, bool withCounts, RequestOptions options = null) | ||||
| => ClientHelper.GetGuildAsync(this, id, withCounts, options); | => ClientHelper.GetGuildAsync(this, id, withCounts, options); | ||||
| [Obsolete("This endpoint is deprecated, use GetGuildWidgetAsync instead.")] | |||||
| public Task<RestGuildEmbed?> GetGuildEmbedAsync(ulong id, RequestOptions options = null) | |||||
| => ClientHelper.GetGuildEmbedAsync(this, id, options); | |||||
| public Task<RestGuildWidget?> GetGuildWidgetAsync(ulong id, RequestOptions options = null) | public Task<RestGuildWidget?> GetGuildWidgetAsync(ulong id, RequestOptions options = null) | ||||
| => ClientHelper.GetGuildWidgetAsync(this, id, options); | => ClientHelper.GetGuildWidgetAsync(this, id, options); | ||||
| public IAsyncEnumerable<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<RestUserGuild>> GetGuildSummariesAsync(RequestOptions options = null) | ||||
| @@ -107,7 +101,19 @@ namespace Discord.Rest | |||||
| => ClientHelper.GetVoiceRegionAsync(this, id, options); | => ClientHelper.GetVoiceRegionAsync(this, id, options); | ||||
| public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null) | public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null) | ||||
| => ClientHelper.GetWebhookAsync(this, id, options); | => ClientHelper.GetWebhookAsync(this, id, options); | ||||
| public Task AddRoleAsync(ulong guildId, ulong userId, ulong roleId) | |||||
| => ClientHelper.AddRoleAsync(this, guildId, userId, roleId); | |||||
| public Task RemoveRoleAsync(ulong guildId, ulong userId, ulong roleId) | |||||
| => ClientHelper.RemoveRoleAsync(this, guildId, userId, roleId); | |||||
| public Task AddReactionAsync(ulong channelId, ulong messageId, IEmote emote, RequestOptions options = null) | |||||
| => MessageHelper.AddReactionAsync(channelId, messageId, emote, this, options); | |||||
| public Task RemoveReactionAsync(ulong channelId, ulong messageId, ulong userId, IEmote emote, RequestOptions options = null) | |||||
| => MessageHelper.RemoveReactionAsync(channelId, messageId, userId, emote, this, options); | |||||
| public Task RemoveAllReactionsAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||||
| => MessageHelper.RemoveAllReactionsAsync(channelId, messageId, this, options); | |||||
| public Task RemoveAllReactionsForEmoteAsync(ulong channelId, ulong messageId, IEmote emote, RequestOptions options = null) | |||||
| => MessageHelper.RemoveAllReactionsForEmoteAsync(channelId, messageId, emote, this, options); | |||||
| //IDiscordClient | //IDiscordClient | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IApplication> IDiscordClient.GetApplicationInfoAsync(RequestOptions options) | async Task<IApplication> IDiscordClient.GetApplicationInfoAsync(RequestOptions options) | ||||
| @@ -5,13 +5,14 @@ namespace Discord.Rest | |||||
| /// </summary> | /// </summary> | ||||
| public struct ChannelInfo | public struct ChannelInfo | ||||
| { | { | ||||
| internal ChannelInfo(string name, string topic, int? rateLimit, bool? nsfw, int? bitrate) | |||||
| internal ChannelInfo(string name, string topic, int? rateLimit, bool? nsfw, int? bitrate, ChannelType? type) | |||||
| { | { | ||||
| Name = name; | Name = name; | ||||
| Topic = topic; | Topic = topic; | ||||
| SlowModeInterval = rateLimit; | SlowModeInterval = rateLimit; | ||||
| IsNsfw = nsfw; | IsNsfw = nsfw; | ||||
| Bitrate = bitrate; | Bitrate = bitrate; | ||||
| ChannelType = type; | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -53,5 +54,12 @@ namespace Discord.Rest | |||||
| /// <c>null</c> if this is not mentioned in this entry. | /// <c>null</c> if this is not mentioned in this entry. | ||||
| /// </returns> | /// </returns> | ||||
| public int? Bitrate { get; } | public int? Bitrate { get; } | ||||
| /// <summary> | |||||
| /// Gets the type of this channel. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// The channel type of this channel; <c>null</c> if not applicable. | |||||
| /// </returns> | |||||
| public ChannelType? ChannelType { get; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -26,6 +26,7 @@ namespace Discord.Rest | |||||
| var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); | var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); | ||||
| var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); | var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); | ||||
| var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); | var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); | ||||
| var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); | |||||
| string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | ||||
| newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | ||||
| @@ -37,9 +38,11 @@ namespace Discord.Rest | |||||
| newNsfw = nsfwModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | newNsfw = nsfwModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | ||||
| int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | ||||
| newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | ||||
| ChannelType? oldType = typeModel?.OldValue?.ToObject<ChannelType>(discord.ApiClient.Serializer), | |||||
| newType = typeModel?.NewValue?.ToObject<ChannelType>(discord.ApiClient.Serializer); | |||||
| var before = new ChannelInfo(oldName, oldTopic, oldRateLimitPerUser, oldNsfw, oldBitrate); | |||||
| var after = new ChannelInfo(newName, newTopic, newRateLimitPerUser, newNsfw, newBitrate); | |||||
| var before = new ChannelInfo(oldName, oldTopic, oldRateLimitPerUser, oldNsfw, oldBitrate, oldType); | |||||
| var after = new ChannelInfo(newName, newTopic, newRateLimitPerUser, newNsfw, newBitrate, newType); | |||||
| return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); | return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); | ||||
| } | } | ||||
| @@ -19,8 +19,14 @@ namespace Discord.Rest | |||||
| internal static MessagePinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | internal static MessagePinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | ||||
| { | { | ||||
| var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||||
| return new MessagePinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, RestUser.Create(discord, userInfo)); | |||||
| RestUser user = null; | |||||
| if (entry.TargetId.HasValue) | |||||
| { | |||||
| var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||||
| user = RestUser.Create(discord, userInfo); | |||||
| } | |||||
| return new MessagePinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, user); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -38,10 +44,10 @@ namespace Discord.Rest | |||||
| /// </returns> | /// </returns> | ||||
| public ulong ChannelId { get; } | public ulong ChannelId { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the user of the message that was pinned. | |||||
| /// Gets the user of the message that was pinned if available. | |||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| /// A user object representing the user that created the pinned message. | |||||
| /// A user object representing the user that created the pinned message or <see langword="null"/>. | |||||
| /// </returns> | /// </returns> | ||||
| public IUser Target { get; } | public IUser Target { get; } | ||||
| } | } | ||||
| @@ -19,8 +19,14 @@ namespace Discord.Rest | |||||
| internal static MessageUnpinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | internal static MessageUnpinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | ||||
| { | { | ||||
| var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||||
| return new MessageUnpinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, RestUser.Create(discord, userInfo)); | |||||
| RestUser user = null; | |||||
| if (entry.TargetId.HasValue) | |||||
| { | |||||
| var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||||
| user = RestUser.Create(discord, userInfo); | |||||
| } | |||||
| return new MessageUnpinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, user); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -38,10 +44,10 @@ namespace Discord.Rest | |||||
| /// </returns> | /// </returns> | ||||
| public ulong ChannelId { get; } | public ulong ChannelId { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the user of the message that was unpinned. | |||||
| /// Gets the user of the message that was unpinned if available. | |||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| /// A user object representing the user that created the unpinned message. | |||||
| /// A user object representing the user that created the unpinned message or <see langword="null"/>. | |||||
| /// </returns> | /// </returns> | ||||
| public IUser Target { get; } | public IUser Target { get; } | ||||
| } | } | ||||
| @@ -22,7 +22,7 @@ namespace Discord.Rest | |||||
| internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model) | internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model) | ||||
| { | { | ||||
| var userInfo = fullLog.Users.FirstOrDefault(x => x.Id == model.UserId); | |||||
| var userInfo = model.UserId != null ? fullLog.Users.FirstOrDefault(x => x.Id == model.UserId) : null; | |||||
| IUser user = null; | IUser user = null; | ||||
| if (userInfo != null) | if (userInfo != null) | ||||
| user = RestUser.Create(discord, userInfo); | user = RestUser.Create(discord, userInfo); | ||||
| @@ -33,8 +33,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -59,8 +59,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -84,8 +84,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -286,6 +286,13 @@ namespace Discord.Rest | |||||
| return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
| } | } | ||||
| public static async Task<RestUserMessage> ModifyMessageAsync(IMessageChannel channel, ulong messageId, Action<MessageProperties> func, | |||||
| BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| var msgModel = await MessageHelper.ModifyAsync(channel.Id, messageId, client, func, options).ConfigureAwait(false); | |||||
| return RestUserMessage.Create(client, channel, msgModel.Author.IsSpecified ? RestUser.Create(client, msgModel.Author.Value) : client.CurrentUser, msgModel); | |||||
| } | |||||
| public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client, | public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client, | ||||
| RequestOptions options) | RequestOptions options) | ||||
| => MessageHelper.DeleteAsync(channel.Id, messageId, client, options); | => MessageHelper.DeleteAsync(channel.Id, messageId, client, options); | ||||
| @@ -321,13 +328,13 @@ namespace Discord.Rest | |||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | ||||
| IUser user, OverwritePermissions perms, RequestOptions options) | IUser user, OverwritePermissions perms, RequestOptions options) | ||||
| { | { | ||||
| var args = new ModifyChannelPermissionsParams("member", perms.AllowValue, perms.DenyValue); | |||||
| var args = new ModifyChannelPermissionsParams((int)PermissionTarget.User, perms.AllowValue.ToString(), perms.DenyValue.ToString()); | |||||
| await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args, options).ConfigureAwait(false); | await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, user.Id, args, options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | ||||
| IRole role, OverwritePermissions perms, RequestOptions options) | IRole role, OverwritePermissions perms, RequestOptions options) | ||||
| { | { | ||||
| var args = new ModifyChannelPermissionsParams("role", perms.AllowValue, perms.DenyValue); | |||||
| var args = new ModifyChannelPermissionsParams((int)PermissionTarget.Role, perms.AllowValue.ToString(), perms.DenyValue.ToString()); | |||||
| await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args, options).ConfigureAwait(false); | await client.ApiClient.ModifyChannelPermissionsAsync(channel.Id, role.Id, args, options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client, | ||||
| @@ -443,8 +450,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| }; | }; | ||||
| await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | ||||
| @@ -135,6 +135,10 @@ namespace Discord.Rest | |||||
| public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -93,6 +93,10 @@ namespace Discord.Rest | |||||
| public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <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> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null) | public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null) | ||||
| @@ -152,6 +152,10 @@ namespace Discord.Rest | |||||
| public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -4,7 +4,6 @@ using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using EmbedModel = Discord.API.GuildEmbed; | |||||
| using WidgetModel = Discord.API.GuildWidget; | using WidgetModel = Discord.API.GuildWidget; | ||||
| using Model = Discord.API.Guild; | using Model = Discord.API.Guild; | ||||
| using RoleModel = Discord.API.Role; | using RoleModel = Discord.API.Role; | ||||
| @@ -81,26 +80,6 @@ namespace Discord.Rest | |||||
| return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <c>null</c>.</exception> | /// <exception cref="ArgumentNullException"><paramref name="func"/> is <c>null</c>.</exception> | ||||
| public static async Task<EmbedModel> ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, | |||||
| Action<GuildEmbedProperties> func, RequestOptions options) | |||||
| { | |||||
| if (func == null) throw new ArgumentNullException(nameof(func)); | |||||
| var args = new GuildEmbedProperties(); | |||||
| func(args); | |||||
| var apiArgs = new API.Rest.ModifyGuildEmbedParams | |||||
| { | |||||
| Enabled = args.Enabled | |||||
| }; | |||||
| if (args.Channel.IsSpecified) | |||||
| apiArgs.ChannelId = args.Channel.Value?.Id; | |||||
| else if (args.ChannelId.IsSpecified) | |||||
| apiArgs.ChannelId = args.ChannelId.Value; | |||||
| return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, apiArgs, options).ConfigureAwait(false); | |||||
| } | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <c>null</c>.</exception> | |||||
| public static async Task<WidgetModel> ModifyWidgetAsync(IGuild guild, BaseDiscordClient client, | public static async Task<WidgetModel> ModifyWidgetAsync(IGuild guild, BaseDiscordClient client, | ||||
| Action<GuildWidgetProperties> func, RequestOptions options) | Action<GuildWidgetProperties> func, RequestOptions options) | ||||
| { | { | ||||
| @@ -205,8 +184,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -233,8 +212,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -258,8 +237,8 @@ namespace Discord.Rest | |||||
| { | { | ||||
| TargetId = overwrite.TargetId, | TargetId = overwrite.TargetId, | ||||
| TargetType = overwrite.TargetType, | TargetType = overwrite.TargetType, | ||||
| Allow = overwrite.Permissions.AllowValue, | |||||
| Deny = overwrite.Permissions.DenyValue | |||||
| Allow = overwrite.Permissions.AllowValue.ToString(), | |||||
| Deny = overwrite.Permissions.DenyValue.ToString() | |||||
| }).ToArray() | }).ToArray() | ||||
| : Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
| }; | }; | ||||
| @@ -304,6 +283,7 @@ namespace Discord.Rest | |||||
| var vanityModel = await client.ApiClient.GetVanityInviteAsync(guild.Id, options).ConfigureAwait(false); | var vanityModel = await client.ApiClient.GetVanityInviteAsync(guild.Id, options).ConfigureAwait(false); | ||||
| if (vanityModel == null) throw new InvalidOperationException("This guild does not have a vanity URL."); | if (vanityModel == null) throw new InvalidOperationException("This guild does not have a vanity URL."); | ||||
| var inviteModel = await client.ApiClient.GetInviteAsync(vanityModel.Code, options).ConfigureAwait(false); | var inviteModel = await client.ApiClient.GetInviteAsync(vanityModel.Code, options).ConfigureAwait(false); | ||||
| inviteModel.Uses = vanityModel.Uses; | |||||
| return RestInviteMetadata.Create(client, guild, null, inviteModel); | return RestInviteMetadata.Create(client, guild, null, inviteModel); | ||||
| } | } | ||||
| @@ -320,7 +300,7 @@ namespace Discord.Rest | |||||
| Hoist = isHoisted, | Hoist = isHoisted, | ||||
| Mentionable = isMentionable, | Mentionable = isMentionable, | ||||
| Name = name, | Name = name, | ||||
| Permissions = permissions?.RawValue ?? Optional.Create<ulong>() | |||||
| Permissions = permissions?.RawValue.ToString() ?? Optional.Create<string>() | |||||
| }; | }; | ||||
| var model = await client.ApiClient.CreateGuildRoleAsync(guild.Id, createGuildRoleParams, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateGuildRoleAsync(guild.Id, createGuildRoleParams, options).ConfigureAwait(false); | ||||
| @@ -496,6 +476,11 @@ namespace Discord.Rest | |||||
| } | } | ||||
| //Emotes | //Emotes | ||||
| public static async Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| var models = await client.ApiClient.GetGuildEmotesAsync(guild.Id, options).ConfigureAwait(false); | |||||
| return models.Select(x => x.ToEntity()).ToImmutableArray(); | |||||
| } | |||||
| public static async Task<GuildEmote> GetEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) | public static async Task<GuildEmote> GetEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) | ||||
| { | { | ||||
| var emote = await client.ApiClient.GetGuildEmoteAsync(guild.Id, id, options).ConfigureAwait(false); | var emote = await client.ApiClient.GetGuildEmoteAsync(guild.Id, id, options).ConfigureAwait(false); | ||||
| @@ -6,7 +6,6 @@ using System.Diagnostics; | |||||
| using System.Globalization; | using System.Globalization; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using EmbedModel = Discord.API.GuildEmbed; | |||||
| using WidgetModel = Discord.API.GuildWidget; | using WidgetModel = Discord.API.GuildWidget; | ||||
| using Model = Discord.API.Guild; | using Model = Discord.API.Guild; | ||||
| @@ -27,8 +26,6 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public int AFKTimeout { get; private set; } | public int AFKTimeout { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public bool IsEmbeddable { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public bool IsWidgetEnabled { get; private set; } | public bool IsWidgetEnabled { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public VerificationLevel VerificationLevel { get; private set; } | public VerificationLevel VerificationLevel { get; private set; } | ||||
| @@ -42,8 +39,6 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public ulong? AFKChannelId { get; private set; } | public ulong? AFKChannelId { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public ulong? EmbedChannelId { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public ulong? WidgetChannelId { get; private set; } | public ulong? WidgetChannelId { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public ulong? SystemChannelId { get; private set; } | public ulong? SystemChannelId { get; private set; } | ||||
| @@ -95,8 +90,6 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | ||||
| [Obsolete("DefaultChannelId is deprecated, use GetDefaultChannelAsync")] | |||||
| public ulong DefaultChannelId => Id; | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); | public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -133,16 +126,12 @@ namespace Discord.Rest | |||||
| internal void Update(Model model) | internal void Update(Model model) | ||||
| { | { | ||||
| AFKChannelId = model.AFKChannelId; | AFKChannelId = model.AFKChannelId; | ||||
| if (model.EmbedChannelId.IsSpecified) | |||||
| EmbedChannelId = model.EmbedChannelId.Value; | |||||
| if (model.WidgetChannelId.IsSpecified) | if (model.WidgetChannelId.IsSpecified) | ||||
| WidgetChannelId = model.WidgetChannelId.Value; | WidgetChannelId = model.WidgetChannelId.Value; | ||||
| SystemChannelId = model.SystemChannelId; | SystemChannelId = model.SystemChannelId; | ||||
| RulesChannelId = model.RulesChannelId; | RulesChannelId = model.RulesChannelId; | ||||
| PublicUpdatesChannelId = model.PublicUpdatesChannelId; | PublicUpdatesChannelId = model.PublicUpdatesChannelId; | ||||
| AFKTimeout = model.AFKTimeout; | AFKTimeout = model.AFKTimeout; | ||||
| if (model.EmbedEnabled.IsSpecified) | |||||
| IsEmbeddable = model.EmbedEnabled.Value; | |||||
| if (model.WidgetEnabled.IsSpecified) | if (model.WidgetEnabled.IsSpecified) | ||||
| IsWidgetEnabled = model.WidgetEnabled.Value; | IsWidgetEnabled = model.WidgetEnabled.Value; | ||||
| IconId = model.Icon; | IconId = model.Icon; | ||||
| @@ -200,11 +189,6 @@ namespace Discord.Rest | |||||
| Available = true; | Available = true; | ||||
| } | } | ||||
| internal void Update(EmbedModel model) | |||||
| { | |||||
| EmbedChannelId = model.ChannelId; | |||||
| IsEmbeddable = model.Enabled; | |||||
| } | |||||
| internal void Update(WidgetModel model) | internal void Update(WidgetModel model) | ||||
| { | { | ||||
| WidgetChannelId = model.ChannelId; | WidgetChannelId = model.ChannelId; | ||||
| @@ -241,15 +225,6 @@ namespace Discord.Rest | |||||
| Update(model); | Update(model); | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| [Obsolete("This endpoint is deprecated, use ModifyWidgetAsync instead.")] | |||||
| public async Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | |||||
| { | |||||
| var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); | |||||
| Update(model); | |||||
| } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | ||||
| public async Task ModifyWidgetAsync(Action<GuildWidgetProperties> func, RequestOptions options = null) | public async Task ModifyWidgetAsync(Action<GuildWidgetProperties> func, RequestOptions options = null) | ||||
| @@ -463,23 +438,6 @@ namespace Discord.Rest | |||||
| .FirstOrDefault(); | .FirstOrDefault(); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild. | |||||
| /// </summary> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous get operation. The task result contains the embed channel set | |||||
| /// within the server's widget settings; <see langword="null"/> if none is set. | |||||
| /// </returns> | |||||
| [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")] | |||||
| public async Task<RestGuildChannel> GetEmbedChannelAsync(RequestOptions options = null) | |||||
| { | |||||
| var embedId = EmbedChannelId; | |||||
| if (embedId.HasValue) | |||||
| return await GuildHelper.GetChannelAsync(this, Discord, embedId.Value, options).ConfigureAwait(false); | |||||
| return null; | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -828,6 +786,9 @@ namespace Discord.Rest | |||||
| //Emotes | //Emotes | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(RequestOptions options = null) | |||||
| => GuildHelper.GetEmotesAsync(this, Discord, options); | |||||
| /// <inheritdoc /> | |||||
| public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | ||||
| => GuildHelper.GetEmoteAsync(this, Discord, id, options); | => GuildHelper.GetEmoteAsync(this, Discord, id, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -934,15 +895,6 @@ namespace Discord.Rest | |||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")] | |||||
| async Task<IGuildChannel> IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return await GetEmbedChannelAsync(options).ConfigureAwait(false); | |||||
| else | |||||
| return null; | |||||
| } | |||||
| /// <inheritdoc /> | |||||
| async Task<IGuildChannel> IGuild.GetWidgetChannelAsync(CacheMode mode, RequestOptions options) | async Task<IGuildChannel> IGuild.GetWidgetChannelAsync(CacheMode mode, RequestOptions options) | ||||
| { | { | ||||
| if (mode == CacheMode.AllowDownload) | if (mode == CacheMode.AllowDownload) | ||||
| @@ -1,25 +0,0 @@ | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.GuildEmbed; | |||||
| namespace Discord.Rest | |||||
| { | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public struct RestGuildEmbed | |||||
| { | |||||
| public bool IsEnabled { get; private set; } | |||||
| public ulong? ChannelId { get; private set; } | |||||
| internal RestGuildEmbed(bool isEnabled, ulong? channelId) | |||||
| { | |||||
| ChannelId = channelId; | |||||
| IsEnabled = isEnabled; | |||||
| } | |||||
| internal static RestGuildEmbed Create(Model model) | |||||
| { | |||||
| return new RestGuildEmbed(model.Enabled, model.ChannelId); | |||||
| } | |||||
| public override string ToString() => ChannelId?.ToString() ?? "Unknown"; | |||||
| private string DebuggerDisplay => $"{ChannelId} ({(IsEnabled ? "Enabled" : "Disabled")})"; | |||||
| } | |||||
| } | |||||
| @@ -8,9 +8,6 @@ namespace Discord.Rest | |||||
| { | { | ||||
| private long _createdAtTicks; | private long _createdAtTicks; | ||||
| /// <inheritdoc /> | |||||
| [Obsolete("This property doesn't exist anymore and shouldn't be used.")] | |||||
| public bool IsRevoked { get; private set; } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public bool IsTemporary { get; private set; } | public bool IsTemporary { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -71,6 +71,48 @@ namespace Discord.Rest | |||||
| return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task<Model> ModifyAsync(ulong channelId, ulong msgId, BaseDiscordClient client, Action<MessageProperties> func, | |||||
| RequestOptions options) | |||||
| { | |||||
| var args = new MessageProperties(); | |||||
| func(args); | |||||
| if ((args.Content.IsSpecified && string.IsNullOrEmpty(args.Content.Value)) && (args.Embed.IsSpecified && args.Embed.Value == null)) | |||||
| Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); | |||||
| if (args.AllowedMentions.IsSpecified) | |||||
| { | |||||
| AllowedMentions allowedMentions = args.AllowedMentions.Value; | |||||
| 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 apiArgs = new API.Rest.ModifyMessageParams | |||||
| { | |||||
| Content = args.Content, | |||||
| Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>(), | |||||
| Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(), | |||||
| AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(), | |||||
| }; | |||||
| return await client.ApiClient.ModifyMessageAsync(channelId, msgId, apiArgs, options).ConfigureAwait(false); | |||||
| } | |||||
| public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | ||||
| => DeleteAsync(msg.Channel.Id, msg.Id, client, options); | => DeleteAsync(msg.Channel.Id, msg.Id, client, options); | ||||
| @@ -80,13 +122,9 @@ namespace Discord.Rest | |||||
| await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false); | await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task SuppressEmbedsAsync(IMessage msg, BaseDiscordClient client, bool suppress, RequestOptions options) | |||||
| public static async Task AddReactionAsync(ulong channelId, ulong messageId, IEmote emote, BaseDiscordClient client, RequestOptions options) | |||||
| { | { | ||||
| var apiArgs = new API.Rest.SuppressEmbedParams | |||||
| { | |||||
| Suppressed = suppress | |||||
| }; | |||||
| await client.ApiClient.SuppressEmbedAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | |||||
| await client.ApiClient.AddReactionAsync(channelId, messageId, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | |||||
| } | } | ||||
| public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) | public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) | ||||
| @@ -94,16 +132,31 @@ namespace Discord.Rest | |||||
| await client.ApiClient.AddReactionAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | await client.ApiClient.AddReactionAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemoveReactionAsync(ulong channelId, ulong messageId, ulong userId, IEmote emote, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| await client.ApiClient.RemoveReactionAsync(channelId, messageId, userId, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | |||||
| } | |||||
| public static async Task RemoveReactionAsync(IMessage msg, ulong userId, IEmote emote, BaseDiscordClient client, RequestOptions options) | public static async Task RemoveReactionAsync(IMessage msg, ulong userId, IEmote emote, BaseDiscordClient client, RequestOptions options) | ||||
| { | { | ||||
| await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, userId, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, userId, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemoveAllReactionsAsync(ulong channelId, ulong messageId, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| await client.ApiClient.RemoveAllReactionsAsync(channelId, messageId, options).ConfigureAwait(false); | |||||
| } | |||||
| public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | ||||
| { | { | ||||
| await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); | await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); | ||||
| } | } | ||||
| public static async Task RemoveAllReactionsForEmoteAsync(ulong channelId, ulong messageId, IEmote emote, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| await client.ApiClient.RemoveAllReactionsForEmoteAsync(channelId, messageId, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | |||||
| } | |||||
| public static async Task RemoveAllReactionsForEmoteAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) | public static async Task RemoveAllReactionsForEmoteAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) | ||||
| { | { | ||||
| await client.ApiClient.RemoveAllReactionsForEmoteAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | await client.ApiClient.RemoveAllReactionsForEmoteAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false); | ||||
| @@ -58,6 +58,8 @@ namespace Discord.Rest | |||||
| public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | public virtual IReadOnlyCollection<RestUser> MentionedUsers => ImmutableArray.Create<RestUser>(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | ||||
| /// <inheritdoc /> | |||||
| public virtual IReadOnlyCollection<Sticker> Stickers => ImmutableArray.Create<Sticker>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| @@ -173,6 +175,8 @@ namespace Discord.Rest | |||||
| IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ISticker> IMessage.Stickers => Stickers; | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); | public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); | ||||
| @@ -21,6 +21,7 @@ namespace Discord.Rest | |||||
| private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>(); | private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>(); | ||||
| private ImmutableArray<ulong> _roleMentionIds = ImmutableArray.Create<ulong>(); | private ImmutableArray<ulong> _roleMentionIds = ImmutableArray.Create<ulong>(); | ||||
| private ImmutableArray<RestUser> _userMentions = ImmutableArray.Create<RestUser>(); | private ImmutableArray<RestUser> _userMentions = ImmutableArray.Create<RestUser>(); | ||||
| private ImmutableArray<Sticker> _stickers = ImmutableArray.Create<Sticker>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override bool IsTTS => _isTTS; | public override bool IsTTS => _isTTS; | ||||
| @@ -45,6 +46,8 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override IReadOnlyCollection<ITag> Tags => _tags; | public override IReadOnlyCollection<ITag> Tags => _tags; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override IReadOnlyCollection<Sticker> Stickers => _stickers; | |||||
| /// <inheritdoc /> | |||||
| public IUserMessage ReferencedMessage => _referencedMessage; | public IUserMessage ReferencedMessage => _referencedMessage; | ||||
| internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) | internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) | ||||
| @@ -132,6 +135,20 @@ namespace Discord.Rest | |||||
| IUser refMsgAuthor = MessageHelper.GetAuthor(Discord, guild, refMsg.Author.Value, refMsg.WebhookId.ToNullable()); | IUser refMsgAuthor = MessageHelper.GetAuthor(Discord, guild, refMsg.Author.Value, refMsg.WebhookId.ToNullable()); | ||||
| _referencedMessage = RestUserMessage.Create(Discord, Channel, refMsgAuthor, refMsg); | _referencedMessage = RestUserMessage.Create(Discord, Channel, refMsgAuthor, refMsg); | ||||
| } | } | ||||
| if (model.Stickers.IsSpecified) | |||||
| { | |||||
| var value = model.Stickers.Value; | |||||
| if (value.Length > 0) | |||||
| { | |||||
| var stickers = ImmutableArray.CreateBuilder<Sticker>(value.Length); | |||||
| for (int i = 0; i < value.Length; i++) | |||||
| stickers.Add(Sticker.Create(value[i])); | |||||
| _stickers = stickers.ToImmutable(); | |||||
| } | |||||
| else | |||||
| _stickers = ImmutableArray.Create<Sticker>(); | |||||
| } | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -147,9 +164,6 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task UnpinAsync(RequestOptions options = null) | public Task UnpinAsync(RequestOptions options = null) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task ModifySuppressionAsync(bool suppressEmbeds, RequestOptions options = null) | |||||
| => MessageHelper.SuppressEmbedsAsync(this, Discord, suppressEmbeds, options); | |||||
| public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| @@ -0,0 +1,48 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.Sticker; | |||||
| namespace Discord | |||||
| { | |||||
| /// <inheritdoc cref="ISticker"/> | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public class Sticker : ISticker | |||||
| { | |||||
| /// <inheritdoc /> | |||||
| public ulong Id { get; } | |||||
| /// <inheritdoc /> | |||||
| public ulong PackId { get; } | |||||
| /// <inheritdoc /> | |||||
| public string Name { get; } | |||||
| /// <inheritdoc /> | |||||
| public string Description { get; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<string> Tags { get; } | |||||
| /// <inheritdoc /> | |||||
| public string Asset { get; } | |||||
| /// <inheritdoc /> | |||||
| public string PreviewAsset { get; } | |||||
| /// <inheritdoc /> | |||||
| public StickerFormatType FormatType { get; } | |||||
| internal Sticker(ulong id, ulong packId, string name, string description, string[] tags, string asset, string previewAsset, StickerFormatType formatType) | |||||
| { | |||||
| Id = id; | |||||
| PackId = packId; | |||||
| Name = name; | |||||
| Description = description; | |||||
| Tags = tags.ToReadOnlyCollection(); | |||||
| Asset = asset; | |||||
| PreviewAsset = previewAsset; | |||||
| FormatType = formatType; | |||||
| } | |||||
| internal static Sticker Create(Model model) | |||||
| { | |||||
| return new Sticker(model.Id, model.PackId, model.Name, model.Desription, | |||||
| model.Tags.IsSpecified ? model.Tags.Value.Split(',') : new string[0], | |||||
| model.Asset, model.PreviewAsset, model.FormatType); | |||||
| } | |||||
| private string DebuggerDisplay => $"{Name} ({Id})"; | |||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| using System; | |||||
| using System; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Role; | using Model = Discord.API.Role; | ||||
| using BulkParams = Discord.API.Rest.ModifyGuildRolesParams; | using BulkParams = Discord.API.Rest.ModifyGuildRolesParams; | ||||
| @@ -24,7 +24,7 @@ namespace Discord.Rest | |||||
| Hoist = args.Hoist, | Hoist = args.Hoist, | ||||
| Mentionable = args.Mentionable, | Mentionable = args.Mentionable, | ||||
| Name = args.Name, | Name = args.Name, | ||||
| Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue : Optional.Create<ulong>() | |||||
| Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue.ToString() : Optional.Create<string>() | |||||
| }; | }; | ||||
| var model = await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false); | var model = await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false); | ||||
| @@ -112,17 +112,29 @@ namespace Discord.Rest | |||||
| public Task KickAsync(string reason = null, RequestOptions options = null) | public Task KickAsync(string reason = null, RequestOptions options = null) | ||||
| => UserHelper.KickAsync(this, Discord, reason, options); | => UserHelper.KickAsync(this, Discord, reason, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task AddRoleAsync(ulong roleId, RequestOptions options = null) | |||||
| => AddRolesAsync(new[] { roleId }, options); | |||||
| /// <inheritdoc /> | |||||
| public Task AddRoleAsync(IRole role, RequestOptions options = null) | public Task AddRoleAsync(IRole role, RequestOptions options = null) | ||||
| => AddRolesAsync(new[] { role }, options); | |||||
| => AddRoleAsync(role.Id, options); | |||||
| /// <inheritdoc /> | |||||
| public Task AddRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null) | |||||
| => UserHelper.AddRolesAsync(this, Discord, roleIds, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | public Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | ||||
| => UserHelper.AddRolesAsync(this, Discord, roles, options); | |||||
| => AddRolesAsync(roles.Select(x => x.Id), options); | |||||
| /// <inheritdoc /> | |||||
| public Task RemoveRoleAsync(ulong roleId, RequestOptions options = null) | |||||
| => RemoveRolesAsync(new[] { roleId }, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task RemoveRoleAsync(IRole role, RequestOptions options = null) | public Task RemoveRoleAsync(IRole role, RequestOptions options = null) | ||||
| => RemoveRolesAsync(new[] { role }, options); | |||||
| => RemoveRoleAsync(role.Id, options); | |||||
| /// <inheritdoc /> | |||||
| public Task RemoveRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null) | |||||
| => UserHelper.RemoveRolesAsync(this, Discord, roleIds, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | public Task RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | ||||
| => UserHelper.RemoveRolesAsync(this, Discord, roles, options); | |||||
| => RemoveRolesAsync(roles.Select(x => x.Id)); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="InvalidOperationException">Resolving permissions requires the parent guild to be downloaded.</exception> | /// <exception cref="InvalidOperationException">Resolving permissions requires the parent guild to be downloaded.</exception> | ||||
| @@ -79,13 +79,13 @@ namespace Discord.Rest | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Returns a direct message channel to this user, or create one if it does not already exist. | |||||
| /// Creates a direct message channel to this user. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous get operation. The task result contains a rest DM channel where the user is the recipient. | /// A task that represents the asynchronous get operation. The task result contains a rest DM channel where the user is the recipient. | ||||
| /// </returns> | /// </returns> | ||||
| public Task<RestDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | |||||
| public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null) | |||||
| => UserHelper.CreateDMChannelAsync(this, Discord, options); | => UserHelper.CreateDMChannelAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -107,7 +107,7 @@ namespace Discord.Rest | |||||
| //IUser | //IUser | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IDMChannel> IUser.GetOrCreateDMChannelAsync(RequestOptions options) | |||||
| => await GetOrCreateDMChannelAsync(options).ConfigureAwait(false); | |||||
| async Task<IDMChannel> IUser.CreateDMChannelAsync(RequestOptions options) | |||||
| => await CreateDMChannelAsync(options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -59,27 +59,35 @@ namespace Discord.Rest | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.KickAsync(string reason, RequestOptions options) => | |||||
| Task IGuildUser.KickAsync(string reason, RequestOptions options) => | |||||
| throw new NotSupportedException("Webhook users cannot be kicked."); | throw new NotSupportedException("Webhook users cannot be kicked."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | |||||
| Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | |||||
| throw new NotSupportedException("Webhook users cannot be modified."); | throw new NotSupportedException("Webhook users cannot be modified."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | |||||
| Task IGuildUser.AddRoleAsync(ulong role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | |||||
| Task IGuildUser.AddRolesAsync(IEnumerable<ulong> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| Task IGuildUser.RemoveRoleAsync(ulong role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<ulong> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| //IVoiceState | //IVoiceState | ||||
| @@ -73,16 +73,16 @@ namespace Discord.Rest | |||||
| return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args, options).ConfigureAwait(false)); | return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args, options).ConfigureAwait(false)); | ||||
| } | } | ||||
| public static async Task AddRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable<IRole> roles, RequestOptions options) | |||||
| public static async Task AddRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable<ulong> roleIds, RequestOptions options) | |||||
| { | { | ||||
| foreach (var role in roles) | |||||
| await client.ApiClient.AddRoleAsync(user.Guild.Id, user.Id, role.Id, options).ConfigureAwait(false); | |||||
| foreach (var roleId in roleIds) | |||||
| await client.ApiClient.AddRoleAsync(user.Guild.Id, user.Id, roleId, options).ConfigureAwait(false); | |||||
| } | } | ||||
| public static async Task RemoveRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable<IRole> roles, RequestOptions options) | |||||
| public static async Task RemoveRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable<ulong> roleIds, RequestOptions options) | |||||
| { | { | ||||
| foreach (var role in roles) | |||||
| await client.ApiClient.RemoveRoleAsync(user.Guild.Id, user.Id, role.Id, options).ConfigureAwait(false); | |||||
| foreach (var roleId in roleIds) | |||||
| await client.ApiClient.RemoveRoleAsync(user.Guild.Id, user.Id, roleId, options).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -11,11 +11,11 @@ namespace Discord.Rest | |||||
| internal IGuild Guild { get; private set; } | internal IGuild Guild { get; private set; } | ||||
| internal ITextChannel Channel { get; private set; } | internal ITextChannel Channel { get; private set; } | ||||
| /// <inheritdoc /> | |||||
| public ulong ChannelId { get; } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string Token { get; } | public string Token { get; } | ||||
| /// <inheritdoc /> | |||||
| public ulong ChannelId { get; private set; } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -56,6 +56,8 @@ namespace Discord.Rest | |||||
| internal void Update(Model model) | internal void Update(Model model) | ||||
| { | { | ||||
| if (ChannelId != model.ChannelId) | |||||
| ChannelId = model.ChannelId; | |||||
| if (model.Avatar.IsSpecified) | if (model.Avatar.IsSpecified) | ||||
| AvatarId = model.Avatar.Value; | AvatarId = model.Avatar.Value; | ||||
| if (model.Creator.IsSpecified) | if (model.Creator.IsSpecified) | ||||
| @@ -73,8 +73,6 @@ namespace Discord.Net.Converters | |||||
| } | } | ||||
| //Enums | //Enums | ||||
| if (type == typeof(PermissionTarget)) | |||||
| return PermissionTargetConverter.Instance; | |||||
| if (type == typeof(UserStatus)) | if (type == typeof(UserStatus)) | ||||
| return UserStatusConverter.Instance; | return UserStatusConverter.Instance; | ||||
| if (type == typeof(EmbedType)) | if (type == typeof(EmbedType)) | ||||
| @@ -1,44 +0,0 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| namespace Discord.Net.Converters | |||||
| { | |||||
| internal class PermissionTargetConverter : JsonConverter | |||||
| { | |||||
| public static readonly PermissionTargetConverter Instance = new PermissionTargetConverter(); | |||||
| public override bool CanConvert(Type objectType) => true; | |||||
| public override bool CanRead => true; | |||||
| public override bool CanWrite => true; | |||||
| /// <exception cref="JsonSerializationException">Unknown permission target.</exception> | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
| { | |||||
| switch ((string)reader.Value) | |||||
| { | |||||
| case "member": | |||||
| return PermissionTarget.User; | |||||
| case "role": | |||||
| return PermissionTarget.Role; | |||||
| default: | |||||
| throw new JsonSerializationException("Unknown permission target."); | |||||
| } | |||||
| } | |||||
| /// <exception cref="JsonSerializationException">Invalid permission target.</exception> | |||||
| public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||||
| { | |||||
| switch ((PermissionTarget)value) | |||||
| { | |||||
| case PermissionTarget.User: | |||||
| writer.WriteValue("member"); | |||||
| break; | |||||
| case PermissionTarget.Role: | |||||
| writer.WriteValue("role"); | |||||
| break; | |||||
| default: | |||||
| throw new JsonSerializationException("Invalid permission target."); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -17,8 +17,6 @@ namespace Discord.API.Gateway | |||||
| public Optional<int[]> ShardingParams { get; set; } | public Optional<int[]> ShardingParams { get; set; } | ||||
| [JsonProperty("presence")] | [JsonProperty("presence")] | ||||
| public Optional<StatusUpdateParams> Presence { get; set; } | public Optional<StatusUpdateParams> Presence { get; set; } | ||||
| [JsonProperty("guild_subscriptions")] | |||||
| public Optional<bool> GuildSubscriptions { get; set; } | |||||
| [JsonProperty("intents")] | [JsonProperty("intents")] | ||||
| public Optional<int> Intents { get; set; } | public Optional<int> Intents { get; set; } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| @@ -12,5 +12,7 @@ namespace Discord.API.Gateway | |||||
| public ulong ChannelId { get; set; } | public ulong ChannelId { get; set; } | ||||
| [JsonProperty("emoji")] | [JsonProperty("emoji")] | ||||
| public Emoji Emoji { get; set; } | public Emoji Emoji { get; set; } | ||||
| [JsonProperty("member")] | |||||
| public Optional<GuildMember> Member { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -70,20 +70,11 @@ namespace Discord.WebSocket | |||||
| /// A read-only collection of private channels that the user currently partakes in. | /// A read-only collection of private channels that the user currently partakes in. | ||||
| /// </returns> | /// </returns> | ||||
| public abstract IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels { get; } | public abstract IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels { get; } | ||||
| /// <summary> | |||||
| /// Gets a collection of available voice regions. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A read-only collection of voice regions that the user has access to. | |||||
| /// </returns> | |||||
| [Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")] | |||||
| public abstract IReadOnlyCollection<RestVoiceRegion> VoiceRegions { get; } | |||||
| internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | ||||
| : base(config, client) => BaseConfig = config; | : base(config, client) => BaseConfig = config; | ||||
| private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | ||||
| => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, | => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, | ||||
| rateLimitPrecision: config.RateLimitPrecision, | |||||
| useSystemClock: config.UseSystemClock); | useSystemClock: config.UseSystemClock); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -164,16 +155,6 @@ namespace Discord.WebSocket | |||||
| /// </returns> | /// </returns> | ||||
| public abstract SocketGuild GetGuild(ulong id); | public abstract SocketGuild GetGuild(ulong id); | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets a voice region. | |||||
| /// </summary> | |||||
| /// <param name="id">The identifier of the voice region (e.g. <c>eu-central</c> ).</param> | |||||
| /// <returns> | |||||
| /// A REST-based voice region associated with the identifier; <c>null</c> if the voice region is not | |||||
| /// found. | |||||
| /// </returns> | |||||
| [Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")] | |||||
| public abstract RestVoiceRegion GetVoiceRegion(string id); | |||||
| /// <summary> | |||||
| /// Gets all voice regions. | /// Gets all voice regions. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="options">The options to be used when sending the request.</param> | /// <param name="options">The options to be used when sending the request.</param> | ||||
| @@ -209,6 +190,12 @@ namespace Discord.WebSocket | |||||
| /// <param name="name">The name of the game.</param> | /// <param name="name">The name of the game.</param> | ||||
| /// <param name="streamUrl">If streaming, the URL of the stream. Must be a valid Twitch URL.</param> | /// <param name="streamUrl">If streaming, the URL of the stream. Must be a valid Twitch URL.</param> | ||||
| /// <param name="type">The type of the game.</param> | /// <param name="type">The type of the game.</param> | ||||
| /// <remarks> | |||||
| /// <note type="warning"> | |||||
| /// Bot accounts cannot set <see cref="ActivityType.CustomStatus"/> as their activity | |||||
| /// type and it will have no effect. | |||||
| /// </note> | |||||
| /// </remarks> | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous set operation. | /// A task that represents the asynchronous set operation. | ||||
| /// </returns> | /// </returns> | ||||
| @@ -222,6 +209,10 @@ namespace Discord.WebSocket | |||||
| /// Discord will only accept setting of name and the type of activity. | /// Discord will only accept setting of name and the type of activity. | ||||
| /// </note> | /// </note> | ||||
| /// <note type="warning"> | /// <note type="warning"> | ||||
| /// Bot accounts cannot set <see cref="ActivityType.CustomStatus"/> as their activity | |||||
| /// type and it will have no effect. | |||||
| /// </note> | |||||
| /// <note type="warning"> | |||||
| /// Rich Presence cannot be set via this method or client. Rich Presence is strictly limited to RPC | /// Rich Presence cannot be set via this method or client. Rich Presence is strictly limited to RPC | ||||
| /// clients only. | /// clients only. | ||||
| /// </note> | /// </note> | ||||
| @@ -317,10 +308,14 @@ namespace Discord.WebSocket | |||||
| => Task.FromResult<IUser>(GetUser(username, discriminator)); | => Task.FromResult<IUser>(GetUser(username, discriminator)); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | |||||
| => Task.FromResult<IVoiceRegion>(GetVoiceRegion(id)); | |||||
| async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | |||||
| { | |||||
| return await GetVoiceRegionAsync(id).ConfigureAwait(false); | |||||
| } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | |||||
| => Task.FromResult<IReadOnlyCollection<IVoiceRegion>>(VoiceRegions); | |||||
| async Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | |||||
| { | |||||
| return await GetVoiceRegionsAsync().ConfigureAwait(false); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -36,9 +36,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount); | public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount); | ||||
| public IReadOnlyCollection<DiscordSocketClient> Shards => _shards; | public IReadOnlyCollection<DiscordSocketClient> Shards => _shards; | ||||
| /// <inheritdoc /> | |||||
| [Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")] | |||||
| public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _shards[0].VoiceRegions; | |||||
| /// <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. | ||||
| @@ -91,8 +88,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | ||||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, | |||||
| rateLimitPrecision: config.RateLimitPrecision); | |||||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); | |||||
| internal async Task AcquireIdentifyLockAsync(int shardId, CancellationToken token) | internal async Task AcquireIdentifyLockAsync(int shardId, CancellationToken token) | ||||
| { | { | ||||
| @@ -264,11 +260,6 @@ namespace Discord.WebSocket | |||||
| return null; | return null; | ||||
| } | } | ||||
| /// <inheritdoc /> | |||||
| [Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")] | |||||
| public override RestVoiceRegion GetVoiceRegion(string id) | |||||
| => _shards[0].GetVoiceRegion(id); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| @@ -432,11 +423,15 @@ namespace Discord.WebSocket | |||||
| => Task.FromResult<IUser>(GetUser(username, discriminator)); | => Task.FromResult<IUser>(GetUser(username, discriminator)); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | |||||
| => Task.FromResult<IReadOnlyCollection<IVoiceRegion>>(VoiceRegions); | |||||
| async Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options) | |||||
| { | |||||
| return await GetVoiceRegionsAsync().ConfigureAwait(false); | |||||
| } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | |||||
| => Task.FromResult<IVoiceRegion>(GetVoiceRegion(id)); | |||||
| async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options) | |||||
| { | |||||
| return await GetVoiceRegionAsync(id).ConfigureAwait(false); | |||||
| } | |||||
| internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
| { | { | ||||
| @@ -40,9 +40,8 @@ namespace Discord.API | |||||
| public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, | public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, | ||||
| string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, | string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, | ||||
| RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, | |||||
| bool useSystemClock = true) | bool useSystemClock = true) | ||||
| : base(restClientProvider, userAgent, defaultRetryMode, serializer, rateLimitPrecision, useSystemClock) | |||||
| : base(restClientProvider, userAgent, defaultRetryMode, serializer, useSystemClock) | |||||
| { | { | ||||
| _gatewayUrl = url; | _gatewayUrl = url; | ||||
| if (url != null) | if (url != null) | ||||
| @@ -216,7 +215,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, GatewayIntents? gatewayIntents = null, (UserStatus, bool, long?, GameModel)? presence = null, RequestOptions options = null) | |||||
| public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, GatewayIntents gatewayIntents = GatewayIntents.AllUnprivileged, (UserStatus, bool, long?, GameModel)? presence = null, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var props = new Dictionary<string, string> | var props = new Dictionary<string, string> | ||||
| @@ -234,10 +233,7 @@ namespace Discord.API | |||||
| options.BucketId = GatewayBucket.Get(GatewayBucketType.Identify).Id; | options.BucketId = GatewayBucket.Get(GatewayBucketType.Identify).Id; | ||||
| if (gatewayIntents.HasValue) | |||||
| msg.Intents = (int)gatewayIntents.Value; | |||||
| else | |||||
| msg.GuildSubscriptions = guildSubscriptions; | |||||
| msg.Intents = (int)gatewayIntents; | |||||
| if (presence.HasValue) | if (presence.HasValue) | ||||
| { | { | ||||
| @@ -43,8 +43,7 @@ namespace Discord.WebSocket | |||||
| private DateTimeOffset? _statusSince; | private DateTimeOffset? _statusSince; | ||||
| private RestApplication _applicationInfo; | private RestApplication _applicationInfo; | ||||
| private bool _isDisposed; | private bool _isDisposed; | ||||
| private bool _guildSubscriptions; | |||||
| private GatewayIntents? _gatewayIntents; | |||||
| 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. | ||||
| @@ -109,9 +108,6 @@ namespace Discord.WebSocket | |||||
| /// </returns> | /// </returns> | ||||
| public IReadOnlyCollection<SocketGroupChannel> GroupChannels | public IReadOnlyCollection<SocketGroupChannel> GroupChannels | ||||
| => State.PrivateChannels.OfType<SocketGroupChannel>().ToImmutableArray(); | => State.PrivateChannels.OfType<SocketGroupChannel>().ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| [Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")] | |||||
| public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => GetVoiceRegionsAsync().GetAwaiter().GetResult(); | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a new REST/WebSocket-based Discord client. | /// Initializes a new REST/WebSocket-based Discord client. | ||||
| @@ -140,7 +136,6 @@ namespace Discord.WebSocket | |||||
| State = new ClientState(0, 0); | State = new ClientState(0, 0); | ||||
| Rest = new DiscordSocketRestClient(config, ApiClient); | Rest = new DiscordSocketRestClient(config, ApiClient); | ||||
| _heartbeatTimes = new ConcurrentQueue<long>(); | _heartbeatTimes = new ConcurrentQueue<long>(); | ||||
| _guildSubscriptions = config.GuildSubscriptions; | |||||
| _gatewayIntents = config.GatewayIntents; | _gatewayIntents = config.GatewayIntents; | ||||
| _stateLock = new SemaphoreSlim(1, 1); | _stateLock = new SemaphoreSlim(1, 1); | ||||
| @@ -182,8 +177,7 @@ namespace Discord.WebSocket | |||||
| _largeGuilds = new ConcurrentQueue<ulong>(); | _largeGuilds = new ConcurrentQueue<ulong>(); | ||||
| } | } | ||||
| private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | ||||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost, | |||||
| rateLimitPrecision: config.RateLimitPrecision); | |||||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
| { | { | ||||
| @@ -243,7 +237,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, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| finally | finally | ||||
| @@ -308,7 +302,7 @@ namespace Discord.WebSocket | |||||
| /// <summary> | /// <summary> | ||||
| /// Clears cached DM channels from the client. | /// Clears cached DM channels from the client. | ||||
| /// </summary> | /// </summary> | ||||
| public void PurgeDMChannelCache() => State.PurgeDMChannels(); | |||||
| public void PurgeDMChannelCache() => RemoveDMChannels(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override SocketUser GetUser(ulong id) | public override SocketUser GetUser(ulong id) | ||||
| @@ -322,12 +316,11 @@ namespace Discord.WebSocket | |||||
| public void PurgeUserCache() => State.PurgeUsers(); | public void PurgeUserCache() => State.PurgeUsers(); | ||||
| internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model) | internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model) | ||||
| { | { | ||||
| return state.GetOrAddUser(model.Id, x => | |||||
| { | |||||
| var user = SocketGlobalUser.Create(this, state, model); | |||||
| user.GlobalUser.AddRef(); | |||||
| return user; | |||||
| }); | |||||
| return state.GetOrAddUser(model.Id, x => SocketGlobalUser.Create(this, state, model)); | |||||
| } | |||||
| internal SocketUser GetOrCreateTemporaryUser(ClientState state, Discord.API.User model) | |||||
| { | |||||
| return state.GetUser(model.Id) ?? (SocketUser)SocketUnknownUser.Create(this, state, model); | |||||
| } | } | ||||
| internal SocketGlobalUser GetOrCreateSelfUser(ClientState state, Discord.API.User model) | internal SocketGlobalUser GetOrCreateSelfUser(ClientState state, Discord.API.User model) | ||||
| { | { | ||||
| @@ -335,18 +328,13 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var user = SocketGlobalUser.Create(this, state, model); | var user = SocketGlobalUser.Create(this, state, model); | ||||
| user.GlobalUser.AddRef(); | user.GlobalUser.AddRef(); | ||||
| user.Presence = new SocketPresence(UserStatus.Online, null, null, null); | |||||
| user.Presence = new SocketPresence(UserStatus.Online, null, null); | |||||
| return user; | return user; | ||||
| }); | }); | ||||
| } | } | ||||
| internal void RemoveUser(ulong id) | internal void RemoveUser(ulong id) | ||||
| => State.RemoveUser(id); | => State.RemoveUser(id); | ||||
| /// <inheritdoc /> | |||||
| [Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")] | |||||
| public override RestVoiceRegion GetVoiceRegion(string id) | |||||
| => GetVoiceRegionAsync(id).GetAwaiter().GetResult(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | ||||
| { | { | ||||
| @@ -469,7 +457,8 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| if (CurrentUser == null) | if (CurrentUser == null) | ||||
| return; | return; | ||||
| CurrentUser.Presence = new SocketPresence(Status, Activity, null, null); | |||||
| var activities = _activity.IsSpecified ? ImmutableList.Create(_activity.Value) : null; | |||||
| CurrentUser.Presence = new SocketPresence(Status, null, activities); | |||||
| var presence = BuildCurrentStatus() ?? (UserStatus.Online, false, null, null); | var presence = BuildCurrentStatus() ?? (UserStatus.Online, false, null, null); | ||||
| @@ -564,7 +553,7 @@ namespace Discord.WebSocket | |||||
| await _shardedClient.AcquireIdentifyLockAsync(ShardId, _connection.CancelToken).ConfigureAwait(false); | await _shardedClient.AcquireIdentifyLockAsync(ShardId, _connection.CancelToken).ConfigureAwait(false); | ||||
| try | try | ||||
| { | { | ||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| @@ -572,7 +561,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case GatewayOpCode.Reconnect: | case GatewayOpCode.Reconnect: | ||||
| @@ -595,7 +584,8 @@ namespace Discord.WebSocket | |||||
| var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); | var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); | ||||
| var currentUser = SocketSelfUser.Create(this, state, data.User); | var currentUser = SocketSelfUser.Create(this, state, data.User); | ||||
| currentUser.Presence = new SocketPresence(Status, Activity, null, null); | |||||
| var activities = _activity.IsSpecified ? ImmutableList.Create(_activity.Value) : null; | |||||
| currentUser.Presence = new SocketPresence(Status, null, activities); | |||||
| ApiClient.CurrentUserId = currentUser.Id; | ApiClient.CurrentUserId = currentUser.Id; | ||||
| int unavailableGuilds = 0; | int unavailableGuilds = 0; | ||||
| for (int i = 0; i < data.Guilds.Length; i++) | for (int i = 0; i < data.Guilds.Length; i++) | ||||
| @@ -1237,56 +1227,63 @@ namespace Discord.WebSocket | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<API.Message>(_serializer); | var data = (payload as JToken).ToObject<API.Message>(_serializer); | ||||
| if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) | |||||
| var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| var guild = (channel as SocketGuildChannel)?.Guild; | |||||
| if (guild != null && !guild.IsSynced) | |||||
| { | { | ||||
| var guild = (channel as SocketGuildChannel)?.Guild; | |||||
| if (guild != null && !guild.IsSynced) | |||||
| await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||||
| return; | |||||
| } | |||||
| if (channel == null) | |||||
| { | |||||
| if (!data.GuildId.IsSpecified) // assume it is a DM | |||||
| { | { | ||||
| await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); | |||||
| return; | |||||
| channel = CreateDMChannel(data.ChannelId, data.Author.Value, State); | |||||
| } | } | ||||
| SocketUser author; | |||||
| if (guild != null) | |||||
| else | |||||
| { | { | ||||
| if (data.WebhookId.IsSpecified) | |||||
| author = SocketWebhookUser.Create(guild, State, data.Author.Value, data.WebhookId.Value); | |||||
| else | |||||
| author = guild.GetUser(data.Author.Value.Id); | |||||
| await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||||
| return; | |||||
| } | } | ||||
| } | |||||
| SocketUser author; | |||||
| if (guild != null) | |||||
| { | |||||
| if (data.WebhookId.IsSpecified) | |||||
| author = SocketWebhookUser.Create(guild, State, data.Author.Value, data.WebhookId.Value); | |||||
| else | else | ||||
| author = (channel as SocketChannel).GetUser(data.Author.Value.Id); | |||||
| author = guild.GetUser(data.Author.Value.Id); | |||||
| } | |||||
| else | |||||
| author = (channel as SocketChannel).GetUser(data.Author.Value.Id); | |||||
| if (author == null) | |||||
| if (author == null) | |||||
| { | |||||
| if (guild != null) | |||||
| { | { | ||||
| if (guild != null) | |||||
| if (data.Member.IsSpecified) // member isn't always included, but use it when we can | |||||
| { | { | ||||
| if (data.Member.IsSpecified) // member isn't always included, but use it when we can | |||||
| { | |||||
| data.Member.Value.User = data.Author.Value; | |||||
| author = guild.AddOrUpdateUser(data.Member.Value); | |||||
| } | |||||
| else | |||||
| author = guild.AddOrUpdateUser(data.Author.Value); // user has no guild-specific data | |||||
| data.Member.Value.User = data.Author.Value; | |||||
| author = guild.AddOrUpdateUser(data.Member.Value); | |||||
| } | } | ||||
| else if (channel is SocketGroupChannel) | |||||
| author = (channel as SocketGroupChannel).GetOrAddUser(data.Author.Value); | |||||
| else | else | ||||
| { | |||||
| await UnknownChannelUserAsync(type, data.Author.Value.Id, channel.Id).ConfigureAwait(false); | |||||
| return; | |||||
| } | |||||
| author = guild.AddOrUpdateUser(data.Author.Value); // user has no guild-specific data | |||||
| } | |||||
| else if (channel is SocketGroupChannel groupChannel) | |||||
| author = groupChannel.GetOrAddUser(data.Author.Value); | |||||
| else | |||||
| { | |||||
| await UnknownChannelUserAsync(type, data.Author.Value.Id, channel.Id).ConfigureAwait(false); | |||||
| return; | |||||
| } | } | ||||
| var msg = SocketMessage.Create(this, State, author, channel, data); | |||||
| SocketChannelHelper.AddMessage(channel, this, msg); | |||||
| await TimedInvokeAsync(_messageReceivedEvent, nameof(MessageReceived), msg).ConfigureAwait(false); | |||||
| } | |||||
| else | |||||
| { | |||||
| await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); | |||||
| return; | |||||
| } | } | ||||
| var msg = SocketMessage.Create(this, State, author, channel, data); | |||||
| SocketChannelHelper.AddMessage(channel, this, msg); | |||||
| await TimedInvokeAsync(_messageReceivedEvent, nameof(MessageReceived), msg).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case "MESSAGE_UPDATE": | case "MESSAGE_UPDATE": | ||||
| @@ -1384,6 +1381,14 @@ namespace Discord.WebSocket | |||||
| ? Optional.Create<SocketUserMessage>() | ? Optional.Create<SocketUserMessage>() | ||||
| : Optional.Create(cachedMsg); | : Optional.Create(cachedMsg); | ||||
| if (data.Member.IsSpecified) | |||||
| { | |||||
| var guild = (channel as SocketGuildChannel)?.Guild; | |||||
| if (guild != null) | |||||
| user = guild.AddOrUpdateUser(data.Member.Value); | |||||
| } | |||||
| var optionalUser = user is null | var optionalUser = user is null | ||||
| ? Optional.Create<IUser>() | ? Optional.Create<IUser>() | ||||
| : Optional.Create(user); | : Optional.Create(user); | ||||
| @@ -1474,7 +1479,7 @@ namespace Discord.WebSocket | |||||
| var cacheable = new Cacheable<IUserMessage, ulong>(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage); | var cacheable = new Cacheable<IUserMessage, ulong>(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage); | ||||
| var emote = data.Emoji.ToIEmote(); | var emote = data.Emoji.ToIEmote(); | ||||
| cachedMsg?.RemoveAllReactionsForEmoteAsync(emote); | |||||
| cachedMsg?.RemoveReactionsForEmote(emote); | |||||
| await TimedInvokeAsync(_reactionsRemovedForEmoteEvent, nameof(ReactionsRemovedForEmote), cacheable, channel, emote).ConfigureAwait(false); | await TimedInvokeAsync(_reactionsRemovedForEmoteEvent, nameof(ReactionsRemovedForEmote), cacheable, channel, emote).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1930,24 +1935,29 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var channel = SocketChannel.CreatePrivate(this, state, model); | var channel = SocketChannel.CreatePrivate(this, state, model); | ||||
| state.AddChannel(channel as SocketChannel); | state.AddChannel(channel as SocketChannel); | ||||
| if (channel is SocketDMChannel dm) | |||||
| dm.Recipient.GlobalUser.DMChannel = dm; | |||||
| return channel; | return channel; | ||||
| } | } | ||||
| internal SocketDMChannel CreateDMChannel(ulong channelId, API.User model, ClientState state) | |||||
| { | |||||
| return SocketDMChannel.Create(this, state, channelId, model); | |||||
| } | |||||
| internal ISocketPrivateChannel RemovePrivateChannel(ulong id) | internal ISocketPrivateChannel RemovePrivateChannel(ulong id) | ||||
| { | { | ||||
| var channel = State.RemoveChannel(id) as ISocketPrivateChannel; | var channel = State.RemoveChannel(id) as ISocketPrivateChannel; | ||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (channel is SocketDMChannel dmChannel) | |||||
| dmChannel.Recipient.GlobalUser.DMChannel = null; | |||||
| foreach (var recipient in channel.Recipients) | foreach (var recipient in channel.Recipients) | ||||
| recipient.GlobalUser.RemoveRef(this); | recipient.GlobalUser.RemoveRef(this); | ||||
| } | } | ||||
| return channel; | return channel; | ||||
| } | } | ||||
| internal void RemoveDMChannels() | |||||
| { | |||||
| var channels = State.DMChannels; | |||||
| State.PurgeDMChannels(); | |||||
| foreach (var channel in channels) | |||||
| channel.Recipient.GlobalUser.RemoveRef(this); | |||||
| } | |||||
| private async Task GuildAvailableAsync(SocketGuild guild) | private async Task GuildAvailableAsync(SocketGuild guild) | ||||
| { | { | ||||
| @@ -127,12 +127,6 @@ namespace Discord.WebSocket | |||||
| /// </remarks> | /// </remarks> | ||||
| public bool? ExclusiveBulkDelete { get; set; } = null; | public bool? ExclusiveBulkDelete { get; set; } = null; | ||||
| /// <summary> | |||||
| /// 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> | |||||
| public bool GuildSubscriptions { get; set; } = true; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets the maximum identify concurrency. | /// Gets or sets the maximum identify concurrency. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -172,14 +166,15 @@ namespace Discord.WebSocket | |||||
| private int maxWaitForGuildAvailable = 10000; | private int maxWaitForGuildAvailable = 10000; | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets or sets gateway intents to limit what events are sent from Discord. Allows for more granular control than the <see cref="GuildSubscriptions"/> property. | |||||
| /// Gets or sets gateway intents to limit what events are sent from Discord. | |||||
| /// The default is <see cref="GatewayIntents.AllUnprivileged"/>. | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// For more information, please see | /// For more information, please see | ||||
| /// <see href="https://discord.com/developers/docs/topics/gateway#gateway-intents">GatewayIntents</see> | /// <see href="https://discord.com/developers/docs/topics/gateway#gateway-intents">GatewayIntents</see> | ||||
| /// on the official Discord API documentation. | /// on the official Discord API documentation. | ||||
| /// </remarks> | /// </remarks> | ||||
| public GatewayIntents? GatewayIntents { get; set; } | |||||
| public GatewayIntents GatewayIntents { get; set; } = GatewayIntents.AllUnprivileged; | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a new instance of the <see cref="DiscordSocketConfig"/> class with the default configuration. | /// Initializes a new instance of the <see cref="DiscordSocketConfig"/> class with the default configuration. | ||||
| @@ -16,32 +16,27 @@ namespace Discord.WebSocket | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class SocketDMChannel : SocketChannel, IDMChannel, ISocketPrivateChannel, ISocketMessageChannel | public class SocketDMChannel : SocketChannel, IDMChannel, ISocketPrivateChannel, ISocketMessageChannel | ||||
| { | { | ||||
| private readonly MessageCache _messages; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the recipient of the channel. | /// Gets the recipient of the channel. | ||||
| /// </summary> | /// </summary> | ||||
| public SocketUser Recipient { get; } | public SocketUser Recipient { get; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>(); | |||||
| public IReadOnlyCollection<SocketMessage> CachedMessages => ImmutableArray.Create<SocketMessage>(); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection that is the current logged-in user and the recipient. | /// Gets a collection that is the current logged-in user and the recipient. | ||||
| /// </summary> | /// </summary> | ||||
| public new IReadOnlyCollection<SocketUser> Users => ImmutableArray.Create(Discord.CurrentUser, Recipient); | public new IReadOnlyCollection<SocketUser> Users => ImmutableArray.Create(Discord.CurrentUser, Recipient); | ||||
| internal SocketDMChannel(DiscordSocketClient discord, ulong id, SocketGlobalUser recipient) | |||||
| internal SocketDMChannel(DiscordSocketClient discord, ulong id, SocketUser recipient) | |||||
| : base(discord, id) | : base(discord, id) | ||||
| { | { | ||||
| Recipient = recipient; | Recipient = recipient; | ||||
| recipient.GlobalUser.AddRef(); | |||||
| if (Discord.MessageCacheSize > 0) | |||||
| _messages = new MessageCache(Discord); | |||||
| } | } | ||||
| internal static SocketDMChannel Create(DiscordSocketClient discord, ClientState state, Model model) | internal static SocketDMChannel Create(DiscordSocketClient discord, ClientState state, Model model) | ||||
| { | { | ||||
| var entity = new SocketDMChannel(discord, model.Id, discord.GetOrCreateUser(state, model.Recipients.Value[0])); | |||||
| var entity = new SocketDMChannel(discord, model.Id, discord.GetOrCreateTemporaryUser(state, model.Recipients.Value[0])); | |||||
| entity.Update(state, model); | entity.Update(state, model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| @@ -49,6 +44,16 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| Recipient.Update(state, model.Recipients.Value[0]); | Recipient.Update(state, model.Recipients.Value[0]); | ||||
| } | } | ||||
| internal static SocketDMChannel Create(DiscordSocketClient discord, ClientState state, ulong channelId, API.User recipient) | |||||
| { | |||||
| var entity = new SocketDMChannel(discord, channelId, discord.GetOrCreateTemporaryUser(state, recipient)); | |||||
| entity.Update(state, recipient); | |||||
| return entity; | |||||
| } | |||||
| internal void Update(ClientState state, API.User recipient) | |||||
| { | |||||
| Recipient.Update(state, recipient); | |||||
| } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task CloseAsync(RequestOptions options = null) | public Task CloseAsync(RequestOptions options = null) | ||||
| @@ -57,7 +62,7 @@ namespace Discord.WebSocket | |||||
| //Messages | //Messages | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public SocketMessage GetCachedMessage(ulong id) | public SocketMessage GetCachedMessage(ulong id) | ||||
| => _messages?.Get(id); | |||||
| => null; | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the message associated with the given <paramref name="id"/>. | /// Gets the message associated with the given <paramref name="id"/>. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -68,10 +73,7 @@ namespace Discord.WebSocket | |||||
| /// </returns> | /// </returns> | ||||
| public async Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null) | public async Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null) | ||||
| { | { | ||||
| IMessage msg = _messages?.Get(id); | |||||
| if (msg == null) | |||||
| msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options).ConfigureAwait(false); | |||||
| return msg; | |||||
| return await ChannelHelper.GetMessageAsync(this, Discord, id, options).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -87,7 +89,7 @@ namespace Discord.WebSocket | |||||
| /// Paged collection of messages. | /// Paged collection of messages. | ||||
| /// </returns> | /// </returns> | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of messages in this channel. | /// Gets a collection of messages in this channel. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -103,7 +105,7 @@ namespace Discord.WebSocket | |||||
| /// Paged collection of messages. | /// Paged collection of messages. | ||||
| /// </returns> | /// </returns> | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets a collection of messages in this channel. | /// Gets a collection of messages in this channel. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -119,16 +121,16 @@ namespace Discord.WebSocket | |||||
| /// Paged collection of messages. | /// Paged collection of messages. | ||||
| /// </returns> | /// </returns> | ||||
| public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, null, Direction.Before, limit); | |||||
| => ImmutableArray.Create<SocketMessage>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessageId, dir, limit); | |||||
| => ImmutableArray.Create<SocketMessage>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | public IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | ||||
| => SocketChannelHelper.GetCachedMessages(this, Discord, _messages, fromMessage.Id, dir, limit); | |||||
| => ImmutableArray.Create<SocketMessage>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | ||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); | ||||
| @@ -152,6 +154,10 @@ namespace Discord.WebSocket | |||||
| public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -160,9 +166,12 @@ namespace Discord.WebSocket | |||||
| => ChannelHelper.EnterTypingState(this, Discord, options); | => ChannelHelper.EnterTypingState(this, Discord, options); | ||||
| internal void AddMessage(SocketMessage msg) | internal void AddMessage(SocketMessage msg) | ||||
| => _messages?.Add(msg); | |||||
| { | |||||
| } | |||||
| internal SocketMessage RemoveMessage(ulong id) | internal SocketMessage RemoveMessage(ulong id) | ||||
| => _messages?.Remove(id); | |||||
| { | |||||
| return null; | |||||
| } | |||||
| //Users | //Users | ||||
| /// <summary> | /// <summary> | ||||
| @@ -218,13 +227,13 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, mode, options); | |||||
| => mode == CacheMode.CacheOnly ? null : GetMessagesAsync(limit, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, mode, options); | |||||
| => mode == CacheMode.CacheOnly ? null : GetMessagesAsync(fromMessageId, dir, limit, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) | ||||
| => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); | |||||
| => mode == CacheMode.CacheOnly ? null : GetMessagesAsync(fromMessage.Id, dir, limit, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | ||||
| => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | => await GetPinnedMessagesAsync(options).ConfigureAwait(false); | ||||
| @@ -180,6 +180,10 @@ namespace Discord.WebSocket | |||||
| public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task TriggerTypingAsync(RequestOptions options = null) | public Task TriggerTypingAsync(RequestOptions options = null) | ||||
| => ChannelHelper.TriggerTypingAsync(this, Discord, options); | => ChannelHelper.TriggerTypingAsync(this, Discord, options); | ||||
| @@ -180,6 +180,10 @@ namespace Discord.WebSocket | |||||
| public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) | public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) | ||||
| => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); | => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); | ||||
| /// <inheritdoc /> | |||||
| public async Task<IUserMessage> ModifyMessageAsync(ulong messageId, Action<MessageProperties> func, RequestOptions options = null) | |||||
| => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); | |||||
| /// <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); | ||||
| @@ -46,8 +46,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public int AFKTimeout { get; private set; } | public int AFKTimeout { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public bool IsEmbeddable { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public bool IsWidgetEnabled { get; private set; } | public bool IsWidgetEnabled { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public VerificationLevel VerificationLevel { get; private set; } | public VerificationLevel VerificationLevel { get; private set; } | ||||
| @@ -84,7 +82,6 @@ namespace Discord.WebSocket | |||||
| public ulong? ApplicationId { get; internal set; } | public ulong? ApplicationId { get; internal set; } | ||||
| internal ulong? AFKChannelId { get; private set; } | internal ulong? AFKChannelId { get; private set; } | ||||
| internal ulong? EmbedChannelId { get; private set; } | |||||
| internal ulong? WidgetChannelId { get; private set; } | internal ulong? WidgetChannelId { get; private set; } | ||||
| internal ulong? SystemChannelId { get; private set; } | internal ulong? SystemChannelId { get; private set; } | ||||
| internal ulong? RulesChannelId { get; private set; } | internal ulong? RulesChannelId { get; private set; } | ||||
| @@ -198,21 +195,6 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A channel set within the server's widget settings; <see langword="null"/> if none is set. | |||||
| /// </returns> | |||||
| [Obsolete("This property is deprecated, use WidgetChannel instead.")] | |||||
| public SocketGuildChannel EmbedChannel | |||||
| { | |||||
| get | |||||
| { | |||||
| var id = EmbedChannelId; | |||||
| return id.HasValue ? GetChannel(id.Value) : null; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | /// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild. | ||||
| /// </summary> | /// </summary> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -440,16 +422,12 @@ namespace Discord.WebSocket | |||||
| internal void Update(ClientState state, Model model) | internal void Update(ClientState state, Model model) | ||||
| { | { | ||||
| AFKChannelId = model.AFKChannelId; | AFKChannelId = model.AFKChannelId; | ||||
| if (model.EmbedChannelId.IsSpecified) | |||||
| EmbedChannelId = model.EmbedChannelId.Value; | |||||
| if (model.WidgetChannelId.IsSpecified) | if (model.WidgetChannelId.IsSpecified) | ||||
| WidgetChannelId = model.WidgetChannelId.Value; | WidgetChannelId = model.WidgetChannelId.Value; | ||||
| SystemChannelId = model.SystemChannelId; | SystemChannelId = model.SystemChannelId; | ||||
| RulesChannelId = model.RulesChannelId; | RulesChannelId = model.RulesChannelId; | ||||
| PublicUpdatesChannelId = model.PublicUpdatesChannelId; | PublicUpdatesChannelId = model.PublicUpdatesChannelId; | ||||
| AFKTimeout = model.AFKTimeout; | AFKTimeout = model.AFKTimeout; | ||||
| if (model.EmbedEnabled.IsSpecified) | |||||
| IsEmbeddable = model.EmbedEnabled.Value; | |||||
| if (model.WidgetEnabled.IsSpecified) | if (model.WidgetEnabled.IsSpecified) | ||||
| IsWidgetEnabled = model.WidgetEnabled.Value; | IsWidgetEnabled = model.WidgetEnabled.Value; | ||||
| IconId = model.Icon; | IconId = model.Icon; | ||||
| @@ -548,11 +526,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | ||||
| [Obsolete("This endpoint is deprecated, use ModifyWidgetAsync instead.")] | |||||
| public Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null) | |||||
| => GuildHelper.ModifyEmbedAsync(this, Discord, func, options); | |||||
| /// <inheritdoc /> | |||||
| /// <exception cref="ArgumentNullException"><paramref name="func"/> is <see langword="null"/>.</exception> | |||||
| public Task ModifyWidgetAsync(Action<GuildWidgetProperties> func, RequestOptions options = null) | public Task ModifyWidgetAsync(Action<GuildWidgetProperties> func, RequestOptions options = null) | ||||
| => GuildHelper.ModifyWidgetAsync(this, Discord, func, options); | => GuildHelper.ModifyWidgetAsync(this, Discord, func, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -1015,6 +988,9 @@ namespace Discord.WebSocket | |||||
| //Emotes | //Emotes | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task<IReadOnlyCollection<GuildEmote>> GetEmotesAsync(RequestOptions options = null) | |||||
| => GuildHelper.GetEmotesAsync(this, Discord, options); | |||||
| /// <inheritdoc /> | |||||
| public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | public Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null) | ||||
| => GuildHelper.GetEmoteAsync(this, Discord, id, options); | => GuildHelper.GetEmoteAsync(this, Discord, id, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -1236,10 +1212,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| bool IGuild.Available => true; | bool IGuild.Available => true; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| ulong IGuild.DefaultChannelId => DefaultChannel?.Id ?? 0; | |||||
| /// <inheritdoc /> | |||||
| ulong? IGuild.EmbedChannelId => EmbedChannelId; | |||||
| /// <inheritdoc /> | |||||
| ulong? IGuild.WidgetChannelId => WidgetChannelId; | ulong? IGuild.WidgetChannelId => WidgetChannelId; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| ulong? IGuild.SystemChannelId => SystemChannelId; | ulong? IGuild.SystemChannelId => SystemChannelId; | ||||
| @@ -1294,10 +1266,6 @@ namespace Discord.WebSocket | |||||
| Task<ITextChannel> IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) | Task<ITextChannel> IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<ITextChannel>(DefaultChannel); | => Task.FromResult<ITextChannel>(DefaultChannel); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| [Obsolete("This method is deprecated, use GetWidgetChannelAsync instead.")] | |||||
| Task<IGuildChannel> IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) | |||||
| => Task.FromResult<IGuildChannel>(EmbedChannel); | |||||
| /// <inheritdoc /> | |||||
| Task<IGuildChannel> IGuild.GetWidgetChannelAsync(CacheMode mode, RequestOptions options) | Task<IGuildChannel> IGuild.GetWidgetChannelAsync(CacheMode mode, RequestOptions options) | ||||
| => Task.FromResult<IGuildChannel>(WidgetChannel); | => Task.FromResult<IGuildChannel>(WidgetChannel); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -49,9 +49,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| int? IInvite.MemberCount => throw new NotImplementedException(); | int? IInvite.MemberCount => throw new NotImplementedException(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| [Obsolete("This property doesn't exist anymore and shouldn't be used.")] | |||||
| bool IInviteMetadata.IsRevoked => throw new NotImplementedException(); | |||||
| /// <inheritdoc /> | |||||
| public bool IsTemporary { get; private set; } | public bool IsTemporary { get; private set; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| int? IInviteMetadata.MaxAge { get => MaxAge; } | int? IInviteMetadata.MaxAge { get => MaxAge; } | ||||
| @@ -99,6 +99,8 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | public virtual IReadOnlyCollection<ITag> Tags => ImmutableArray.Create<ITag>(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public virtual IReadOnlyCollection<Sticker> Stickers => ImmutableArray.Create<Sticker>(); | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); | public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -194,6 +196,8 @@ namespace Discord.WebSocket | |||||
| IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | ||||
| /// <inheritdoc /> | |||||
| IReadOnlyCollection<ISticker> IMessage.Stickers => Stickers; | |||||
| internal void AddReaction(SocketReaction reaction) | internal void AddReaction(SocketReaction reaction) | ||||
| { | { | ||||
| @@ -23,6 +23,7 @@ namespace Discord.WebSocket | |||||
| private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>(); | private ImmutableArray<ITag> _tags = ImmutableArray.Create<ITag>(); | ||||
| private ImmutableArray<SocketRole> _roleMentions = ImmutableArray.Create<SocketRole>(); | private ImmutableArray<SocketRole> _roleMentions = ImmutableArray.Create<SocketRole>(); | ||||
| private ImmutableArray<SocketUser> _userMentions = ImmutableArray.Create<SocketUser>(); | private ImmutableArray<SocketUser> _userMentions = ImmutableArray.Create<SocketUser>(); | ||||
| private ImmutableArray<Sticker> _stickers = ImmutableArray.Create<Sticker>(); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override bool IsTTS => _isTTS; | public override bool IsTTS => _isTTS; | ||||
| @@ -47,6 +48,8 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override IReadOnlyCollection<SocketUser> MentionedUsers => _userMentions; | public override IReadOnlyCollection<SocketUser> MentionedUsers => _userMentions; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override IReadOnlyCollection<Sticker> Stickers => _stickers; | |||||
| /// <inheritdoc /> | |||||
| public IUserMessage ReferencedMessage => _referencedMessage; | public IUserMessage ReferencedMessage => _referencedMessage; | ||||
| internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) | ||||
| @@ -158,6 +161,20 @@ namespace Discord.WebSocket | |||||
| refMsgAuthor = new SocketUnknownUser(Discord, id: 0); | refMsgAuthor = new SocketUnknownUser(Discord, id: 0); | ||||
| _referencedMessage = SocketUserMessage.Create(Discord, state, refMsgAuthor, Channel, refMsg); | _referencedMessage = SocketUserMessage.Create(Discord, state, refMsgAuthor, Channel, refMsg); | ||||
| } | } | ||||
| if (model.Stickers.IsSpecified) | |||||
| { | |||||
| var value = model.Stickers.Value; | |||||
| if (value.Length > 0) | |||||
| { | |||||
| var stickers = ImmutableArray.CreateBuilder<Sticker>(value.Length); | |||||
| for (int i = 0; i < value.Length; i++) | |||||
| stickers.Add(Sticker.Create(value[i])); | |||||
| _stickers = stickers.ToImmutable(); | |||||
| } | |||||
| else | |||||
| _stickers = ImmutableArray.Create<Sticker>(); | |||||
| } | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -172,9 +189,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task UnpinAsync(RequestOptions options = null) | public Task UnpinAsync(RequestOptions options = null) | ||||
| => MessageHelper.UnpinAsync(this, Discord, options); | => MessageHelper.UnpinAsync(this, Discord, options); | ||||
| /// <inheritdoc /> | |||||
| public Task ModifySuppressionAsync(bool suppressEmbeds, RequestOptions options = null) | |||||
| => MessageHelper.SuppressEmbedsAsync(this, Discord, suppressEmbeds, options); | |||||
| public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | ||||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | ||||
| @@ -12,7 +12,6 @@ namespace Discord.WebSocket | |||||
| public override string Username { get; internal set; } | public override string Username { get; internal set; } | ||||
| public override ushort DiscriminatorValue { get; internal set; } | public override ushort DiscriminatorValue { get; internal set; } | ||||
| public override string AvatarId { get; internal set; } | public override string AvatarId { get; internal set; } | ||||
| public SocketDMChannel DMChannel { get; internal set; } | |||||
| internal override SocketPresence Presence { get; set; } | internal override SocketPresence Presence { get; set; } | ||||
| public override bool IsWebhook => false; | public override bool IsWebhook => false; | ||||
| @@ -52,7 +51,6 @@ namespace Discord.WebSocket | |||||
| internal void Update(ClientState state, PresenceModel model) | internal void Update(ClientState state, PresenceModel model) | ||||
| { | { | ||||
| Presence = SocketPresence.Create(model); | Presence = SocketPresence.Create(model); | ||||
| DMChannel = state.DMChannels.FirstOrDefault(x => x.Recipient.Id == Id); | |||||
| } | } | ||||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")}, Global)"; | private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")}, Global)"; | ||||
| @@ -63,7 +63,7 @@ namespace Discord.WebSocket | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns a collection of roles that the user possesses. | /// Returns a collection of roles that the user possesses. | ||||
| /// </summary> | /// </summary> | ||||
| public IReadOnlyCollection<SocketRole> Roles | |||||
| public IReadOnlyCollection<SocketRole> Roles | |||||
| => _roleIds.Select(id => Guild.GetRole(id)).Where(x => x != null).ToReadOnlyCollection(() => _roleIds.Length); | => _roleIds.Select(id => Guild.GetRole(id)).Where(x => x != null).ToReadOnlyCollection(() => _roleIds.Length); | ||||
| /// <summary> | /// <summary> | ||||
| /// Returns the voice channel the user is in, or <c>null</c> if none. | /// Returns the voice channel the user is in, or <c>null</c> if none. | ||||
| @@ -177,17 +177,29 @@ namespace Discord.WebSocket | |||||
| public Task KickAsync(string reason = null, RequestOptions options = null) | public Task KickAsync(string reason = null, RequestOptions options = null) | ||||
| => UserHelper.KickAsync(this, Discord, reason, options); | => UserHelper.KickAsync(this, Discord, reason, options); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task AddRoleAsync(ulong roleId, RequestOptions options = null) | |||||
| => AddRolesAsync(new[] { roleId }, options); | |||||
| /// <inheritdoc /> | |||||
| public Task AddRoleAsync(IRole role, RequestOptions options = null) | public Task AddRoleAsync(IRole role, RequestOptions options = null) | ||||
| => AddRolesAsync(new[] { role }, options); | |||||
| => AddRoleAsync(role.Id, options); | |||||
| /// <inheritdoc /> | |||||
| public Task AddRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null) | |||||
| => UserHelper.AddRolesAsync(this, Discord, roleIds, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | public Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | ||||
| => UserHelper.AddRolesAsync(this, Discord, roles, options); | |||||
| => AddRolesAsync(roles.Select(x => x.Id), options); | |||||
| /// <inheritdoc /> | |||||
| public Task RemoveRoleAsync(ulong roleId, RequestOptions options = null) | |||||
| => RemoveRolesAsync(new[] { roleId }, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task RemoveRoleAsync(IRole role, RequestOptions options = null) | public Task RemoveRoleAsync(IRole role, RequestOptions options = null) | ||||
| => RemoveRolesAsync(new[] { role }, options); | |||||
| => RemoveRoleAsync(role.Id, options); | |||||
| /// <inheritdoc /> | |||||
| public Task RemoveRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null) | |||||
| => UserHelper.RemoveRolesAsync(this, Discord, roleIds, options); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | public Task RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null) | ||||
| => UserHelper.RemoveRolesAsync(this, Discord, roles, options); | |||||
| => RemoveRolesAsync(roles.Select(x => x.Id)); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public ChannelPermissions GetPermissions(IGuildChannel channel) | public ChannelPermissions GetPermissions(IGuildChannel channel) | ||||
| @@ -2,6 +2,7 @@ using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Linq; | |||||
| using Model = Discord.API.Presence; | using Model = Discord.API.Presence; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| @@ -15,15 +16,12 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public UserStatus Status { get; } | public UserStatus Status { get; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IActivity Activity { get; } | |||||
| /// <inheritdoc /> | |||||
| public IImmutableSet<ClientType> ActiveClients { get; } | public IImmutableSet<ClientType> ActiveClients { get; } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IImmutableList<IActivity> Activities { get; } | public IImmutableList<IActivity> Activities { get; } | ||||
| internal SocketPresence(UserStatus status, IActivity activity, IImmutableSet<ClientType> activeClients, IImmutableList<IActivity> activities) | |||||
| internal SocketPresence(UserStatus status, IImmutableSet<ClientType> activeClients, IImmutableList<IActivity> activities) | |||||
| { | { | ||||
| Status = status; | Status = status; | ||||
| Activity = activity; | |||||
| ActiveClients = activeClients ?? ImmutableHashSet<ClientType>.Empty; | ActiveClients = activeClients ?? ImmutableHashSet<ClientType>.Empty; | ||||
| Activities = activities ?? ImmutableList<IActivity>.Empty; | Activities = activities ?? ImmutableList<IActivity>.Empty; | ||||
| } | } | ||||
| @@ -31,7 +29,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var clients = ConvertClientTypesDict(model.ClientStatus.GetValueOrDefault()); | var clients = ConvertClientTypesDict(model.ClientStatus.GetValueOrDefault()); | ||||
| var activities = ConvertActivitiesList(model.Activities); | var activities = ConvertActivitiesList(model.Activities); | ||||
| return new SocketPresence(model.Status, model.Game?.ToEntity(), clients, activities); | |||||
| return new SocketPresence(model.Status, clients, activities); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Creates a new <see cref="IReadOnlyCollection{T}"/> containing all of the client types | /// Creates a new <see cref="IReadOnlyCollection{T}"/> containing all of the client types | ||||
| @@ -84,7 +82,7 @@ namespace Discord.WebSocket | |||||
| /// A string that resolves to <see cref="Discord.WebSocket.SocketPresence.Status" />. | /// A string that resolves to <see cref="Discord.WebSocket.SocketPresence.Status" />. | ||||
| /// </returns> | /// </returns> | ||||
| public override string ToString() => Status.ToString(); | public override string ToString() => Status.ToString(); | ||||
| private string DebuggerDisplay => $"{Status}{(Activity != null ? $", {Activity.Name}": "")}"; | |||||
| private string DebuggerDisplay => $"{Status}{(Activities?.FirstOrDefault()?.Name ?? "")}"; | |||||
| internal SocketPresence Clone() => this; | internal SocketPresence Clone() => this; | ||||
| } | } | ||||
| @@ -25,7 +25,7 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override bool IsWebhook => false; | public override bool IsWebhook => false; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null, null); } set { } } | |||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null); } set { } } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">This field is not supported for an unknown user.</exception> | /// <exception cref="NotSupportedException">This field is not supported for an unknown user.</exception> | ||||
| internal override SocketGlobalUser GlobalUser => | internal override SocketGlobalUser GlobalUser => | ||||
| @@ -38,8 +38,6 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string Mention => MentionUtils.MentionUser(Id); | public string Mention => MentionUtils.MentionUser(Id); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IActivity Activity => Presence.Activity; | |||||
| /// <inheritdoc /> | |||||
| public UserStatus Status => Presence.Status; | public UserStatus Status => Presence.Status; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public IImmutableSet<ClientType> ActiveClients => Presence.ActiveClients ?? ImmutableHashSet<ClientType>.Empty; | public IImmutableSet<ClientType> ActiveClients => Presence.ActiveClients ?? ImmutableHashSet<ClientType>.Empty; | ||||
| @@ -94,8 +92,8 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public async Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null) | |||||
| => GlobalUser.DMChannel ?? await UserHelper.CreateDMChannelAsync(this, Discord, options).ConfigureAwait(false) as IDMChannel; | |||||
| public async Task<IDMChannel> CreateDMChannelAsync(RequestOptions options = null) | |||||
| => await UserHelper.CreateDMChannelAsync(this, Discord, options).ConfigureAwait(false); | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | ||||
| @@ -30,8 +30,8 @@ namespace Discord.WebSocket | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public override bool IsWebhook => true; | public override bool IsWebhook => true; | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null, null); } set { } } | |||||
| internal override SocketGlobalUser GlobalUser => | |||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null); } set { } } | |||||
| internal override SocketGlobalUser GlobalUser => | |||||
| throw new NotSupportedException(); | throw new NotSupportedException(); | ||||
| internal SocketWebhookUser(SocketGuild guild, ulong id, ulong webhookId) | internal SocketWebhookUser(SocketGuild guild, ulong id, ulong webhookId) | ||||
| @@ -73,32 +73,52 @@ namespace Discord.WebSocket | |||||
| ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Webhook users cannot be kicked.</exception> | /// <exception cref="NotSupportedException">Webhook users cannot be kicked.</exception> | ||||
| Task IGuildUser.KickAsync(string reason, RequestOptions options) => | |||||
| Task IGuildUser.KickAsync(string reason, RequestOptions options) => | |||||
| throw new NotSupportedException("Webhook users cannot be kicked."); | throw new NotSupportedException("Webhook users cannot be kicked."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Webhook users cannot be modified.</exception> | /// <exception cref="NotSupportedException">Webhook users cannot be modified.</exception> | ||||
| Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | |||||
| Task IGuildUser.ModifyAsync(Action<GuildUserProperties> func, RequestOptions options) => | |||||
| throw new NotSupportedException("Webhook users cannot be modified."); | throw new NotSupportedException("Webhook users cannot be modified."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | ||||
| Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | |||||
| Task IGuildUser.AddRoleAsync(ulong roleId, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | ||||
| Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | ||||
| Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | |||||
| Task IGuildUser.AddRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | ||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| Task IGuildUser.AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRoleAsync(ulong roleId, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<ulong> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | |||||
| /// <inheritdoc /> | |||||
| /// <exception cref="NotSupportedException">Roles are not supported on webhook users.</exception> | |||||
| Task IGuildUser.RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options) => | |||||
| throw new NotSupportedException("Roles are not supported on webhook users."); | throw new NotSupportedException("Roles are not supported on webhook users."); | ||||
| //IVoiceState | //IVoiceState | ||||
| @@ -91,6 +91,35 @@ namespace Discord.Webhook | |||||
| string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null) | string username = null, string avatarUrl = null, RequestOptions options = null, AllowedMentions allowedMentions = null) | ||||
| => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options); | => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, allowedMentions, options); | ||||
| /// <summary> | |||||
| /// Modifies a message posted using this webhook. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method can only modify messages that were sent using the same webhook. | |||||
| /// </remarks> | |||||
| /// <param name="messageId">ID of the modified message.</param> | |||||
| /// <param name="func">A delegate containing the properties to modify the message with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous modification operation. | |||||
| /// </returns> | |||||
| public Task ModifyMessageAsync(ulong messageId, Action<WebhookMessageProperties> func, RequestOptions options = null) | |||||
| => WebhookClientHelper.ModifyMessageAsync(this, messageId, func, options); | |||||
| /// <summary> | |||||
| /// Deletes a message posted using this webhook. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This method can only delete messages that were sent using the same webhook. | |||||
| /// </remarks> | |||||
| /// <param name="messageId">ID of the deleted message.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous deletion operation. | |||||
| /// </returns> | |||||
| public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) | |||||
| => WebhookClientHelper.DeleteMessageAsync(this, messageId, options); | |||||
| /// <summary> Sends a message to the channel for this webhook with an attachment. </summary> | /// <summary> Sends a message to the channel for this webhook with an attachment. </summary> | ||||
| /// <returns> Returns the ID of the created message. </returns> | /// <returns> Returns the ID of the created message. </returns> | ||||
| public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false, | public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false, | ||||
| @@ -0,0 +1,26 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord.Webhook | |||||
| { | |||||
| /// <summary> | |||||
| /// Properties that are used to modify an Webhook message with the specified changes. | |||||
| /// </summary> | |||||
| public class WebhookMessageProperties | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets or sets the content of the message. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This must be less than the constant defined by <see cref="DiscordConfig.MaxMessageSize"/>. | |||||
| /// </remarks> | |||||
| public Optional<string> Content { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the embed array that the message should display. | |||||
| /// </summary> | |||||
| public Optional<IEnumerable<Embed>> Embeds { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the allowed mentions of the message. | |||||
| /// </summary> | |||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | |||||
| } | |||||
| } | |||||