From faf23dee35ec250e35df6ebe7a39b9b5b325b617 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Fri, 21 Jun 2019 14:34:45 -0700 Subject: [PATCH] [ifcbrk] feature: BOOST (#1319) * add new MessageTypes * Add new properties to the updated models * add the SystemChannelMessageDeny unsure if there would be a better name for this enum, given it's inverted nature, open for suggestions * add PremiumTier flag, add Guild description property * add method for getting vanity image from CDN * make the size of GetGuildVanityUrl optional * lint: remove commented out code from prev commit * add a None flag to SystemChannelMessage enum * implement the new modify guild params * implement additional model properties in IGuild types * implement GuildMember PremiumSince * docs: reword size param explanation * add extension methods that make it easier to check the SystemChannelMessage flags for end users because the flag is inverted, this ideally should make it easier for the user. it may also be useful to do something similar for modifying this property * docs: correct typo from copy-paste * add the premium_subscription_count property * fix vanity url code and banner switchup a mistake was made somewhere, that's all I know for sure * clarify remark on inverted logic for system channel flags * fix PremiumSubscriptionCount optional value * add another example to the systemchannelflags xmldoc remark * docs: fix typos, clarify wording * use DateTimeOffset for PremiumSince, follow conventions from other prop --- src/Discord.Net.Core/CDN.cs | 18 ++++++- .../Entities/Guilds/GuildProperties.cs | 14 +++++ .../Entities/Guilds/IGuild.cs | 52 +++++++++++++++++++ .../Entities/Guilds/PremiumTier.cs | 22 ++++++++ .../Guilds/SystemChannelMessageDeny.cs | 22 ++++++++ .../Entities/Messages/MessageType.cs | 18 ++++++- .../Entities/Users/IGuildUser.cs | 7 +++ .../Extensions/GuildExtensions.cs | 24 +++++++++ src/Discord.Net.Rest/API/Common/Guild.cs | 13 +++++ .../API/Common/GuildMember.cs | 4 +- .../API/Rest/ModifyGuildParams.cs | 2 + .../Entities/Guilds/GuildHelper.cs | 6 ++- .../Entities/Guilds/RestGuild.cs | 20 +++++++ .../Entities/Users/RestGuildUser.cs | 6 ++- .../Entities/Users/RestWebhookUser.cs | 2 + .../Entities/Guilds/SocketGuild.cs | 20 +++++++ .../Entities/Users/SocketGuildUser.cs | 5 ++ .../Entities/Users/SocketWebhookUser.cs | 2 + 18 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Guilds/PremiumTier.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/SystemChannelMessageDeny.cs create mode 100644 src/Discord.Net.Core/Extensions/GuildExtensions.cs diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 0fefc9a0d..32ffbba90 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -23,7 +23,7 @@ namespace Discord /// /// The user snowflake identifier. /// The avatar identifier. - /// The size of the image to return in. This can be any power of two between 16 and 2048. + /// The size of the image to return in horizontal pixels. This can be any power of two between 16 and 2048. /// The format to return. /// /// A URL pointing to the user's avatar in the specified size. @@ -76,6 +76,22 @@ namespace Discord /// public static string GetChannelIconUrl(ulong channelId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null; + + /// + /// Returns a guild banner URL. + /// + /// The guild snowflake identifier. + /// The banner image identifier. + /// The size of the image to return in horizontal pixels. This can be any power of two between 16 and 2048 inclusive. + /// + /// A URL pointing to the guild's banner image. + /// + public static string GetGuildBannerUrl(ulong guildId, string bannerId, ushort? size = null) + { + if (!string.IsNullOrEmpty(bannerId)) + return $"{DiscordConfig.CDNUrl}banners/{guildId}/{bannerId}.jpg" + (size.HasValue ? $"?size={size}" : string.Empty); + return null; + } /// /// Returns an emoji URL. /// diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs index e6d21a463..e13536a97 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs @@ -70,5 +70,19 @@ namespace Discord /// Gets or sets the explicit content filter level of this guild. /// public Optional ExplicitContentFilter { get; set; } + /// + /// Gets or sets the flags that DISABLE types of system channel messages. + /// + /// + /// These flags are inverted. Setting a flag will disable that system channel message from being sent. + /// A value of will allow all system channel message types to be sent, + /// given that the has also been set. + /// A value of will deny guild boost messages from being sent, and allow all + /// other types of messages. + /// Refer to the extension methods and + /// to check if these system channel message types + /// are enabled, without the need to manipulate the logic of the flag. + /// + public Optional SystemChannelFlags { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 571afef15..378ca069b 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -196,6 +196,58 @@ namespace Discord /// A read-only collection of roles found within this guild. /// IReadOnlyCollection Roles { get; } + /// + /// Gets the tier of guild boosting in this guild. + /// + /// + /// The tier of guild boosting in this guild. + /// + PremiumTier PremiumTier { get; } + /// + /// Gets the identifier for this guilds banner image. + /// + /// + /// An identifier for the banner image; null if none is set. + /// + string BannerId { get; } + /// + /// Gets the URL of this guild's banner image. + /// + /// + /// A URL pointing to the guild's banner image; null if none is set. + /// + string BannerUrl { get; } + /// + /// Gets the code for this guild's vanity invite URL. + /// + /// + /// A string containing the vanity invite code for this guild; null if none is set. + /// + string VanityURLCode { get; } + /// + /// Gets the flags for the types of system channel messages that are disabled. + /// + /// + /// The flags for the types of system channel messages that are disabled. + /// + SystemChannelMessageDeny SystemChannelFlags { get; } + /// + /// Gets the description for the guild. + /// + /// + /// The description for the guild; null if none is set. + /// + string Description { get; } + /// + /// Gets the number of premium subscribers of this guild. + /// + /// + /// This is the number of users who have boosted this guild. + /// + /// + /// The number of premium subscribers of this guild. + /// + int PremiumSubscriptionCount { get; } /// /// Modifies this guild. diff --git a/src/Discord.Net.Core/Entities/Guilds/PremiumTier.cs b/src/Discord.Net.Core/Entities/Guilds/PremiumTier.cs new file mode 100644 index 000000000..b7e4c9323 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/PremiumTier.cs @@ -0,0 +1,22 @@ +namespace Discord +{ + public enum PremiumTier + { + /// + /// Used for guilds that have no guild boosts. + /// + None = 0, + /// + /// Used for guilds that have Tier 1 guild boosts. + /// + Tier1 = 1, + /// + /// Used for guilds that have Tier 2 guild boosts. + /// + Tier2 = 2, + /// + /// Used for guilds that have Tier 3 guild boosts. + /// + Tier3 = 3 + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/SystemChannelMessageDeny.cs b/src/Discord.Net.Core/Entities/Guilds/SystemChannelMessageDeny.cs new file mode 100644 index 000000000..3f69693d0 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/SystemChannelMessageDeny.cs @@ -0,0 +1,22 @@ +using System; + +namespace Discord +{ + [Flags] + public enum SystemChannelMessageDeny + { + /// + /// Deny none of the system channel messages. + /// This will enable all of the system channel messages. + /// + None = 0, + /// + /// Deny the messages that are sent when a user joins the guild. + /// + WelcomeMessage = 0b1, + /// + /// Deny the messages that are sent when a user boosts the guild. + /// + GuildBoost = 0b10 + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/MessageType.cs b/src/Discord.Net.Core/Entities/Messages/MessageType.cs index 8f3af843b..66b49e557 100644 --- a/src/Discord.Net.Core/Entities/Messages/MessageType.cs +++ b/src/Discord.Net.Core/Entities/Messages/MessageType.cs @@ -36,6 +36,22 @@ namespace Discord /// /// The message when a new member joined. /// - GuildMemberJoin = 7 + GuildMemberJoin = 7, + /// + /// The message for when a user boosts a guild. + /// + UserPremiumGuildSubscription = 8, + /// + /// The message for when a guild reaches Tier 1 of Nitro boosts. + /// + UserPremiumGuildSubscriptionTier1 = 9, + /// + /// The message for when a guild reaches Tier 2 of Nitro boosts. + /// + UserPremiumGuildSubscriptionTier2 = 10, + /// + /// The message for when a guild reaches Tier 3 of Nitro boosts. + /// + UserPremiumGuildSubscriptionTier3 = 11 } } diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index 718587ae4..d162b5164 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -48,6 +48,13 @@ namespace Discord /// ulong GuildId { get; } /// + /// Gets the date and time for when this user's guild boost began. + /// + /// + /// A for when the user began boosting this guild; null if they are not boosting the guild. + /// + DateTimeOffset? PremiumSince { get; } + /// /// Gets a collection of IDs for the roles that this user currently possesses in the guild. /// /// diff --git a/src/Discord.Net.Core/Extensions/GuildExtensions.cs b/src/Discord.Net.Core/Extensions/GuildExtensions.cs new file mode 100644 index 000000000..58b749cc4 --- /dev/null +++ b/src/Discord.Net.Core/Extensions/GuildExtensions.cs @@ -0,0 +1,24 @@ +namespace Discord +{ + /// + /// An extension class for . + /// + public static class GuildExtensions + { + /// + /// Gets if welcome system messages are enabled. + /// + /// The guild to check. + /// A bool indicating if the welcome messages are enabled in the system channel. + public static bool GetWelcomeMessagesEnabled(this IGuild guild) + => !guild.SystemChannelFlags.HasFlag(SystemChannelMessageDeny.WelcomeMessage); + + /// + /// Gets if guild boost system messages are enabled. + /// + /// The guild to check. + /// A bool indicating if the guild boost messages are enabled in the system channel. + public static bool GetGuildBoostMessagesEnabled(this IGuild guild) + => !guild.SystemChannelFlags.HasFlag(SystemChannelMessageDeny.GuildBoost); + } +} diff --git a/src/Discord.Net.Rest/API/Common/Guild.cs b/src/Discord.Net.Rest/API/Common/Guild.cs index a84b55a93..343d5b12c 100644 --- a/src/Discord.Net.Rest/API/Common/Guild.cs +++ b/src/Discord.Net.Rest/API/Common/Guild.cs @@ -45,5 +45,18 @@ namespace Discord.API public ulong? ApplicationId { get; set; } [JsonProperty("system_channel_id")] public ulong? SystemChannelId { get; set; } + [JsonProperty("premium_tier")] + public PremiumTier PremiumTier { get; set; } + [JsonProperty("vanity_url_code")] + public string VanityURLCode { get; set; } + [JsonProperty("banner")] + public string Banner { get; set; } + [JsonProperty("description")] + public string Description { get; set; } + // this value is inverted, flags set will turn OFF features + [JsonProperty("system_channel_flags")] + public SystemChannelMessageDeny SystemChannelFlags { get; set; } + [JsonProperty("premium_subscription_count")] + public int? PremiumSubscriptionCount { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/GuildMember.cs b/src/Discord.Net.Rest/API/Common/GuildMember.cs index 24ad17c14..940eb925a 100644 --- a/src/Discord.Net.Rest/API/Common/GuildMember.cs +++ b/src/Discord.Net.Rest/API/Common/GuildMember.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; using System; @@ -18,5 +18,7 @@ namespace Discord.API public Optional Deaf { get; set; } [JsonProperty("mute")] public Optional Mute { get; set; } + [JsonProperty("premium_since")] + public Optional PremiumSince { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs index ba70c58d6..9c519d3a8 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs @@ -30,5 +30,7 @@ namespace Discord.API.Rest public Optional OwnerId { get; set; } [JsonProperty("explicit_content_filter")] public Optional ExplicitContentFilter { get; set; } + [JsonProperty("system_channel_flags")] + public Optional SystemChannelFlags { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index cfb7c4ff7..a9b51c113 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -33,7 +33,8 @@ namespace Discord.Rest Name = args.Name, Splash = args.Splash.IsSpecified ? args.Splash.Value?.ToModel() : Optional.Create(), VerificationLevel = args.VerificationLevel, - ExplicitContentFilter = args.ExplicitContentFilter + ExplicitContentFilter = args.ExplicitContentFilter, + SystemChannelFlags = args.SystemChannelFlags }; if (args.AfkChannel.IsSpecified) @@ -64,6 +65,9 @@ namespace Discord.Rest if (args.ExplicitContentFilter.IsSpecified) apiArgs.ExplicitContentFilter = args.ExplicitContentFilter.Value; + if (args.SystemChannelFlags.IsSpecified) + apiArgs.SystemChannelFlags = args.SystemChannelFlags.Value; + return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } /// is null. diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 902d2c9a8..7ed258155 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -52,6 +52,18 @@ namespace Discord.Rest internal bool Available { get; private set; } /// public ulong? ApplicationId { get; private set; } + /// + public PremiumTier PremiumTier { get; private set; } + /// + public string BannerId { get; private set; } + /// + public string VanityURLCode { get; private set; } + /// + public SystemChannelMessageDeny SystemChannelFlags { get; private set; } + /// + public string Description { get; private set; } + /// + public int PremiumSubscriptionCount { get; private set; } /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); @@ -62,6 +74,8 @@ namespace Discord.Rest public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); /// public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); + /// + public string BannerUrl => CDN.GetGuildBannerUrl(Id, BannerId); /// /// Gets the built-in role containing all users in this guild. @@ -104,6 +118,12 @@ namespace Discord.Rest DefaultMessageNotifications = model.DefaultMessageNotifications; ExplicitContentFilter = model.ExplicitContentFilter; ApplicationId = model.ApplicationId; + PremiumTier = model.PremiumTier; + VanityURLCode = model.VanityURLCode; + BannerId = model.Banner; + SystemChannelFlags = model.SystemChannelFlags; + Description = model.Description; + PremiumSubscriptionCount = model.PremiumSubscriptionCount.GetValueOrDefault(); if (model.Emojis != null) { diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index e59d92407..27a910576 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -14,6 +14,7 @@ namespace Discord.Rest [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RestGuildUser : RestUser, IGuildUser { + private long? _premiumSinceTicks; private long? _joinedAtTicks; private ImmutableArray _roleIds; @@ -24,7 +25,8 @@ namespace Discord.Rest public bool IsDeafened { get; private set; } /// public bool IsMuted { get; private set; } - + /// + public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks); /// public ulong GuildId => Guild.Id; @@ -69,6 +71,8 @@ namespace Discord.Rest IsMuted = model.Mute.Value; if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); + if (model.PremiumSince.IsSpecified) + _premiumSinceTicks = model.PremiumSince.Value?.UtcTicks; } private void UpdateRoles(ulong[] roleIds) { diff --git a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs index bee4892fe..67914e873 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs @@ -13,6 +13,8 @@ namespace Discord.Rest /// public ulong WebhookId { get; } internal IGuild Guild { get; } + /// + public DateTimeOffset? PremiumSince { get; private set; } /// public override bool IsWebhook => true; diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index d952b3d92..d5cbfdd64 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -93,6 +93,18 @@ namespace Discord.WebSocket public string IconId { get; private set; } /// public string SplashId { get; private set; } + /// + public PremiumTier PremiumTier { get; private set; } + /// + public string BannerId { get; private set; } + /// + public string VanityURLCode { get; private set; } + /// + public SystemChannelMessageDeny SystemChannelFlags { get; private set; } + /// + public string Description { get; private set; } + /// + public int PremiumSubscriptionCount { get; private set; } /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); @@ -100,6 +112,8 @@ namespace Discord.WebSocket public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); /// public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); + /// + public string BannerUrl => CDN.GetGuildBannerUrl(Id, BannerId); /// Indicates whether the client has all the members downloaded to the local guild cache. public bool HasAllMembers => MemberCount == DownloadedMemberCount;// _downloaderPromise.Task.IsCompleted; /// Indicates whether the guild cache is synced to this guild. @@ -354,6 +368,12 @@ namespace Discord.WebSocket DefaultMessageNotifications = model.DefaultMessageNotifications; ExplicitContentFilter = model.ExplicitContentFilter; ApplicationId = model.ApplicationId; + PremiumTier = model.PremiumTier; + VanityURLCode = model.VanityURLCode; + BannerId = model.Banner; + SystemChannelFlags = model.SystemChannelFlags; + Description = model.Description; + PremiumSubscriptionCount = model.PremiumSubscriptionCount.GetValueOrDefault(); if (model.Emojis != null) { diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 659a2eeea..dee450cdd 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -18,6 +18,7 @@ namespace Discord.WebSocket [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGuildUser : SocketUser, IGuildUser { + private long? _premiumSinceTicks; private long? _joinedAtTicks; private ImmutableArray _roleIds; @@ -75,6 +76,8 @@ namespace Discord.WebSocket /// public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id); public AudioInStream AudioStream => Guild.GetAudioStream(Id); + /// + public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks); /// /// Returns the position of the user within the role hierarchy. @@ -135,6 +138,8 @@ namespace Discord.WebSocket Nickname = model.Nick.Value; if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); + if (model.PremiumSince.IsSpecified) + _premiumSinceTicks = model.PremiumSince.Value?.UtcTicks; } internal void Update(ClientState state, PresenceModel model, bool updatePresence) { diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs index 3169be682..2d701ef64 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs @@ -63,6 +63,8 @@ namespace Discord.WebSocket /// string IGuildUser.Nickname => null; /// + DateTimeOffset? IGuildUser.PremiumSince => null; + /// GuildPermissions IGuildUser.GuildPermissions => GuildPermissions.Webhook; ///