* feature: Add MessageFlags and AllowedMentions to Modify * Change exception messagetags/2.3.0
| @@ -164,6 +164,17 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions { get; } | IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions { 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. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -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, | |||||
| } | |||||
| } | |||||
| @@ -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; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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, | |||||
| } | |||||
| } | |||||
| @@ -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; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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); | ||||
| } | } | ||||
| @@ -67,6 +67,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) | ||||
| @@ -124,6 +126,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; | ||||
| @@ -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>(); | ||||
| @@ -27,7 +27,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 /> | ||||
| @@ -70,10 +70,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(); | ||||
| @@ -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> | ||||
| @@ -156,6 +159,9 @@ namespace Discord.WebSocket | |||||
| MessageId = model.Reference.Value.MessageId | MessageId = model.Reference.Value.MessageId | ||||
| }; | }; | ||||
| } | } | ||||
| if (model.Flags.IsSpecified) | |||||
| Flags = model.Flags.Value; | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| @@ -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>(); | ||||
| @@ -29,7 +29,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 /> | ||||
| @@ -74,10 +74,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(); | ||||