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

feat: support entry point command for activity enabled applications #594

Merged
merged 2 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 13 additions & 3 deletions DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ public IServiceProvider Services
/// </summary>
public static bool FinishFired { get; set; } = false;

internal static DiscordApplicationCommand? EntryPointCommand { get; set; } = null;

/// <summary>
/// Runs setup.
/// <note type="caution">DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS.</note>
Expand Down Expand Up @@ -519,7 +521,7 @@ internal async Task UpdateAsync()
List<ulong> 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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -849,7 +858,7 @@ private async Task RegisterCommands(List<ApplicationCommandsModuleConfiguration>
{
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();
Expand All @@ -861,7 +870,8 @@ private async Task RegisterCommands(List<ApplicationCommandsModuleConfiguration>
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)
{
Expand Down
19 changes: 14 additions & 5 deletions DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ internal class RegistrationWorker
/// </summary>
/// <param name="client">The discord client.</param>
/// <param name="commands">The command list.</param>
/// <param name="entryPointCommand">The entry point command.</param>
/// <returns>A list of registered commands.</returns>
internal static async Task<List<DiscordApplicationCommand>?> RegisterGlobalCommandsAsync(DiscordClient client, List<DiscordApplicationCommand> commands)
internal static async Task<List<DiscordApplicationCommand>?> RegisterGlobalCommandsAsync(DiscordClient client, List<DiscordApplicationCommand> 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())
{
Expand Down Expand Up @@ -420,8 +421,9 @@ private static (
/// </summary>
/// <param name="client">The discord client.</param>
/// <param name="updateList">The command list.</param>
/// <param name="entryPointCommand">The entry point command.</param>
/// <returns>A list of command ids.</returns>
private static List<ulong>? BuildGlobalDeleteList(DiscordClient client, List<DiscordApplicationCommand>? updateList = null)
private static List<ulong>? BuildGlobalDeleteList(DiscordClient client, List<DiscordApplicationCommand>? updateList = null, DiscordApplicationCommand? entryPointCommand = null)
{
if (ApplicationCommandsExtension.GlobalDiscordCommands.Count is 0)
return null;
Expand All @@ -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;
}

Expand All @@ -446,8 +451,9 @@ private static (
/// </summary>
/// <param name="client">The discord client.</param>
/// <param name="updateList">The command list.</param>
/// <param name="entryPointCommand">The entry point command.</param>
/// <returns>A list of commands.</returns>
private static List<DiscordApplicationCommand>? BuildGlobalCreateList(DiscordClient client, List<DiscordApplicationCommand>? updateList = null)
private static List<DiscordApplicationCommand>? BuildGlobalCreateList(DiscordClient client, List<DiscordApplicationCommand>? updateList = null, DiscordApplicationCommand? entryPointCommand = null)
{
if (updateList is null)
return null;
Expand All @@ -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;
}

Expand Down
14 changes: 14 additions & 0 deletions DisCatSharp/DiscordConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -498,4 +500,16 @@ internal List<Type> TrackExceptions
/// <para>Defaults to <see langword="false" />.</para>
/// </summary>
public bool ShowReleaseNotesInUpdateCheck { internal get; set; } = false;

/// <summary>
/// Whether this app uses activities.
/// <para>Defaults to <see langword="false" />.</para>
/// </summary>
public bool HasActivitiesEnabled { internal get; set; } = false;

/// <summary>
/// If <see cref="HasActivitiesEnabled" /> is <see langword="true" />, determines which handler type we use..
/// <para>Defaults to <see cref="ApplicationCommandHandlerType.DiscordLaunchActivity" />.</para>
/// </summary>
public ApplicationCommandHandlerType ActivityHandlerType { internal get; set; } = ApplicationCommandHandlerType.DiscordLaunchActivity;
}
72 changes: 53 additions & 19 deletions DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class DiscordApplicationCommand : SnowflakeObject, IEquatable<DiscordAppl
/// <param name="isNsfw">Whether this command is NSFW.</param>
/// <param name="allowedContexts">Where the command can be used.</param>
/// <param name="integrationTypes">The allowed integration types.</param>
/// <param name="handlerType">The handler type.</param>
public DiscordApplicationCommand(
string name,
string? description,
Expand All @@ -39,47 +40,80 @@ public DiscordApplicationCommand(
bool? dmPermission = null,
bool isNsfw = false,
List<InteractionContextType>? allowedContexts = null,
List<ApplicationCommandIntegrationTypes>? integrationTypes = null
List<ApplicationCommandIntegrationTypes>? 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;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class DiscordApplicationCommandLocalization
/// <summary>
/// Gets valid [locales](xref:modules_application_commands_translations_reference#valid-locales) for Discord.
/// </summary>
internal readonly List<string> 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<string> 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"];

/// <summary>
/// Initializes a new instance of <see cref="DiscordApplicationCommandLocalization" />.
Expand Down
2 changes: 1 addition & 1 deletion DisCatSharp/Net/Rest/DiscordApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6685,7 +6685,7 @@ internal async Task<DiscordApplicationCommand> 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(),
Expand Down
Loading