diff --git a/src/Discord.Net.Core/Entities/Guilds/ExplicitContentFilterLevel.cs b/src/Discord.Net.Core/Entities/Guilds/ExplicitContentFilterLevel.cs new file mode 100644 index 000000000..54c0bdafe --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/ExplicitContentFilterLevel.cs @@ -0,0 +1,13 @@ +namespace Discord +{ + public enum ExplicitContentFilterLevel + { + /// No messages will be scanned. + Disabled = 0, + /// Scans messages from all guild members that do not have a role. + /// Recommented option for servers that use roles for trusted membership. + MembersWithoutRoles = 1, + /// Scan messages sent by all guild members. + AllMembers = 2 + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs index 0ffe8db35..e6d21a463 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs @@ -66,5 +66,9 @@ namespace Discord /// Gets or sets the ID of the owner of this guild. /// public Optional OwnerId { get; set; } + /// + /// Gets or sets the explicit content filter level of this guild. + /// + public Optional ExplicitContentFilter { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 9b91b9440..c321cd2e3 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -53,6 +53,13 @@ namespace Discord /// VerificationLevel VerificationLevel { get; } /// + /// Gets the level of content filtering applied to user's content in a Guild. + /// + /// + /// The level of explicit content filtering. + /// + ExplicitContentFilterLevel ExplicitContentFilter { get; } + /// /// Gets the ID of this guild's icon. /// /// @@ -141,6 +148,13 @@ namespace Discord /// ulong OwnerId { get; } /// + /// Gets the application ID of the guild creator if it is bot-created. + /// + /// + /// A representing the snowflake identifier of the application ID that created this guild, or null if it was not bot-created. + /// + ulong? ApplicationId { get; } + /// /// Gets the ID of the region hosting this guild's voice channels. /// /// diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 87754eecd..33e019419 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -100,5 +100,25 @@ namespace Discord /// A read-only collection of user IDs. /// IReadOnlyCollection MentionedUserIds { get; } + /// + /// Returns the Activity associated with a message. + /// + /// + /// Sent with Rich Presence-related chat embeds. + /// + /// + /// A message's activity, if any is associated. + /// + MessageActivity Activity { get; } + /// + /// Returns the Application associated with a messsage. + /// + /// + /// Sent with Rich-Presence-related chat embeds. + /// + /// + /// A message's application, if any is associated. + /// + MessageApplication Application { get; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/MessageActivity.cs b/src/Discord.Net.Core/Entities/Messages/MessageActivity.cs new file mode 100644 index 000000000..d19e6a8e9 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/MessageActivity.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class MessageActivity + { + /// + /// Gets the type of activity of this message. + /// + public MessageActivityType Type { get; set; } + /// + /// Gets the party ID of this activity, if any. + /// + public string PartyId { get; set; } + + private string DebuggerDisplay + => $"{Type}{(string.IsNullOrWhiteSpace(PartyId) ? "" : $" {PartyId}")}"; + + public override string ToString() => DebuggerDisplay; + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/MessageActivityType.cs b/src/Discord.Net.Core/Entities/Messages/MessageActivityType.cs new file mode 100644 index 000000000..68b99a9c1 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/MessageActivityType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + public enum MessageActivityType + { + Join = 1, + Spectate = 2, + Listen = 3, + JoinRequest = 5 + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/MessageApplication.cs b/src/Discord.Net.Core/Entities/Messages/MessageApplication.cs new file mode 100644 index 000000000..960d1700f --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/MessageApplication.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class MessageApplication + { + /// + /// Gets the snowflake ID of the application. + /// + public ulong Id { get; set; } + /// + /// Gets the ID of the embed's image asset. + /// + public string CoverImage { get; set; } + /// + /// Gets the application's description. + /// + public string Description { get; set; } + /// + /// Gets the ID of the application's icon. + /// + public string Icon { get; set; } + /// + /// Gets the Url of the application's icon. + /// + public string IconUrl + => $"https://cdn.discordapp.com/app-icons/{Id}/{Icon}"; + /// + /// Gets the name of the application. + /// + public string Name { get; set; } + private string DebuggerDisplay + => $"{Name} ({Id}): {Description}"; + public override string ToString() + => DebuggerDisplay; + } +} diff --git a/src/Discord.Net.Rest/API/Common/Guild.cs b/src/Discord.Net.Rest/API/Common/Guild.cs index 0ca1bc236..a84b55a93 100644 --- a/src/Discord.Net.Rest/API/Common/Guild.cs +++ b/src/Discord.Net.Rest/API/Common/Guild.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -25,10 +25,12 @@ namespace Discord.API public bool EmbedEnabled { get; set; } [JsonProperty("embed_channel_id")] public ulong? EmbedChannelId { get; set; } - [JsonProperty("system_channel_id")] - public ulong? SystemChannelId { get; set; } [JsonProperty("verification_level")] public VerificationLevel VerificationLevel { get; set; } + [JsonProperty("default_message_notifications")] + public DefaultMessageNotifications DefaultMessageNotifications { get; set; } + [JsonProperty("explicit_content_filter")] + public ExplicitContentFilterLevel ExplicitContentFilter { get; set; } [JsonProperty("voice_states")] public VoiceState[] VoiceStates { get; set; } [JsonProperty("roles")] @@ -39,7 +41,9 @@ namespace Discord.API public string[] Features { get; set; } [JsonProperty("mfa_level")] public MfaLevel MfaLevel { get; set; } - [JsonProperty("default_message_notifications")] - public DefaultMessageNotifications DefaultMessageNotifications { get; set; } + [JsonProperty("application_id")] + public ulong? ApplicationId { get; set; } + [JsonProperty("system_channel_id")] + public ulong? SystemChannelId { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs index 229249ccf..10bdbe568 100644 --- a/src/Discord.Net.Rest/API/Common/Message.cs +++ b/src/Discord.Net.Rest/API/Common/Message.cs @@ -44,5 +44,11 @@ namespace Discord.API public Optional Pinned { get; set; } [JsonProperty("reactions")] public Optional Reactions { get; set; } + // sent with Rich Presence-related chat embeds + [JsonProperty("activity")] + public Optional Activity { get; set; } + // sent with Rich Presence-related chat embeds + [JsonProperty("application")] + public Optional Application { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/MessageActivity.cs b/src/Discord.Net.Rest/API/Common/MessageActivity.cs new file mode 100644 index 000000000..701f6fc03 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/MessageActivity.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.API +{ + public class MessageActivity + { + [JsonProperty("type")] + public Optional Type { get; set; } + [JsonProperty("party_id")] + public Optional PartyId { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Common/MessageApplication.cs b/src/Discord.Net.Rest/API/Common/MessageApplication.cs new file mode 100644 index 000000000..7302185ad --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/MessageApplication.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.API +{ + public class MessageApplication + { + /// + /// Gets the snowflake ID of the application. + /// + [JsonProperty("id")] + public ulong Id { get; set; } + /// + /// Gets the ID of the embed's image asset. + /// + [JsonProperty("cover_image")] + public string CoverImage { get; set; } + /// + /// Gets the application's description. + /// + [JsonProperty("description")] + public string Description { get; set; } + /// + /// Gets the ID of the application's icon. + /// + [JsonProperty("icon")] + public string Icon { get; set; } + /// + /// Gets the name of the application. + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs index 8de10f534..ba70c58d6 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Rest @@ -28,5 +28,7 @@ namespace Discord.API.Rest public Optional AfkChannelId { get; set; } [JsonProperty("owner_id")] public Optional OwnerId { get; set; } + [JsonProperty("explicit_content_filter")] + public Optional ExplicitContentFilter { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index b8b66f802..7b77dafc7 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -32,7 +32,8 @@ namespace Discord.Rest Icon = args.Icon.IsSpecified ? args.Icon.Value?.ToModel() : Optional.Create(), Name = args.Name, Splash = args.Splash.IsSpecified ? args.Splash.Value?.ToModel() : Optional.Create(), - VerificationLevel = args.VerificationLevel + VerificationLevel = args.VerificationLevel, + ExplicitContentFilter = args.ExplicitContentFilter }; if (args.AfkChannel.IsSpecified) @@ -60,6 +61,9 @@ namespace Discord.Rest if (!apiArgs.Icon.IsSpecified && guild.IconId != null) apiArgs.Icon = new ImageModel(guild.IconId); + if (args.ExplicitContentFilter.IsSpecified) + apiArgs.ExplicitContentFilter = args.ExplicitContentFilter.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 9c4ecd848..8cd81f218 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -32,6 +32,8 @@ namespace Discord.Rest public MfaLevel MfaLevel { get; private set; } /// public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } + /// + public ExplicitContentFilterLevel ExplicitContentFilter { get; private set; } /// public ulong? AFKChannelId { get; private set; } @@ -48,6 +50,8 @@ namespace Discord.Rest /// public string SplashId { get; private set; } internal bool Available { get; private set; } + /// + public ulong? ApplicationId { get; private set; } /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); @@ -98,6 +102,8 @@ namespace Discord.Rest VerificationLevel = model.VerificationLevel; MfaLevel = model.MfaLevel; DefaultMessageNotifications = model.DefaultMessageNotifications; + ExplicitContentFilter = model.ExplicitContentFilter; + ApplicationId = model.ApplicationId; if (model.Emojis != null) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index a4ecf3647..fae1aff99 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -55,6 +55,10 @@ namespace Discord.Rest /// public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); + /// + public MessageActivity Activity { get; private set; } + /// + public MessageApplication Application { get; private set; } internal RestMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source) : base(discord, id) @@ -77,6 +81,29 @@ namespace Discord.Rest if (model.Content.IsSpecified) Content = model.Content.Value; + + if (model.Application.IsSpecified) + { + // create a new Application from the API model + Application = new MessageApplication() + { + Id = model.Application.Value.Id, + CoverImage = model.Application.Value.CoverImage, + Description = model.Application.Value.Description, + Icon = model.Application.Value.Icon, + Name = model.Application.Value.Name + }; + } + + if (model.Activity.IsSpecified) + { + // create a new Activity from the API model + Activity = new MessageActivity() + { + Type = model.Activity.Value.Type.Value, + PartyId = model.Activity.Value.PartyId.Value + }; + } } /// diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index b4f87e12a..ce8cd08de 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -50,6 +50,8 @@ namespace Discord.WebSocket public MfaLevel MfaLevel { get; private set; } /// public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } + /// + public ExplicitContentFilterLevel ExplicitContentFilter { get; private set; } /// /// Gets the number of members. /// @@ -73,6 +75,8 @@ namespace Discord.WebSocket internal bool IsAvailable { get; private set; } /// Indicates whether the client is connected to this guild. public bool IsConnected { get; internal set; } + /// + public ulong? ApplicationId { get; internal set; } internal ulong? AFKChannelId { get; private set; } internal ulong? EmbedChannelId { get; private set; } @@ -346,6 +350,8 @@ namespace Discord.WebSocket VerificationLevel = model.VerificationLevel; MfaLevel = model.MfaLevel; DefaultMessageNotifications = model.DefaultMessageNotifications; + ExplicitContentFilter = model.ExplicitContentFilter; + ApplicationId = model.ApplicationId; if (model.Emojis != null) { diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 0767f2ad7..2cfcee270 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -43,6 +43,13 @@ namespace Discord.WebSocket public virtual bool IsPinned => false; /// public virtual DateTimeOffset? EditedTimestamp => null; + + /// + public MessageActivity Activity { get; private set; } + + /// + public MessageApplication Application { get; private set; } + /// /// Returns all attachments included in this message. /// @@ -105,6 +112,29 @@ namespace Discord.WebSocket if (model.Content.IsSpecified) Content = model.Content.Value; + + if (model.Application.IsSpecified) + { + // create a new Application from the API model + Application = new MessageApplication() + { + Id = model.Application.Value.Id, + CoverImage = model.Application.Value.CoverImage, + Description = model.Application.Value.Description, + Icon = model.Application.Value.Icon, + Name = model.Application.Value.Name + }; + } + + if (model.Activity.IsSpecified) + { + // create a new Activity from the API model + Activity = new MessageActivity() + { + Type = model.Activity.Value.Type.Value, + PartyId = model.Activity.Value.PartyId.Value + }; + } } /// diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.Guilds.cs similarity index 94% rename from test/Discord.Net.Tests/Tests.GuildPermissions.cs rename to test/Discord.Net.Tests/Tests.Guilds.cs index f49f431b5..09e3d044d 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.Guilds.cs @@ -5,8 +5,25 @@ using Xunit; namespace Discord { - public class GuidPermissionsTests + public partial class Tests { + /// + /// Tests the behavior of modifying the ExplicitContentFilter property of a Guild. + /// + [Fact] + public async Task TestExplicitContentFilter() + { + foreach (var level in Enum.GetValues(typeof(ExplicitContentFilterLevel))) + { + await _guild.ModifyAsync(x => x.ExplicitContentFilter = (ExplicitContentFilterLevel)level); + await _guild.UpdateAsync(); + Assert.Equal(level, _guild.ExplicitContentFilter); + } + } + + /// + /// Tests the behavior of the GuildPermissions class. + /// [Fact] public Task TestGuildPermission() {