[Feature] Message Forwards (#2918)
* code * no guild * iTS ALIVE (THANKS aDVAITH)
This commit is contained in:
@@ -30,6 +30,11 @@ namespace Discord
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
IMessageInteractionMetadata InteractionMetadata { get; }
|
IMessageInteractionMetadata InteractionMetadata { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a collection of partial messages that were forwarded with this message.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyCollection<MessageSnapshot> ForwardedMessages { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the poll sent with this message.
|
/// Gets the poll sent with this message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ namespace Discord
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Optional<bool> FailIfNotExists { get; internal set; }
|
public Optional<bool> FailIfNotExists { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of message reference.
|
||||||
|
/// </summary>
|
||||||
|
public Optional<MessageReferenceType> ReferenceType { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MessageReference"/> class.
|
/// Initializes a new instance of the <see cref="MessageReference"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -48,12 +53,14 @@ namespace Discord
|
|||||||
/// <param name="failIfNotExists">
|
/// <param name="failIfNotExists">
|
||||||
/// Whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message. Defaults to true.
|
/// Whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message. Defaults to true.
|
||||||
/// </param>
|
/// </param>
|
||||||
public MessageReference(ulong? messageId = null, ulong? channelId = null, ulong? guildId = null, bool? failIfNotExists = null)
|
public MessageReference(ulong? messageId = null, ulong? channelId = null, ulong? guildId = null, bool? failIfNotExists = null,
|
||||||
|
MessageReferenceType referenceType = MessageReferenceType.Default)
|
||||||
{
|
{
|
||||||
MessageId = messageId ?? Optional.Create<ulong>();
|
MessageId = messageId ?? Optional.Create<ulong>();
|
||||||
InternalChannelId = channelId ?? Optional.Create<ulong>();
|
InternalChannelId = channelId ?? Optional.Create<ulong>();
|
||||||
GuildId = guildId ?? Optional.Create<ulong>();
|
GuildId = guildId ?? Optional.Create<ulong>();
|
||||||
FailIfNotExists = failIfNotExists ?? Optional.Create<bool>();
|
FailIfNotExists = failIfNotExists ?? Optional.Create<bool>();
|
||||||
|
ReferenceType = referenceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DebuggerDisplay
|
private string DebuggerDisplay
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Discord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines how associated data is populated.
|
||||||
|
/// </summary>
|
||||||
|
public enum MessageReferenceType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A standard reference used by replies.
|
||||||
|
/// </summary>
|
||||||
|
Default = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reference used to point to a message at a point in time.
|
||||||
|
/// </summary>
|
||||||
|
Forward = 1,
|
||||||
|
}
|
||||||
17
src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
Normal file
17
src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Discord;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a snapshot of a message.
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct MessageSnapshot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the partial message that was forwarded.
|
||||||
|
/// </summary>
|
||||||
|
public readonly IMessage Message;
|
||||||
|
|
||||||
|
internal MessageSnapshot(IMessage message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,7 +98,7 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void MessageAtLeastOneOf(string text = null, MessageComponent components = null, ICollection<IEmbed> embeds = null,
|
public static void MessageAtLeastOneOf(string text = null, MessageComponent components = null, ICollection<IEmbed> embeds = null,
|
||||||
ICollection<ISticker> stickers = null, IEnumerable<FileAttachment> attachments = null, PollProperties poll = null)
|
ICollection<ISticker> stickers = null, IEnumerable<FileAttachment> attachments = null, PollProperties poll = null, MessageReference messageReference = null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(text))
|
if (!string.IsNullOrEmpty(text))
|
||||||
return;
|
return;
|
||||||
@@ -118,6 +118,9 @@ namespace Discord
|
|||||||
if (poll is not null)
|
if (poll is not null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (messageReference?.ReferenceType.GetValueOrDefault(MessageReferenceType.Default) is MessageReferenceType.Forward)
|
||||||
|
return;
|
||||||
|
|
||||||
throw new ArgumentException($"At least one of 'Content', 'Embeds', 'Components', 'Stickers', 'Attachments' or 'Poll' must be specified.");
|
throw new ArgumentException($"At least one of 'Content', 'Embeds', 'Components', 'Stickers', 'Attachments' or 'Poll' must be specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,9 @@ internal class Message
|
|||||||
[JsonProperty("interaction_metadata")]
|
[JsonProperty("interaction_metadata")]
|
||||||
public Optional<MessageInteractionMetadata> InteractionMetadata { get; set; }
|
public Optional<MessageInteractionMetadata> InteractionMetadata { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("message_snapshots")]
|
||||||
|
public Optional<MessageSnapshot[]> MessageSnapshots { get; set; }
|
||||||
|
|
||||||
[JsonProperty("poll")]
|
[JsonProperty("poll")]
|
||||||
public Optional<Poll> Poll { get; set; }
|
public Optional<Poll> Poll { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
internal class MessageReference
|
internal class MessageReference
|
||||||
{
|
{
|
||||||
|
[JsonProperty("type")]
|
||||||
|
public Optional<MessageReferenceType> Type { get; set; }
|
||||||
|
|
||||||
[JsonProperty("message_id")]
|
[JsonProperty("message_id")]
|
||||||
public Optional<ulong> MessageId { get; set; }
|
public Optional<ulong> MessageId { get; set; }
|
||||||
|
|
||||||
|
|||||||
9
src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
Normal file
9
src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Discord.API;
|
||||||
|
|
||||||
|
internal class MessageSnapshot
|
||||||
|
{
|
||||||
|
[JsonProperty("message")]
|
||||||
|
public Message Message { get; set; }
|
||||||
|
}
|
||||||
@@ -284,7 +284,7 @@ namespace Discord.Rest
|
|||||||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
||||||
Preconditions.AtMost(embeds.Length, DiscordConfig.MaxEmbedsPerMessage, nameof(embeds), $"A max of {DiscordConfig.MaxEmbedsPerMessage} Embeds are allowed.");
|
Preconditions.AtMost(embeds.Length, DiscordConfig.MaxEmbedsPerMessage, nameof(embeds), $"A max of {DiscordConfig.MaxEmbedsPerMessage} Embeds are allowed.");
|
||||||
|
|
||||||
Preconditions.MessageAtLeastOneOf(text, components, embeds, stickers, poll: poll);
|
Preconditions.MessageAtLeastOneOf(text, components, embeds, stickers, poll: poll, messageReference: messageReference);
|
||||||
Preconditions.ValidatePoll(poll);
|
Preconditions.ValidatePoll(poll);
|
||||||
|
|
||||||
// check that user flag and user Id list are exclusive, same with role flag and role Id list
|
// check that user flag and user Id list are exclusive, same with role flag and role Id list
|
||||||
@@ -400,7 +400,7 @@ namespace Discord.Rest
|
|||||||
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
|
||||||
Preconditions.AtMost(embeds.Length, DiscordConfig.MaxEmbedsPerMessage, nameof(embeds), $"A max of {DiscordConfig.MaxEmbedsPerMessage} Embeds are allowed.");
|
Preconditions.AtMost(embeds.Length, DiscordConfig.MaxEmbedsPerMessage, nameof(embeds), $"A max of {DiscordConfig.MaxEmbedsPerMessage} Embeds are allowed.");
|
||||||
|
|
||||||
Preconditions.MessageAtLeastOneOf(text, components, embeds, stickers, attachments, poll);
|
Preconditions.MessageAtLeastOneOf(text, components, embeds, stickers, attachments, poll, messageReference);
|
||||||
Preconditions.ValidatePoll(poll);
|
Preconditions.ValidatePoll(poll);
|
||||||
|
|
||||||
foreach (var attachment in attachments)
|
foreach (var attachment in attachments)
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ namespace Discord.Rest
|
|||||||
GuildId = model.Reference.Value.GuildId,
|
GuildId = model.Reference.Value.GuildId,
|
||||||
InternalChannelId = model.Reference.Value.ChannelId,
|
InternalChannelId = model.Reference.Value.ChannelId,
|
||||||
MessageId = model.Reference.Value.MessageId,
|
MessageId = model.Reference.Value.MessageId,
|
||||||
FailIfNotExists = model.Reference.Value.FailIfNotExists
|
FailIfNotExists = model.Reference.Value.FailIfNotExists,
|
||||||
|
ReferenceType = model.Reference.Value.Type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ namespace Discord.Rest
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MessageResolvedData ResolvedData { get; internal set; }
|
public MessageResolvedData ResolvedData { get; internal set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyCollection<MessageSnapshot> ForwardedMessages { get; internal set; }
|
||||||
|
|
||||||
internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
|
internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
|
||||||
: base(discord, id, channel, author, source)
|
: base(discord, id, channel, author, source)
|
||||||
{
|
{
|
||||||
@@ -171,6 +174,14 @@ namespace Discord.Rest
|
|||||||
if (model.InteractionMetadata.IsSpecified)
|
if (model.InteractionMetadata.IsSpecified)
|
||||||
InteractionMetadata = model.InteractionMetadata.Value.ToInteractionMetadata(Discord);
|
InteractionMetadata = model.InteractionMetadata.Value.ToInteractionMetadata(Discord);
|
||||||
|
|
||||||
|
if (model.MessageSnapshots.IsSpecified)
|
||||||
|
{
|
||||||
|
ForwardedMessages = model.MessageSnapshots.Value.Select(x =>
|
||||||
|
new MessageSnapshot(RestMessage.Create(Discord, null, null, x.Message))).ToImmutableArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ForwardedMessages = ImmutableArray<MessageSnapshot>.Empty;
|
||||||
|
|
||||||
if (model.Poll.IsSpecified)
|
if (model.Poll.IsSpecified)
|
||||||
Poll = model.Poll.Value.ToEntity();
|
Poll = model.Poll.Value.ToEntity();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ namespace Discord.Rest
|
|||||||
ChannelId = entity.InternalChannelId,
|
ChannelId = entity.InternalChannelId,
|
||||||
GuildId = entity.GuildId,
|
GuildId = entity.GuildId,
|
||||||
MessageId = entity.MessageId,
|
MessageId = entity.MessageId,
|
||||||
FailIfNotExists = entity.FailIfNotExists
|
FailIfNotExists = entity.FailIfNotExists,
|
||||||
|
Type = entity.ReferenceType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static IEnumerable<string> EnumerateMentionTypes(this AllowedMentionTypes mentionTypes)
|
public static IEnumerable<string> EnumerateMentionTypes(this AllowedMentionTypes mentionTypes)
|
||||||
|
|||||||
@@ -198,7 +198,8 @@ namespace Discord.WebSocket
|
|||||||
GuildId = model.Reference.Value.GuildId,
|
GuildId = model.Reference.Value.GuildId,
|
||||||
InternalChannelId = model.Reference.Value.ChannelId,
|
InternalChannelId = model.Reference.Value.ChannelId,
|
||||||
MessageId = model.Reference.Value.MessageId,
|
MessageId = model.Reference.Value.MessageId,
|
||||||
FailIfNotExists = model.Reference.Value.FailIfNotExists
|
FailIfNotExists = model.Reference.Value.FailIfNotExists,
|
||||||
|
ReferenceType = model.Reference.Value.Type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ namespace Discord.WebSocket
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MessageResolvedData ResolvedData { get; internal set; }
|
public MessageResolvedData ResolvedData { get; internal set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyCollection<MessageSnapshot> ForwardedMessages { get; internal set; }
|
||||||
|
|
||||||
internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source)
|
internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source)
|
||||||
: base(discord, id, channel, author, source)
|
: base(discord, id, channel, author, source)
|
||||||
{
|
{
|
||||||
@@ -213,6 +216,14 @@ namespace Discord.WebSocket
|
|||||||
if (model.InteractionMetadata.IsSpecified)
|
if (model.InteractionMetadata.IsSpecified)
|
||||||
InteractionMetadata = model.InteractionMetadata.Value.ToInteractionMetadata(Discord);
|
InteractionMetadata = model.InteractionMetadata.Value.ToInteractionMetadata(Discord);
|
||||||
|
|
||||||
|
if (model.MessageSnapshots.IsSpecified)
|
||||||
|
{
|
||||||
|
ForwardedMessages = model.MessageSnapshots.Value.Select(x =>
|
||||||
|
new MessageSnapshot(RestMessage.Create(Discord, null, null, x.Message))).ToImmutableArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ForwardedMessages = ImmutableArray<MessageSnapshot>.Empty;
|
||||||
|
|
||||||
if (model.Poll.IsSpecified)
|
if (model.Poll.IsSpecified)
|
||||||
Poll = model.Poll.Value.ToEntity();
|
Poll = model.Poll.Value.ToEntity();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user