Browse Source

Merge branch 'dev' into stickers

pull/1726/head
Paulo GitHub 4 years ago
parent
commit
b187009a72
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 526 additions and 98 deletions
  1. +103
    -0
      CHANGELOG.md
  2. +1
    -1
      Discord.Net.targets
  3. +0
    -2
      README.md
  4. +1
    -1
      samples/03_sharded_client/Modules/PublicModule.cs
  5. +1
    -1
      src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
  6. +12
    -1
      src/Discord.Net.Core/Entities/Messages/IMessage.cs
  7. +36
    -0
      src/Discord.Net.Core/Entities/Messages/MessageFlags.cs
  8. +12
    -0
      src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
  9. +7
    -0
      src/Discord.Net.Core/Entities/Roles/IRole.cs
  10. +40
    -0
      src/Discord.Net.Core/Entities/Roles/RoleTags.cs
  11. +5
    -0
      src/Discord.Net.Core/Entities/Users/IGuildUser.cs
  12. +10
    -0
      src/Discord.Net.Core/Entities/Users/IUser.cs
  13. +39
    -9
      src/Discord.Net.Core/Entities/Users/UserProperties.cs
  14. +1
    -1
      src/Discord.Net.Rest/API/Common/AllowedMentions.cs
  15. +1
    -1
      src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
  16. +2
    -0
      src/Discord.Net.Rest/API/Common/GuildMember.cs
  17. +0
    -10
      src/Discord.Net.Rest/API/Common/MessageFlags.cs
  18. +3
    -1
      src/Discord.Net.Rest/API/Common/Role.cs
  19. +15
    -0
      src/Discord.Net.Rest/API/Common/RoleTags.cs
  20. +2
    -0
      src/Discord.Net.Rest/API/Common/User.cs
  21. +5
    -1
      src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs
  22. +2
    -0
      src/Discord.Net.Rest/API/Rest/UploadFileParams.cs
  23. +9
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs
  24. +5
    -2
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
  25. +10
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs
  26. +10
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs
  27. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
  28. +29
    -4
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  29. +5
    -0
      src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
  30. +2
    -6
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  31. +4
    -0
      src/Discord.Net.Rest/Entities/Roles/RestRole.cs
  32. +4
    -0
      src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
  33. +4
    -0
      src/Discord.Net.Rest/Entities/Users/RestUser.cs
  34. +2
    -0
      src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs
  35. +4
    -2
      src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs
  36. +7
    -0
      src/Discord.Net.Rest/Extensions/EntityExtensions.cs
  37. +31
    -0
      src/Discord.Net.WebSocket/BaseSocketClient.cs
  38. +14
    -0
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  39. +42
    -21
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  40. +6
    -0
      src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
  41. +2
    -6
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
  42. +4
    -0
      src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs
  43. +4
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs
  44. +7
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
  45. +2
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs
  46. +4
    -2
      src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs
  47. +16
    -16
      src/Discord.Net/Discord.Net.nuspec

+ 103
- 0
CHANGELOG.md View File

@@ -1,5 +1,108 @@
# Changelog # Changelog


## [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 <summary> to <remarks> (62539f0)

## [2.2.0] - 2020-04-16 ## [2.2.0] - 2020-04-16
### Added ### Added
- #1247 Implement Client Status Support (9da11b4) - #1247 Implement Client Status Support (9da11b4)


+ 1
- 1
Discord.Net.targets View File

@@ -1,6 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<VersionPrefix>2.3.0</VersionPrefix>
<VersionPrefix>2.4.0</VersionPrefix>
<VersionSuffix>dev</VersionSuffix> <VersionSuffix>dev</VersionSuffix>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Authors>Discord.Net Contributors</Authors> <Authors>Discord.Net Contributors</Authors>


+ 0
- 2
README.md View File

@@ -8,8 +8,6 @@ An unofficial .NET API Wrapper for the Discord client (https://discord.com).


## Documentation ## Documentation


- [Stable](https://discord.foxbot.me/)
- Hosted by @foxbot
- [Nightly](https://docs.stillu.cc/) - [Nightly](https://docs.stillu.cc/)
- [Latest CI repo](https://github.com/discord-net/docs-static) - [Latest CI repo](https://github.com/discord-net/docs-static)




+ 1
- 1
samples/03_sharded_client/Modules/PublicModule.cs View File

@@ -9,7 +9,7 @@ namespace _03_sharded_client.Modules
[Command("info")] [Command("info")]
public async Task InfoAsync() public async Task InfoAsync()
{ {
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards} shards!
var msg = $@"Hi {Context.User}! There are currently {Context.Client.Shards.Count} shards!
This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}"; This guild is being served by shard number {Context.Client.GetShardFor(Context.Guild).ShardId}";
await ReplyAsync(msg); await ReplyAsync(msg);
} }


+ 1
- 1
src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs View File

@@ -136,7 +136,7 @@ namespace Discord.Commands
builder.Name = typeInfo.Name; builder.Name = typeInfo.Name;


// Get all methods (including from inherited members), that are valid commands // Get all methods (including from inherited members), that are valid commands
var validCommands = typeInfo.GetMethods().Where(IsValidCommandDefinition);
var validCommands = typeInfo.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsValidCommandDefinition);


foreach (var method in validCommands) foreach (var method in validCommands)
{ {


+ 12
- 1
src/Discord.Net.Core/Entities/Messages/IMessage.cs View File

@@ -92,10 +92,10 @@ namespace Discord
/// Gets all embeds included in this message. /// Gets all embeds included in this message.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// </remarks>
/// This property gets a read-only collection of embeds associated with this message. Depending on the /// 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 /// message, a sent message may contain one or more embeds. This is usually true when multiple link previews
/// are generated; however, only one <see cref="EmbedType.Rich"/> <see cref="Embed"/> can be featured. /// are generated; however, only one <see cref="EmbedType.Rich"/> <see cref="Embed"/> can be featured.
/// </remarks>
/// <returns> /// <returns>
/// A read-only collection of embed objects. /// A read-only collection of embed objects.
/// </returns> /// </returns>
@@ -171,6 +171,17 @@ namespace Discord
/// A read-only collection of sticker objects. /// A read-only collection of sticker objects.
/// </returns> /// </returns>
IReadOnlyCollection<ISticker> Stickers { get; } IReadOnlyCollection<ISticker> Stickers { get; }
/// <summary>
/// Gets the flags related to this message.
/// </summary>
/// <remarks>
/// This value is determined by bitwise OR-ing <see cref="MessageFlags"/> values together.
/// </remarks>
/// <returns>
/// A message's flags, if any is associated.
/// </returns>
MessageFlags? Flags { get; }


/// <summary> /// <summary>
/// Adds a reaction to this message. /// Adds a reaction to this message.


+ 36
- 0
src/Discord.Net.Core/Entities/Messages/MessageFlags.cs View File

@@ -0,0 +1,36 @@
using System;

namespace Discord
{
[Flags]
public enum MessageFlags
{
/// <summary>
/// Default value for flags, when none are given to a message.
/// </summary>
None = 0,
/// <summary>
/// Flag given to messages that have been published to subscribed
/// channels (via Channel Following).
/// </summary>
Crossposted = 1 << 0,
/// <summary>
/// Flag given to messages that originated from a message in another
/// channel (via Channel Following).
/// </summary>
IsCrosspost = 1 << 1,
/// <summary>
/// Flag given to messages that do not display any embeds.
/// </summary>
SuppressEmbeds = 1 << 2,
/// <summary>
/// Flag given to messages that the source message for this crosspost
/// has been deleted (via Channel Following).
/// </summary>
SourceMessageDeleted = 1 << 3,
/// <summary>
/// Flag given to messages that came from the urgent message system.
/// </summary>
Urgent = 1 << 4,
}
}

+ 12
- 0
src/Discord.Net.Core/Entities/Messages/MessageProperties.cs View File

@@ -21,5 +21,17 @@ namespace Discord
/// Gets or sets the embed the message should display. /// Gets or sets the embed the message should display.
/// </summary> /// </summary>
public Optional<Embed> Embed { get; set; } public Optional<Embed> Embed { get; set; }
/// <summary>
/// Gets or sets the flags of the message.
/// </summary>
/// <remarks>
/// Only <see cref="MessageFlags.SuppressEmbeds"/> can be set/unset and you need to be
/// the author of the message.
/// </remarks>
public Optional<MessageFlags?> Flags { get; set; }
/// <summary>
/// Gets or sets the allowed mentions of the message.
/// </summary>
public Optional<AllowedMentions> AllowedMentions { get; set; }
} }
} }

+ 7
- 0
src/Discord.Net.Core/Entities/Roles/IRole.cs View File

@@ -65,6 +65,13 @@ namespace Discord
/// An <see cref="int"/> representing the position of the role in the role list of the guild. /// An <see cref="int"/> representing the position of the role in the role list of the guild.
/// </returns> /// </returns>
int Position { get; } int Position { get; }
/// <summary>
/// Gets the tags related to this role.
/// </summary>
/// <returns>
/// A <see cref="RoleTags"/> object containing all tags related to this role.
/// </returns>
RoleTags Tags { get; }


/// <summary> /// <summary>
/// Modifies this role. /// Modifies this role.


+ 40
- 0
src/Discord.Net.Core/Entities/Roles/RoleTags.cs View File

@@ -0,0 +1,40 @@
namespace Discord
{
/// <summary>
/// Provides tags related to a discord role.
/// </summary>
public class RoleTags
{
/// <summary>
/// Gets the identifier of the bot that this role belongs to, if it does.
/// </summary>
/// <returns>
/// A <see langword="ulong"/> if this role belongs to a bot; otherwise
/// <see langword="null"/>.
/// </returns>
public ulong? BotId { get; }
/// <summary>
/// Gets the identifier of the integration that this role belongs to, if it does.
/// </summary>
/// <returns>
/// A <see langword="ulong"/> if this role belongs to an integration; otherwise
/// <see langword="null"/>.
/// </returns>
public ulong? IntegrationId { get; }
/// <summary>
/// Gets if this role is the guild's premium subscriber (booster) role.
/// </summary>
/// <returns>
/// <see langword="true"/> if this role is the guild's premium subscriber role;
/// otherwise <see langword="false"/>.
/// </returns>
public bool IsPremiumSubscriberRole { get; }

internal RoleTags(ulong? botId, ulong? integrationId, bool isPremiumSubscriber)
{
BotId = botId;
IntegrationId = integrationId;
IsPremiumSubscriberRole = isPremiumSubscriber;
}
}
}

+ 5
- 0
src/Discord.Net.Core/Entities/Users/IGuildUser.cs View File

@@ -68,6 +68,11 @@ namespace Discord
/// </returns> /// </returns>
IReadOnlyCollection<ulong> RoleIds { get; } IReadOnlyCollection<ulong> RoleIds { get; }


/// <summary>
/// Whether the user has passed the guild's Membership Screening requirements.
/// </summary>
bool? IsPending { get; }

/// <summary> /// <summary>
/// Gets the level permissions granted to this user to a given channel. /// Gets the level permissions granted to this user to a given channel.
/// </summary> /// </summary>


+ 10
- 0
src/Discord.Net.Core/Entities/Users/IUser.cs View File

@@ -75,6 +75,16 @@ namespace Discord
/// Gets the username for this user. /// Gets the username for this user.
/// </summary> /// </summary>
string Username { get; } string Username { get; }
/// <summary>
/// Gets the public flags that are applied to this user's account.
/// </summary>
/// <remarks>
/// This value is determined by bitwise OR-ing <see cref="UserProperties"/> values together.
/// </remarks>
/// <returns>
/// The value of public flags for this user.
/// </returns>
UserProperties? PublicFlags { get; }


/// <summary> /// <summary>
/// Gets the direct message channel of this user, or create one if it does not already exist. /// Gets the direct message channel of this user, or create one if it does not already exist.


+ 39
- 9
src/Discord.Net.Core/Entities/Users/UserProperties.cs View File

@@ -10,32 +10,62 @@ namespace Discord
/// </summary> /// </summary>
None = 0, None = 0,
/// <summary> /// <summary>
/// Flag given to Discord staff.
/// Flag given to users who are a Discord employee.
/// </summary> /// </summary>
Staff = 0b1,
Staff = 1 << 0,
/// <summary> /// <summary>
/// Flag given to Discord partners.
/// Flag given to users who are owners of a partnered Discord server.
/// </summary> /// </summary>
Partner = 0b10,
Partner = 1 << 1,
/// <summary>
/// Flag given to users in HypeSquad events.
/// </summary>
HypeSquadEvents = 1 << 2,
/// <summary> /// <summary>
/// Flag given to users who have participated in the bug report program. /// Flag given to users who have participated in the bug report program.
/// This flag is obsolete, use <see cref="BugHunterLevel1"/> instead.
/// </summary>
[Obsolete("Use BugHunterLevel1 instead.")]
BugHunter = 1 << 3,
/// <summary>
/// Flag given to users who have participated in the bug report program and are level 1.
/// </summary> /// </summary>
BugHunter = 0b1000,
BugHunterLevel1 = 1 << 3,
/// <summary> /// <summary>
/// Flag given to users who are in the HypeSquad House of Bravery. /// Flag given to users who are in the HypeSquad House of Bravery.
/// </summary> /// </summary>
HypeSquadBravery = 0b100_0000,
HypeSquadBravery = 1 << 6,
/// <summary> /// <summary>
/// Flag given to users who are in the HypeSquad House of Brilliance. /// Flag given to users who are in the HypeSquad House of Brilliance.
/// </summary> /// </summary>
HypeSquadBrilliance = 0b1000_0000,
HypeSquadBrilliance = 1 << 7,
/// <summary> /// <summary>
/// Flag given to users who are in the HypeSquad House of Balance. /// Flag given to users who are in the HypeSquad House of Balance.
/// </summary> /// </summary>
HypeSquadBalance = 0b1_0000_0000,
HypeSquadBalance = 1 << 8,
/// <summary> /// <summary>
/// Flag given to users who subscribed to Nitro before games were added. /// Flag given to users who subscribed to Nitro before games were added.
/// </summary> /// </summary>
EarlySupporter = 0b10_0000_0000,
EarlySupporter = 1 << 9,
/// <summary>
/// Flag given to users who are part of a team.
/// </summary>
TeamUser = 1 << 10,
/// <summary>
/// Flag given to users who represent Discord (System).
/// </summary>
System = 1 << 12,
/// <summary>
/// Flag given to users who have participated in the bug report program and are level 2.
/// </summary>
BugHunterLevel2 = 1 << 14,
/// <summary>
/// Flag given to users who are verified bots.
/// </summary>
VerifiedBot = 1 << 16,
/// <summary>
/// Flag given to users that developed bots and early verified their accounts.
/// </summary>
EarlyVerifiedBotDeveloper = 1 << 17,
} }
} }

src/Discord.Net.Rest/Entities/Messages/AllowedMentions.cs → src/Discord.Net.Rest/API/Common/AllowedMentions.cs View File

@@ -2,7 +2,7 @@ using Newtonsoft.Json;


namespace Discord.API namespace Discord.API
{ {
public class AllowedMentions
internal class AllowedMentions
{ {
[JsonProperty("parse")] [JsonProperty("parse")]
public Optional<string[]> Parse { get; set; } public Optional<string[]> Parse { get; set; }

+ 1
- 1
src/Discord.Net.Rest/API/Common/AuditLogEntry.cs View File

@@ -7,7 +7,7 @@ namespace Discord.API
[JsonProperty("target_id")] [JsonProperty("target_id")]
public ulong? TargetId { get; set; } public ulong? TargetId { get; set; }
[JsonProperty("user_id")] [JsonProperty("user_id")]
public ulong UserId { get; set; }
public ulong? UserId { get; set; }


[JsonProperty("changes")] [JsonProperty("changes")]
public AuditLogChange[] Changes { get; set; } public AuditLogChange[] Changes { get; set; }


+ 2
- 0
src/Discord.Net.Rest/API/Common/GuildMember.cs View File

@@ -18,6 +18,8 @@ namespace Discord.API
public Optional<bool> Deaf { get; set; } public Optional<bool> Deaf { get; set; }
[JsonProperty("mute")] [JsonProperty("mute")]
public Optional<bool> Mute { get; set; } public Optional<bool> Mute { get; set; }
[JsonProperty("pending")]
public Optional<bool> Pending { get; set; }
[JsonProperty("premium_since")] [JsonProperty("premium_since")]
public Optional<DateTimeOffset?> PremiumSince { get; set; } public Optional<DateTimeOffset?> PremiumSince { get; set; }
} }


+ 0
- 10
src/Discord.Net.Rest/API/Common/MessageFlags.cs View File

@@ -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,
}
}

+ 3
- 1
src/Discord.Net.Rest/API/Common/Role.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API namespace Discord.API
@@ -21,5 +21,7 @@ namespace Discord.API
public ulong Permissions { get; set; } public ulong Permissions { get; set; }
[JsonProperty("managed")] [JsonProperty("managed")]
public bool Managed { get; set; } public bool Managed { get; set; }
[JsonProperty("tags")]
public Optional<RoleTags> Tags { get; set; }
} }
} }

+ 15
- 0
src/Discord.Net.Rest/API/Common/RoleTags.cs View File

@@ -0,0 +1,15 @@
#pragma warning disable CS1591
using Newtonsoft.Json;

namespace Discord.API
{
internal class RoleTags
{
[JsonProperty("bot_id")]
public Optional<ulong> BotId { get; set; }
[JsonProperty("integration_id")]
public Optional<ulong> IntegrationId { get; set; }
[JsonProperty("premium_subscriber")]
public Optional<bool?> IsPremiumSubscriber { get; set; }
}
}

+ 2
- 0
src/Discord.Net.Rest/API/Common/User.cs View File

@@ -29,5 +29,7 @@ namespace Discord.API
public Optional<PremiumType> PremiumType { get; set; } public Optional<PremiumType> PremiumType { get; set; }
[JsonProperty("locale")] [JsonProperty("locale")]
public Optional<string> Locale { get; set; } public Optional<string> Locale { get; set; }
[JsonProperty("public_flags")]
public Optional<UserProperties> PublicFlags { get; set; }
} }
} }

+ 5
- 1
src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API.Rest namespace Discord.API.Rest
@@ -10,5 +10,9 @@ namespace Discord.API.Rest
public Optional<string> Content { get; set; } public Optional<string> Content { get; set; }
[JsonProperty("embed")] [JsonProperty("embed")]
public Optional<Embed> Embed { get; set; } public Optional<Embed> Embed { get; set; }
[JsonProperty("flags")]
public Optional<MessageFlags?> Flags { get; set; }
[JsonProperty("allowed_mentions")]
public Optional<AllowedMentions> AllowedMentions { get; set; }
} }
} }

+ 2
- 0
src/Discord.Net.Rest/API/Rest/UploadFileParams.cs View File

@@ -49,6 +49,8 @@ namespace Discord.API.Rest
payload["allowed_mentions"] = AllowedMentions.Value; payload["allowed_mentions"] = AllowedMentions.Value;
if (IsSpoiler) if (IsSpoiler)
payload["hasSpoiler"] = IsSpoiler.ToString(); payload["hasSpoiler"] = IsSpoiler.ToString();
if (MessageReference.IsSpecified)
payload["message_reference"] = MessageReference.Value;


var json = new StringBuilder(); var json = new StringBuilder();
using (var text = new StringWriter(json)) using (var text = new StringWriter(json))


+ 9
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs View File

@@ -5,13 +5,14 @@ namespace Discord.Rest
/// </summary> /// </summary>
public struct ChannelInfo public struct ChannelInfo
{ {
internal ChannelInfo(string name, string topic, int? rateLimit, bool? nsfw, int? bitrate)
internal ChannelInfo(string name, string topic, int? rateLimit, bool? nsfw, int? bitrate, ChannelType? type)
{ {
Name = name; Name = name;
Topic = topic; Topic = topic;
SlowModeInterval = rateLimit; SlowModeInterval = rateLimit;
IsNsfw = nsfw; IsNsfw = nsfw;
Bitrate = bitrate; Bitrate = bitrate;
ChannelType = type;
} }


/// <summary> /// <summary>
@@ -53,5 +54,12 @@ namespace Discord.Rest
/// <c>null</c> if this is not mentioned in this entry. /// <c>null</c> if this is not mentioned in this entry.
/// </returns> /// </returns>
public int? Bitrate { get; } public int? Bitrate { get; }
/// <summary>
/// Gets the type of this channel.
/// </summary>
/// <returns>
/// The channel type of this channel; <c>null</c> if not applicable.
/// </returns>
public ChannelType? ChannelType { get; }
} }
} }

+ 5
- 2
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs View File

@@ -26,6 +26,7 @@ namespace Discord.Rest
var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user");
var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw");
var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate");
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");


string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
@@ -37,9 +38,11 @@ namespace Discord.Rest
newNsfw = nsfwModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); newNsfw = nsfwModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);
ChannelType? oldType = typeModel?.OldValue?.ToObject<ChannelType>(discord.ApiClient.Serializer),
newType = typeModel?.NewValue?.ToObject<ChannelType>(discord.ApiClient.Serializer);


var before = new ChannelInfo(oldName, oldTopic, oldRateLimitPerUser, oldNsfw, oldBitrate);
var after = new ChannelInfo(newName, newTopic, newRateLimitPerUser, newNsfw, newBitrate);
var before = new ChannelInfo(oldName, oldTopic, oldRateLimitPerUser, oldNsfw, oldBitrate, oldType);
var after = new ChannelInfo(newName, newTopic, newRateLimitPerUser, newNsfw, newBitrate, newType);


return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after);
} }


+ 10
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessagePinAuditLogData.cs View File

@@ -19,8 +19,14 @@ namespace Discord.Rest


internal static MessagePinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) internal static MessagePinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{ {
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
return new MessagePinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, RestUser.Create(discord, userInfo));
RestUser user = null;
if (entry.TargetId.HasValue)
{
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
user = RestUser.Create(discord, userInfo);
}

return new MessagePinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, user);
} }


/// <summary> /// <summary>
@@ -38,10 +44,10 @@ namespace Discord.Rest
/// </returns> /// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary> /// <summary>
/// Gets the user of the message that was pinned.
/// Gets the user of the message that was pinned if available.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A user object representing the user that created the pinned message.
/// A user object representing the user that created the pinned message or <see langword="null"/>.
/// </returns> /// </returns>
public IUser Target { get; } public IUser Target { get; }
} }


+ 10
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageUnpinAuditLogData.cs View File

@@ -19,8 +19,14 @@ namespace Discord.Rest


internal static MessageUnpinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) internal static MessageUnpinAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{ {
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
return new MessageUnpinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, RestUser.Create(discord, userInfo));
RestUser user = null;
if (entry.TargetId.HasValue)
{
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
user = RestUser.Create(discord, userInfo);
}

return new MessageUnpinAuditLogData(entry.Options.MessageId.Value, entry.Options.ChannelId.Value, user);
} }


/// <summary> /// <summary>
@@ -38,10 +44,10 @@ namespace Discord.Rest
/// </returns> /// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary> /// <summary>
/// Gets the user of the message that was unpinned.
/// Gets the user of the message that was unpinned if available.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A user object representing the user that created the unpinned message.
/// A user object representing the user that created the unpinned message or <see langword="null"/>.
/// </returns> /// </returns>
public IUser Target { get; } public IUser Target { get; }
} }


+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs View File

@@ -22,7 +22,7 @@ namespace Discord.Rest


internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model) internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model)
{ {
var userInfo = fullLog.Users.FirstOrDefault(x => x.Id == model.UserId);
var userInfo = model.UserId != null ? fullLog.Users.FirstOrDefault(x => x.Id == model.UserId) : null;
IUser user = null; IUser user = null;
if (userInfo != null) if (userInfo != null)
user = RestUser.Create(discord, userInfo); user = RestUser.Create(discord, userInfo);


+ 29
- 4
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -27,21 +27,46 @@ namespace Discord.Rest
public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func, public static async Task<Model> ModifyAsync(IMessage msg, BaseDiscordClient client, Action<MessageProperties> func,
RequestOptions options) 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(); var args = new MessageProperties();
func(args); 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 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(); bool hasEmbed = args.Embed.IsSpecified ? args.Embed.Value != null : msg.Embeds.Any();
if (!hasText && !hasEmbed) if (!hasText && !hasEmbed)
Preconditions.NotNullOrEmpty(args.Content.IsSpecified ? args.Content.Value : string.Empty, nameof(args.Content)); 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 var apiArgs = new API.Rest.ModifyMessageParams
{ {
Content = args.Content, Content = args.Content,
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>()
Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create<API.Embed>(),
Flags = args.Flags.IsSpecified ? args.Flags.Value : Optional.Create<MessageFlags?>(),
AllowedMentions = args.AllowedMentions.IsSpecified ? args.AllowedMentions.Value.ToModel() : Optional.Create<API.AllowedMentions>(),
}; };
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false);
} }


+ 5
- 0
src/Discord.Net.Rest/Entities/Messages/RestMessage.cs View File

@@ -69,6 +69,8 @@ namespace Discord.Rest
public MessageApplication Application { get; private set; } public MessageApplication Application { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public MessageReference Reference { get; private set; } public MessageReference Reference { get; private set; }
/// <inheritdoc />
public MessageFlags? Flags { get; private set; }


internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
: base(discord, id) : base(discord, id)
@@ -126,6 +128,9 @@ namespace Discord.Rest
}; };
} }


if (model.Flags.IsSpecified)
Flags = model.Flags.Value;

if (model.Reactions.IsSpecified) if (model.Reactions.IsSpecified)
{ {
var value = model.Reactions.Value; var value = model.Reactions.Value;


+ 2
- 6
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -13,7 +13,7 @@ namespace Discord.Rest
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestUserMessage : RestMessage, IUserMessage public class RestUserMessage : RestMessage, IUserMessage
{ {
private bool _isMentioningEveryone, _isTTS, _isPinned, _isSuppressed;
private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks; private long? _editedTimestampTicks;
private IUserMessage _referencedMessage; private IUserMessage _referencedMessage;
private ImmutableArray<Attachment> _attachments = ImmutableArray.Create<Attachment>(); private ImmutableArray<Attachment> _attachments = ImmutableArray.Create<Attachment>();
@@ -28,7 +28,7 @@ namespace Discord.Rest
/// <inheritdoc /> /// <inheritdoc />
public override bool IsPinned => _isPinned; public override bool IsPinned => _isPinned;
/// <inheritdoc /> /// <inheritdoc />
public override bool IsSuppressed => _isSuppressed;
public override bool IsSuppressed => Flags.HasValue && Flags.Value.HasFlag(MessageFlags.SuppressEmbeds);
/// <inheritdoc /> /// <inheritdoc />
public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks);
/// <inheritdoc /> /// <inheritdoc />
@@ -73,10 +73,6 @@ namespace Discord.Rest
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; _editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks;
if (model.MentionEveryone.IsSpecified) if (model.MentionEveryone.IsSpecified)
_isMentioningEveryone = model.MentionEveryone.Value; _isMentioningEveryone = model.MentionEveryone.Value;
if (model.Flags.IsSpecified)
{
_isSuppressed = model.Flags.Value.HasFlag(API.MessageFlags.Suppressed);
}
if (model.RoleMentions.IsSpecified) if (model.RoleMentions.IsSpecified)
_roleMentionIds = model.RoleMentions.Value.ToImmutableArray(); _roleMentionIds = model.RoleMentions.Value.ToImmutableArray();




+ 4
- 0
src/Discord.Net.Rest/Entities/Roles/RestRole.cs View File

@@ -26,6 +26,8 @@ namespace Discord.Rest
public GuildPermissions Permissions { get; private set; } public GuildPermissions Permissions { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public int Position { get; private set; } public int Position { get; private set; }
/// <inheritdoc />
public RoleTags Tags { get; private set; }


/// <inheritdoc /> /// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -56,6 +58,8 @@ namespace Discord.Rest
Position = model.Position; Position = model.Position;
Color = new Color(model.Color); Color = new Color(model.Color);
Permissions = new GuildPermissions(model.Permissions); Permissions = new GuildPermissions(model.Permissions);
if (model.Tags.IsSpecified)
Tags = model.Tags.Value.ToEntity();
} }


/// <inheritdoc /> /// <inheritdoc />


+ 4
- 0
src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs View File

@@ -29,6 +29,8 @@ namespace Discord.Rest
public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks); public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks);
/// <inheritdoc /> /// <inheritdoc />
public ulong GuildId => Guild.Id; public ulong GuildId => Guild.Id;
/// <inheritdoc />
public bool? IsPending { get; private set; }


/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="InvalidOperationException" accessor="get">Resolving permissions requires the parent guild to be downloaded.</exception> /// <exception cref="InvalidOperationException" accessor="get">Resolving permissions requires the parent guild to be downloaded.</exception>
@@ -73,6 +75,8 @@ namespace Discord.Rest
UpdateRoles(model.Roles.Value); UpdateRoles(model.Roles.Value);
if (model.PremiumSince.IsSpecified) if (model.PremiumSince.IsSpecified)
_premiumSinceTicks = model.PremiumSince.Value?.UtcTicks; _premiumSinceTicks = model.PremiumSince.Value?.UtcTicks;
if (model.Pending.IsSpecified)
IsPending = model.Pending.Value;
} }
private void UpdateRoles(ulong[] roleIds) private void UpdateRoles(ulong[] roleIds)
{ {


+ 4
- 0
src/Discord.Net.Rest/Entities/Users/RestUser.cs View File

@@ -21,6 +21,8 @@ namespace Discord.Rest
public ushort DiscriminatorValue { get; private set; } public ushort DiscriminatorValue { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public string AvatarId { get; private set; } public string AvatarId { get; private set; }
/// <inheritdoc />
public UserProperties? PublicFlags { get; private set; }


/// <inheritdoc /> /// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -65,6 +67,8 @@ namespace Discord.Rest
IsBot = model.Bot.Value; IsBot = model.Bot.Value;
if (model.Username.IsSpecified) if (model.Username.IsSpecified)
Username = model.Username.Value; Username = model.Username.Value;
if (model.PublicFlags.IsSpecified)
PublicFlags = model.PublicFlags.Value;
} }


/// <inheritdoc /> /// <inheritdoc />


+ 2
- 0
src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs View File

@@ -52,6 +52,8 @@ namespace Discord.Rest
/// <inheritdoc /> /// <inheritdoc />
string IGuildUser.Nickname => null; string IGuildUser.Nickname => null;
/// <inheritdoc /> /// <inheritdoc />
bool? IGuildUser.IsPending => null;
/// <inheritdoc />
GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook; GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook;


/// <inheritdoc /> /// <inheritdoc />


+ 4
- 2
src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs View File

@@ -11,11 +11,11 @@ namespace Discord.Rest
internal IGuild Guild { get; private set; } internal IGuild Guild { get; private set; }
internal ITextChannel Channel { get; private set; } internal ITextChannel Channel { get; private set; }


/// <inheritdoc />
public ulong ChannelId { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Token { get; } public string Token { get; }


/// <inheritdoc />
public ulong ChannelId { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public string Name { get; private set; } public string Name { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
@@ -56,6 +56,8 @@ namespace Discord.Rest


internal void Update(Model model) internal void Update(Model model)
{ {
if (ChannelId != model.ChannelId)
ChannelId = model.ChannelId;
if (model.Avatar.IsSpecified) if (model.Avatar.IsSpecified)
AvatarId = model.Avatar.Value; AvatarId = model.Avatar.Value;
if (model.Creator.IsSpecified) if (model.Creator.IsSpecified)


+ 7
- 0
src/Discord.Net.Rest/Extensions/EntityExtensions.cs View File

@@ -34,6 +34,13 @@ namespace Discord.Rest
model.Thumbnail.IsSpecified ? model.Thumbnail.Value.ToEntity() : (EmbedThumbnail?)null, model.Thumbnail.IsSpecified ? model.Thumbnail.Value.ToEntity() : (EmbedThumbnail?)null,
model.Fields.IsSpecified ? model.Fields.Value.Select(x => x.ToEntity()).ToImmutableArray() : ImmutableArray.Create<EmbedField>()); model.Fields.IsSpecified ? model.Fields.Value.Select(x => x.ToEntity()).ToImmutableArray() : ImmutableArray.Create<EmbedField>());
} }
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) public static API.Embed ToModel(this Embed entity)
{ {
if (entity == null) return null; if (entity == null) return null;


+ 31
- 0
src/Discord.Net.WebSocket/BaseSocketClient.cs View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -75,6 +76,7 @@ namespace Discord.WebSocket
/// <returns> /// <returns>
/// A read-only collection of voice regions that the user has access to. /// A read-only collection of voice regions that the user has access to.
/// </returns> /// </returns>
[Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")]
public abstract IReadOnlyCollection<RestVoiceRegion> VoiceRegions { get; } public abstract IReadOnlyCollection<RestVoiceRegion> VoiceRegions { get; }


internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client)
@@ -169,7 +171,26 @@ namespace Discord.WebSocket
/// A REST-based voice region associated with the identifier; <c>null</c> if the voice region is not /// A REST-based voice region associated with the identifier; <c>null</c> if the voice region is not
/// found. /// found.
/// </returns> /// </returns>
[Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")]
public abstract RestVoiceRegion GetVoiceRegion(string id); public abstract RestVoiceRegion GetVoiceRegion(string id);
/// <summary>
/// Gets all voice regions.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that contains a read-only collection of REST-based voice regions.
/// </returns>
public abstract ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null);
/// <summary>
/// Gets a voice region.
/// </summary>
/// <param name="id">The identifier of the voice region (e.g. <c>eu-central</c> ).</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that contains a REST-based voice region associated with the identifier; <c>null</c> if the
/// voice region is not found.
/// </returns>
public abstract ValueTask<RestVoiceRegion> GetVoiceRegionAsync(string id, RequestOptions options = null);
/// <inheritdoc /> /// <inheritdoc />
public abstract Task StartAsync(); public abstract Task StartAsync();
/// <inheritdoc /> /// <inheritdoc />
@@ -188,6 +209,12 @@ namespace Discord.WebSocket
/// <param name="name">The name of the game.</param> /// <param name="name">The name of the game.</param>
/// <param name="streamUrl">If streaming, the URL of the stream. Must be a valid Twitch URL.</param> /// <param name="streamUrl">If streaming, the URL of the stream. Must be a valid Twitch URL.</param>
/// <param name="type">The type of the game.</param> /// <param name="type">The type of the game.</param>
/// <remarks>
/// <note type="warning">
/// Bot accounts cannot set <see cref="ActivityType.CustomStatus"/> as their activity
/// type and it will have no effect.
/// </note>
/// </remarks>
/// <returns> /// <returns>
/// A task that represents the asynchronous set operation. /// A task that represents the asynchronous set operation.
/// </returns> /// </returns>
@@ -201,6 +228,10 @@ namespace Discord.WebSocket
/// Discord will only accept setting of name and the type of activity. /// Discord will only accept setting of name and the type of activity.
/// </note> /// </note>
/// <note type="warning"> /// <note type="warning">
/// Bot accounts cannot set <see cref="ActivityType.CustomStatus"/> as their activity
/// type and it will have no effect.
/// </note>
/// <note type="warning">
/// Rich Presence cannot be set via this method or client. Rich Presence is strictly limited to RPC /// Rich Presence cannot be set via this method or client. Rich Presence is strictly limited to RPC
/// clients only. /// clients only.
/// </note> /// </note>


+ 14
- 0
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -37,6 +37,7 @@ namespace Discord.WebSocket
public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount); public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount);
public IReadOnlyCollection<DiscordSocketClient> Shards => _shards; public IReadOnlyCollection<DiscordSocketClient> Shards => _shards;
/// <inheritdoc /> /// <inheritdoc />
[Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")]
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _shards[0].VoiceRegions; public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _shards[0].VoiceRegions;


/// <summary> /// <summary>
@@ -264,9 +265,22 @@ namespace Discord.WebSocket
} }


/// <inheritdoc /> /// <inheritdoc />
[Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")]
public override RestVoiceRegion GetVoiceRegion(string id) public override RestVoiceRegion GetVoiceRegion(string id)
=> _shards[0].GetVoiceRegion(id); => _shards[0].GetVoiceRegion(id);


/// <inheritdoc />
public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null)
{
return await _shards[0].GetVoiceRegionsAsync().ConfigureAwait(false);
}

/// <inheritdoc />
public override async ValueTask<RestVoiceRegion> GetVoiceRegionAsync(string id, RequestOptions options = null)
{
return await _shards[0].GetVoiceRegionAsync(id, options).ConfigureAwait(false);
}

/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentNullException"><paramref name="guilds"/> is <see langword="null"/></exception> /// <exception cref="ArgumentNullException"><paramref name="guilds"/> is <see langword="null"/></exception>
public override async Task DownloadUsersAsync(IEnumerable<IGuild> guilds) public override async Task DownloadUsersAsync(IEnumerable<IGuild> guilds)


+ 42
- 21
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -110,7 +110,8 @@ namespace Discord.WebSocket
public IReadOnlyCollection<SocketGroupChannel> GroupChannels public IReadOnlyCollection<SocketGroupChannel> GroupChannels
=> State.PrivateChannels.OfType<SocketGroupChannel>().ToImmutableArray(); => State.PrivateChannels.OfType<SocketGroupChannel>().ToImmutableArray();
/// <inheritdoc /> /// <inheritdoc />
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();
[Obsolete("This property is obsolete, use the GetVoiceRegionsAsync method instead.")]
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => GetVoiceRegionsAsync().GetAwaiter().GetResult();


/// <summary> /// <summary>
/// Initializes a new REST/WebSocket-based Discord client. /// Initializes a new REST/WebSocket-based Discord client.
@@ -178,7 +179,6 @@ namespace Discord.WebSocket
return Task.Delay(0); return Task.Delay(0);
}; };


_voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>();
_largeGuilds = new ConcurrentQueue<ulong>(); _largeGuilds = new ConcurrentQueue<ulong>();
} }
private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
@@ -204,13 +204,6 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
internal override async Task OnLoginAsync(TokenType tokenType, string token) 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); await Rest.OnLoginAsync(tokenType, token);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -218,7 +211,7 @@ namespace Discord.WebSocket
{ {
await StopAsync().ConfigureAwait(false); await StopAsync().ConfigureAwait(false);
_applicationInfo = null; _applicationInfo = null;
_voiceRegions = ImmutableDictionary.Create<string, RestVoiceRegion>();
_voiceRegions = null;
await Rest.OnLogoutAsync(); await Rest.OnLogoutAsync();
} }


@@ -252,15 +245,15 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false);
await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false);
} }

//Wait for READY
await _connection.WaitAsync().ConfigureAwait(false);
} }
finally finally
{ {
if (locked) if (locked)
_shardedClient.ReleaseIdentifyLock(); _shardedClient.ReleaseIdentifyLock();
} }

//Wait for READY
await _connection.WaitAsync().ConfigureAwait(false);
} }
private async Task OnDisconnectingAsync(Exception ex) private async Task OnDisconnectingAsync(Exception ex)
{ {
@@ -350,11 +343,39 @@ namespace Discord.WebSocket
=> State.RemoveUser(id); => State.RemoveUser(id);


/// <inheritdoc /> /// <inheritdoc />
[Obsolete("This method is obsolete, use GetVoiceRegionAsync instead.")]
public override RestVoiceRegion GetVoiceRegion(string id) public override RestVoiceRegion GetVoiceRegion(string id)
=> GetVoiceRegionAsync(id).GetAwaiter().GetResult();

/// <inheritdoc />
public override async ValueTask<IReadOnlyCollection<RestVoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = 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);
}

/// <inheritdoc />
public override async ValueTask<RestVoiceRegion> GetVoiceRegionAsync(string id, RequestOptions options = null)
{ {
if (_voiceRegions.TryGetValue(id, out RestVoiceRegion region))
return region;
return 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);
} }


/// <inheritdoc /> /// <inheritdoc />
@@ -611,7 +632,7 @@ namespace Discord.WebSocket
} }
else if (_connection.CancelToken.IsCancellationRequested) else if (_connection.CancelToken.IsCancellationRequested)
return; return;
if (BaseConfig.AlwaysDownloadUsers) if (BaseConfig.AlwaysDownloadUsers)
_ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers)); _ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers));


@@ -2120,11 +2141,11 @@ namespace Discord.WebSocket
=> Task.FromResult<IUser>(GetUser(username, discriminator)); => Task.FromResult<IUser>(GetUser(username, discriminator));


/// <inheritdoc /> /// <inheritdoc />
Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
=> Task.FromResult<IReadOnlyCollection<IVoiceRegion>>(VoiceRegions);
async Task<IReadOnlyCollection<IVoiceRegion>> IDiscordClient.GetVoiceRegionsAsync(RequestOptions options)
=> await GetVoiceRegionsAsync(options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
=> Task.FromResult<IVoiceRegion>(GetVoiceRegion(id));
async Task<IVoiceRegion> IDiscordClient.GetVoiceRegionAsync(string id, RequestOptions options)
=> await GetVoiceRegionAsync(id, options).ConfigureAwait(false);


/// <inheritdoc /> /// <inheritdoc />
async Task IDiscordClient.StartAsync() async Task IDiscordClient.StartAsync()


+ 6
- 0
src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs View File

@@ -58,6 +58,9 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public MessageReference Reference { get; private set; } public MessageReference Reference { get; private set; }


/// <inheritdoc />
public MessageFlags? Flags { get; private set; }

/// <summary> /// <summary>
/// Returns all attachments included in this message. /// Returns all attachments included in this message.
/// </summary> /// </summary>
@@ -158,6 +161,9 @@ namespace Discord.WebSocket
MessageId = model.Reference.Value.MessageId MessageId = model.Reference.Value.MessageId
}; };
} }

if (model.Flags.IsSpecified)
Flags = model.Flags.Value;
} }


/// <inheritdoc /> /// <inheritdoc />


+ 2
- 6
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -15,7 +15,7 @@ namespace Discord.WebSocket
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class SocketUserMessage : SocketMessage, IUserMessage public class SocketUserMessage : SocketMessage, IUserMessage
{ {
private bool _isMentioningEveryone, _isTTS, _isPinned, _isSuppressed;
private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks; private long? _editedTimestampTicks;
private IUserMessage _referencedMessage; private IUserMessage _referencedMessage;
private ImmutableArray<Attachment> _attachments = ImmutableArray.Create<Attachment>(); private ImmutableArray<Attachment> _attachments = ImmutableArray.Create<Attachment>();
@@ -30,7 +30,7 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public override bool IsPinned => _isPinned; public override bool IsPinned => _isPinned;
/// <inheritdoc /> /// <inheritdoc />
public override bool IsSuppressed => _isSuppressed;
public override bool IsSuppressed => Flags.HasValue && Flags.Value.HasFlag(MessageFlags.SuppressEmbeds);
/// <inheritdoc /> /// <inheritdoc />
public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); public override DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks);
/// <inheritdoc /> /// <inheritdoc />
@@ -77,10 +77,6 @@ namespace Discord.WebSocket
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks; _editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks;
if (model.MentionEveryone.IsSpecified) if (model.MentionEveryone.IsSpecified)
_isMentioningEveryone = model.MentionEveryone.Value; _isMentioningEveryone = model.MentionEveryone.Value;
if (model.Flags.IsSpecified)
{
_isSuppressed = model.Flags.Value.HasFlag(API.MessageFlags.Suppressed);
}
if (model.RoleMentions.IsSpecified) if (model.RoleMentions.IsSpecified)
_roleMentions = model.RoleMentions.Value.Select(x => guild.GetRole(x)).ToImmutableArray(); _roleMentions = model.RoleMentions.Value.Select(x => guild.GetRole(x)).ToImmutableArray();




+ 4
- 0
src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs View File

@@ -36,6 +36,8 @@ namespace Discord.WebSocket
public GuildPermissions Permissions { get; private set; } public GuildPermissions Permissions { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public int Position { get; private set; } public int Position { get; private set; }
/// <inheritdoc />
public RoleTags Tags { get; private set; }


/// <inheritdoc /> /// <inheritdoc />
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
@@ -71,6 +73,8 @@ namespace Discord.WebSocket
Position = model.Position; Position = model.Position;
Color = new Color(model.Color); Color = new Color(model.Color);
Permissions = new GuildPermissions(model.Permissions); Permissions = new GuildPermissions(model.Permissions);
if (model.Tags.IsSpecified)
Tags = model.Tags.Value.ToEntity();
} }


/// <inheritdoc /> /// <inheritdoc />


+ 4
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs View File

@@ -57,6 +57,8 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public bool IsStreaming => VoiceState?.IsStreaming ?? false; public bool IsStreaming => VoiceState?.IsStreaming ?? false;
/// <inheritdoc /> /// <inheritdoc />
public bool? IsPending { get; private set; }
/// <inheritdoc />
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);
/// <summary> /// <summary>
/// Returns a collection of roles that the user possesses. /// Returns a collection of roles that the user possesses.
@@ -142,6 +144,8 @@ namespace Discord.WebSocket
UpdateRoles(model.Roles.Value); UpdateRoles(model.Roles.Value);
if (model.PremiumSince.IsSpecified) if (model.PremiumSince.IsSpecified)
_premiumSinceTicks = model.PremiumSince.Value?.UtcTicks; _premiumSinceTicks = model.PremiumSince.Value?.UtcTicks;
if (model.Pending.IsSpecified)
IsPending = model.Pending.Value;
} }
internal void Update(ClientState state, PresenceModel model, bool updatePresence) internal void Update(ClientState state, PresenceModel model, bool updatePresence)
{ {


+ 7
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs View File

@@ -26,6 +26,8 @@ namespace Discord.WebSocket
public abstract string AvatarId { get; internal set; } public abstract string AvatarId { get; internal set; }
/// <inheritdoc /> /// <inheritdoc />
public abstract bool IsWebhook { get; } public abstract bool IsWebhook { get; }
/// <inheritdoc />
public UserProperties? PublicFlags { get; private set; }
internal abstract SocketGlobalUser GlobalUser { get; } internal abstract SocketGlobalUser GlobalUser { get; }
internal abstract SocketPresence Presence { get; set; } internal abstract SocketPresence Presence { get; set; }


@@ -83,6 +85,11 @@ namespace Discord.WebSocket
Username = model.Username.Value; Username = model.Username.Value;
hasChanges = true; hasChanges = true;
} }
if (model.PublicFlags.IsSpecified && model.PublicFlags.Value != PublicFlags)
{
PublicFlags = model.PublicFlags.Value;
hasChanges = true;
}
return hasChanges; return hasChanges;
} }




+ 2
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs View File

@@ -65,6 +65,8 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
DateTimeOffset? IGuildUser.PremiumSince => null; DateTimeOffset? IGuildUser.PremiumSince => null;
/// <inheritdoc /> /// <inheritdoc />
bool? IGuildUser.IsPending => null;
/// <inheritdoc />
GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook; GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook;


/// <inheritdoc /> /// <inheritdoc />


+ 4
- 2
src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.Webhook; using Model = Discord.API.Webhook;
@@ -11,9 +11,9 @@ namespace Discord.Webhook
private DiscordWebhookClient _client; private DiscordWebhookClient _client;


public ulong Id { get; } public ulong Id { get; }
public ulong ChannelId { get; }
public string Token { get; } public string Token { get; }


public ulong ChannelId { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
public string AvatarId { get; private set; } public string AvatarId { get; private set; }
public ulong? GuildId { get; private set; } public ulong? GuildId { get; private set; }
@@ -36,6 +36,8 @@ namespace Discord.Webhook


internal void Update(Model model) internal void Update(Model model)
{ {
if (ChannelId != model.ChannelId)
ChannelId = model.ChannelId;
if (model.Avatar.IsSpecified) if (model.Avatar.IsSpecified)
AvatarId = model.Avatar.Value; AvatarId = model.Avatar.Value;
if (model.GuildId.IsSpecified) if (model.GuildId.IsSpecified)


+ 16
- 16
src/Discord.Net/Discord.Net.nuspec View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Discord.Net</id> <id>Discord.Net</id>
<version>2.3.0-dev$suffix$</version>
<version>2.4.0$suffix$</version>
<title>Discord.Net</title> <title>Discord.Net</title>
<authors>Discord.Net Contributors</authors> <authors>Discord.Net Contributors</authors>
<owners>foxbot</owners> <owners>foxbot</owners>
@@ -14,25 +14,25 @@
<iconUrl>https://github.com/RogueException/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png</iconUrl> <iconUrl>https://github.com/RogueException/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png</iconUrl>
<dependencies> <dependencies>
<group targetFramework="net461"> <group targetFramework="net461">
<dependency id="Discord.Net.Core" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Rest" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Commands" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Core" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Rest" version="2.4.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Commands" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.4.0$suffix$" />
</group> </group>
<group targetFramework="netstandard2.0"> <group targetFramework="netstandard2.0">
<dependency id="Discord.Net.Core" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Rest" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Commands" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Core" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Rest" version="2.4.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Commands" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.4.0$suffix$" />
</group> </group>
<group targetFramework="netstandard2.1"> <group targetFramework="netstandard2.1">
<dependency id="Discord.Net.Core" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Rest" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Commands" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.3.0-dev$suffix$" />
<dependency id="Discord.Net.Core" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Rest" version="2.4.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Commands" version="2.4.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="2.4.0$suffix$" />
</group> </group>
</dependencies> </dependencies>
</metadata> </metadata>


Loading…
Cancel
Save