Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Components V2 #3065

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
rework message flags + auto add ComponentsV2
Misha-133 committed Feb 5, 2025
commit de10572374227116b88adf0917a15df6c6faa7a2
Original file line number Diff line number Diff line change
@@ -85,12 +85,12 @@ public static BuilderT WithFile<BuilderT>(this BuilderT container, FileComponent
}

public static BuilderT WithFile<BuilderT>(this BuilderT container,
string url,
UnfurledMediaItemProperties file,
bool isSpoiler = false,
int? id = null)
where BuilderT : class, IStaticComponentContainer
=> container.WithFile(new FileComponentBuilder()
.WithFile(new UnfurledMediaItemProperties(url))
.WithFile(file)
.WithIsSpoiler(isSpoiler)
.WithId(id));

Original file line number Diff line number Diff line change
@@ -10,6 +10,15 @@ public class FileComponentBuilder : IMessageComponentBuilder

public bool? IsSpoiler { get; set; }

public FileComponentBuilder() {}

public FileComponentBuilder(UnfurledMediaItemProperties media, bool isSpoiler = false, int? id = null)
{
File = media;
Id = id;
IsSpoiler = isSpoiler;
}

public FileComponentBuilder WithFile(UnfurledMediaItemProperties file)
{
File = file;
Original file line number Diff line number Diff line change
@@ -12,6 +12,14 @@ public class MediaGalleryBuilder : IMessageComponentBuilder

private List<MediaGalleryItemProperties> _items = new();

public MediaGalleryBuilder() { }

public MediaGalleryBuilder(IEnumerable<MediaGalleryItemProperties> items, int? id = null)
{
Items = items.ToList();
Id = id;
}

public List<MediaGalleryItemProperties> Items
{
get => _items;
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ public class ThumbnailBuilder : IMessageComponentBuilder

public string Description { get; set; }

public bool IsSpoiler { get; set; } = false;
public bool IsSpoiler { get; set; }

public ThumbnailBuilder() { }

6 changes: 6 additions & 0 deletions src/Discord.Net.Core/Utils/Preconditions.cs
Original file line number Diff line number Diff line change
@@ -401,5 +401,11 @@ public static void Options(string name, string description)
}

#endregion

public static void ValidateMessageFlags(MessageFlags flags)
{
if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification and not MessageFlags.ComponentsV2 and not MessageFlags.Ephemeral)
throw new ArgumentException("The only valid MessageFlags are Ephemeral, SuppressEmbeds, SuppressNotification, ComponentsV2 and None.", nameof(flags));
}
}
}
13 changes: 11 additions & 2 deletions src/Discord.Net.Rest/API/Rest/CreateWebhookMessageParams.cs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Discord.API.Rest
@@ -55,6 +56,7 @@ public IReadOnlyDictionary<string, object> ToDictionary()
{
var d = new Dictionary<string, object>();

var extraFlags = MessageFlags.None;
if (File.IsSpecified)
{
d["file"] = File.Value;
@@ -77,14 +79,21 @@ public IReadOnlyDictionary<string, object> ToDictionary()
payload["embeds"] = Embeds.Value;
if (AllowedMentions.IsSpecified)
payload["allowed_mentions"] = AllowedMentions.Value;


if (Components.IsSpecified)
{
payload["components"] = Components.Value;
if (Components.Value.Any(x => x.Type is not ComponentType.ActionRow))
extraFlags |= MessageFlags.ComponentsV2;
}

payload["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;

if (ThreadName.IsSpecified)
payload["thread_name"] = ThreadName.Value;
if (AppliedTags.IsSpecified)
payload["applied_tags"] = AppliedTags.Value;
if (Flags.IsSpecified)
payload["flags"] = Flags.Value;
if (Poll.IsSpecified)
payload["poll"] = Poll.Value;

18 changes: 13 additions & 5 deletions src/Discord.Net.Rest/API/Rest/UploadInteractionFileParams.cs
Original file line number Diff line number Diff line change
@@ -44,8 +44,10 @@ public IReadOnlyDictionary<string, object> ToDictionary()
{
var d = new Dictionary<string, object>();

var extraFlags = MessageFlags.None;

if (Files.Any(x => x.Waveform is not null && x.DurationSeconds is not null))
Flags = Flags.GetValueOrDefault(MessageFlags.None) | MessageFlags.VoiceMessage;
extraFlags |= MessageFlags.VoiceMessage;

var payload = new Dictionary<string, object>();
payload["type"] = Type;
@@ -55,14 +57,20 @@ public IReadOnlyDictionary<string, object> ToDictionary()
data["content"] = Content.Value;
if (IsTTS.IsSpecified)
data["tts"] = IsTTS.Value;
if (MessageComponents.IsSpecified)
data["components"] = MessageComponents.Value;
if (Embeds.IsSpecified)
data["embeds"] = Embeds.Value;
if (AllowedMentions.IsSpecified)
data["allowed_mentions"] = AllowedMentions.Value;
if (Flags.IsSpecified)
data["flags"] = Flags.Value;

if (MessageComponents.IsSpecified)
{
data["components"] = MessageComponents.Value;
if (MessageComponents.Value.Any(x => x.Type is not ComponentType.ActionRow))
extraFlags |= MessageFlags.ComponentsV2;
}

data["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;

if (Poll.IsSpecified)
data["poll"] = Poll.Value;

18 changes: 13 additions & 5 deletions src/Discord.Net.Rest/API/Rest/UploadWebhookFileParams.cs
Original file line number Diff line number Diff line change
@@ -37,8 +37,10 @@ public IReadOnlyDictionary<string, object> ToDictionary()
{
var d = new Dictionary<string, object>();

var extraFlags = MessageFlags.None;

if (Files.Any(x => x.Waveform is not null && x.DurationSeconds is not null))
Flags = Flags.GetValueOrDefault(MessageFlags.None) | MessageFlags.VoiceMessage;
extraFlags |= MessageFlags.VoiceMessage;

var payload = new Dictionary<string, object>();
if (Content.IsSpecified)
@@ -51,14 +53,20 @@ public IReadOnlyDictionary<string, object> ToDictionary()
payload["username"] = Username.Value;
if (AvatarUrl.IsSpecified)
payload["avatar_url"] = AvatarUrl.Value;
if (MessageComponents.IsSpecified)
payload["components"] = MessageComponents.Value;
if (Embeds.IsSpecified)
payload["embeds"] = Embeds.Value;
if (AllowedMentions.IsSpecified)
payload["allowed_mentions"] = AllowedMentions.Value;
if (Flags.IsSpecified)
payload["flags"] = Flags.Value;

if (MessageComponents.IsSpecified)
{
payload["components"] = MessageComponents.Value;
if (MessageComponents.Value.Any(x => x.Type is not ComponentType.ActionRow))
extraFlags |= MessageFlags.ComponentsV2;
}

payload["flags"] = Flags.GetValueOrDefault(MessageFlags.None) | extraFlags;

if (ThreadName.IsSpecified)
payload["thread_name"] = ThreadName.Value;
if (AppliedTags.IsSpecified)
12 changes: 7 additions & 5 deletions src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
Original file line number Diff line number Diff line change
@@ -308,10 +308,10 @@ public static async Task<RestUserMessage> SendMessageAsync(IMessageChannel chann
Preconditions.AtMost(stickers.Length, 3, nameof(stickers), "A max of 3 stickers are allowed.");
}

if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;


Preconditions.ValidateMessageFlags(flags);

var args = new CreateMessageParams
{
@@ -434,8 +434,10 @@ public static async Task<RestUserMessage> SendFilesAsync(IMessageChannel channel
}
}

if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds and not MessageFlags.SuppressNotification)
throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds, SuppressNotification and none.", nameof(flags));
if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;

Preconditions.ValidateMessageFlags(flags);

if (stickers != null)
{
Original file line number Diff line number Diff line change
@@ -116,6 +116,13 @@ public override async Task RespondWithFilesAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
{
Type = InteractionResponseType.ChannelMessageWithSource,
@@ -124,11 +131,7 @@ public override async Task RespondWithFilesAsync(
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
IsTTS = isTTS,
MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
};

@@ -188,6 +191,13 @@ public override async Task RespondAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.InteractionResponse
{
Type = InteractionResponseType.ChannelMessageWithSource,
@@ -197,11 +207,7 @@ public override async Task RespondAsync(
AllowedMentions = allowedMentions?.ToModel(),
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
TTS = isTTS,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
}
@@ -346,6 +352,14 @@ public override Task<RestFollowupMessage> FollowupAsync(
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.ValidatePoll(poll);

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);


var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
@@ -354,11 +368,7 @@ public override Task<RestFollowupMessage> FollowupAsync(
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
};

return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
@@ -410,14 +420,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
}
}
if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
{
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Content = text,
IsTTS = isTTS,
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
Original file line number Diff line number Diff line change
@@ -112,6 +112,12 @@ public override async Task RespondWithFilesAsync(
throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
}
}
if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
{
@@ -121,11 +127,7 @@ public override async Task RespondWithFilesAsync(
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
IsTTS = isTTS,
MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
};

@@ -141,7 +143,7 @@ public override async Task RespondWithFilesAsync(
HasResponded = true;
}

/// <inheritdoc/>
/// <inheritdoc cref="IDiscordInteraction.RespondAsync"/>
public override async Task RespondAsync(
string text = null,
Embed[] embeds = null,
@@ -185,6 +187,13 @@ public override async Task RespondAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.InteractionResponse
{
Type = InteractionResponseType.ChannelMessageWithSource,
@@ -194,11 +203,7 @@ public override async Task RespondAsync(
AllowedMentions = allowedMentions?.ToModel(),
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
TTS = isTTS,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
}
@@ -345,18 +350,21 @@ public override Task<RestFollowupMessage> FollowupAsync(
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.ValidatePoll(poll);

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text,
AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified,
IsTTS = isTTS,
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
};

@@ -410,13 +418,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
{
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Content = text,
IsTTS = isTTS,
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
Original file line number Diff line number Diff line change
@@ -111,6 +111,13 @@ public override async Task RespondAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.InteractionResponse
{
Type = InteractionResponseType.ChannelMessageWithSource,
@@ -121,11 +128,7 @@ public override async Task RespondAsync(
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
TTS = isTTS,
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
}
};
@@ -222,18 +225,21 @@ public override async Task RespondWithFilesAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var response = new API.Rest.UploadInteractionFileParams(attachments?.ToArray())
{
Type = InteractionResponseType.ChannelMessageWithSource,
Content = text ?? Optional<string>.Unspecified,
AllowedMentions = allowedMentions != null ? allowedMentions?.ToModel() : Optional<API.AllowedMentions>.Unspecified,
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,
IsTTS = isTTS,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
MessageComponents = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified
};
@@ -275,6 +281,13 @@ public override Task<RestFollowupMessage> FollowupAsync(
Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
Preconditions.ValidatePoll(poll);

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var args = new API.Rest.CreateWebhookMessageParams
{
Content = text ?? Optional<string>.Unspecified,
@@ -283,11 +296,7 @@ public override Task<RestFollowupMessage> FollowupAsync(
Embeds = embeds.Select(x => x.ToModel()).ToArray(),
Components = components?.Components.Select(x => x.ToModel()).ToArray() ?? Optional<IMessageComponent[]>.Unspecified,
Poll = poll?.ToModel() ?? Optional<CreatePollParams>.Unspecified,
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
};

return InteractionHelper.SendFollowupAsync(Discord.Rest, args, Token, Channel, options);
@@ -340,13 +349,16 @@ public override Task<RestFollowupMessage> FollowupWithFilesAsync(
}
}

if (components?.Components?.Any(x => x.Type != ComponentType.ActionRow) ?? false)
flags |= MessageFlags.ComponentsV2;
if (ephemeral)
flags |= MessageFlags.Ephemeral;

Preconditions.ValidateMessageFlags(flags);

var args = new API.Rest.UploadWebhookFileParams(attachments.ToArray())
{
Flags = ephemeral
? flags | MessageFlags.Ephemeral
: flags == MessageFlags.None
? Optional<MessageFlags>.Unspecified
: flags,
Flags = flags,
Content = text,
IsTTS = isTTS,
Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional<API.Embed[]>.Unspecified,