Skip to content

Commit

Permalink
[Feature] Message Forwards (#2918)
Browse files Browse the repository at this point in the history
* code

* no guild

* iTS ALIVE (THANKS aDVAITH)
  • Loading branch information
Misha-133 authored Aug 31, 2024
1 parent 623a457 commit c4d90cd
Show file tree
Hide file tree
Showing 14 changed files with 96 additions and 7 deletions.
5 changes: 5 additions & 0 deletions src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public interface IUserMessage : IMessage
/// </remarks>
IMessageInteractionMetadata InteractionMetadata { get; }

/// <summary>
/// Gets a collection of partial messages that were forwarded with this message.
/// </summary>
IReadOnlyCollection<MessageSnapshot> ForwardedMessages { get; }

/// <summary>
/// Gets the poll sent with this message.
/// </summary>
Expand Down
9 changes: 8 additions & 1 deletion src/Discord.Net.Core/Entities/Messages/MessageReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class MessageReference
/// </summary>
public Optional<bool> FailIfNotExists { get; internal set; }

/// <summary>
/// Gets the type of message reference.
/// </summary>
public Optional<MessageReferenceType> ReferenceType { get; internal set; }

/// <summary>
/// Initializes a new instance of the <see cref="MessageReference"/> class.
/// </summary>
Expand All @@ -48,12 +53,14 @@ public class MessageReference
/// <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.
/// </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>();
InternalChannelId = channelId ?? Optional.Create<ulong>();
GuildId = guildId ?? Optional.Create<ulong>();
FailIfNotExists = failIfNotExists ?? Optional.Create<bool>();
ReferenceType = referenceType;
}

private string DebuggerDisplay
Expand Down
17 changes: 17 additions & 0 deletions src/Discord.Net.Core/Entities/Messages/MessageReferenceType.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions src/Discord.Net.Core/Entities/Messages/MessageSnapshot.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
5 changes: 4 additions & 1 deletion src/Discord.Net.Core/Utils/Preconditions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static void WebhookMessageAtLeastOneOf(string text = null, MessageCompone
}

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))
return;
Expand All @@ -118,6 +118,9 @@ public static void MessageAtLeastOneOf(string text = null, MessageComponent comp
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.");
}

Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/API/Common/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ internal class Message
[JsonProperty("interaction_metadata")]
public Optional<MessageInteractionMetadata> InteractionMetadata { get; set; }

[JsonProperty("message_snapshots")]
public Optional<MessageSnapshot[]> MessageSnapshots { get; set; }

[JsonProperty("poll")]
public Optional<Poll> Poll { get; set; }

Expand Down
3 changes: 3 additions & 0 deletions src/Discord.Net.Rest/API/Common/MessageReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Discord.API
{
internal class MessageReference
{
[JsonProperty("type")]
public Optional<MessageReferenceType> Type { get; set; }

[JsonProperty("message_id")]
public Optional<ulong> MessageId { get; set; }

Expand Down
9 changes: 9 additions & 0 deletions src/Discord.Net.Rest/API/Common/MessageSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Newtonsoft.Json;

namespace Discord.API;

internal class MessageSnapshot
{
[JsonProperty("message")]
public Message Message { get; set; }
}
4 changes: 2 additions & 2 deletions src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel chann
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
Expand Down Expand Up @@ -400,7 +400,7 @@ public static async Task<RestUserMessage> SendFilesAsync(IMessageChannel channel
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)
Expand Down
3 changes: 2 additions & 1 deletion src/Discord.Net.Rest/Entities/Messages/RestMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ internal virtual void Update(Model model)
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
};
}

Expand Down
11 changes: 11 additions & 0 deletions src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public class RestUserMessage : RestMessage, IUserMessage
/// <inheritdoc />
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)
: base(discord, id, channel, author, source)
{
Expand Down Expand Up @@ -171,6 +174,14 @@ internal override void Update(Model model)
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<MessageSnapshot>.Empty;

if (model.Poll.IsSpecified)
Poll = model.Poll.Value.ToEntity();
}
Expand Down
3 changes: 2 additions & 1 deletion src/Discord.Net.Rest/Extensions/EntityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ public static API.MessageReference ToModel(this MessageReference entity)
ChannelId = entity.InternalChannelId,
GuildId = entity.GuildId,
MessageId = entity.MessageId,
FailIfNotExists = entity.FailIfNotExists
FailIfNotExists = entity.FailIfNotExists,
Type = entity.ReferenceType,
};
}
public static IEnumerable<string> EnumerateMentionTypes(this AllowedMentionTypes mentionTypes)
Expand Down
3 changes: 2 additions & 1 deletion src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ internal virtual void Update(ClientState state, Model model)
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
};
}

Expand Down
11 changes: 11 additions & 0 deletions src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public class SocketUserMessage : SocketMessage, IUserMessage
/// <inheritdoc />
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)
: base(discord, id, channel, author, source)
{
Expand Down Expand Up @@ -213,6 +216,14 @@ internal override void Update(ClientState state, Model model)
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<MessageSnapshot>.Empty;

if (model.Poll.IsSpecified)
Poll = model.Poll.Value.ToEntity();
}
Expand Down

0 comments on commit c4d90cd

Please sign in to comment.