diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
index f54c208d3..1eab885ce 100644
--- a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
+++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
@@ -270,6 +270,8 @@ public IServiceProvider Services
///
public static bool FinishFired { get; set; } = false;
+ internal static DiscordApplicationCommand? EntryPointCommand { get; set; } = null;
+
///
/// Runs setup.
/// DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS.
@@ -519,7 +521,7 @@ internal async Task UpdateAsync()
List failedGuilds = [];
var globalCommands = IsCalledByUnitTest ? null : (await this.Client.GetGlobalApplicationCommandsAsync(Configuration?.EnableLocalization ?? false).ConfigureAwait(false))?.ToList() ?? null;
- var guilds = CheckAllGuilds ? this.Client.ReadyGuildIds : this._updateList.Where(x => x.Key is not null)?.Select(x => x.Key.Value).Distinct().ToList();
+ var guilds = CheckAllGuilds ? this.Client.ReadyGuildIds : this._updateList.Where(x => x.Key is not null)?.Select(x => x.Key!.Value).Distinct().ToList();
var wrongShards = guilds is not null && this.Client.ReadyGuildIds.Count is not 0 ? guilds.Where(x => !this.Client.ReadyGuildIds.Contains(x)).ToList() : [];
if (wrongShards.Count is not 0)
{
@@ -585,7 +587,14 @@ internal async Task UpdateAsync()
}
if (globalCommands is not null && globalCommands.Count is not 0)
+ {
GlobalDiscordCommands.AddRange(globalCommands);
+ if (this.Client.Configuration.HasActivitiesEnabled)
+ {
+ var entryPointCommand = globalCommands.First(command => command.Name == "launch");
+ EntryPointCommand = entryPointCommand;
+ }
+ }
foreach (var key in commandsPending)
{
@@ -849,7 +858,7 @@ private async Task RegisterCommands(List
{
if (updateList.Count is not 0)
{
- var regCommands = await RegistrationWorker.RegisterGlobalCommandsAsync(this.Client, updateList).ConfigureAwait(false);
+ var regCommands = await RegistrationWorker.RegisterGlobalCommandsAsync(this.Client, updateList, EntryPointCommand).ConfigureAwait(false);
if (regCommands is not null)
{
var actualCommands = regCommands.Distinct().ToList();
@@ -861,7 +870,8 @@ private async Task RegisterCommands(List
foreach (var cmd in GlobalDiscordCommands)
try
{
- await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id).ConfigureAwait(false);
+ if (EntryPointCommand is null || cmd.Name is not "launch")
+ await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id).ConfigureAwait(false);
}
catch (NotFoundException)
{
diff --git a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
index be0ea845a..3d151a572 100644
--- a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
+++ b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
@@ -21,12 +21,13 @@ internal class RegistrationWorker
///
/// The discord client.
/// The command list.
+ /// The entry point command.
/// A list of registered commands.
- internal static async Task?> RegisterGlobalCommandsAsync(DiscordClient client, List commands)
+ internal static async Task?> RegisterGlobalCommandsAsync(DiscordClient client, List commands, DiscordApplicationCommand? entryPointCommand = null)
{
var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(client, commands);
- var globalCommandsCreateList = BuildGlobalCreateList(client, commands);
- var globalCommandsDeleteList = BuildGlobalDeleteList(client, commands);
+ var globalCommandsCreateList = BuildGlobalCreateList(client, commands, entryPointCommand);
+ var globalCommandsDeleteList = BuildGlobalDeleteList(client, commands, entryPointCommand);
if (globalCommandsCreateList!.NotEmptyAndNotNull() && unchangedCommands!.NotEmptyAndNotNull() && changedCommands!.NotEmptyAndNotNull())
{
@@ -420,8 +421,9 @@ private static (
///
/// The discord client.
/// The command list.
+ /// The entry point command.
/// A list of command ids.
- private static List? BuildGlobalDeleteList(DiscordClient client, List? updateList = null)
+ private static List? BuildGlobalDeleteList(DiscordClient client, List? updateList = null, DiscordApplicationCommand? entryPointCommand = null)
{
if (ApplicationCommandsExtension.GlobalDiscordCommands.Count is 0)
return null;
@@ -438,6 +440,9 @@ private static (
else
invalidCommandIds.AddRange(from cmd in discord where updateList.All(ul => ul.Name != cmd.Name) select cmd.Id);
+ if (entryPointCommand is not null && invalidCommandIds.Contains(entryPointCommand.Id))
+ invalidCommandIds.Remove(entryPointCommand.Id);
+
return invalidCommandIds;
}
@@ -446,8 +451,9 @@ private static (
///
/// The discord client.
/// The command list.
+ /// The entry point command.
/// A list of commands.
- private static List? BuildGlobalCreateList(DiscordClient client, List? updateList = null)
+ private static List? BuildGlobalCreateList(DiscordClient client, List? updateList = null, DiscordApplicationCommand? entryPointCommand = null)
{
if (updateList is null)
return null;
@@ -461,6 +467,9 @@ private static (
newCommands.AddRange(updateList.Where(cmd => discord.All(d => d.Name != cmd.Name)));
+ if (entryPointCommand is not null && newCommands.All(command => command.Name != "launch"))
+ newCommands.Add(entryPointCommand);
+
return newCommands;
}
diff --git a/DisCatSharp/DiscordConfiguration.cs b/DisCatSharp/DiscordConfiguration.cs
index 734b30ded..cf3a1ef5d 100644
--- a/DisCatSharp/DiscordConfiguration.cs
+++ b/DisCatSharp/DiscordConfiguration.cs
@@ -110,6 +110,8 @@ public DiscordConfiguration(DiscordConfiguration other)
this.UpdateCheckGitHubToken = other.UpdateCheckGitHubToken;
this.ShowReleaseNotesInUpdateCheck = other.ShowReleaseNotesInUpdateCheck;
this.AutoFetchApplicationEmojis = other.AutoFetchApplicationEmojis;
+ this.HasActivitiesEnabled = other.HasActivitiesEnabled;
+ this.ActivityHandlerType = other.ActivityHandlerType;
}
///
@@ -498,4 +500,16 @@ internal List TrackExceptions
/// Defaults to .
///
public bool ShowReleaseNotesInUpdateCheck { internal get; set; } = false;
+
+ ///
+ /// Whether this app uses activities.
+ /// Defaults to .
+ ///
+ public bool HasActivitiesEnabled { internal get; set; } = false;
+
+ ///
+ /// If is , determines which handler type we use..
+ /// Defaults to .
+ ///
+ public ApplicationCommandHandlerType ActivityHandlerType { internal get; set; } = ApplicationCommandHandlerType.DiscordLaunchActivity;
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
index 2c74c9874..4aeeabf29 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
@@ -28,6 +28,7 @@ public class DiscordApplicationCommand : SnowflakeObject, IEquatableWhether this command is NSFW.
/// Where the command can be used.
/// The allowed integration types.
+ /// The handler type.
public DiscordApplicationCommand(
string name,
string? description,
@@ -39,47 +40,80 @@ public DiscordApplicationCommand(
bool? dmPermission = null,
bool isNsfw = false,
List? allowedContexts = null,
- List? integrationTypes = null
+ List? integrationTypes = null,
+ ApplicationCommandHandlerType? handlerType = null
)
: base(["guild_id", "name_localizations", "description_localizations"])
{
if (type is ApplicationCommandType.ChatInput)
{
if (!Utilities.IsValidSlashCommandName(name))
- throw new ArgumentException("Invalid slash command name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));
+ throw new ArgumentException($"Invalid slash command name specified. It must be below 32 characters and not contain any whitespace. Error for command {name}.", nameof(name));
if (name.Any(char.IsUpper))
- throw new ArgumentException("Slash command name cannot have any upper case characters.", nameof(name));
+ throw new ArgumentException($"Slash command name cannot have any upper case characters. Error for command {name}.", nameof(name));
if (description?.Length > 100)
- throw new ArgumentException("Slash command description cannot exceed 100 characters.", nameof(description));
+ throw new ArgumentException($"Slash command description cannot exceed 100 characters. Error for command {name}.", nameof(description));
if (string.IsNullOrWhiteSpace(description))
- throw new ArgumentException("Slash commands need a description.", nameof(description));
+ throw new ArgumentException($"Slash commands need a description. Error for command {name}.", nameof(description));
this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ this.Type = type;
+ this.Name = name;
+ this.Description = description;
+ this.Options = options != null && options.Any() ? options.ToList() : null;
+ this.DefaultMemberPermissions = defaultMemberPermissions;
+ this.DmPermission = dmPermission;
+ this.IsNsfw = isNsfw;
+ this.AllowedContexts = allowedContexts;
+ this.IntegrationTypes = integrationTypes;
+ }
+ else if (type is ApplicationCommandType.PrimaryEntryPoint)
+ {
+ if (!Utilities.IsValidSlashCommandName(name))
+ throw new ArgumentException($"Invalid slash command name specified. It must be below 32 characters and not contain any whitespace. Error for command {name}.", nameof(name));
+ if (name.Any(char.IsUpper))
+ throw new ArgumentException($"Slash command name cannot have any upper case characters. Error for command {name}.", nameof(name));
+ if (description?.Length > 100)
+ throw new ArgumentException($"Slash command description cannot exceed 100 characters. Error for command {name}.", nameof(description));
+ if (string.IsNullOrWhiteSpace(description))
+ throw new ArgumentException($"Slash commands need a description. Error for command {name}.", nameof(description));
+ if (options?.Any() ?? false)
+ throw new ArgumentException($"Primary entrypoints do not support options. Error for command {name}.");
+
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ this.Type = type;
+ this.Name = name;
+ this.Description = description;
+ this.Options = null;
+ this.DefaultMemberPermissions = defaultMemberPermissions;
+ this.DmPermission = dmPermission;
+ this.IsNsfw = isNsfw;
+ this.AllowedContexts = allowedContexts;
+ this.IntegrationTypes = integrationTypes;
+ this.HandlerType = handlerType;
}
else
{
if (!string.IsNullOrWhiteSpace(description))
- throw new ArgumentException("Context menus do not support descriptions.");
+ throw new ArgumentException($"Context menus do not support descriptions. Error for command {name}.");
if (options?.Any() ?? false)
- throw new ArgumentException("Context menus do not support options.");
+ throw new ArgumentException($"Context menus do not support options. Error for command {name}.");
description = string.Empty;
this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.Type = type;
+ this.Name = name;
+ this.Description = description;
+ this.Options = null;
+ this.DefaultMemberPermissions = defaultMemberPermissions;
+ this.DmPermission = dmPermission;
+ this.IsNsfw = isNsfw;
+ this.AllowedContexts = allowedContexts;
+ this.IntegrationTypes = integrationTypes;
}
-
- var optionsList = options != null && options.Any() ? options.ToList() : null;
-
- this.Type = type;
- this.Name = name;
- this.Description = description;
- this.Options = optionsList;
- this.DefaultMemberPermissions = defaultMemberPermissions;
- this.DmPermission = dmPermission;
- this.IsNsfw = isNsfw;
- this.AllowedContexts = allowedContexts;
- this.IntegrationTypes = integrationTypes;
}
///
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
index 46172e6f3..b238a21b4 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
@@ -12,7 +12,7 @@ public sealed class DiscordApplicationCommandLocalization
///
/// Gets valid [locales](xref:modules_application_commands_translations_reference#valid-locales) for Discord.
///
- internal readonly List ValidLocales = ["ru", "fi", "hr", "de", "hu", "sv-SE", "cs", "fr", "it", "en-GB", "pt-BR", "ja", "tr", "en-US", "es-ES", "uk", "hi", "th", "el", "no", "ro", "ko", "zh-TW", "vi", "zh-CN", "pl", "bg", "da", "nl", "lt", "id", "es-419"];
+ internal readonly List ValidLocales = ["ar", "bg", "cs", "da", "de", "el", "en-GB", "en-US", "es-419", "es-ES", "fi", "fr", "he", "hi", "hr", "hu", "id", "it", "ja", "ko", "lt", "nl", "no", "pl", "pt-BR", "ro", "ru", "sv-SE", "th", "tr", "uk", "vi", "zh-CN", "zh-TW"];
///
/// Initializes a new instance of .
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index 55514bcd8..eceecefa2 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -6685,7 +6685,7 @@ internal async Task CreateGlobalApplicationCommandAsy
{
Type = command.Type,
Name = command.Name,
- Description = command.Type is ApplicationCommandType.ChatInput ? command.Description : null,
+ Description = command.Type is ApplicationCommandType.ChatInput or ApplicationCommandType.PrimaryEntryPoint ? command.Description : null,
Options = command.Options,
NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),