diff --git a/CHANGELOG.md b/CHANGELOG.md
index 886754052..21e37b295 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,127 @@
# 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
+### Added
+- #1491 Add INVITE_CREATE and INVITE_DELETE events (1ab670b)
+- #1520 Support reading multiple activities (421a0c1)
+- #1521 Allow for inherited commands in modules (a51cdf6)
+- #1526 Add Direction.Around to GetMessagesAsync (f2130f8)
+- #1537 Implement gateway ratelimit (ec673e1)
+- #1544 Add MESSAGE_REACTION_REMOVE_EMOJI and RemoveAllReactionsForEmoteAsync (a89f076)
+- #1549 Add GetUsersAsync to SocketGuild (30b5a83)
+- #1566 Support Gateway Intents (d5d10d3)
+- #1573 Add missing properties to Guild and deprecate GuildEmbed (ec212b1)
+- #1581 Add includeRoleIds to PruneUsersAsync (a80e5ff)
+- #1588 Add GetStreams to AudioClient (1e012ac)
+- #1596 Add missing channel properties (2d80037)
+- #1604 Add missing application properties (including Teams) (10fcde0)
+- #1619 Add "View Guild Insights" to GuildPermission (2592264)
+- #1637 Added CultureInvariant RegexOption to WebhookUrlRegex (e3925a7)
+- #1659 Add inline replies (e3850e1)
+- #1688 Send presence on Identify payload (25d5d36)
+- #1721 Add role tags (6a62c47)
+- #1722 Add user public flags (c683b29)
+- #1724 Add MessageFlags and AllowedMentions to message modify (225550d)
+- #1731 Add GuildUser IsPending property (8b25c9b)
+- #1690 Add max bitrate value to SocketGuild (aacfea0)
+
+### Fixed
+- #1244 Missing AddReactions permission for DM channels. (e40ca4a)
+- #1469 unsupported property causes an exception (468f826)
+- #1525 AllowedMentions and AllowedMentionTypes (3325031)
+- #1531 Add AllowedMentions to SendFileAsync (ab32607)
+- #1532 GuildEmbed.ChannelId as nullable per API documentation (971d519)
+- #1546 Different ratelimits for the same route (implement discord buckets) (2f6c017)
+- #1548 Incomplete Ready, DownloadUsersAsync, and optimize AlwaysDownloadUsers (dc8c959)
+- #1555 InvalidOperationException at MESSAGE_CREATE (bd4672a)
+- #1557 Sending 2 requests instead of 1 to create a Guild role. (5430cc8)
+- #1571 Not using the new domain name. (df8a0f7)
+- #1578 Trim token before passing it to the authorization header (42ba372)
+- #1580 Stop TaskCanceledException from bubbling up (b8fa464)
+- #1599 Invite audit log without inviter (b95b95b)
+- #1602 Add AllowedMentions to webhooks (bd4516b)
+- #1603 Cancel reconnection when 4014 (f396cd9)
+- #1608 Voice overwrites and CategoryId remarks (43c8fc0)
+- #1614 Check error 404 and return null for GetBanAsync (ae9fff6)
+- #1621 Parse mentions from message payload (366ca9a)
+- #1622 Do not update overwrite cache locally (3860da0)
+- #1623 Invoke UserUpdated from GuildMemberUpdated if needed (3085e88)
+- #1624 Handle null PreferredLocale in rare cases (c1d04b4)
+- #1639 Invite and InviteMetadata properties (dd2e524)
+- #1642 Add missing permissions (4b389f3)
+- #1647 handicap member downloading for verified bots (fa5ef5e)
+- #1652 Update README.MD to reflect new discord domain (03b831e)
+- #1667 Audio stream dispose (a2af985)
+- #1671 Crosspost throwing InvalidOperationException (9134443)
+- #1672 Team is nullable, not optional (be60d81)
+- #1681 Emoji url encode (04389a4)
+- #1683 SocketGuild.HasAllMembers is false if a user left a guild (47f571e)
+- #1686 Revert PremiumSubscriptionCount type (97e71cd)
+- #1695 Possible NullReferenceException when receiving InvalidSession (5213916)
+- #1702 Rollback Activities to Game (9d7cb39)
+- #1727 Move and fix internal AllowedMentions object (4a7f8fe)
+- limit request members batch size (084db25)
+- UserMentions throwing NullRef (5ed01a3)
+- Wrong author for SocketUserMessage.ReferencedMessage (1e9b252)
+- Discord sends null when there's no team (05a1f0a)
+- IMessage.Embeds docs remarks (a4d32d3)
+- Missing MessageReference when sending files (2095701)
+
+### Misc
+- #1545 MutualGuilds optimization (323a677)
+- #1551 Update webhook regex to support discord.com (7585789)
+- #1556 Add SearchUsersAsync (57880de)
+- #1561 Minor refactor to switch expression (42826df)
+- #1576 Updating comments for privileged intents (c42bfa6)
+- #1678 Change ratelimit messages (47ed806)
+- #1714 Update summary of SocketVoiceChannel.Users (e385c40)
+- #1720 VoiceRegions and related changes (5934c79)
+- Add updated libraries for LastModified (d761846)
+- Add alternative documentation link (accd351)
+- Temporarily disable StyleCops until all the fixes are impl'd (36de7b2)
+- Remove redundant CreateGuildRoleParams (3df0539)
+- Add minor tweaks to DiscordSocketConfig docs strings (2cd1880)
+- Fix MaxWaitBetweenGuildAvailablesBeforeReady docs string (e31cdc7)
+- Missing summary tag for GatewayIntents (3a10018)
+- Add new method of role ID copy (857ef77)
+- Resolve inheritdocs for IAttachment (9ea3291)
+- Mark null as a specific langword in summary (13a41f8)
+- Cleanup GatewayReconnectException docs (833ee42)
+- Update Docfx.Plugins.LastModified to v1.2.4 (28a6f97)
+- Update framework version for tests to Core 3.1 to comply with LTS (4988a07)
+- Move bulk deletes remarks from to (62539f0)
+
## [2.2.0] - 2020-04-16
### Added
- #1247 Implement Client Status Support (9da11b4)
diff --git a/Discord.Net.targets b/Discord.Net.targets
index 9502e91dd..febd921d1 100644
--- a/Discord.Net.targets
+++ b/Discord.Net.targets
@@ -1,6 +1,6 @@
- 2.3.0
+ 3.0.0
dev
latest
Discord.Net Contributors
diff --git a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
index 409a78e94..85c292dd2 100644
--- a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
+++ b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
@@ -28,7 +28,7 @@ public async Task SendRichEmbedAsync()
var embed = new EmbedBuilder
{
// Embed property can be set within object initializer
- Title = "Hello world!"
+ Title = "Hello world!",
Description = "I am a description set by initializer."
};
// Or with methods
diff --git a/docs/guides/commands/intro.md b/docs/guides/commands/intro.md
index abe7065c1..14341a32b 100644
--- a/docs/guides/commands/intro.md
+++ b/docs/guides/commands/intro.md
@@ -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
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.
### Command Context
diff --git a/docs/guides/getting_started/first-bot.md b/docs/guides/getting_started/first-bot.md
index 150466be1..e1af20d30 100644
--- a/docs/guides/getting_started/first-bot.md
+++ b/docs/guides/getting_started/first-bot.md
@@ -80,15 +80,11 @@ recommended for these operations to be awaited in a
properly established async context whenever possible.
To establish an async context, we will be creating an async main method
-in your console application, and rewriting the static main method to
-invoke the new async main.
+in your console application.
[!code-csharp[Async Context](samples/first-bot/async-context.cs)]
-As a result of this, your program will now start and immediately
-jump into an async context. This allows us to create a connection
-to Discord later on without having to worry about setting up the
-correct async implementation.
+As a result of this, your program will now start into an async context.
> [!WARNING]
> If your application throws any exceptions within an async context,
diff --git a/docs/guides/getting_started/samples/first-bot/async-context.cs b/docs/guides/getting_started/samples/first-bot/async-context.cs
index 3c98c9e46..98a3cea15 100644
--- a/docs/guides/getting_started/samples/first-bot/async-context.cs
+++ b/docs/guides/getting_started/samples/first-bot/async-context.cs
@@ -1,7 +1,6 @@
public class Program
{
- public static void Main(string[] args)
- => new Program().MainAsync().GetAwaiter().GetResult();
+ public static Task Main(string[] args) => new Program().MainAsync();
public async Task MainAsync()
{
diff --git a/docs/guides/getting_started/samples/first-bot/complete.cs b/docs/guides/getting_started/samples/first-bot/complete.cs
index 871641e23..542056435 100644
--- a/docs/guides/getting_started/samples/first-bot/complete.cs
+++ b/docs/guides/getting_started/samples/first-bot/complete.cs
@@ -2,8 +2,7 @@ public class Program
{
private DiscordSocketClient _client;
- public static void Main(string[] args)
- => new Program().MainAsync().GetAwaiter().GetResult();
+ public static Task Main(string[] args) => new Program().MainAsync();
public async Task MainAsync()
{
diff --git a/docs/guides/getting_started/samples/first-bot/structure.cs b/docs/guides/getting_started/samples/first-bot/structure.cs
index 5165e2fdb..4e64b1732 100644
--- a/docs/guides/getting_started/samples/first-bot/structure.cs
+++ b/docs/guides/getting_started/samples/first-bot/structure.cs
@@ -10,11 +10,11 @@ using Discord.WebSocket;
class Program
{
// Program entry point
- static void Main(string[] args)
+ static Task Main(string[] args)
{
// Call the Program constructor, followed by the
// MainAsync method and wait until it finishes (which should be never).
- new Program().MainAsync().GetAwaiter().GetResult();
+ return new Program().MainAsync();
}
private readonly DiscordSocketClient _client;
diff --git a/samples/03_sharded_client/Modules/PublicModule.cs b/samples/03_sharded_client/Modules/PublicModule.cs
index 60e57563a..fad2ba98c 100644
--- a/samples/03_sharded_client/Modules/PublicModule.cs
+++ b/samples/03_sharded_client/Modules/PublicModule.cs
@@ -9,7 +9,7 @@ namespace _03_sharded_client.Modules
[Command("info")]
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}";
await ReplyAsync(msg);
}
diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
index 28037b0fa..7a752090e 100644
--- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
+++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
@@ -136,7 +136,7 @@ namespace Discord.Commands
builder.Name = typeInfo.Name;
// 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)
{
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
index 1d4b0e15a..8659b0130 100644
--- a/src/Discord.Net.Commands/CommandService.cs
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -408,7 +408,7 @@ namespace Discord.Commands
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsEnum)
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)
{
@@ -511,7 +511,7 @@ namespace Discord.Commands
await _commandExecutedEvent.InvokeAsync(Optional.Create(), context, searchResult).ConfigureAwait(false);
return searchResult;
}
-
+
var commands = searchResult.Commands;
var preconditionResults = new Dictionary();
diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs
index 429ad7b0c..da8525644 100644
--- a/src/Discord.Net.Core/DiscordConfig.cs
+++ b/src/Discord.Net.Core/DiscordConfig.cs
@@ -16,7 +16,7 @@ namespace Discord
/// Discord API documentation
/// .
///
- public const int APIVersion = 6;
+ public const int APIVersion = 9;
///
/// Returns the Voice API version Discord.Net uses.
///
@@ -43,7 +43,7 @@ namespace Discord
///
/// The user agent used in each Discord.Net request.
///
- 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})";
///
/// Returns the base Discord API URL.
///
@@ -141,18 +141,6 @@ namespace Discord
///
internal bool DisplayInitialLog { get; set; } = true;
- ///
- /// Gets or sets the level of precision of the rate limit reset response.
- ///
- ///
- /// If set to , this value will be rounded up to the
- /// nearest second.
- ///
- ///
- /// The currently set .
- ///
- public RateLimitPrecision RateLimitPrecision { get; set; } = RateLimitPrecision.Millisecond;
-
///
/// Gets or sets whether or not rate-limits should use the system clock.
///
diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
index 656b67a61..11998c28b 100644
--- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
@@ -260,6 +260,21 @@ namespace Discord
///
Task DeleteMessageAsync(IMessage message, RequestOptions options = null);
+ ///
+ /// Modifies a message.
+ ///
+ ///
+ /// This method modifies this message with the specified properties. To see an example of this
+ /// method and what properties are available, please refer to .
+ ///
+ /// The snowflake identifier of the message that would be changed.
+ /// A delegate containing the properties to modify the message with.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous modification operation.
+ ///
+ Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null);
+
///
/// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.
///
diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs b/src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs
deleted file mode 100644
index 34473e93c..000000000
--- a/src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Discord
-{
- ///
- /// Provides properties that are used to modify the widget of an with the specified changes.
- ///
- public class GuildEmbedProperties
- {
- ///
- /// Sets whether the widget should be enabled.
- ///
- public Optional Enabled { get; set; }
- ///
- /// Sets the channel that the invite should place its users in, if not null.
- ///
- public Optional Channel { get; set; }
- ///
- /// Sets the channel the invite should place its users in, if not null.
- ///
- public Optional ChannelId { get; set; }
- }
-}
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index 6283508e5..b8fd858df 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -28,13 +28,6 @@ namespace Discord
///
int AFKTimeout { get; }
///
- /// Gets a value that indicates whether this guild is embeddable (i.e. can use widget).
- ///
- ///
- /// if this guild has a widget enabled; otherwise .
- ///
- bool IsEmbeddable { get; }
- ///
/// Gets a value that indicates whether this guild has the widget enabled.
///
///
@@ -132,29 +125,6 @@ namespace Discord
///
ulong? AFKChannelId { get; }
///
- /// Gets the ID of the default channel for this guild.
- ///
- ///
- /// This property retrieves the snowflake identifier of the first viewable text channel for this guild.
- ///
- /// This channel does not guarantee the user can send message to it, as it only looks for the first viewable
- /// text channel.
- ///
- ///
- ///
- /// A representing the snowflake identifier of the default text channel; 0 if
- /// none can be found.
- ///
- ulong DefaultChannelId { get; }
- ///
- /// Gets the ID of the widget embed channel of this guild.
- ///
- ///
- /// A representing the snowflake identifier of the embedded channel found within the
- /// widget settings of this guild; if none is set.
- ///
- ulong? EmbedChannelId { get; }
- ///
/// Gets the ID of the channel assigned to the widget of this guild.
///
///
@@ -364,16 +334,6 @@ namespace Discord
///
Task ModifyAsync(Action func, RequestOptions options = null);
///
- /// Modifies this guild's embed channel.
- ///
- /// The delegate containing the properties to modify the guild widget with.
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous modification operation.
- ///
- [Obsolete("This endpoint is deprecated, use ModifyWidgetAsync instead.")]
- Task ModifyEmbedAsync(Action func, RequestOptions options = null);
- ///
/// Modifies this guild's widget.
///
/// The delegate containing the properties to modify the guild widget with.
@@ -592,17 +552,6 @@ namespace Discord
///
Task GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
///
- /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild.
- ///
- /// The that determines whether the object should be fetched from cache.
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous get operation. The task result contains the embed channel set
- /// within the server's widget settings; if none is set.
- ///
- [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")]
- Task GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
- ///
/// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild.
///
/// The that determines whether the object should be fetched from cache.
@@ -892,6 +841,15 @@ namespace Discord
///
Task> GetWebhooksAsync(RequestOptions options = null);
+ ///
+ /// Gets a collection of emotes from this guild.
+ ///
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous get operation. The task result contains a read-only collection
+ /// of emotes found within the guild.
+ ///
+ Task> GetEmotesAsync(RequestOptions options = null);
///
/// Gets a specific emote from this guild.
///
diff --git a/src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs b/src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs
index 3da2fb147..fb759e4c5 100644
--- a/src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs
@@ -8,10 +8,10 @@ namespace Discord
///
/// The target of the permission is a role.
///
- Role,
+ Role = 0,
///
/// The target of the permission is a user.
///
- User
+ User = 1,
}
}
diff --git a/src/Discord.Net.Core/Entities/Invites/IInvite.cs b/src/Discord.Net.Core/Entities/Invites/IInvite.cs
index 993f1f047..47ffffacb 100644
--- a/src/Discord.Net.Core/Entities/Invites/IInvite.cs
+++ b/src/Discord.Net.Core/Entities/Invites/IInvite.cs
@@ -20,6 +20,13 @@ namespace Discord
///
string Url { get; }
+ ///
+ /// Gets the user that created this invite.
+ ///
+ ///
+ /// A user that created this invite.
+ ///
+ IUser Inviter { get; }
///
/// Gets the channel this invite is linked to.
///
@@ -83,5 +90,19 @@ namespace Discord
/// invite points to; null if one cannot be obtained.
///
int? MemberCount { get; }
+ ///
+ /// Gets the user this invite is linked to via .
+ ///
+ ///
+ /// A user that is linked to this invite.
+ ///
+ IUser TargetUser { get; }
+ ///
+ /// Gets the type of the linked for this invite.
+ ///
+ ///
+ /// The type of the linked user that is linked to this invite.
+ ///
+ TargetUserType TargetUserType { get; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs
index 471dc377f..c2580c853 100644
--- a/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs
+++ b/src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs
@@ -7,20 +7,6 @@ namespace Discord
///
public interface IInviteMetadata : IInvite
{
- ///
- /// Gets the user that created this invite.
- ///
- ///
- /// A user that created this invite.
- ///
- IUser Inviter { get; }
- ///
- /// Gets a value that indicates whether the invite has been revoked.
- ///
- ///
- /// true if this invite was revoked; otherwise false.
- ///
- bool IsRevoked { get; }
///
/// Gets a value that indicates whether the invite is a temporary one.
///
diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
index 555fd95df..f1238ddcf 100644
--- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
@@ -12,7 +12,6 @@ namespace Discord
{
private string _title;
private string _description;
- private string _url;
private EmbedImage? _image;
private EmbedThumbnail? _thumbnail;
private List _fields;
@@ -70,26 +69,14 @@ namespace Discord
/// Gets or sets the URL of an .
/// Url is not a well-formed .
/// The URL of the embed.
- 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; }
/// Gets or sets the thumbnail URL of an .
/// Url is not a well-formed .
/// The thumbnail URL of the embed.
public string ThumbnailUrl
{
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);
}
/// Gets or sets the image URL of an .
/// Url is not a well-formed .
@@ -97,11 +84,7 @@ namespace Discord
public string ImageUrl
{
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);
}
/// Gets or sets the list of of an .
@@ -553,8 +536,6 @@ namespace Discord
public class EmbedAuthorBuilder
{
private string _name;
- private string _url;
- private string _iconUrl;
///
/// Gets the maximum author name length allowed by Discord.
///
@@ -585,15 +566,7 @@ namespace Discord
///
/// The URL of the author field.
///
- 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; }
///
/// Gets or sets the icon URL of the author field.
///
@@ -601,15 +574,7 @@ namespace Discord
///
/// The icon URL of the author field.
///
- 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; }
///
/// Sets the name of the author field.
@@ -671,7 +636,6 @@ namespace Discord
public class EmbedFooterBuilder
{
private string _text;
- private string _iconUrl;
///
/// Gets the maximum footer length allowed by Discord.
@@ -703,15 +667,7 @@ namespace Discord
///
/// The icon URL of the footer field.
///
- 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; }
///
/// Sets the name of the footer field.
diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
index d13b6b592..cba8ce29f 100644
--- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs
+++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs
@@ -92,10 +92,10 @@ namespace Discord
/// Gets all embeds included in this message.
///
///
- ///
/// This property gets a read-only collection of embeds associated with this message. Depending on the
/// message, a sent message may contain one or more embeds. This is usually true when multiple link previews
/// are generated; however, only one can be featured.
+ ///
///
/// A read-only collection of embed objects.
///
@@ -168,6 +168,25 @@ namespace Discord
/// The 's attached to this message
///
IReadOnlyCollection Components { get; }
+
+ /// Gets all stickers included in this message.
+ ///
+ ///
+ /// A read-only collection of sticker objects.
+ ///
+ IReadOnlyCollection Stickers { get; }
+
+ ///
+ /// Gets the flags related to this message.
+ ///
+ ///
+ /// This value is determined by bitwise OR-ing values together.
+ ///
+ ///
+ /// A message's flags, if any is associated.
+ ///
+ MessageFlags? Flags { get; }
+
///
/// Adds a reaction to this message.
///
diff --git a/src/Discord.Net.Core/Entities/Messages/ISticker.cs b/src/Discord.Net.Core/Entities/Messages/ISticker.cs
new file mode 100644
index 000000000..e7e4405b6
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/ISticker.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+
+namespace Discord
+{
+ ///
+ /// Represents a discord sticker.
+ ///
+ public interface ISticker
+ {
+ ///
+ /// Gets the ID of this sticker.
+ ///
+ ///
+ /// A snowflake ID associated with this sticker.
+ ///
+ ulong Id { get; }
+ ///
+ /// Gets the ID of the pack of this sticker.
+ ///
+ ///
+ /// A snowflake ID associated with the pack of this sticker.
+ ///
+ ulong PackId { get; }
+ ///
+ /// Gets the name of this sticker.
+ ///
+ ///
+ /// A with the name of this sticker.
+ ///
+ string Name { get; }
+ ///
+ /// Gets the description of this sticker.
+ ///
+ ///
+ /// A with the description of this sticker.
+ ///
+ string Description { get; }
+ ///
+ /// Gets the list of tags of this sticker.
+ ///
+ ///
+ /// A read-only list with the tags of this sticker.
+ ///
+ IReadOnlyCollection Tags { get; }
+ ///
+ /// Gets the asset hash of this sticker.
+ ///
+ ///
+ /// A with the asset hash of this sticker.
+ ///
+ string Asset { get; }
+ ///
+ /// Gets the preview asset hash of this sticker.
+ ///
+ ///
+ /// A with the preview asset hash of this sticker.
+ ///
+ string PreviewAsset { get; }
+ ///
+ /// Gets the format type of this sticker.
+ ///
+ ///
+ /// A with the format type of this sticker.
+ ///
+ StickerFormatType FormatType { get; }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
index 1589e2ae5..c2d0e13bc 100644
--- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
+++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
@@ -36,18 +36,6 @@ namespace Discord
///
Task ModifyAsync(Action func, RequestOptions options = null);
///
- /// Modifies the suppression of this message.
- ///
- ///
- /// This method modifies whether or not embeds in this message are suppressed (hidden).
- ///
- /// Whether or not embeds in this message should be suppressed.
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous modification operation.
- ///
- Task ModifySuppressionAsync(bool suppressEmbeds, RequestOptions options = null);
- ///
/// Adds this message to its channel's pinned messages.
///
/// The options to be used when sending the request.
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
new file mode 100644
index 000000000..52d0f0e9e
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Discord
+{
+ [Flags]
+ public enum MessageFlags
+ {
+ ///
+ /// Default value for flags, when none are given to a message.
+ ///
+ None = 0,
+ ///
+ /// Flag given to messages that have been published to subscribed
+ /// channels (via Channel Following).
+ ///
+ Crossposted = 1 << 0,
+ ///
+ /// Flag given to messages that originated from a message in another
+ /// channel (via Channel Following).
+ ///
+ IsCrosspost = 1 << 1,
+ ///
+ /// Flag given to messages that do not display any embeds.
+ ///
+ SuppressEmbeds = 1 << 2,
+ ///
+ /// Flag given to messages that the source message for this crosspost
+ /// has been deleted (via Channel Following).
+ ///
+ SourceMessageDeleted = 1 << 3,
+ ///
+ /// Flag given to messages that came from the urgent message system.
+ ///
+ Urgent = 1 << 4,
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
index 1e4846a94..c71d29520 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
@@ -26,5 +26,18 @@ namespace Discord
/// Gets or sets the components for this message.
///
public Optional Components { get; set; }
+
+ ///
+ /// Gets or sets the flags of the message.
+ ///
+ ///
+ /// Only can be set/unset and you need to be
+ /// the author of the message.
+ ///
+ public Optional Flags { get; set; }
+ ///
+ /// Gets or sets the allowed mentions of the message.
+ ///
+ public Optional AllowedMentions { get; set; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageType.cs b/src/Discord.Net.Core/Entities/Messages/MessageType.cs
index e6a117ba5..c384e29ea 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageType.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageType.cs
@@ -60,9 +60,6 @@ namespace Discord
///
/// The message is an inline reply.
///
- ///
- /// Only available in API v8.
- ///
Reply = 19,
///
/// The message is an Application Command
diff --git a/src/Discord.Net.Core/Entities/Messages/SticketFormatType.cs b/src/Discord.Net.Core/Entities/Messages/SticketFormatType.cs
new file mode 100644
index 000000000..d24a38534
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/SticketFormatType.cs
@@ -0,0 +1,15 @@
+namespace Discord
+{
+ /// Defines the types of formats for stickers.
+ public enum StickerFormatType
+ {
+ /// Default value for a sticker format type.
+ None = 0,
+ /// The sticker format type is png.
+ Png = 1,
+ /// The sticker format type is apng.
+ Apng = 2,
+ /// The sticker format type is lottie.
+ Lottie = 3,
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
index e1f78373e..bf08887bd 100644
--- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs
@@ -22,11 +22,6 @@ namespace Discord
///
AddReactions = 0x00_00_00_40,
///
- /// Allows for reading of messages. This flag is obsolete, use instead.
- ///
- [Obsolete("Use ViewChannel instead.")]
- ReadMessages = ViewChannel,
- ///
/// Allows guild members to view a channel, which includes reading messages in text channels.
///
ViewChannel = 0x00_00_04_00,
diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
index ed675b5f3..d774cc51d 100644
--- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
@@ -45,9 +45,6 @@ namespace Discord
/// If true, a user may add reactions.
public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions);
- /// If true, a user may join channels.
- [Obsolete("Use ViewChannel instead.")]
- public bool ReadMessages => ViewChannel;
/// If true, a user may view channels.
public bool ViewChannel => Permissions.GetValue(RawValue, ChannelPermission.ViewChannel);
diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
index 645b67489..31bd6164a 100644
--- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
@@ -65,8 +65,6 @@ namespace Discord
/// Allows for viewing of audit logs.
///
ViewAuditLog = 0x00_00_00_80,
- [Obsolete("Use ViewChannel instead.")]
- ReadMessages = ViewChannel,
ViewChannel = 0x00_00_04_00,
SendMessages = 0x00_00_08_00,
///
diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
index ba6757fc6..b03c0e1a8 100644
--- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
@@ -37,9 +37,6 @@ namespace Discord
/// If true, a user may view the guild insights.
public bool ViewGuildInsights => Permissions.GetValue(RawValue, GuildPermission.ViewGuildInsights);
- /// If True, a user may join channels.
- [Obsolete("Use ViewChannel instead.")]
- public bool ReadMessages => ViewChannel;
/// If True, a user may view channels.
public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel);
/// If True, a user may send messages.
@@ -90,6 +87,9 @@ namespace Discord
/// Creates a new with the provided packed value.
public GuildPermissions(ulong rawValue) { RawValue = rawValue; }
+ /// Creates a new with the provided packed value after converting to ulong.
+ public GuildPermissions(string rawValue) { RawValue = ulong.Parse(rawValue); }
+
private GuildPermissions(ulong initialValue,
bool? createInstantInvite = null,
bool? kickMembers = null,
diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
index 7876d49ff..4f144c74b 100644
--- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
+++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
@@ -43,9 +43,6 @@ namespace Discord
/// If Allowed, a user may add reactions.
public PermValue AddReactions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AddReactions);
/// If Allowed, a user may join channels.
- [Obsolete("Use ViewChannel instead.")]
- public PermValue ReadMessages => ViewChannel;
- /// If Allowed, a user may join channels.
public PermValue ViewChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ViewChannel);
/// If Allowed, a user may send messages.
public PermValue SendMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.SendMessages);
@@ -93,6 +90,13 @@ namespace Discord
DenyValue = denyValue;
}
+ /// Creates a new OverwritePermissions with the provided allow and deny packed values after converting to ulong.
+ public OverwritePermissions(string allowValue, string denyValue)
+ {
+ AllowValue = ulong.Parse(allowValue);
+ DenyValue = ulong.Parse(denyValue);
+ }
+
private OverwritePermissions(ulong allowValue, ulong denyValue,
PermValue? createInstantInvite = null,
PermValue? manageChannel = null,
diff --git a/src/Discord.Net.Core/Entities/Roles/IRole.cs b/src/Discord.Net.Core/Entities/Roles/IRole.cs
index 66556fc2c..c02322be9 100644
--- a/src/Discord.Net.Core/Entities/Roles/IRole.cs
+++ b/src/Discord.Net.Core/Entities/Roles/IRole.cs
@@ -65,6 +65,13 @@ namespace Discord
/// An representing the position of the role in the role list of the guild.
///
int Position { get; }
+ ///
+ /// Gets the tags related to this role.
+ ///
+ ///
+ /// A object containing all tags related to this role.
+ ///
+ RoleTags Tags { get; }
///
/// Modifies this role.
diff --git a/src/Discord.Net.Core/Entities/Roles/RoleTags.cs b/src/Discord.Net.Core/Entities/Roles/RoleTags.cs
new file mode 100644
index 000000000..d0cbd3580
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Roles/RoleTags.cs
@@ -0,0 +1,40 @@
+namespace Discord
+{
+ ///
+ /// Provides tags related to a discord role.
+ ///
+ public class RoleTags
+ {
+ ///
+ /// Gets the identifier of the bot that this role belongs to, if it does.
+ ///
+ ///
+ /// A if this role belongs to a bot; otherwise
+ /// .
+ ///
+ public ulong? BotId { get; }
+ ///
+ /// Gets the identifier of the integration that this role belongs to, if it does.
+ ///
+ ///
+ /// A if this role belongs to an integration; otherwise
+ /// .
+ ///
+ public ulong? IntegrationId { get; }
+ ///
+ /// Gets if this role is the guild's premium subscriber (booster) role.
+ ///
+ ///
+ /// if this role is the guild's premium subscriber role;
+ /// otherwise .
+ ///
+ public bool IsPremiumSubscriberRole { get; }
+
+ internal RoleTags(ulong? botId, ulong? integrationId, bool isPremiumSubscriber)
+ {
+ BotId = botId;
+ IntegrationId = integrationId;
+ IsPremiumSubscriberRole = isPremiumSubscriber;
+ }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs
index 0e4004c46..492cb9566 100644
--- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs
+++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs
@@ -68,6 +68,11 @@ namespace Discord
///
IReadOnlyCollection RoleIds { get; }
+ ///
+ /// Whether the user has passed the guild's Membership Screening requirements.
+ ///
+ bool? IsPending { get; }
+
///
/// Gets the level permissions granted to this user to a given channel.
///
@@ -108,7 +113,15 @@ namespace Discord
/// A task that represents the asynchronous modification operation.
///
Task ModifyAsync(Action func, RequestOptions options = null);
-
+ ///
+ /// Adds the specified role to this user in the guild.
+ ///
+ /// The role to be added to the user.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous role addition operation.
+ ///
+ Task AddRoleAsync(ulong roleId, RequestOptions options = null);
///
/// Adds the specified role to this user in the guild.
///
@@ -119,6 +132,15 @@ namespace Discord
///
Task AddRoleAsync(IRole role, RequestOptions options = null);
///
+ /// Adds the specified to this user in the guild.
+ ///
+ /// The roles to be added to the user.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous role addition operation.
+ ///
+ Task AddRolesAsync(IEnumerable roleIds, RequestOptions options = null);
+ ///
/// Adds the specified to this user in the guild.
///
/// The roles to be added to the user.
@@ -128,6 +150,15 @@ namespace Discord
///
Task AddRolesAsync(IEnumerable roles, RequestOptions options = null);
///
+ /// Removes the specified from this user in the guild.
+ ///
+ /// The role to be removed from the user.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous role removal operation.
+ ///
+ Task RemoveRoleAsync(ulong roleId, RequestOptions options = null);
+ ///
/// Removes the specified from this user in the guild.
///
/// The role to be removed from the user.
@@ -137,6 +168,15 @@ namespace Discord
///
Task RemoveRoleAsync(IRole role, RequestOptions options = null);
///
+ /// Removes the specified from this user in the guild.
+ ///
+ /// The roles to be removed from the user.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous role removal operation.
+ ///
+ Task RemoveRolesAsync(IEnumerable roleIds, RequestOptions options = null);
+ ///
/// Removes the specified from this user in the guild.
///
/// The roles to be removed from the user.
diff --git a/src/Discord.Net.Core/Entities/Users/IPresence.cs b/src/Discord.Net.Core/Entities/Users/IPresence.cs
index a17ac0df2..6972037f0 100644
--- a/src/Discord.Net.Core/Entities/Users/IPresence.cs
+++ b/src/Discord.Net.Core/Entities/Users/IPresence.cs
@@ -7,10 +7,6 @@ namespace Discord
///
public interface IPresence
{
- ///
- /// Gets the activity this user is currently doing.
- ///
- IActivity Activity { get; }
///
/// Gets the current status of this user.
///
diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs
index c36fb2326..9596a8338 100644
--- a/src/Discord.Net.Core/Entities/Users/IUser.cs
+++ b/src/Discord.Net.Core/Entities/Users/IUser.cs
@@ -75,9 +75,19 @@ namespace Discord
/// Gets the username for this user.
///
string Username { get; }
+ ///
+ /// Gets the public flags that are applied to this user's account.
+ ///
+ ///
+ /// This value is determined by bitwise OR-ing values together.
+ ///
+ ///
+ /// The value of public flags for this user.
+ ///
+ UserProperties? PublicFlags { get; }
///
- /// 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.
///
///
/// This method is used to obtain or create a channel used to send a direct message.
@@ -92,7 +102,7 @@ namespace Discord
///
/// The following example attempts to send a direct message to the target user and logs the incident should
/// it fail.
- ///
///
/// The options to be used when sending the request.
@@ -100,6 +110,6 @@ namespace Discord
/// 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.
///
- Task GetOrCreateDMChannelAsync(RequestOptions options = null);
+ Task CreateDMChannelAsync(RequestOptions options = null);
}
}
diff --git a/src/Discord.Net.Core/Entities/Users/UserProperties.cs b/src/Discord.Net.Core/Entities/Users/UserProperties.cs
index 4f7272daa..68232b254 100644
--- a/src/Discord.Net.Core/Entities/Users/UserProperties.cs
+++ b/src/Discord.Net.Core/Entities/Users/UserProperties.cs
@@ -10,32 +10,60 @@ namespace Discord
///
None = 0,
///
- /// Flag given to Discord staff.
+ /// Flag given to users who are a Discord employee.
///
- Staff = 0b1,
+ Staff = 1 << 0,
///
- /// Flag given to Discord partners.
+ /// Flag given to users who are owners of a partnered Discord server.
///
- Partner = 0b10,
+ Partner = 1 << 1,
///
- /// Flag given to users who have participated in the bug report program.
+ /// Flag given to users in HypeSquad events.
///
- BugHunter = 0b1000,
+ HypeSquadEvents = 1 << 2,
+ ///
+ /// Flag given to users who have participated in the bug report program and are level 1.
+ ///
+ BugHunterLevel1 = 1 << 3,
///
/// Flag given to users who are in the HypeSquad House of Bravery.
///
- HypeSquadBravery = 0b100_0000,
+ HypeSquadBravery = 1 << 6,
///
/// Flag given to users who are in the HypeSquad House of Brilliance.
///
- HypeSquadBrilliance = 0b1000_0000,
+ HypeSquadBrilliance = 1 << 7,
///
/// Flag given to users who are in the HypeSquad House of Balance.
///
- HypeSquadBalance = 0b1_0000_0000,
+ HypeSquadBalance = 1 << 8,
///
/// Flag given to users who subscribed to Nitro before games were added.
///
- EarlySupporter = 0b10_0000_0000,
+ EarlySupporter = 1 << 9,
+ ///
+ /// Flag given to users who are part of a team.
+ ///
+ TeamUser = 1 << 10,
+ ///
+ /// Flag given to users who represent Discord (System).
+ ///
+ System = 1 << 12,
+ ///
+ /// Flag given to users who have participated in the bug report program and are level 2.
+ ///
+ BugHunterLevel2 = 1 << 14,
+ ///
+ /// Flag given to users who are verified bots.
+ ///
+ VerifiedBot = 1 << 16,
+ ///
+ /// Flag given to users that developed bots and early verified their accounts.
+ ///
+ EarlyVerifiedBotDeveloper = 1 << 17,
+ ///
+ /// Flag given to users that are discord certified moderators who has give discord's exam.
+ ///
+ DiscordCertifiedModerator = 1 << 18,
}
}
diff --git a/src/Discord.Net.Core/Extensions/StringExtensions.cs b/src/Discord.Net.Core/Extensions/StringExtensions.cs
deleted file mode 100644
index c0ebb2626..000000000
--- a/src/Discord.Net.Core/Extensions/StringExtensions.cs
+++ /dev/null
@@ -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);
- }
-}
diff --git a/src/Discord.Net.Core/Extensions/UserExtensions.cs b/src/Discord.Net.Core/Extensions/UserExtensions.cs
index fdcc12295..01d9f4dde 100644
--- a/src/Discord.Net.Core/Extensions/UserExtensions.cs
+++ b/src/Discord.Net.Core/Extensions/UserExtensions.cs
@@ -43,7 +43,7 @@ namespace Discord
AllowedMentions allowedMentions = null,
MessageComponent component = null)
{
- return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions, component: component).ConfigureAwait(false);
+ return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options, allowedMentions, component: component).ConfigureAwait(false);
}
///
@@ -95,7 +95,7 @@ namespace Discord
RequestOptions options = null,
MessageComponent component = null)
{
- return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options, component: component).ConfigureAwait(false);
+ return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options, component: component).ConfigureAwait(false);
}
///
@@ -151,7 +151,7 @@ namespace Discord
RequestOptions options = null,
MessageComponent component = null)
{
- return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options, component: component).ConfigureAwait(false);
+ return await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options, component: component).ConfigureAwait(false);
}
///
diff --git a/src/Discord.Net.Core/GatewayIntents.cs b/src/Discord.Net.Core/GatewayIntents.cs
index f3dc5ceb9..6976806b2 100644
--- a/src/Discord.Net.Core/GatewayIntents.cs
+++ b/src/Discord.Net.Core/GatewayIntents.cs
@@ -39,5 +39,16 @@ namespace Discord
DirectMessageReactions = 1 << 13,
/// This intent includes TYPING_START
DirectMessageTyping = 1 << 14,
+ ///
+ /// This intent includes all but and
+ /// that are privileged must be enabled for the application.
+ ///
+ AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites |
+ GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages |
+ DirectMessageReactions | DirectMessageTyping,
+ ///
+ /// This intent includes all of them, including privileged ones.
+ ///
+ All = AllUnprivileged | GuildMembers | GuildPresences
}
}
diff --git a/src/Discord.Net.Core/RateLimitPrecision.cs b/src/Discord.Net.Core/RateLimitPrecision.cs
deleted file mode 100644
index fe3c1b90e..000000000
--- a/src/Discord.Net.Core/RateLimitPrecision.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Discord
-{
- ///
- /// Specifies the level of precision to request in the rate limit
- /// response header.
- ///
- public enum RateLimitPrecision
- {
- ///
- /// Specifies precision rounded up to the nearest whole second
- ///
- Second,
- ///
- /// Specifies precision rounded to the nearest millisecond.
- ///
- Millisecond
- }
-}
diff --git a/src/Discord.Net.Core/TokenType.cs b/src/Discord.Net.Core/TokenType.cs
index 8ca3f031c..03b840830 100644
--- a/src/Discord.Net.Core/TokenType.cs
+++ b/src/Discord.Net.Core/TokenType.cs
@@ -5,8 +5,6 @@ namespace Discord
/// Specifies the type of token to use with the client.
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,
///
/// An OAuth2 token type.
///
diff --git a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs
index dd8f8ca66..e52c99376 100644
--- a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs
+++ b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs
@@ -12,7 +12,7 @@ namespace Discord
///
/// The snowflake identifier to resolve.
///
- /// A representing the time for when the object is geenrated.
+ /// A representing the time for when the object is generated.
///
public static DateTimeOffset FromSnowflake(ulong value)
=> DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL));
diff --git a/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
index 79a90b46d..83daedaa0 100644
--- a/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
+++ b/src/Discord.Net.Examples/Core/Entities/Users/IUser.Examples.cs
@@ -18,11 +18,11 @@ namespace Discord.Net.Examples.Core.Entities.Users
#endregion
- #region GetOrCreateDMChannelAsync
+ #region CreateDMChannelAsync
public async Task MessageUserAsync(IUser user)
{
- var channel = await user.GetOrCreateDMChannelAsync();
+ var channel = await user.CreateDMChannelAsync();
try
{
await channel.SendMessageAsync("Awesome stuff!");
diff --git a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
index 387584877..27d393c07 100644
--- a/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
+++ b/src/Discord.Net.Examples/WebSocket/BaseSocketClient.Events.Examples.cs
@@ -15,7 +15,7 @@ namespace Discord.Net.Examples.WebSocket
=> client.ReactionAdded += HandleReactionAddedAsync;
public async Task HandleReactionAddedAsync(Cacheable cachedMessage,
- ISocketMessageChannel originChannel, SocketReaction reaction)
+ Cacheable originChannel, SocketReaction reaction)
{
var message = await cachedMessage.GetOrDownloadAsync();
if (message != null && reaction.User.IsSpecified)
@@ -100,16 +100,17 @@ namespace Discord.Net.Examples.WebSocket
public void HookMessageDeleted(BaseSocketClient client)
=> client.MessageDeleted += HandleMessageDelete;
- public Task HandleMessageDelete(Cacheable cachedMessage, ISocketMessageChannel channel)
+ public async Task HandleMessageDelete(Cacheable cachedMessage, Cacheable cachedChannel)
{
// check if the message exists in cache; if not, we cannot report what was removed
- if (!cachedMessage.HasValue) return Task.CompletedTask;
+ if (!cachedMessage.HasValue) return;
+ // gets or downloads the channel if it's not in the cache
+ IMessageChannel channel = await cachedChannel.GetOrDownloadAsync();
var message = cachedMessage.Value;
Console.WriteLine(
$"A message ({message.Id}) from {message.Author} was removed from the channel {channel.Name} ({channel.Id}):"
+ Environment.NewLine
+ message.Content);
- return Task.CompletedTask;
}
#endregion
diff --git a/src/Discord.Net.Rest/Entities/Messages/AllowedMentions.cs b/src/Discord.Net.Rest/API/Common/AllowedMentions.cs
similarity index 92%
rename from src/Discord.Net.Rest/Entities/Messages/AllowedMentions.cs
rename to src/Discord.Net.Rest/API/Common/AllowedMentions.cs
index 8b8870be2..7737a464f 100644
--- a/src/Discord.Net.Rest/Entities/Messages/AllowedMentions.cs
+++ b/src/Discord.Net.Rest/API/Common/AllowedMentions.cs
@@ -2,7 +2,7 @@ using Newtonsoft.Json;
namespace Discord.API
{
- public class AllowedMentions
+ internal class AllowedMentions
{
[JsonProperty("parse")]
public Optional Parse { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs b/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
index 80d9a9e97..7458a19cb 100644
--- a/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
+++ b/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
@@ -7,7 +7,7 @@ namespace Discord.API
[JsonProperty("target_id")]
public ulong? TargetId { get; set; }
[JsonProperty("user_id")]
- public ulong UserId { get; set; }
+ public ulong? UserId { get; set; }
[JsonProperty("changes")]
public AuditLogChange[] Changes { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/Guild.cs b/src/Discord.Net.Rest/API/Common/Guild.cs
index 46075ce4d..bd25c7e1a 100644
--- a/src/Discord.Net.Rest/API/Common/Guild.cs
+++ b/src/Discord.Net.Rest/API/Common/Guild.cs
@@ -23,10 +23,6 @@ namespace Discord.API
public ulong? AFKChannelId { get; set; }
[JsonProperty("afk_timeout")]
public int AFKTimeout { get; set; }
- [JsonProperty("embed_enabled")]
- public Optional EmbedEnabled { get; set; }
- [JsonProperty("embed_channel_id")]
- public Optional EmbedChannelId { get; set; }
[JsonProperty("verification_level")]
public VerificationLevel VerificationLevel { get; set; }
[JsonProperty("default_message_notifications")]
diff --git a/src/Discord.Net.Rest/API/Common/GuildEmbed.cs b/src/Discord.Net.Rest/API/Common/GuildEmbed.cs
deleted file mode 100644
index d81632181..000000000
--- a/src/Discord.Net.Rest/API/Common/GuildEmbed.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/src/Discord.Net.Rest/API/Common/GuildMember.cs b/src/Discord.Net.Rest/API/Common/GuildMember.cs
index 940eb925a..fc2092d6c 100644
--- a/src/Discord.Net.Rest/API/Common/GuildMember.cs
+++ b/src/Discord.Net.Rest/API/Common/GuildMember.cs
@@ -18,6 +18,8 @@ namespace Discord.API
public Optional Deaf { get; set; }
[JsonProperty("mute")]
public Optional Mute { get; set; }
+ [JsonProperty("pending")]
+ public Optional Pending { get; set; }
[JsonProperty("premium_since")]
public Optional PremiumSince { get; set; }
}
diff --git a/src/Discord.Net.Rest/API/Common/Invite.cs b/src/Discord.Net.Rest/API/Common/Invite.cs
index 649bc37ec..aba267f34 100644
--- a/src/Discord.Net.Rest/API/Common/Invite.cs
+++ b/src/Discord.Net.Rest/API/Common/Invite.cs
@@ -11,9 +11,15 @@ namespace Discord.API
public Optional Guild { get; set; }
[JsonProperty("channel")]
public InviteChannel Channel { get; set; }
+ [JsonProperty("inviter")]
+ public Optional Inviter { get; set; }
[JsonProperty("approximate_presence_count")]
public Optional PresenceCount { get; set; }
[JsonProperty("approximate_member_count")]
public Optional MemberCount { get; set; }
+ [JsonProperty("target_user")]
+ public Optional TargetUser { get; set; }
+ [JsonProperty("target_user_type")]
+ public Optional TargetUserType { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/InviteMetadata.cs b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs
index ca019b79b..f818de699 100644
--- a/src/Discord.Net.Rest/API/Common/InviteMetadata.cs
+++ b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs
@@ -6,19 +6,15 @@ namespace Discord.API
{
internal class InviteMetadata : Invite
{
- [JsonProperty("inviter")]
- public User Inviter { get; set; }
[JsonProperty("uses")]
- public Optional Uses { get; set; }
+ public int Uses { get; set; }
[JsonProperty("max_uses")]
- public Optional MaxUses { get; set; }
+ public int MaxUses { get; set; }
[JsonProperty("max_age")]
- public Optional MaxAge { get; set; }
+ public int MaxAge { get; set; }
[JsonProperty("temporary")]
public bool Temporary { get; set; }
[JsonProperty("created_at")]
- public Optional CreatedAt { get; set; }
- [JsonProperty("revoked")]
- public bool Revoked { get; set; }
+ public DateTimeOffset CreatedAt { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/InviteVanity.cs b/src/Discord.Net.Rest/API/Common/InviteVanity.cs
index d39792674..a36ddee46 100644
--- a/src/Discord.Net.Rest/API/Common/InviteVanity.cs
+++ b/src/Discord.Net.Rest/API/Common/InviteVanity.cs
@@ -6,5 +6,7 @@ namespace Discord.API
{
[JsonProperty("code")]
public string Code { get; set; }
+ [JsonProperty("uses")]
+ public int Uses { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs
index 4ce8956eb..0474fec5b 100644
--- a/src/Discord.Net.Rest/API/Common/Message.cs
+++ b/src/Discord.Net.Rest/API/Common/Message.cs
@@ -60,5 +60,7 @@ namespace Discord.API
public Optional ReferencedMessage { get; set; }
[JsonProperty("components")]
public Optional Components { get; set; }
+ [JsonProperty("stickers")]
+ public Optional Stickers { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/MessageFlags.cs b/src/Discord.Net.Rest/API/Common/MessageFlags.cs
deleted file mode 100644
index ebe4e80ca..000000000
--- a/src/Discord.Net.Rest/API/Common/MessageFlags.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Discord.API
-{
- [Flags]
- internal enum MessageFlags : byte // probably safe to constrain this to 8 values, if not, it's internal so who cares
- {
- Suppressed = 0x04,
- }
-}
diff --git a/src/Discord.Net.Rest/API/Common/Overwrite.cs b/src/Discord.Net.Rest/API/Common/Overwrite.cs
index 1f3548a1c..3d94b0640 100644
--- a/src/Discord.Net.Rest/API/Common/Overwrite.cs
+++ b/src/Discord.Net.Rest/API/Common/Overwrite.cs
@@ -10,8 +10,8 @@ namespace Discord.API
[JsonProperty("type")]
public PermissionTarget TargetType { get; set; }
[JsonProperty("deny"), Int53]
- public ulong Deny { get; set; }
+ public string Deny { get; set; }
[JsonProperty("allow"), Int53]
- public ulong Allow { get; set; }
+ public string Allow { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/Presence.cs b/src/Discord.Net.Rest/API/Common/Presence.cs
index b37ad4229..b44e9185d 100644
--- a/src/Discord.Net.Rest/API/Common/Presence.cs
+++ b/src/Discord.Net.Rest/API/Common/Presence.cs
@@ -13,8 +13,6 @@ namespace Discord.API
public Optional GuildId { get; set; }
[JsonProperty("status")]
public UserStatus Status { get; set; }
- [JsonProperty("game")]
- public Game Game { get; set; }
[JsonProperty("roles")]
public Optional Roles { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/Role.cs b/src/Discord.Net.Rest/API/Common/Role.cs
index 856a8695f..c655175da 100644
--- a/src/Discord.Net.Rest/API/Common/Role.cs
+++ b/src/Discord.Net.Rest/API/Common/Role.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API
@@ -18,8 +18,10 @@ namespace Discord.API
[JsonProperty("position")]
public int Position { get; set; }
[JsonProperty("permissions"), Int53]
- public ulong Permissions { get; set; }
+ public string Permissions { get; set; }
[JsonProperty("managed")]
public bool Managed { get; set; }
+ [JsonProperty("tags")]
+ public Optional Tags { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/RoleTags.cs b/src/Discord.Net.Rest/API/Common/RoleTags.cs
new file mode 100644
index 000000000..6446f2037
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/RoleTags.cs
@@ -0,0 +1,15 @@
+#pragma warning disable CS1591
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ internal class RoleTags
+ {
+ [JsonProperty("bot_id")]
+ public Optional BotId { get; set; }
+ [JsonProperty("integration_id")]
+ public Optional IntegrationId { get; set; }
+ [JsonProperty("premium_subscriber")]
+ public Optional IsPremiumSubscriber { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Common/Sticker.cs b/src/Discord.Net.Rest/API/Common/Sticker.cs
new file mode 100644
index 000000000..0d1cac974
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/Sticker.cs
@@ -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 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; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Common/User.cs b/src/Discord.Net.Rest/API/Common/User.cs
index 2eff3753d..d1f436afb 100644
--- a/src/Discord.Net.Rest/API/Common/User.cs
+++ b/src/Discord.Net.Rest/API/Common/User.cs
@@ -29,5 +29,7 @@ namespace Discord.API
public Optional PremiumType { get; set; }
[JsonProperty("locale")]
public Optional Locale { get; set; }
+ [JsonProperty("public_flags")]
+ public Optional PublicFlags { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/UserGuild.cs b/src/Discord.Net.Rest/API/Common/UserGuild.cs
index f4f763885..825e9a09a 100644
--- a/src/Discord.Net.Rest/API/Common/UserGuild.cs
+++ b/src/Discord.Net.Rest/API/Common/UserGuild.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API
@@ -14,6 +14,6 @@ namespace Discord.API
[JsonProperty("owner")]
public bool Owner { get; set; }
[JsonProperty("permissions"), Int53]
- public ulong Permissions { get; set; }
+ public string Permissions { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs
index 0fe5f7e5a..269111a61 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
@@ -7,13 +7,13 @@ namespace Discord.API.Rest
internal class ModifyChannelPermissionsParams
{
[JsonProperty("type")]
- public string Type { get; }
+ public int Type { get; }
[JsonProperty("allow")]
- public ulong Allow { get; }
+ public string Allow { get; }
[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;
Allow = allow;
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs
index 287e1cafe..8605411c5 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs
@@ -1,4 +1,4 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
@@ -9,7 +9,7 @@ namespace Discord.API.Rest
[JsonProperty("name")]
public Optional Name { get; set; }
[JsonProperty("permissions")]
- public Optional Permissions { get; set; }
+ public Optional Permissions { get; set; }
[JsonProperty("color")]
public Optional Color { get; set; }
[JsonProperty("hoist")]
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
index 195525afc..69d962767 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
@@ -12,5 +12,9 @@ namespace Discord.API.Rest
public Optional
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;
Topic = topic;
SlowModeInterval = rateLimit;
IsNsfw = nsfw;
Bitrate = bitrate;
+ ChannelType = type;
}
///
@@ -53,5 +54,12 @@ namespace Discord.Rest
/// null if this is not mentioned in this entry.
///
public int? Bitrate { get; }
+ ///
+ /// Gets the type of this channel.
+ ///
+ ///
+ /// The channel type of this channel; null if not applicable.
+ ///
+ public ChannelType? ChannelType { get; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
index fa5233145..b2294f183 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
@@ -26,6 +26,7 @@ namespace Discord.Rest
var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user");
var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw");
var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate");
+ var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
string oldName = nameModel?.OldValue?.ToObject(discord.ApiClient.Serializer),
newName = nameModel?.NewValue?.ToObject(discord.ApiClient.Serializer);
@@ -37,9 +38,11 @@ namespace Discord.Rest
newNsfw = nsfwModel?.NewValue?.ToObject(discord.ApiClient.Serializer);
int? oldBitrate = bitrateModel?.OldValue?.ToObject(discord.ApiClient.Serializer),
newBitrate = bitrateModel?.NewValue?.ToObject(discord.ApiClient.Serializer);
+ ChannelType? oldType = typeModel?.OldValue?.ToObject(discord.ApiClient.Serializer),
+ newType = typeModel?.NewValue?.ToObject(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);
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs
index 020171152..be66ac846 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs
@@ -19,8 +19,14 @@ namespace Discord.Rest
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);
}
///
@@ -38,10 +44,10 @@ namespace Discord.Rest
///
public ulong ChannelId { get; }
///
- /// Gets the user of the message that was pinned.
+ /// Gets the user of the message that was pinned if available.
///
///
- /// A user object representing the user that created the pinned message.
+ /// A user object representing the user that created the pinned message or .
///
public IUser Target { get; }
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs
index 1b3ff96f3..b4fa389cc 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs
@@ -19,8 +19,14 @@ namespace Discord.Rest
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);
}
///
@@ -38,10 +44,10 @@ namespace Discord.Rest
///
public ulong ChannelId { get; }
///
- /// Gets the user of the message that was unpinned.
+ /// Gets the user of the message that was unpinned if available.
///
///
- /// A user object representing the user that created the unpinned message.
+ /// A user object representing the user that created the unpinned message or .
///
public IUser Target { get; }
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
index d604077f4..2176eab71 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
@@ -22,7 +22,7 @@ namespace Discord.Rest
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;
if (userInfo != null)
user = RestUser.Create(discord, userInfo);
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 894b820c8..f52174675 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -33,8 +33,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -59,8 +59,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -84,8 +84,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -286,6 +286,13 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model);
}
+ public static async Task ModifyMessageAsync(IMessageChannel channel, ulong messageId, Action 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,
RequestOptions options)
=> MessageHelper.DeleteAsync(channel.Id, messageId, client, options);
@@ -321,13 +328,13 @@ namespace Discord.Rest
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
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);
}
public static async Task AddPermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
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);
}
public static async Task RemovePermissionOverwriteAsync(IGuildChannel channel, BaseDiscordClient client,
@@ -443,8 +450,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
};
await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
index 1d12ee944..7bfd73ee6 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
@@ -135,6 +135,10 @@ namespace Discord.Rest
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);
+ ///
+ public async Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null)
+ => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false);
+
///
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
index 0f814a2d5..fb7a7a215 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
@@ -93,6 +93,10 @@ namespace Discord.Rest
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);
+ ///
+ public async Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null)
+ => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false);
+
///
/// Message content is too long, length must be less or equal to .
public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null, MessageComponent component = null)
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
index cd2230a61..800ee1d8d 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
@@ -152,6 +152,10 @@ namespace Discord.Rest
public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);
+ ///
+ public async Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null)
+ => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false);
+
///
public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options);
diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
index 04ec27930..58a4ea2c8 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
-using EmbedModel = Discord.API.GuildEmbed;
using WidgetModel = Discord.API.GuildWidget;
using Model = Discord.API.Guild;
using RoleModel = Discord.API.Role;
@@ -81,26 +80,6 @@ namespace Discord.Rest
return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false);
}
/// is null.
- public static async Task ModifyEmbedAsync(IGuild guild, BaseDiscordClient client,
- Action 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);
- }
- /// is null.
public static async Task ModifyWidgetAsync(IGuild guild, BaseDiscordClient client,
Action func, RequestOptions options)
{
@@ -205,8 +184,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -233,8 +212,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -258,8 +237,8 @@ namespace Discord.Rest
{
TargetId = overwrite.TargetId,
TargetType = overwrite.TargetType,
- Allow = overwrite.Permissions.AllowValue,
- Deny = overwrite.Permissions.DenyValue
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
};
@@ -304,6 +283,7 @@ namespace Discord.Rest
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.");
var inviteModel = await client.ApiClient.GetInviteAsync(vanityModel.Code, options).ConfigureAwait(false);
+ inviteModel.Uses = vanityModel.Uses;
return RestInviteMetadata.Create(client, guild, null, inviteModel);
}
@@ -320,7 +300,7 @@ namespace Discord.Rest
Hoist = isHoisted,
Mentionable = isMentionable,
Name = name,
- Permissions = permissions?.RawValue ?? Optional.Create()
+ Permissions = permissions?.RawValue.ToString() ?? Optional.Create()
};
var model = await client.ApiClient.CreateGuildRoleAsync(guild.Id, createGuildRoleParams, options).ConfigureAwait(false);
@@ -496,6 +476,11 @@ namespace Discord.Rest
}
//Emotes
+ public static async Task> 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 GetEmoteAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options)
{
var emote = await client.ApiClient.GetGuildEmoteAsync(guild.Id, id, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
index c74e128a8..ea703a26a 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
@@ -6,7 +6,6 @@ using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
-using EmbedModel = Discord.API.GuildEmbed;
using WidgetModel = Discord.API.GuildWidget;
using Model = Discord.API.Guild;
@@ -27,8 +26,6 @@ namespace Discord.Rest
///
public int AFKTimeout { get; private set; }
///
- public bool IsEmbeddable { get; private set; }
- ///
public bool IsWidgetEnabled { get; private set; }
///
public VerificationLevel VerificationLevel { get; private set; }
@@ -42,8 +39,6 @@ namespace Discord.Rest
///
public ulong? AFKChannelId { get; private set; }
///
- public ulong? EmbedChannelId { get; private set; }
- ///
public ulong? WidgetChannelId { get; private set; }
///
public ulong? SystemChannelId { get; private set; }
@@ -95,8 +90,6 @@ namespace Discord.Rest
///
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
- [Obsolete("DefaultChannelId is deprecated, use GetDefaultChannelAsync")]
- public ulong DefaultChannelId => Id;
///
public string IconUrl => CDN.GetGuildIconUrl(Id, IconId);
///
@@ -133,16 +126,12 @@ namespace Discord.Rest
internal void Update(Model model)
{
AFKChannelId = model.AFKChannelId;
- if (model.EmbedChannelId.IsSpecified)
- EmbedChannelId = model.EmbedChannelId.Value;
if (model.WidgetChannelId.IsSpecified)
WidgetChannelId = model.WidgetChannelId.Value;
SystemChannelId = model.SystemChannelId;
RulesChannelId = model.RulesChannelId;
PublicUpdatesChannelId = model.PublicUpdatesChannelId;
AFKTimeout = model.AFKTimeout;
- if (model.EmbedEnabled.IsSpecified)
- IsEmbeddable = model.EmbedEnabled.Value;
if (model.WidgetEnabled.IsSpecified)
IsWidgetEnabled = model.WidgetEnabled.Value;
IconId = model.Icon;
@@ -200,11 +189,6 @@ namespace Discord.Rest
Available = true;
}
- internal void Update(EmbedModel model)
- {
- EmbedChannelId = model.ChannelId;
- IsEmbeddable = model.Enabled;
- }
internal void Update(WidgetModel model)
{
WidgetChannelId = model.ChannelId;
@@ -241,15 +225,6 @@ namespace Discord.Rest
Update(model);
}
- ///
- /// is .
- [Obsolete("This endpoint is deprecated, use ModifyWidgetAsync instead.")]
- public async Task ModifyEmbedAsync(Action func, RequestOptions options = null)
- {
- var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false);
- Update(model);
- }
-
///
/// is .
public async Task ModifyWidgetAsync(Action func, RequestOptions options = null)
@@ -463,23 +438,6 @@ namespace Discord.Rest
.FirstOrDefault();
}
- ///
- /// Gets the embed channel (i.e. the channel set in the guild's widget settings) in this guild.
- ///
- /// The options to be used when sending the request.
- ///
- /// A task that represents the asynchronous get operation. The task result contains the embed channel set
- /// within the server's widget settings; if none is set.
- ///
- [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")]
- public async Task GetEmbedChannelAsync(RequestOptions options = null)
- {
- var embedId = EmbedChannelId;
- if (embedId.HasValue)
- return await GuildHelper.GetChannelAsync(this, Discord, embedId.Value, options).ConfigureAwait(false);
- return null;
- }
-
///
/// Gets the widget channel (i.e. the channel set in the guild's widget settings) in this guild.
///
@@ -828,6 +786,9 @@ namespace Discord.Rest
//Emotes
///
+ public Task> GetEmotesAsync(RequestOptions options = null)
+ => GuildHelper.GetEmotesAsync(this, Discord, options);
+ ///
public Task GetEmoteAsync(ulong id, RequestOptions options = null)
=> GuildHelper.GetEmoteAsync(this, Discord, id, options);
///
@@ -934,15 +895,6 @@ namespace Discord.Rest
return null;
}
///
- [Obsolete("This endpoint is deprecated, use GetWidgetChannelAsync instead.")]
- async Task IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options)
- {
- if (mode == CacheMode.AllowDownload)
- return await GetEmbedChannelAsync(options).ConfigureAwait(false);
- else
- return null;
- }
- ///
async Task IGuild.GetWidgetChannelAsync(CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs
deleted file mode 100644
index 41c76eb06..000000000
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildEmbed.cs
+++ /dev/null
@@ -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")})";
- }
-}
diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs
index 153eb6c41..95b454c20 100644
--- a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs
+++ b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs
@@ -21,6 +21,12 @@ namespace Discord.Rest
public ulong ChannelId { get; private set; }
///
public ulong? GuildId { get; private set; }
+ ///
+ public IUser Inviter { get; private set; }
+ ///
+ public IUser TargetUser { get; private set; }
+ ///
+ public TargetUserType TargetUserType { get; private set; }
internal IChannel Channel { get; }
internal IGuild Guild { get; }
@@ -50,6 +56,9 @@ namespace Discord.Rest
MemberCount = model.MemberCount.IsSpecified ? model.MemberCount.Value : null;
PresenceCount = model.PresenceCount.IsSpecified ? model.PresenceCount.Value : null;
ChannelType = (ChannelType)model.Channel.Type;
+ Inviter = model.Inviter.IsSpecified ? RestUser.Create(Discord, model.Inviter.Value) : null;
+ TargetUser = model.TargetUser.IsSpecified ? RestUser.Create(Discord, model.TargetUser.Value) : null;
+ TargetUserType = model.TargetUserType.IsSpecified ? model.TargetUserType.Value : TargetUserType.Undefined;
}
///
diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs
index 55acd5f45..a0ed9ec81 100644
--- a/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs
+++ b/src/Discord.Net.Rest/Entities/Invites/RestInviteMetadata.cs
@@ -6,10 +6,8 @@ namespace Discord.Rest
/// Represents additional information regarding the REST-based invite object.
public class RestInviteMetadata : RestInvite, IInviteMetadata
{
- private long? _createdAtTicks;
+ private long _createdAtTicks;
- ///
- public bool IsRevoked { get; private set; }
///
public bool IsTemporary { get; private set; }
///
@@ -18,10 +16,6 @@ namespace Discord.Rest
public int? MaxUses { get; private set; }
///
public int? Uses { get; private set; }
- ///
- /// Gets the user that created this invite.
- ///
- public RestUser Inviter { get; private set; }
///
public DateTimeOffset? CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks);
@@ -39,16 +33,11 @@ namespace Discord.Rest
internal void Update(Model model)
{
base.Update(model);
- Inviter = model.Inviter != null ? RestUser.Create(Discord, model.Inviter) : null;
- IsRevoked = model.Revoked;
IsTemporary = model.Temporary;
- MaxAge = model.MaxAge.IsSpecified ? model.MaxAge.Value : (int?)null;
- MaxUses = model.MaxUses.IsSpecified ? model.MaxUses.Value : (int?)null;
- Uses = model.Uses.IsSpecified ? model.Uses.Value : (int?)null;
- _createdAtTicks = model.CreatedAt.IsSpecified ? model.CreatedAt.Value.UtcTicks : (long?)null;
+ MaxAge = model.MaxAge;
+ MaxUses = model.MaxUses;
+ Uses = model.Uses;
+ _createdAtTicks = model.CreatedAt.UtcTicks;
}
-
- ///
- IUser IInviteMetadata.Inviter => Inviter;
}
}
diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
index 85b040f43..b24db3960 100644
--- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
@@ -27,26 +27,93 @@ namespace Discord.Rest
public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action func,
RequestOptions options)
{
- if (msg.Author.Id != client.CurrentUser.Id)
- throw new InvalidOperationException("Only the author of a message may modify the message.");
-
var args = new MessageProperties();
func(args);
+ if (msg.Author.Id != client.CurrentUser.Id && (args.Content.IsSpecified || args.Embed.IsSpecified || args.AllowedMentions.IsSpecified))
+ throw new InvalidOperationException("Only the author of a message may modify the message content, embed, or allowed mentions.");
+
bool hasText = args.Content.IsSpecified ? !string.IsNullOrEmpty(args.Content.Value) : !string.IsNullOrEmpty(msg.Content);
bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : msg.Embeds.Any();
if (!hasText && !hasEmbed)
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(),
Components = args.Components.IsSpecified ? args.Components.Value?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified
+ Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create(),
+ AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create(),
};
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false);
}
+ public static async Task ModifyAsync(ulong channelId, ulong msgId, BaseDiscordClient client, Action 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(),
+ Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create(),
+ AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create(),
+ };
+ return await client.ApiClient.ModifyMessageAsync(channelId, msgId, apiArgs, options).ConfigureAwait(false);
+ }
+
public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options)
=> DeleteAsync(msg.Channel.Id, msg.Id, client, options);
@@ -56,13 +123,14 @@ namespace Discord.Rest
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(ulong channelId, ulong messageId, IEmote emote, BaseDiscordClient client, RequestOptions options)
+ {
+ 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)
@@ -70,16 +138,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);
}
+ 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)
{
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)
{
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)
{
await client.ApiClient.RemoveAllReactionsForEmoteAsync(msg.Channel.Id, msg.Id, emote is Emote e ? $"{e.Name}:{e.Id}" : UrlEncode(emote.Name), options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index 90bdc8636..9f62c45be 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -58,6 +58,8 @@ namespace Discord.Rest
public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create();
///
public virtual IReadOnlyCollection Tags => ImmutableArray.Create();
+ ///
+ public virtual IReadOnlyCollection Stickers => ImmutableArray.Create();
///
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);
@@ -67,6 +69,8 @@ namespace Discord.Rest
public MessageApplication Application { get; private set; }
///
public MessageReference Reference { get; private set; }
+ ///
+ public MessageFlags? Flags { get; private set; }
///
public IReadOnlyCollection Components { get; private set; }
@@ -143,6 +147,9 @@ namespace Discord.Rest
else
Components = new List();
+ if (model.Flags.IsSpecified)
+ Flags = model.Flags.Value;
+
if (model.Reactions.IsSpecified)
{
var value = model.Reactions.Value;
@@ -187,8 +194,13 @@ namespace Discord.Rest
IReadOnlyCollection IMessage.Embeds => Embeds;
///
IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray();
+
///
IReadOnlyCollection IMessage.Components => Components;
+
+ ///
+ IReadOnlyCollection IMessage.Stickers => Stickers;
+
///
public IReadOnlyDictionary Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me });
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index 2c76a4253..aa6b44da6 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -13,7 +13,7 @@ namespace Discord.Rest
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestUserMessage : RestMessage, IUserMessage
{
- private bool _isMentioningEveryone, _isTTS, _isPinned, _isSuppressed;
+ private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks;
private IUserMessage _referencedMessage;
private ImmutableArray _attachments = ImmutableArray.Create();
@@ -21,13 +21,14 @@ namespace Discord.Rest
private ImmutableArray _tags = ImmutableArray.Create();
private ImmutableArray _roleMentionIds = ImmutableArray.Create();
private ImmutableArray _userMentions = ImmutableArray.Create();
+ private ImmutableArray _stickers = ImmutableArray.Create();
///
public override bool IsTTS => _isTTS;
///
public override bool IsPinned => _isPinned;
///
- public override bool IsSuppressed => _isSuppressed;
+ public override bool IsSuppressed => Flags.HasValue && Flags.Value.HasFlag(MessageFlags.SuppressEmbeds);
///
public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks);
///
@@ -45,6 +46,8 @@ namespace Discord.Rest
///
public override IReadOnlyCollection Tags => _tags;
///
+ public override IReadOnlyCollection Stickers => _stickers;
+ ///
public IUserMessage ReferencedMessage => _referencedMessage;
internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
@@ -70,10 +73,6 @@ namespace Discord.Rest
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks;
if (model.MentionEveryone.IsSpecified)
_isMentioningEveryone = model.MentionEveryone.Value;
- if (model.Flags.IsSpecified)
- {
- _isSuppressed = model.Flags.Value.HasFlag(API.MessageFlags.Suppressed);
- }
if (model.RoleMentions.IsSpecified)
_roleMentionIds = model.RoleMentions.Value.ToImmutableArray();
@@ -136,6 +135,20 @@ namespace Discord.Rest
IUser refMsgAuthor = MessageHelper.GetAuthor(Discord, guild, refMsg.Author.Value, refMsg.WebhookId.ToNullable());
_referencedMessage = RestUserMessage.Create(Discord, Channel, refMsgAuthor, refMsg);
}
+
+ if (model.Stickers.IsSpecified)
+ {
+ var value = model.Stickers.Value;
+ if (value.Length > 0)
+ {
+ var stickers = ImmutableArray.CreateBuilder(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ stickers.Add(Sticker.Create(value[i]));
+ _stickers = stickers.ToImmutable();
+ }
+ else
+ _stickers = ImmutableArray.Create();
+ }
}
///
@@ -151,9 +164,6 @@ namespace Discord.Rest
///
public Task UnpinAsync(RequestOptions options = null)
=> MessageHelper.UnpinAsync(this, Discord, options);
- ///
- 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,
TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name)
diff --git a/src/Discord.Net.Rest/Entities/Messages/Sticker.cs b/src/Discord.Net.Rest/Entities/Messages/Sticker.cs
new file mode 100644
index 000000000..5482bed74
--- /dev/null
+++ b/src/Discord.Net.Rest/Entities/Messages/Sticker.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using Model = Discord.API.Sticker;
+
+namespace Discord
+{
+ ///
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class Sticker : ISticker
+ {
+ ///
+ public ulong Id { get; }
+ ///
+ public ulong PackId { get; }
+ ///
+ public string Name { get; }
+ ///
+ public string Description { get; }
+ ///
+ public IReadOnlyCollection Tags { get; }
+ ///
+ public string Asset { get; }
+ ///
+ public string PreviewAsset { get; }
+ ///
+ 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})";
+ }
+}
diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs
index 7c1a3aaa2..aa33ae7e5 100644
--- a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs
+++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs
@@ -26,6 +26,8 @@ namespace Discord.Rest
public GuildPermissions Permissions { get; private set; }
///
public int Position { get; private set; }
+ ///
+ public RoleTags Tags { get; private set; }
///
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -56,6 +58,8 @@ namespace Discord.Rest
Position = model.Position;
Color = new Color(model.Color);
Permissions = new GuildPermissions(model.Permissions);
+ if (model.Tags.IsSpecified)
+ Tags = model.Tags.Value.ToEntity();
}
///
diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs
index d570f078b..73ab7ca31 100644
--- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Model = Discord.API.Role;
using BulkParams = Discord.API.Rest.ModifyGuildRolesParams;
@@ -24,7 +24,7 @@ namespace Discord.Rest
Hoist = args.Hoist,
Mentionable = args.Mentionable,
Name = args.Name,
- Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue : Optional.Create()
+ Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue.ToString() : Optional.Create()
};
var model = await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
index d6a8c2eda..6e6bbe09c 100644
--- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
+++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
@@ -29,6 +29,8 @@ namespace Discord.Rest
public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks);
///
public ulong GuildId => Guild.Id;
+ ///
+ public bool? IsPending { get; private set; }
///
/// Resolving permissions requires the parent guild to be downloaded.
@@ -73,6 +75,8 @@ namespace Discord.Rest
UpdateRoles(model.Roles.Value);
if (model.PremiumSince.IsSpecified)
_premiumSinceTicks = model.PremiumSince.Value?.UtcTicks;
+ if (model.Pending.IsSpecified)
+ IsPending = model.Pending.Value;
}
private void UpdateRoles(ulong[] roleIds)
{
@@ -108,17 +112,29 @@ namespace Discord.Rest
public Task KickAsync(string reason = null, RequestOptions options = null)
=> UserHelper.KickAsync(this, Discord, reason, options);
///
+ public Task AddRoleAsync(ulong roleId, RequestOptions options = null)
+ => AddRolesAsync(new[] { roleId }, options);
+ ///
public Task AddRoleAsync(IRole role, RequestOptions options = null)
- => AddRolesAsync(new[] { role }, options);
+ => AddRoleAsync(role.Id, options);
+ ///
+ public Task AddRolesAsync(IEnumerable roleIds, RequestOptions options = null)
+ => UserHelper.AddRolesAsync(this, Discord, roleIds, options);
///
public Task AddRolesAsync(IEnumerable roles, RequestOptions options = null)
- => UserHelper.AddRolesAsync(this, Discord, roles, options);
+ => AddRolesAsync(roles.Select(x => x.Id), options);
+ ///
+ public Task RemoveRoleAsync(ulong roleId, RequestOptions options = null)
+ => RemoveRolesAsync(new[] { roleId }, options);
///
public Task RemoveRoleAsync(IRole role, RequestOptions options = null)
- => RemoveRolesAsync(new[] { role }, options);
+ => RemoveRoleAsync(role.Id, options);
+ ///
+ public Task RemoveRolesAsync(IEnumerable roleIds, RequestOptions options = null)
+ => UserHelper.RemoveRolesAsync(this, Discord, roleIds, options);
///
public Task RemoveRolesAsync(IEnumerable roles, RequestOptions options = null)
- => UserHelper.RemoveRolesAsync(this, Discord, roles, options);
+ => RemoveRolesAsync(roles.Select(x => x.Id));
///
/// Resolving permissions requires the parent guild to be downloaded.
diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
index f5becd3ff..7bc1447fe 100644
--- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs
+++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
@@ -21,6 +21,8 @@ namespace Discord.Rest
public ushort DiscriminatorValue { get; private set; }
///
public string AvatarId { get; private set; }
+ ///
+ public UserProperties? PublicFlags { get; private set; }
///
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -65,6 +67,8 @@ namespace Discord.Rest
IsBot = model.Bot.Value;
if (model.Username.IsSpecified)
Username = model.Username.Value;
+ if (model.PublicFlags.IsSpecified)
+ PublicFlags = model.PublicFlags.Value;
}
///
@@ -75,13 +79,13 @@ namespace Discord.Rest
}
///
- /// 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.
///
/// The options to be used when sending the request.
///
/// A task that represents the asynchronous get operation. The task result contains a rest DM channel where the user is the recipient.
///
- public Task GetOrCreateDMChannelAsync(RequestOptions options = null)
+ public Task CreateDMChannelAsync(RequestOptions options = null)
=> UserHelper.CreateDMChannelAsync(this, Discord, options);
///
@@ -103,7 +107,7 @@ namespace Discord.Rest
//IUser
///
- async Task IUser.GetOrCreateDMChannelAsync(RequestOptions options)
- => await GetOrCreateDMChannelAsync(options).ConfigureAwait(false);
+ async Task IUser.CreateDMChannelAsync(RequestOptions options)
+ => await CreateDMChannelAsync(options).ConfigureAwait(false);
}
}
diff --git a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs
index 8462cb8d4..2131fec93 100644
--- a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs
+++ b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs
@@ -52,32 +52,42 @@ namespace Discord.Rest
///
string IGuildUser.Nickname => null;
///
+ bool? IGuildUser.IsPending => null;
+ ///
GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook;
///
ChannelPermissions IGuildUser.GetPermissions(IGuildChannel channel) => Permissions.ToChannelPerms(channel, GuildPermissions.Webhook.RawValue);
///
- Task IGuildUser.KickAsync(string reason, RequestOptions options) =>
+ Task IGuildUser.KickAsync(string reason, RequestOptions options) =>
throw new NotSupportedException("Webhook users cannot be kicked.");
///
- Task IGuildUser.ModifyAsync(Action func, RequestOptions options) =>
+ Task IGuildUser.ModifyAsync(Action func, RequestOptions options) =>
throw new NotSupportedException("Webhook users cannot be modified.");
-
///
- Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) =>
+ Task IGuildUser.AddRoleAsync(ulong role, RequestOptions options) =>
throw new NotSupportedException("Roles are not supported on webhook users.");
-
///
- Task IGuildUser.AddRolesAsync(IEnumerable roles, RequestOptions options) =>
+ Task IGuildUser.AddRoleAsync(IRole role, RequestOptions options) =>
throw new NotSupportedException("Roles are not supported on webhook users.");
-
///
- Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) =>
+ Task IGuildUser.AddRolesAsync(IEnumerable roles, RequestOptions options) =>
+ throw new NotSupportedException("Roles are not supported on webhook users.");
+ ///
+ Task IGuildUser.AddRolesAsync(IEnumerable roles, RequestOptions options) =>
+ throw new NotSupportedException("Roles are not supported on webhook users.");
+ ///
+ Task IGuildUser.RemoveRoleAsync(ulong role, RequestOptions options) =>
+ throw new NotSupportedException("Roles are not supported on webhook users.");
+ ///
+ Task IGuildUser.RemoveRoleAsync(IRole role, RequestOptions options) =>
+ throw new NotSupportedException("Roles are not supported on webhook users.");
+ ///
+ Task IGuildUser.RemoveRolesAsync(IEnumerable roles, RequestOptions options) =>
throw new NotSupportedException("Roles are not supported on webhook users.");
-
///
- Task IGuildUser.RemoveRolesAsync(IEnumerable roles, RequestOptions options) =>
+ Task IGuildUser.RemoveRolesAsync(IEnumerable roles, RequestOptions options) =>
throw new NotSupportedException("Roles are not supported on webhook users.");
//IVoiceState
diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs
index 58e8cd417..3a19fcfc1 100644
--- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs
@@ -73,16 +73,16 @@ namespace Discord.Rest
return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args, options).ConfigureAwait(false));
}
- public static async Task AddRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable roles, RequestOptions options)
+ public static async Task AddRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable 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 roles, RequestOptions options)
+ public static async Task RemoveRolesAsync(IGuildUser user, BaseDiscordClient client, IEnumerable 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);
}
}
}
diff --git a/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs b/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs
index 1fdc95a63..9baddf003 100644
--- a/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs
+++ b/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs
@@ -11,11 +11,11 @@ namespace Discord.Rest
internal IGuild Guild { get; private set; }
internal ITextChannel Channel { get; private set; }
- ///
- public ulong ChannelId { get; }
///
public string Token { get; }
+ ///
+ public ulong ChannelId { get; private set; }
///
public string Name { get; private set; }
///
@@ -56,6 +56,8 @@ namespace Discord.Rest
internal void Update(Model model)
{
+ if (ChannelId != model.ChannelId)
+ ChannelId = model.ChannelId;
if (model.Avatar.IsSpecified)
AvatarId = model.Avatar.Value;
if (model.Creator.IsSpecified)
diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
index 8e1b9c6d8..f8676c783 100644
--- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
+++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
@@ -34,6 +34,13 @@ namespace Discord.Rest
model.Thumbnail.IsSpecified ? model.Thumbnail.Value.ToEntity() : (EmbedThumbnail?)null,
model.Fields.IsSpecified ? model.Fields.Value.Select(x => x.ToEntity()).ToImmutableArray() : ImmutableArray.Create());
}
+ public static RoleTags ToEntity(this API.RoleTags model)
+ {
+ return new RoleTags(
+ model.BotId.IsSpecified ? model.BotId.Value : null,
+ model.IntegrationId.IsSpecified ? model.IntegrationId.Value : null,
+ model.IsPremiumSubscriber.IsSpecified ? true : false);
+ }
public static API.Embed ToModel(this Embed entity)
{
if (entity == null) return null;
diff --git a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
index a1ed20c6f..931c0c4c9 100644
--- a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
+++ b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
@@ -73,8 +73,6 @@ namespace Discord.Net.Converters
}
//Enums
- if (type == typeof(PermissionTarget))
- return PermissionTargetConverter.Instance;
if (type == typeof(UserStatus))
return UserStatusConverter.Instance;
if (type == typeof(EmbedType))
diff --git a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs b/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs
deleted file mode 100644
index de2e379d7..000000000
--- a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs
+++ /dev/null
@@ -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;
-
- /// Unknown permission target.
- 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.");
- }
- }
-
- /// Invalid permission target.
- 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.");
- }
- }
- }
-}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
index 92a494b71..bb54d4cdd 100644
--- a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
+++ b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
@@ -17,8 +17,6 @@ namespace Discord.API.Gateway
public Optional ShardingParams { get; set; }
[JsonProperty("presence")]
public Optional Presence { get; set; }
- [JsonProperty("guild_subscriptions")]
- public Optional GuildSubscriptions { get; set; }
[JsonProperty("intents")]
public Optional Intents { get; set; }
}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/InviteCreatedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/InviteCreatedEvent.cs
new file mode 100644
index 000000000..8f8002029
--- /dev/null
+++ b/src/Discord.Net.WebSocket/API/Gateway/InviteCreatedEvent.cs
@@ -0,0 +1,32 @@
+using Discord.API;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.API.Gateway
+{
+ internal class InviteCreatedEvent
+ {
+ [JsonProperty("channel_id")]
+ public ulong ChannelID { get; set; }
+ [JsonProperty("code")]
+ public string InviteCode { get; set; }
+ [JsonProperty("timestamp")]
+ public Optional RawTimestamp { get; set; }
+ [JsonProperty("guild_id")]
+ public ulong? GuildID { get; set; }
+ [JsonProperty("inviter")]
+ public Optional inviter { get; set; }
+ [JsonProperty("max_age")]
+ public int RawAge { get; set; }
+ [JsonProperty("max_uses")]
+ public int MaxUsers { get; set; }
+ [JsonProperty("temporary")]
+ public bool TempInvite { get; set; }
+ [JsonProperty("uses")]
+ public int Uses { get; set; }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/InviteDeletedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/InviteDeletedEvent.cs
new file mode 100644
index 000000000..6bdd337f5
--- /dev/null
+++ b/src/Discord.Net.WebSocket/API/Gateway/InviteDeletedEvent.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.WebSocket
+{
+ internal class InviteDeletedEvent
+ {
+ [JsonProperty("channel_id")]
+ public ulong ChannelID { get; set; }
+ [JsonProperty("guild_id")]
+ public Optional GuildID { get; set; }
+ [JsonProperty("code")]
+ public string Code { get; set; }
+ }
+}
diff --git a/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs
index 62de456e2..0d17cbff8 100644
--- a/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs
+++ b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using Newtonsoft.Json;
namespace Discord.API.Gateway
{
@@ -10,7 +10,11 @@ namespace Discord.API.Gateway
public ulong MessageId { get; set; }
[JsonProperty("channel_id")]
public ulong ChannelId { get; set; }
+ [JsonProperty("guild_id")]
+ public Optional GuildId { get; set; }
[JsonProperty("emoji")]
public Emoji Emoji { get; set; }
+ [JsonProperty("member")]
+ public Optional Member { get; set; }
}
}
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
index af1a9f147..3b6b3ec5a 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
@@ -127,12 +127,12 @@ namespace Discord.WebSocket
///
///
- public event Func, ISocketMessageChannel, Task> MessageDeleted
- {
+
+ public event Func, Cacheable, Task> MessageDeleted {
add { _messageDeletedEvent.Add(value); }
remove { _messageDeletedEvent.Remove(value); }
}
- internal readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+ internal readonly AsyncEvent, Cacheable, Task>> _messageDeletedEvent = new AsyncEvent, Cacheable, Task>>();
/// Fired when multiple messages are bulk deleted.
///
///
@@ -159,12 +159,12 @@ namespace Discord.WebSocket
/// parameter.
///
///
- public event Func>, ISocketMessageChannel, Task> MessagesBulkDeleted
+ public event Func>, Cacheable, Task> MessagesBulkDeleted
{
add { _messagesBulkDeletedEvent.Add(value); }
remove { _messagesBulkDeletedEvent.Remove(value); }
}
- internal readonly AsyncEvent>, ISocketMessageChannel, Task>> _messagesBulkDeletedEvent = new AsyncEvent>, ISocketMessageChannel, Task>>();
+ internal readonly AsyncEvent>, Cacheable, Task>> _messagesBulkDeletedEvent = new AsyncEvent>, Cacheable, Task>>();
/// Fired when a message is updated.
///
///
@@ -222,26 +222,23 @@ namespace Discord.WebSocket
///
///
- public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded
- {
+ public event Func, Cacheable, SocketReaction, Task> ReactionAdded {
add { _reactionAddedEvent.Add(value); }
remove { _reactionAddedEvent.Remove(value); }
}
- internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ internal readonly AsyncEvent, Cacheable, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, Cacheable, SocketReaction, Task>>();
/// Fired when a reaction is removed from a message.
- public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved
- {
+ public event Func, Cacheable, SocketReaction, Task> ReactionRemoved {
add { _reactionRemovedEvent.Add(value); }
remove { _reactionRemovedEvent.Remove(value); }
}
- internal readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>();
+ internal readonly AsyncEvent, Cacheable, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, Cacheable, SocketReaction, Task>>();
/// Fired when all reactions to a message are cleared.
- public event Func, ISocketMessageChannel, Task> ReactionsCleared
- {
+ public event Func, Cacheable, Task> ReactionsCleared {
add { _reactionsClearedEvent.Add(value); }
remove { _reactionsClearedEvent.Remove(value); }
}
- internal readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>();
+ internal readonly AsyncEvent, Cacheable, Task>> _reactionsClearedEvent = new AsyncEvent, Cacheable, Task>>();
///
/// Fired when all reactions to a message with a specific emote are removed.
///
@@ -258,12 +255,12 @@ namespace Discord.WebSocket
/// The emoji that all reactions had and were removed will be passed into the parameter.
///
///
- public event Func, ISocketMessageChannel, IEmote, Task> ReactionsRemovedForEmote
+ public event Func, Cacheable, IEmote, Task> ReactionsRemovedForEmote
{
add { _reactionsRemovedForEmoteEvent.Add(value); }
remove { _reactionsRemovedForEmoteEvent.Remove(value); }
}
- internal readonly AsyncEvent, ISocketMessageChannel, IEmote, Task>> _reactionsRemovedForEmoteEvent = new AsyncEvent, ISocketMessageChannel, IEmote, Task>>();
+ internal readonly AsyncEvent, Cacheable, IEmote, Task>> _reactionsRemovedForEmoteEvent = new AsyncEvent, Cacheable, IEmote, Task>>();
//Roles
/// Fired when a role is created.
@@ -369,12 +366,11 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent> _userUpdatedEvent = new AsyncEvent>();
/// Fired when a guild member is updated, or a member presence is updated.
- public event Func GuildMemberUpdated
- {
+ public event Func, SocketGuildUser, Task> GuildMemberUpdated {
add { _guildMemberUpdatedEvent.Add(value); }
remove { _guildMemberUpdatedEvent.Remove(value); }
}
- internal readonly AsyncEvent> _guildMemberUpdatedEvent = new AsyncEvent>();
+ internal readonly AsyncEvent, SocketGuildUser, Task>> _guildMemberUpdatedEvent = new AsyncEvent, SocketGuildUser, Task>>();
/// Fired when a user joins, leaves, or moves voice channels.
public event Func UserVoiceStateUpdated
{
@@ -397,12 +393,11 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent> _selfUpdatedEvent = new AsyncEvent>();
/// Fired when a user starts typing.
- public event Func UserIsTyping
- {
+ public event Func, Cacheable, Task> UserIsTyping {
add { _userIsTypingEvent.Add(value); }
remove { _userIsTypingEvent.Remove(value); }
}
- internal readonly AsyncEvent> _userIsTypingEvent = new AsyncEvent>();
+ internal readonly AsyncEvent, Cacheable, Task>> _userIsTypingEvent = new AsyncEvent, Cacheable, Task>>();
/// Fired when a user joins a group channel.
public event Func RecipientAdded
{
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs
index 548bb75bf..1cfe6c8bf 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@@ -46,7 +47,7 @@ namespace Discord.WebSocket
///
/// Gets the current logged-in user.
///
- public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; protected set => base.CurrentUser = value; }
+ public virtual new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; protected set => base.CurrentUser = value; }
///
/// Gets a collection of guilds that the user is currently in.
///
@@ -69,19 +70,11 @@ namespace Discord.WebSocket
/// A read-only collection of private channels that the user currently partakes in.
///
public abstract IReadOnlyCollection PrivateChannels { get; }
- ///
- /// Gets a collection of available voice regions.
- ///
- ///
- /// A read-only collection of voice regions that the user has access to.
- ///
- public abstract IReadOnlyCollection VoiceRegions { get; }
internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client)
: base(config, client) => BaseConfig = config;
private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
=> new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent,
- rateLimitPrecision: config.RateLimitPrecision,
useSystemClock: config.UseSystemClock);
///
@@ -162,14 +155,23 @@ namespace Discord.WebSocket
///
public abstract SocketGuild GetGuild(ulong id);
///
+ /// Gets all voice regions.
+ ///
+ /// The options to be used when sending the request.
+ ///
+ /// A task that contains a read-only collection of REST-based voice regions.
+ ///
+ public abstract ValueTask> GetVoiceRegionsAsync(RequestOptions options = null);
+ ///
/// Gets a voice region.
///
/// The identifier of the voice region (e.g. eu-central ).
+ /// The options to be used when sending the request.
///
- /// A REST-based voice region associated with the identifier; null if the voice region is not
- /// found.
+ /// A task that contains a REST-based voice region associated with the identifier; null if the
+ /// voice region is not found.
///
- public abstract RestVoiceRegion GetVoiceRegion(string id);
+ public abstract ValueTask GetVoiceRegionAsync(string id, RequestOptions options = null);
///
public abstract Task StartAsync();
///
@@ -188,6 +190,12 @@ namespace Discord.WebSocket
/// The name of the game.
/// If streaming, the URL of the stream. Must be a valid Twitch URL.
/// The type of the game.
+ ///
+ ///
+ /// Bot accounts cannot set as their activity
+ /// type and it will have no effect.
+ ///
+ ///
///
/// A task that represents the asynchronous set operation.
///
@@ -201,6 +209,10 @@ namespace Discord.WebSocket
/// Discord will only accept setting of name and the type of activity.
///
///
+ /// Bot accounts cannot set as their activity
+ /// type and it will have no effect.
+ ///
+ ///
/// Rich Presence cannot be set via this method or client. Rich Presence is strictly limited to RPC
/// clients only.
///
@@ -296,10 +308,14 @@ namespace Discord.WebSocket
=> Task.FromResult(GetUser(username, discriminator));
///
- Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
- => Task.FromResult(GetVoiceRegion(id));
+ async Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
+ {
+ return await GetVoiceRegionAsync(id).ConfigureAwait(false);
+ }
///
- Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
- => Task.FromResult>(VoiceRegions);
+ async Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
+ {
+ return await GetVoiceRegionsAsync().ConfigureAwait(false);
+ }
}
}
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index 7d62596f7..386f9f7e5 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -36,13 +36,13 @@ namespace Discord.WebSocket
///
public override IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount);
public IReadOnlyCollection Shards => _shards;
- ///
- public override IReadOnlyCollection VoiceRegions => _shards[0].VoiceRegions;
///
/// Provides access to a REST-only client with a shared state from this client.
///
- public override DiscordSocketRestClient Rest => _shards[0].Rest;
+ public override DiscordSocketRestClient Rest => _shards?[0].Rest;
+
+ public override SocketSelfUser CurrentUser { get => _shards?.FirstOrDefault(x => x.CurrentUser != null)?.CurrentUser; protected set => throw new InvalidOperationException(); }
/// Creates a new REST/WebSocket Discord client.
public DiscordShardedClient() : this(null, new DiscordSocketConfig()) { }
@@ -90,8 +90,7 @@ namespace Discord.WebSocket
}
}
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)
{
@@ -264,8 +263,16 @@ namespace Discord.WebSocket
}
///
- public override RestVoiceRegion GetVoiceRegion(string id)
- => _shards[0].GetVoiceRegion(id);
+ public override async ValueTask> GetVoiceRegionsAsync(RequestOptions options = null)
+ {
+ return await _shards[0].GetVoiceRegionsAsync().ConfigureAwait(false);
+ }
+
+ ///
+ public override async ValueTask GetVoiceRegionAsync(string id, RequestOptions options = null)
+ {
+ return await _shards[0].GetVoiceRegionAsync(id, options).ConfigureAwait(false);
+ }
///
/// is
@@ -325,14 +332,6 @@ namespace Discord.WebSocket
}
return Task.Delay(0);
};
- if (isPrimary)
- {
- client.Ready += () =>
- {
- CurrentUser = client.CurrentUser;
- return Task.Delay(0);
- };
- }
client.Connected += () => _shardConnectedEvent.InvokeAsync(client);
client.Disconnected += (exception) => _shardDisconnectedEvent.InvokeAsync(exception, client);
@@ -420,11 +419,15 @@ namespace Discord.WebSocket
=> Task.FromResult(GetUser(username, discriminator));
///
- Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
- => Task.FromResult>(VoiceRegions);
+ async Task> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
+ {
+ return await GetVoiceRegionsAsync().ConfigureAwait(false);
+ }
///
- Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
- => Task.FromResult(GetVoiceRegion(id));
+ async Task IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
+ {
+ return await GetVoiceRegionAsync(id).ConfigureAwait(false);
+ }
internal override void Dispose(bool disposing)
{
diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
index d14a314d6..d1407da01 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
@@ -40,9 +40,8 @@ namespace Discord.API
public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent,
string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null,
- RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second,
bool useSystemClock = true)
- : base(restClientProvider, userAgent, defaultRetryMode, serializer, rateLimitPrecision, useSystemClock)
+ : base(restClientProvider, userAgent, defaultRetryMode, serializer, useSystemClock)
{
_gatewayUrl = url;
if (url != null)
@@ -216,7 +215,7 @@ namespace Discord.API
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);
var props = new Dictionary
@@ -234,10 +233,7 @@ namespace Discord.API
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)
{
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 76c17a2ec..c0f4263fb 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -43,8 +43,7 @@ namespace Discord.WebSocket
private DateTimeOffset? _statusSince;
private RestApplication _applicationInfo;
private bool _isDisposed;
- private bool _guildSubscriptions;
- private GatewayIntents? _gatewayIntents;
+ private GatewayIntents _gatewayIntents;
///
/// Provides access to a REST-only client with a shared state from this client.
@@ -72,7 +71,6 @@ namespace Discord.WebSocket
internal WebSocketProvider WebSocketProvider { get; private set; }
internal bool AlwaysDownloadUsers { get; private set; }
internal int? HandlerTimeout { get; private set; }
- internal bool? ExclusiveBulkDelete { get; private set; }
internal bool AlwaysAcknowledgeInteractions { get; private set; }
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
@@ -110,8 +108,6 @@ namespace Discord.WebSocket
///
public IReadOnlyCollection GroupChannels
=> State.PrivateChannels.OfType().ToImmutableArray();
- ///
- public override IReadOnlyCollection VoiceRegions => _voiceRegions.ToReadOnlyCollection();
///
/// Initializes a new REST/WebSocket-based Discord client.
@@ -137,11 +133,9 @@ namespace Discord.WebSocket
AlwaysDownloadUsers = config.AlwaysDownloadUsers;
AlwaysAcknowledgeInteractions = config.AlwaysAcknowledgeInteractions;
HandlerTimeout = config.HandlerTimeout;
- ExclusiveBulkDelete = config.ExclusiveBulkDelete;
State = new ClientState(0, 0);
Rest = new DiscordSocketRestClient(config, ApiClient);
_heartbeatTimes = new ConcurrentQueue();
- _guildSubscriptions = config.GuildSubscriptions;
_gatewayIntents = config.GatewayIntents;
_stateLock = new SemaphoreSlim(1, 1);
@@ -180,12 +174,10 @@ namespace Discord.WebSocket
return Task.Delay(0);
};
- _voiceRegions = ImmutableDictionary.Create();
_largeGuilds = new ConcurrentQueue();
}
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);
///
internal override void Dispose(bool disposing)
{
@@ -203,24 +195,12 @@ namespace Discord.WebSocket
base.Dispose(disposing);
}
- ///
- internal override async Task OnLoginAsync(TokenType tokenType, string token)
- {
- if (_parentClient == null)
- {
- var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false);
- _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id);
- }
- else
- _voiceRegions = _parentClient._voiceRegions;
- await Rest.OnLoginAsync(tokenType, token);
- }
///
internal override async Task OnLogoutAsync()
{
await StopAsync().ConfigureAwait(false);
_applicationInfo = null;
- _voiceRegions = ImmutableDictionary.Create();
+ _voiceRegions = null;
await Rest.OnLogoutAsync();
}
@@ -252,17 +232,17 @@ namespace Discord.WebSocket
else
{
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);
}
-
- //Wait for READY
- await _connection.WaitAsync().ConfigureAwait(false);
}
finally
{
if (locked)
_shardedClient.ReleaseIdentifyLock();
}
+
+ //Wait for READY
+ await _connection.WaitAsync().ConfigureAwait(false);
}
private async Task OnDisconnectingAsync(Exception ex)
{
@@ -311,13 +291,51 @@ namespace Discord.WebSocket
public override SocketChannel GetChannel(ulong id)
=> State.GetChannel(id);
///
+ /// Gets a generic channel from the cache or does a rest request if unavailable.
+ ///
+ ///
+ ///
+ /// var channel = await _client.GetChannelAsync(381889909113225237);
+ /// if (channel != null && channel is IMessageChannel msgChannel)
+ /// {
+ /// await msgChannel.SendMessageAsync($"{msgChannel} is created at {msgChannel.CreatedAt}");
+ /// }
+ ///
+ ///
+ /// The snowflake identifier of the channel (e.g. `381889909113225237`).
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous get operation. The task result contains the channel associated
+ /// with the snowflake identifier; null when the channel cannot be found.
+ ///
+ public async ValueTask GetChannelAsync(ulong id, RequestOptions options = null)
+ => GetChannel(id) ?? (IChannel)await ClientHelper.GetChannelAsync(this, id, options).ConfigureAwait(false);
+ ///
+ /// Gets a user from the cache or does a rest request if unavailable.
+ ///
+ ///
+ ///
+ /// var user = await _client.GetUserAsync(168693960628371456);
+ /// if (user != null)
+ /// Console.WriteLine($"{user} is created at {user.CreatedAt}.";
+ ///
+ ///
+ /// The snowflake identifier of the user (e.g. `168693960628371456`).
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous get operation. The task result contains the user associated with
+ /// the snowflake identifier; null if the user is not found.
+ ///
+ public async ValueTask GetUserAsync(ulong id, RequestOptions options = null)
+ => await ClientHelper.GetUserAsync(this, id, options).ConfigureAwait(false);
+ ///
/// Clears all cached channels from the client.
///
public void PurgeChannelCache() => State.PurgeAllChannels();
///
/// Clears cached DM channels from the client.
///
- public void PurgeDMChannelCache() => State.PurgeDMChannels();
+ public void PurgeDMChannelCache() => RemoveDMChannels();
///
public override SocketUser GetUser(ulong id)
@@ -331,12 +349,11 @@ namespace Discord.WebSocket
public void PurgeUserCache() => State.PurgeUsers();
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)
{
@@ -344,7 +361,7 @@ namespace Discord.WebSocket
{
var user = SocketGlobalUser.Create(this, state, model);
user.GlobalUser.AddRef();
- user.Presence = new SocketPresence(UserStatus.Online, null, null, null);
+ user.Presence = new SocketPresence(UserStatus.Online, null, null);
return user;
});
}
@@ -352,11 +369,34 @@ namespace Discord.WebSocket
=> State.RemoveUser(id);
///
- public override RestVoiceRegion GetVoiceRegion(string id)
+ public override async ValueTask> GetVoiceRegionsAsync(RequestOptions options = null)
{
- if (_voiceRegions.TryGetValue(id, out RestVoiceRegion region))
- return region;
- return null;
+ if (_parentClient == null)
+ {
+ if (_voiceRegions == null)
+ {
+ options = RequestOptions.CreateOrClone(options);
+ options.IgnoreState = true;
+ var voiceRegions = await ApiClient.GetVoiceRegionsAsync(options).ConfigureAwait(false);
+ _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id);
+ }
+ return _voiceRegions.ToReadOnlyCollection();
+ }
+ return await _parentClient.GetVoiceRegionsAsync().ConfigureAwait(false);
+ }
+
+ ///
+ public override async ValueTask GetVoiceRegionAsync(string id, RequestOptions options = null)
+ {
+ if (_parentClient == null)
+ {
+ if (_voiceRegions == null)
+ await GetVoiceRegionsAsync().ConfigureAwait(false);
+ if (_voiceRegions.TryGetValue(id, out RestVoiceRegion region))
+ return region;
+ return null;
+ }
+ return await _parentClient.GetVoiceRegionAsync(id, options).ConfigureAwait(false);
}
///
@@ -450,7 +490,8 @@ namespace Discord.WebSocket
{
if (CurrentUser == null)
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);
@@ -545,7 +586,7 @@ namespace Discord.WebSocket
await _shardedClient.AcquireIdentifyLockAsync(ShardId, _connection.CancelToken).ConfigureAwait(false);
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
{
@@ -553,7 +594,7 @@ namespace Discord.WebSocket
}
}
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;
case GatewayOpCode.Reconnect:
@@ -576,7 +617,8 @@ namespace Discord.WebSocket
var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);
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;
int unavailableGuilds = 0;
for (int i = 0; i < data.Guilds.Length; i++)
@@ -730,7 +772,8 @@ namespace Discord.WebSocket
break;
case "GUILD_SYNC":
{
- await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false);
+ await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_SYNC)").ConfigureAwait(false);
+ /*await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false); //TODO remove? userbot related
var data = (payload as JToken).ToObject(_serializer);
var guild = State.GetGuild(data.Id);
if (guild != null)
@@ -747,7 +790,7 @@ namespace Discord.WebSocket
{
await UnknownGuildAsync(type, data.Id).ConfigureAwait(false);
return;
- }
+ }*/
}
break;
case "GUILD_DELETE":
@@ -947,15 +990,15 @@ namespace Discord.WebSocket
var before = user.Clone();
user.Update(State, data);
- await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false);
+
+ var cacheableBefore = new Cacheable(before, user.Id, true, () => Task.FromResult((SocketGuildUser)null));
+ await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false);
}
else
{
- if (!guild.HasAllMembers)
- await IncompleteGuildUserAsync(type, data.User.Id, data.GuildId).ConfigureAwait(false);
- else
- await UnknownGuildUserAsync(type, data.User.Id, data.GuildId).ConfigureAwait(false);
- return;
+ user = guild.AddOrUpdateUser(data);
+ var cacheableBefore = new Cacheable(null, user.Id, false, () => Task.FromResult((SocketGuildUser)null));
+ await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false);
}
}
else
@@ -1218,56 +1261,63 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
+ var channel = 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
- 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
- {
- 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;
case "MESSAGE_UPDATE":
@@ -1275,52 +1325,85 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
+ var channel = 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;
- }
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
+ return;
+ }
- SocketMessage before = null, after = null;
- SocketMessage cachedMsg = channel.GetCachedMessage(data.Id);
- bool isCached = cachedMsg != null;
- if (isCached)
+ SocketMessage before = null, after = null;
+ SocketMessage cachedMsg = channel?.GetCachedMessage(data.Id);
+ bool isCached = cachedMsg != null;
+ if (isCached)
+ {
+ before = cachedMsg.Clone();
+ cachedMsg.Update(State, data);
+ after = cachedMsg;
+ }
+ else
+ {
+ //Edited message isnt in cache, create a detached one
+ SocketUser author;
+ if (data.Author.IsSpecified)
{
- before = cachedMsg.Clone();
- cachedMsg.Update(State, data);
- after = cachedMsg;
+ if (guild != null)
+ {
+ if (data.WebhookId.IsSpecified)
+ author = SocketWebhookUser.Create(guild, State, data.Author.Value, data.WebhookId.Value);
+ else
+ author = guild.GetUser(data.Author.Value.Id);
+ }
+ else
+ author = (channel as SocketChannel)?.GetUser(data.Author.Value.Id);
+
+ if (author == null)
+ {
+ if (guild != null)
+ {
+ 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
+ }
+ else if (channel is SocketGroupChannel groupChannel)
+ author = groupChannel.GetOrAddUser(data.Author.Value);
+ }
}
else
+ // Message author wasn't specified in the payload, so create a completely anonymous unknown user
+ author = new SocketUnknownUser(this, id: 0);
+
+ if (channel == null)
{
- //Edited message isnt in cache, create a detached one
- SocketUser author;
- if (data.Author.IsSpecified)
+ if (!data.GuildId.IsSpecified) // assume it is a DM
{
- if (guild != null)
- author = guild.GetUser(data.Author.Value.Id);
+ if (data.Author.IsSpecified)
+ {
+ var dmChannel = CreateDMChannel(data.ChannelId, data.Author.Value, State);
+ channel = dmChannel;
+ author = dmChannel.Recipient;
+ }
else
- author = (channel as SocketChannel).GetUser(data.Author.Value.Id);
- if (author == null)
- author = SocketUnknownUser.Create(this, State, data.Author.Value);
+ channel = CreateDMChannel(data.ChannelId, author, State);
}
else
- // Message author wasn't specified in the payload, so create a completely anonymous unknown user
- author = new SocketUnknownUser(this, id: 0);
-
- after = SocketMessage.Create(this, State, author, channel, data);
+ {
+ await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
+ return;
+ }
}
- var cacheableBefore = new Cacheable(before, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id).ConfigureAwait(false));
- await TimedInvokeAsync(_messageUpdatedEvent, nameof(MessageUpdated), cacheableBefore, after, channel).ConfigureAwait(false);
- }
- else
- {
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
+ after = SocketMessage.Create(this, State, author, channel, data);
}
+ var cacheableBefore = new Cacheable(before, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id).ConfigureAwait(false));
+
+ await TimedInvokeAsync(_messageUpdatedEvent, nameof(MessageUpdated), cacheableBefore, after, channel).ConfigureAwait(false);
}
break;
case "MESSAGE_DELETE":
@@ -1328,26 +1411,22 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
- {
- var guild = (channel as SocketGuildChannel)?.Guild;
- if (!(guild?.IsSynced ?? true))
- {
- await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
- return;
- }
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
- var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id);
- bool isCached = msg != null;
- var cacheable = new Cacheable(msg, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id).ConfigureAwait(false));
-
- await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false);
- }
- else
+ var guild = (channel as SocketGuildChannel)?.Guild;
+ if (!(guild?.IsSynced ?? true))
{
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}
+
+ SocketMessage msg = null;
+ if (channel != null)
+ msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id);
+ var cacheableMsg = new Cacheable(msg, data.Id, msg != null, () => Task.FromResult((IMessage)null));
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+
+ await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheableMsg, cacheableChannel).ConfigureAwait(false);
}
break;
case "MESSAGE_REACTION_ADD":
@@ -1355,32 +1434,43 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
- {
- var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
- bool isCached = cachedMsg != null;
- var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly).ConfigureAwait(false);
-
- var optionalMsg = !isCached
- ? Optional.Create()
- : Optional.Create(cachedMsg);
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
- var optionalUser = user is null
- ? Optional.Create()
- : Optional.Create(user);
+ var cachedMsg = channel?.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isMsgCached = cachedMsg != null;
+ IUser user = null;
+ if (channel != null)
+ user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly).ConfigureAwait(false);
- var reaction = SocketReaction.Create(data, channel, optionalMsg, optionalUser);
- var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage);
+ var optionalMsg = !isMsgCached
+ ? Optional.Create()
+ : Optional.Create(cachedMsg);
- cachedMsg?.AddReaction(reaction);
+ if (data.Member.IsSpecified)
+ {
+ var guild = (channel as SocketGuildChannel)?.Guild;
- await TimedInvokeAsync(_reactionAddedEvent, nameof(ReactionAdded), cacheable, channel, reaction).ConfigureAwait(false);
+ if (guild != null)
+ user = guild.AddOrUpdateUser(data.Member.Value);
}
else
+ user = GetUser(data.UserId);
+
+ var optionalUser = user is null
+ ? Optional.Create()
+ : Optional.Create(user);
+
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+ var cacheableMsg = new Cacheable(cachedMsg, data.MessageId, isMsgCached, async () =>
{
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
+ var channelObj = await cacheableChannel.GetOrDownloadAsync().ConfigureAwait(false);
+ return await channelObj.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage;
+ });
+ var reaction = SocketReaction.Create(data, channel, optionalMsg, optionalUser);
+
+ cachedMsg?.AddReaction(reaction);
+
+ await TimedInvokeAsync(_reactionAddedEvent, nameof(ReactionAdded), cacheableMsg, cacheableChannel, reaction).ConfigureAwait(false);
}
break;
case "MESSAGE_REACTION_REMOVE":
@@ -1388,32 +1478,35 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
- {
- var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
- bool isCached = cachedMsg != null;
- var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly).ConfigureAwait(false);
-
- var optionalMsg = !isCached
- ? Optional.Create()
- : Optional.Create(cachedMsg);
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
- var optionalUser = user is null
- ? Optional.Create()
- : Optional.Create(user);
+ var cachedMsg = channel?.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isMsgCached = cachedMsg != null;
+ IUser user = null;
+ if (channel != null)
+ user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly).ConfigureAwait(false);
+ else if (!data.GuildId.IsSpecified)
+ user = GetUser(data.UserId);
- var reaction = SocketReaction.Create(data, channel, optionalMsg, optionalUser);
- var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage);
+ var optionalMsg = !isMsgCached
+ ? Optional.Create()
+ : Optional.Create(cachedMsg);
- cachedMsg?.RemoveReaction(reaction);
+ var optionalUser = user is null
+ ? Optional.Create()
+ : Optional.Create(user);
- await TimedInvokeAsync(_reactionRemovedEvent, nameof(ReactionRemoved), cacheable, channel, reaction).ConfigureAwait(false);
- }
- else
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+ var cacheableMsg = new Cacheable(cachedMsg, data.MessageId, isMsgCached, async () =>
{
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
+ var channelObj = await cacheableChannel.GetOrDownloadAsync().ConfigureAwait(false);
+ return await channelObj.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage;
+ });
+ var reaction = SocketReaction.Create(data, channel, optionalMsg, optionalUser);
+
+ cachedMsg?.RemoveReaction(reaction);
+
+ await TimedInvokeAsync(_reactionRemovedEvent, nameof(ReactionRemoved), cacheableMsg, cacheableChannel, reaction).ConfigureAwait(false);
}
break;
case "MESSAGE_REACTION_REMOVE_ALL":
@@ -1421,21 +1514,20 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
+
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+ var cachedMsg = channel?.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isMsgCached = cachedMsg != null;
+ var cacheableMsg = new Cacheable(cachedMsg, data.MessageId, isMsgCached, async () =>
{
- var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
- bool isCached = cachedMsg != null;
- var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => (await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false)) as IUserMessage);
+ var channelObj = await cacheableChannel.GetOrDownloadAsync().ConfigureAwait(false);
+ return await channelObj.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage;
+ });
- cachedMsg?.ClearReactions();
+ cachedMsg?.ClearReactions();
- await TimedInvokeAsync(_reactionsClearedEvent, nameof(ReactionsCleared), cacheable, channel).ConfigureAwait(false);
- }
- else
- {
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
+ await TimedInvokeAsync(_reactionsClearedEvent, nameof(ReactionsCleared), cacheableMsg, cacheableChannel).ConfigureAwait(false);
}
break;
case "MESSAGE_REACTION_REMOVE_EMOJI":
@@ -1443,70 +1535,55 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_EMOJI)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
- {
- var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
- bool isCached = cachedMsg != null;
-
- var optionalMsg = !isCached
- ? Optional.Create()
- : Optional.Create(cachedMsg);
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
- var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage);
- var emote = data.Emoji.ToIEmote();
+ var cachedMsg = channel?.GetCachedMessage(data.MessageId) as SocketUserMessage;
+ bool isMsgCached = cachedMsg != null;
- cachedMsg?.RemoveAllReactionsForEmoteAsync(emote);
+ var optionalMsg = !isMsgCached
+ ? Optional.Create()
+ : Optional.Create(cachedMsg);
- await TimedInvokeAsync(_reactionsRemovedForEmoteEvent, nameof(ReactionsRemovedForEmote), cacheable, channel, emote).ConfigureAwait(false);
- }
- else
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+ var cacheableMsg = new Cacheable(cachedMsg, data.MessageId, isMsgCached, async () =>
{
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
- return;
- }
+ var channelObj = await cacheableChannel.GetOrDownloadAsync().ConfigureAwait(false);
+ return await channelObj.GetMessageAsync(data.MessageId).ConfigureAwait(false) as IUserMessage;
+ });
+ var emote = data.Emoji.ToIEmote();
+
+ cachedMsg?.RemoveReactionsForEmote(emote);
+
+ await TimedInvokeAsync(_reactionsRemovedForEmoteEvent, nameof(ReactionsRemovedForEmote), cacheableMsg, cacheableChannel, emote).ConfigureAwait(false);
}
break;
case "MESSAGE_DELETE_BULK":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
- if (!ExclusiveBulkDelete.HasValue)
- {
- await _gatewayLogger.WarningAsync("A bulk delete event has been received, but the event handling behavior has not been set. " +
- "To suppress this message, set the ExclusiveBulkDelete configuration property. " +
- "This message will appear only once.");
- ExclusiveBulkDelete = false;
- }
-
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
- {
- var guild = (channel as SocketGuildChannel)?.Guild;
- if (!(guild?.IsSynced ?? true))
- {
- await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
- return;
- }
-
- var cacheableList = new List>(data.Ids.Length);
- foreach (ulong id in data.Ids)
- {
- var msg = SocketChannelHelper.RemoveMessage(channel, this, id);
- bool isCached = msg != null;
- var cacheable = new Cacheable(msg, id, isCached, async () => await channel.GetMessageAsync(id).ConfigureAwait(false));
- cacheableList.Add(cacheable);
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
- if (!ExclusiveBulkDelete ?? false) // this shouldn't happen, but we'll play it safe anyways
- await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false);
- }
-
- await TimedInvokeAsync(_messagesBulkDeletedEvent, nameof(MessagesBulkDeleted), cacheableList, channel).ConfigureAwait(false);
- }
- else
+ var guild = (channel as SocketGuildChannel)?.Guild;
+ if (!(guild?.IsSynced ?? true))
{
- await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}
+
+ var cacheableChannel = new Cacheable(channel, data.ChannelId, channel != null, async () => await GetChannelAsync(data.ChannelId).ConfigureAwait(false) as IMessageChannel);
+ var cacheableList = new List>(data.Ids.Length);
+ foreach (ulong id in data.Ids)
+ {
+ SocketMessage msg = null;
+ if (channel != null)
+ msg = SocketChannelHelper.RemoveMessage(channel, this, id);
+ bool isMsgCached = msg != null;
+ var cacheableMsg = new Cacheable(msg, id, isMsgCached, () => Task.FromResult((IMessage)null));
+ cacheableList.Add(cacheableMsg);
+ }
+
+ await TimedInvokeAsync(_messagesBulkDeletedEvent, nameof(MessagesBulkDeleted), cacheableList, cacheableChannel).ConfigureAwait(false);
}
break;
@@ -1552,7 +1629,8 @@ namespace Discord.WebSocket
var before = user.Clone();
user.Update(State, data, true);
- await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false);
+ var cacheableBefore = new Cacheable(before, user.Id, true, () => Task.FromResult(user));
+ await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), cacheableBefore, user).ConfigureAwait(false);
}
else
{
@@ -1575,24 +1653,26 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false);
var data = (payload as JToken).ToObject(_serializer);
- if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
+ var channel = GetChannel(data.ChannelId) as ISocketMessageChannel;
+
+ var guild = (channel as SocketGuildChannel)?.Guild;
+ if (!(guild?.IsSynced ?? true))
{
- var guild = (channel as SocketGuildChannel)?.Guild;
- if (!(guild?.IsSynced ?? true))
- {
- await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
- return;
- }
+ await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
+ return;
+ }
- var user = (channel as SocketChannel).GetUser(data.UserId);
- if (user == null)
- {
- if (guild != null)
- user = guild.AddOrUpdateUser(data.Member);
- }
- if (user != null)
- await TimedInvokeAsync(_userIsTypingEvent, nameof(UserIsTyping), user, channel).ConfigureAwait(false);
+ var cacheableChannel = new Cacheable