From bb51b8433dd35066a567af0a34cdeb29e5c77219 Mon Sep 17 00:00:00 2001 From: Tais993 Date: Sun, 20 Feb 2022 14:12:18 +0100 Subject: [PATCH 1/3] Added DiscordClientAction This allows the user to generate URL's which link to items in the Discord app (a channel, settings etc) --- .../togetherjava/tjbot/commands/Features.java | 5 +- .../commands/utils/DiscordClientAction.java | 242 ++++++++++++++++++ 2 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java 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..fabfa59b3d --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java @@ -0,0 +1,242 @@ +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; + *

+ */ +public class DiscordClientAction { + + /** + * Contains some of the more general actions + */ + public static class 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 static class Guild { + 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"); + + /** Beta Discord feature */ + public static final DiscordClientAction GUILD_HOME_CHANNEL = + new DiscordClientAction("discord://-/channels/{GUILD-ID}/@home"); + 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}"); + } + + /** + * Contains actions related to channels + */ + public static class 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 + */ + public static class Settings { + + /** + * Contains all user settings + */ + public static class 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 static class 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 static class 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 static class 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 static class 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 {} */ + public final static Pattern argumentPattern = Pattern.compile("\\{[^}]*}"); + + private final String url; + + @Contract(pure = true) + private DiscordClientAction(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(String @NotNull... arguments) { + String localUrl = url; + + for (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 String label, String... arguments) { + return Button.link(formatUrl(arguments), label); + } +} From cd888e18ab46402ff9558a239138abed1d5b6af8 Mon Sep 17 00:00:00 2001 From: Tais993 Date: Thu, 24 Feb 2022 23:33:51 +0100 Subject: [PATCH 2/3] Added example --- .../commands/utils/DiscordClientAction.java | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) 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 index fabfa59b3d..7d905f1fec 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java @@ -9,7 +9,7 @@ /** * 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. * @@ -19,6 +19,25 @@ *

  • iOS and Android are NOT supported
  • *
  • It opens the LAST installed Discord version (Discord, Canary, PTB)
  • * + * + *

    + * 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 class DiscordClientAction { @@ -58,13 +77,17 @@ public static class Guild { public static final DiscordClientAction GUILDS_CREATE = new DiscordClientAction("discord://-/guilds/create"); - /** Beta Discord feature */ - public static final DiscordClientAction GUILD_HOME_CHANNEL = - new DiscordClientAction("discord://-/channels/{GUILD-ID}/@home"); + 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"); } /** @@ -135,11 +158,15 @@ public static class App { public static final DiscordClientAction LOCALE = new DiscordClientAction("discord://-/settings/locale"); - /** @see #LINUX */ + /** + * @see #LINUX + */ public static final DiscordClientAction WINDOWS = new DiscordClientAction("discord://-/settings/windows"); - /** @see #WINDOWS */ + /** + * @see #WINDOWS + */ public static final DiscordClientAction LINUX = new DiscordClientAction("discord://-/settings/linux"); @@ -195,7 +222,6 @@ private DiscordClientAction(String url) { * argument is lacking. * * @return A {@link String} of the URL - * * @see #formatUrl(String...) */ public String getRawUrl() { @@ -206,9 +232,7 @@ public String getRawUrl() { * 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(String @NotNull... arguments) { @@ -231,9 +255,7 @@ public String formatUrl(String @NotNull... arguments) { * @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 String label, String... arguments) { From fb158c3d120021996aaf9e5a9e69818fad27b18a Mon Sep 17 00:00:00 2001 From: Tais993 Date: Fri, 25 Feb 2022 10:30:16 +0100 Subject: [PATCH 3/3] Fix linter issues --- .../commands/utils/DiscordClientAction.java | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) 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 index 7d905f1fec..4effea23c3 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/DiscordClientAction.java @@ -39,12 +39,14 @@ * * */ -public class DiscordClientAction { +public final class DiscordClientAction { /** * Contains some of the more general actions */ - public static class General { + public enum General { + ; + public static final DiscordClientAction HOME = new DiscordClientAction("discord://-/"); public static final DiscordClientAction FRIENDS = new DiscordClientAction("discord://-/"); @@ -66,7 +68,10 @@ public static class General { /** * Contains guild specific actions */ - public static class Guild { + public enum Guild { + ; + + @SuppressWarnings("squid:S1700") public static final DiscordClientAction GUILD = new DiscordClientAction("discord://-/channels/{GUILD-ID}"); public static final DiscordClientAction GUILD_CHANNEL = @@ -93,7 +98,9 @@ public static class Guild { /** * Contains actions related to channels */ - public static class Channels { + public enum Channels { + ; + public static final DiscordClientAction DM_CHANNEL = new DiscordClientAction("discord://-/channels/@me/{CHANNEL-ID}"); public static final DiscordClientAction DM_CHANNEL_MESSAGE = @@ -107,12 +114,21 @@ public static class Channels { /** * Contains actions related to the settings menu */ - public static class Settings { + /* + * 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 static class User { + public enum User { + ; + public static final DiscordClientAction ACCOUNT = new DiscordClientAction("discord://-/settings/account"); public static final DiscordClientAction PROFILE_CUSTOMIZATION = @@ -128,7 +144,9 @@ public static class User { /** * Contains all payment settings */ - public static class Payment { + public enum Payment { + ; + public static final DiscordClientAction PREMIUM = new DiscordClientAction("discord://-/settings/premium"); public static final DiscordClientAction SUBSCRIPTIONS = @@ -142,7 +160,9 @@ public static class Payment { /** * Contains all app settings */ - public static class App { + public enum App { + ; + public static final DiscordClientAction APPEARANCE = new DiscordClientAction("discord://-/settings/appearance"); public static final DiscordClientAction ACCESSIBILITY = @@ -179,7 +199,9 @@ public static class App { /** * Contains some of the more general settings */ - public static class General { + public enum General { + ; + public static final DiscordClientAction ACTIVITY_STATUS = new DiscordClientAction("discord://-/settings/activity-status"); public static final DiscordClientAction ACTIVITY_OVERLAY = @@ -191,7 +213,9 @@ public static class General { } } - public static class Library { + public enum Library { + ; + public static final DiscordClientAction LIBRARY_GAMES = new DiscordClientAction("discord://-/library"); public static final DiscordClientAction LIBRARY_SETTINGS = @@ -204,13 +228,15 @@ public static class Library { new DiscordClientAction("discord://-/store/applications/{APPLICATION-ID}"); } - /* pattern for the arguments, finds everything within {} */ - public final static Pattern argumentPattern = Pattern.compile("\\{[^}]*}"); + /** + * Pattern for the arguments, finds everything within brackets + */ + public static final Pattern argumentPattern = Pattern.compile("\\{[^}]*}"); private final String url; @Contract(pure = true) - private DiscordClientAction(String url) { + private DiscordClientAction(final String url) { this.url = url; } @@ -235,10 +261,10 @@ public String getRawUrl() { * @return The formatted URL as an {@link String} * @throws IllegalArgumentException When missing arguments */ - public String formatUrl(String @NotNull... arguments) { + public String formatUrl(final String @NotNull... arguments) { String localUrl = url; - for (String argument : arguments) { + for (final String argument : arguments) { localUrl = argumentPattern.matcher(localUrl).replaceFirst(argument); } @@ -258,7 +284,12 @@ public String formatUrl(String @NotNull... arguments) { * @return A {@link Button} of {@link ButtonStyle#LINK} with the given label * @throws IllegalArgumentException When missing arguments */ - public Button asLinkButton(@NotNull String label, String... 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 + '\'' + '}'; + } }