From c4d90cd6f1dd2b59304aaf93bce2dc53ac625bb4 Mon Sep 17 00:00:00 2001
From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com>
Date: Sat, 31 Aug 2024 14:29:20 +0300
Subject: [PATCH] [Feature] Message Forwards (#2918)
* code
* no guild
* iTS ALIVE (THANKS aDVAITH)
---
.../Entities/Messages/IUserMessage.cs | 5 +++++
.../Entities/Messages/MessageReference.cs | 9 ++++++++-
.../Entities/Messages/MessageReferenceType.cs | 17 +++++++++++++++++
.../Entities/Messages/MessageSnapshot.cs | 17 +++++++++++++++++
src/Discord.Net.Core/Utils/Preconditions.cs | 5 ++++-
src/Discord.Net.Rest/API/Common/Message.cs | 3 +++
.../API/Common/MessageReference.cs | 3 +++
.../API/Common/MessageSnapshot.cs | 9 +++++++++
.../Entities/Channels/ChannelHelper.cs | 4 ++--
.../Entities/Messages/RestMessage.cs | 3 ++-
.../Entities/Messages/RestUserMessage.cs | 11 +++++++++++
.../Extensions/EntityExtensions.cs | 3 ++-
.../Entities/Messages/SocketMessage.cs | 3 ++-
.../Entities/Messages/SocketUserMessage.cs | 11 +++++++++++
14 files changed, 96 insertions(+), 7 deletions(-)
create mode 100644 src/Discord.Net.Core/Entities/Messages/MessageReferenceType.cs
create mode 100644 src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
create mode 100644 src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
index 6bd764f9..e8bcc6ba 100644
--- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
+++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
@@ -30,6 +30,11 @@ namespace Discord
///
IMessageInteractionMetadata InteractionMetadata { get; }
+ ///
+ /// Gets a collection of partial messages that were forwarded with this message.
+ ///
+ IReadOnlyCollection ForwardedMessages { get; }
+
///
/// Gets the poll sent with this message.
///
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageReference.cs b/src/Discord.Net.Core/Entities/Messages/MessageReference.cs
index 7fdc448a..032879a0 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageReference.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageReference.cs
@@ -33,6 +33,11 @@ namespace Discord
///
public Optional FailIfNotExists { get; internal set; }
+ ///
+ /// Gets the type of message reference.
+ ///
+ public Optional ReferenceType { get; internal set; }
+
///
/// Initializes a new instance of the class.
///
@@ -48,12 +53,14 @@ namespace Discord
///
/// Whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message. Defaults to true.
///
- 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();
InternalChannelId = channelId ?? Optional.Create();
GuildId = guildId ?? Optional.Create();
FailIfNotExists = failIfNotExists ?? Optional.Create();
+ ReferenceType = referenceType;
}
private string DebuggerDisplay
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageReferenceType.cs b/src/Discord.Net.Core/Entities/Messages/MessageReferenceType.cs
new file mode 100644
index 00000000..4fed6f41
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/MessageReferenceType.cs
@@ -0,0 +1,17 @@
+namespace Discord;
+
+///
+/// Determines how associated data is populated.
+///
+public enum MessageReferenceType
+{
+ ///
+ /// A standard reference used by replies.
+ ///
+ Default = 0,
+
+ ///
+ /// Reference used to point to a message at a point in time.
+ ///
+ Forward = 1,
+}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs b/src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
new file mode 100644
index 00000000..918b2299
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
@@ -0,0 +1,17 @@
+namespace Discord;
+
+///
+/// Represents a snapshot of a message.
+///
+public readonly struct MessageSnapshot
+{
+ ///
+ /// Gets the partial message that was forwarded.
+ ///
+ public readonly IMessage Message;
+
+ internal MessageSnapshot(IMessage message)
+ {
+ Message = message;
+ }
+}
diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs
index aa61495b..83da3a66 100644
--- a/src/Discord.Net.Core/Utils/Preconditions.cs
+++ b/src/Discord.Net.Core/Utils/Preconditions.cs
@@ -98,7 +98,7 @@ namespace Discord
}
public static void MessageAtLeastOneOf(string text = null, MessageComponent components = null, ICollection embeds = null,
- ICollection stickers = null, IEnumerable attachments = null, PollProperties poll = null)
+ ICollection stickers = null, IEnumerable attachments = null, PollProperties poll = null, MessageReference messageReference = null)
{
if (!string.IsNullOrEmpty(text))
return;
@@ -118,6 +118,9 @@ namespace Discord
if (poll is not null)
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.");
}
diff --git a/src/Discord.Net.Rest/API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs
index 3f579cdd..48d9d9a3 100644
--- a/src/Discord.Net.Rest/API/Common/Message.cs
+++ b/src/Discord.Net.Rest/API/Common/Message.cs
@@ -102,6 +102,9 @@ internal class Message
[JsonProperty("interaction_metadata")]
public Optional InteractionMetadata { get; set; }
+ [JsonProperty("message_snapshots")]
+ public Optional MessageSnapshots { get; set; }
+
[JsonProperty("poll")]
public Optional Poll { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/MessageReference.cs b/src/Discord.Net.Rest/API/Common/MessageReference.cs
index 70ef4e67..3e582b2f 100644
--- a/src/Discord.Net.Rest/API/Common/MessageReference.cs
+++ b/src/Discord.Net.Rest/API/Common/MessageReference.cs
@@ -4,6 +4,9 @@ namespace Discord.API
{
internal class MessageReference
{
+ [JsonProperty("type")]
+ public Optional Type { get; set; }
+
[JsonProperty("message_id")]
public Optional MessageId { get; set; }
diff --git a/src/Discord.Net.Rest/API/Common/MessageSnapshot.cs b/src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
new file mode 100644
index 00000000..8ec3173a
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
@@ -0,0 +1,9 @@
+using Newtonsoft.Json;
+
+namespace Discord.API;
+
+internal class MessageSnapshot
+{
+ [JsonProperty("message")]
+ public Message Message { get; set; }
+}
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index 6b1fa561..3fd65d02 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -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(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);
// 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(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);
foreach (var attachment in attachments)
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
index 51ea852a..132f1c97 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
@@ -165,7 +165,8 @@ namespace Discord.Rest
GuildId = model.Reference.Value.GuildId,
InternalChannelId = model.Reference.Value.ChannelId,
MessageId = model.Reference.Value.MessageId,
- FailIfNotExists = model.Reference.Value.FailIfNotExists
+ FailIfNotExists = model.Reference.Value.FailIfNotExists,
+ ReferenceType = model.Reference.Value.Type
};
}
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index 985798a5..4f80fa43 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -57,6 +57,9 @@ namespace Discord.Rest
///
public MessageResolvedData ResolvedData { get; internal set; }
+ ///
+ public IReadOnlyCollection ForwardedMessages { get; internal set; }
+
internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, IUser author, MessageSource source)
: base(discord, id, channel, author, source)
{
@@ -171,6 +174,14 @@ namespace Discord.Rest
if (model.InteractionMetadata.IsSpecified)
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.Empty;
+
if (model.Poll.IsSpecified)
Poll = model.Poll.Value.ToEntity();
}
diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
index b6b27ee6..240cf71b 100644
--- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
+++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
@@ -102,7 +102,8 @@ namespace Discord.Rest
ChannelId = entity.InternalChannelId,
GuildId = entity.GuildId,
MessageId = entity.MessageId,
- FailIfNotExists = entity.FailIfNotExists
+ FailIfNotExists = entity.FailIfNotExists,
+ Type = entity.ReferenceType,
};
}
public static IEnumerable EnumerateMentionTypes(this AllowedMentionTypes mentionTypes)
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
index 4e86208e..c778137e 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
@@ -198,7 +198,8 @@ namespace Discord.WebSocket
GuildId = model.Reference.Value.GuildId,
InternalChannelId = model.Reference.Value.ChannelId,
MessageId = model.Reference.Value.MessageId,
- FailIfNotExists = model.Reference.Value.FailIfNotExists
+ FailIfNotExists = model.Reference.Value.FailIfNotExists,
+ ReferenceType = model.Reference.Value.Type
};
}
diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
index 34af97d8..dc86503c 100644
--- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
+++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
@@ -58,6 +58,9 @@ namespace Discord.WebSocket
///
public MessageResolvedData ResolvedData { get; internal set; }
+ ///
+ public IReadOnlyCollection ForwardedMessages { get; internal set; }
+
internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source)
: base(discord, id, channel, author, source)
{
@@ -213,6 +216,14 @@ namespace Discord.WebSocket
if (model.InteractionMetadata.IsSpecified)
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.Empty;
+
if (model.Poll.IsSpecified)
Poll = model.Poll.Value.ToEntity();
}