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; ///