diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java index 7a00a2a8da..db7d86f85e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/Features.java @@ -2,10 +2,7 @@ import net.dv8tion.jda.api.JDA; import org.jetbrains.annotations.NotNull; -import org.togetherjava.tjbot.commands.basic.PingCommand; -import org.togetherjava.tjbot.commands.basic.RoleSelectCommand; -import org.togetherjava.tjbot.commands.basic.SuggestionsUpDownVoter; -import org.togetherjava.tjbot.commands.basic.VcActivityCommand; +import org.togetherjava.tjbot.commands.basic.*; import org.togetherjava.tjbot.commands.free.FreeCommand; import org.togetherjava.tjbot.commands.mathcommands.TeXCommand; import org.togetherjava.tjbot.commands.moderation.*; diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java new file mode 100644 index 0000000000..4effea23c3 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java @@ -0,0 +1,295 @@ +package org.togetherjava.tjbot.commands.utils; + +import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.ButtonStyle; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +/** + * Class which contains all actions a Discord client accepts. + *

+ * This allows you to open DM's {@link Channels#DM_CHANNEL}, specific settings + * {@link Settings.App#VOICE} and much more. + * + *

+ * A few notes; + *

+ * + *

+ * Example: + * + *

+ * 
+ * event.reply("Open Discord's secret home page!")
+ *      .addActionRow(DiscordClientAction.Guild.GUILD_HOME_CHANNEL.asLinkButton("Open home page!", event.getGuild().getId())
+ * 
+ * 
+ * + * To improve readability, one might want to use a static import like: + * + *
+ * 
+ * event.reply(whoIsCommandOutput)
+ *      .addActionRow(USER.asLinkButton("Open home page!", target.getId())
+ * 
+ * 
+ */ +public final class DiscordClientAction { + + /** + * Contains some of the more general actions + */ + public enum General { + ; + + public static final DiscordClientAction HOME = new DiscordClientAction("discord://-/"); + public static final DiscordClientAction FRIENDS = new DiscordClientAction("discord://-/"); + + public static final DiscordClientAction USER = + new DiscordClientAction("discord://-/users/{USER-ID}"); + public static final DiscordClientAction JOIN_INVITE = + new DiscordClientAction("discord://-/invite/{INVITE-CODE}"); + public static final DiscordClientAction HUB_MEMBERSHIP_SCREENING = + new DiscordClientAction("discord://-/member-verification-for-hub/{HUB-ID}"); + public static final DiscordClientAction STORE = + new DiscordClientAction("discord://-/store"); + + public static final DiscordClientAction HYPESQUAD = + new DiscordClientAction("discord://-/settings/hypesquad_online"); + public static final DiscordClientAction CHANGELOGS = + new DiscordClientAction("discord://-/settings/changelogs"); + } + + /** + * Contains guild specific actions + */ + public enum Guild { + ; + + @SuppressWarnings("squid:S1700") + public static final DiscordClientAction GUILD = + new DiscordClientAction("discord://-/channels/{GUILD-ID}"); + public static final DiscordClientAction GUILD_CHANNEL = + new DiscordClientAction("discord://-/channels/{GUILD-ID}/{CHANNEL-ID}"); + + public static final DiscordClientAction GUILD_DISCOVERY = + new DiscordClientAction("discord://-/guild-discovery"); + public static final DiscordClientAction GUILDS_CREATE = + new DiscordClientAction("discord://-/guilds/create"); + + + public static final DiscordClientAction GUILD_EVENT = + new DiscordClientAction("discord://-/events/{GUILD-ID}/{EVENT-ID}"); + public static final DiscordClientAction GUILD_MEMBERSHIP_SCREENING = + new DiscordClientAction("discord://-/member-verification/{GUILD-ID}"); + + /** + * Beta Discord feature + */ + public static final DiscordClientAction GUILD_HOME_CHANNEL = + new DiscordClientAction("discord://-/channels/{GUILD-ID}/@home"); + } + + /** + * Contains actions related to channels + */ + public enum Channels { + ; + + public static final DiscordClientAction DM_CHANNEL = + new DiscordClientAction("discord://-/channels/@me/{CHANNEL-ID}"); + public static final DiscordClientAction DM_CHANNEL_MESSAGE = + new DiscordClientAction("discord://-/channels/@me/{CHANNEL-ID}/{MESSAGE-ID}"); + public static final DiscordClientAction GUILD_CHANNEL = + new DiscordClientAction("discord://-/channels/{GUILD-ID}/{CHANNEL-ID}"); + public static final DiscordClientAction GUILD_CHANNEL_MESSAGE = new DiscordClientAction( + "discord://-/channels/{GUILD-ID}/{CHANNEL-ID}/{MESSAGE-ID}"); + } + + /** + * Contains actions related to the settings menu + */ + /* + * The warning is about this inner class being too long, and that it should be external This + * won't become an external class since it makes no sense for the design, and it requires the + * developer to remember all classes. + */ + @SuppressWarnings("squid:S2972") + public enum Settings { + ; + + /** + * Contains all user settings + */ + public enum User { + ; + + public static final DiscordClientAction ACCOUNT = + new DiscordClientAction("discord://-/settings/account"); + public static final DiscordClientAction PROFILE_CUSTOMIZATION = + new DiscordClientAction("discord://-/settings/profile-customization"); + public static final DiscordClientAction PRIVACY_AND_SAFETY = + new DiscordClientAction("discord://-/settings/privacy-and-safety"); + public static final DiscordClientAction AUTHORIZED_APPS = + new DiscordClientAction("discord://-/settings/authorized-apps"); + public static final DiscordClientAction CONNECTIONS = + new DiscordClientAction("discord://-/settings/connections"); + } + + /** + * Contains all payment settings + */ + public enum Payment { + ; + + public static final DiscordClientAction PREMIUM = + new DiscordClientAction("discord://-/settings/premium"); + public static final DiscordClientAction SUBSCRIPTIONS = + new DiscordClientAction("discord://-/settings/subscriptions"); + public static final DiscordClientAction INVENTORY = + new DiscordClientAction("discord://-/settings/inventory"); + public static final DiscordClientAction BILLING = + new DiscordClientAction("discord://-/settings/billing"); + } + + /** + * Contains all app settings + */ + public enum App { + ; + + public static final DiscordClientAction APPEARANCE = + new DiscordClientAction("discord://-/settings/appearance"); + public static final DiscordClientAction ACCESSIBILITY = + new DiscordClientAction("discord://-/settings/accessibility"); + public static final DiscordClientAction VOICE = + new DiscordClientAction("discord://-/settings/voice"); + public static final DiscordClientAction TEXT = + new DiscordClientAction("discord://-/settings/text"); + public static final DiscordClientAction NOTIFICATIONS = + new DiscordClientAction("discord://-/settings/notifications"); + public static final DiscordClientAction KEYBINDS = + new DiscordClientAction("discord://-/settings/keybinds"); + public static final DiscordClientAction LOCALE = + new DiscordClientAction("discord://-/settings/locale"); + + /** + * @see #LINUX + */ + public static final DiscordClientAction WINDOWS = + new DiscordClientAction("discord://-/settings/windows"); + + /** + * @see #WINDOWS + */ + public static final DiscordClientAction LINUX = + new DiscordClientAction("discord://-/settings/linux"); + + public static final DiscordClientAction STREAMER_MODE = + new DiscordClientAction("discord://-/settings/streamer-mode"); + public static final DiscordClientAction ADVANCED = + new DiscordClientAction("discord://-/settings/advanced"); + } + + /** + * Contains some of the more general settings + */ + public enum General { + ; + + public static final DiscordClientAction ACTIVITY_STATUS = + new DiscordClientAction("discord://-/settings/activity-status"); + public static final DiscordClientAction ACTIVITY_OVERLAY = + new DiscordClientAction("discord://-/settings/overlay"); + public static final DiscordClientAction HYPESQUAD = + new DiscordClientAction("discord://-/settings/hypesquad_online"); + public static final DiscordClientAction CHANGELOGS = + new DiscordClientAction("discord://-/settings/changelogs"); + } + } + + public enum Library { + ; + + public static final DiscordClientAction LIBRARY_GAMES = + new DiscordClientAction("discord://-/library"); + public static final DiscordClientAction LIBRARY_SETTINGS = + new DiscordClientAction("discord://-/library/settings"); + public static final DiscordClientAction LIBRARY_ITEM_ACTION = + new DiscordClientAction("discord://-/library/{SKU-ID}/LAUNCH"); + public static final DiscordClientAction SKU_STORE_PAGE = + new DiscordClientAction("discord://-/store/skus/{SKU-ID}"); + public static final DiscordClientAction APPLICATION_STORE_PAGE = + new DiscordClientAction("discord://-/store/applications/{APPLICATION-ID}"); + } + + /** + * Pattern for the arguments, finds everything within brackets + */ + public static final Pattern argumentPattern = Pattern.compile("\\{[^}]*}"); + + private final String url; + + @Contract(pure = true) + private DiscordClientAction(final String url) { + this.url = url; + } + + /** + * The raw URL without any arguments. + * + *

+ * Most likely you should use {@link #formatUrl(String...)} instead, that one throws when an + * argument is lacking. + * + * @return A {@link String} of the URL + * @see #formatUrl(String...) + */ + public String getRawUrl() { + return url; + } + + /** + * Format's the URL with the given arguments. + * + * @param arguments An array of the arguments this action requires + * @return The formatted URL as an {@link String} + * @throws IllegalArgumentException When missing arguments + */ + public String formatUrl(final String @NotNull... arguments) { + String localUrl = url; + + for (final String argument : arguments) { + localUrl = argumentPattern.matcher(localUrl).replaceFirst(argument); + } + + if (argumentPattern.matcher(localUrl).find()) { + throw new IllegalArgumentException("Missing arguments for URL " + localUrl + "!"); + } + + return localUrl; + } + + /** + * Format's the action as a link button. + * + * @param label The label of the button, see {@link Button#link(String, String)} for the + * requirements + * @param arguments An array of the arguments this action requires + * @return A {@link Button} of {@link ButtonStyle#LINK} with the given label + * @throws IllegalArgumentException When missing arguments + */ + public Button asLinkButton(@NotNull final String label, final String... arguments) { + return Button.link(formatUrl(arguments), label); + } + + @Override + public String toString() { + return "DiscordClientAction{" + "url='" + url + '\'' + '}'; + } +}