From 05acdde63951db6250344dad61f26bfd56f2fe65 Mon Sep 17 00:00:00 2001 From: RGoyalmc <76780190+RGoyalmc@users.noreply.github.com> Date: Tue, 11 Jan 2022 19:28:25 +0530 Subject: [PATCH 1/4] added component support --- .../discord/webhook/send/WebhookMessage.java | 25 +++- .../webhook/send/WebhookMessageBuilder.java | 55 ++++++++- .../send/component/ActionComponent.java | 9 ++ .../webhook/send/component/ActionRow.java | 51 ++++++++ .../webhook/send/component/Button.java | 113 ++++++++++++++++++ .../webhook/send/component/Component.java | 9 ++ .../send/component/LayoutComponent.java | 11 ++ .../webhook/send/component/PartialEmoji.java | 47 ++++++++ .../webhook/send/component/SelectMenu.java | 64 ++++++++++ .../webhook/send/component/SelectOption.java | 74 ++++++++++++ 10 files changed, 450 insertions(+), 8 deletions(-) create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/Button.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/Component.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java diff --git a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java index b412643..36323a7 100644 --- a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java +++ b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java @@ -19,6 +19,7 @@ import club.minnced.discord.webhook.IOUtil; import club.minnced.discord.webhook.MessageFlags; import club.minnced.discord.webhook.receive.ReadonlyMessage; +import club.minnced.discord.webhook.send.component.LayoutComponent; import okhttp3.MultipartBody; import okhttp3.RequestBody; import org.jetbrains.annotations.NotNull; @@ -46,19 +47,21 @@ public class WebhookMessage { protected final String username, avatarUrl, content; protected final List embeds; + protected final List components; protected final boolean isTTS; protected final MessageAttachment[] attachments; protected final AllowedMentions allowedMentions; protected final int flags; protected WebhookMessage(final String username, final String avatarUrl, final String content, - final List embeds, final boolean isTTS, + final List embeds, List components, final boolean isTTS, final MessageAttachment[] files, final AllowedMentions allowedMentions, final int flags) { this.username = username; this.avatarUrl = avatarUrl; this.content = content; this.embeds = embeds; + this.components = components; this.isTTS = isTTS; this.attachments = files; this.allowedMentions = allowedMentions; @@ -149,7 +152,7 @@ public WebhookMessage asEphemeral(boolean ephemeral) { flags |= MessageFlags.EPHEMERAL; else flags &= ~MessageFlags.EPHEMERAL; - return new WebhookMessage(username, avatarUrl, content, embeds, isTTS, attachments, allowedMentions, flags); + return new WebhookMessage(username, avatarUrl, content, embeds, components, isTTS, attachments, allowedMentions, flags); } /** @@ -205,7 +208,7 @@ public static WebhookMessage embeds(@NotNull WebhookEmbed first, @NotNull Webhoo List list = new ArrayList<>(1 + embeds.length); list.add(first); Collections.addAll(list, embeds); - return new WebhookMessage(null, null, null, list, false, null, AllowedMentions.all(), 0); + return new WebhookMessage(null, null, null, list, null, false, null, AllowedMentions.all(), 0); } /** @@ -230,7 +233,7 @@ public static WebhookMessage embeds(@NotNull Collection embeds) { if (embeds.isEmpty()) throw new IllegalArgumentException("Cannot build an empty message"); embeds.forEach(Objects::requireNonNull); - return new WebhookMessage(null, null, null, new ArrayList<>(embeds), false, null, AllowedMentions.all(), 0); + return new WebhookMessage(null, null, null, new ArrayList<>(embeds), null, false, null, AllowedMentions.all(), 0); } /** @@ -267,7 +270,7 @@ public static WebhookMessage files(@NotNull Map attachments) { Object data = attachment.getValue(); files[i++] = convertAttachment(name, data); } - return new WebhookMessage(null, null, null, null, false, files, AllowedMentions.all(), 0); + return new WebhookMessage(null, null, null, null, null, false, files, AllowedMentions.all(), 0); } /** @@ -313,7 +316,7 @@ public static WebhookMessage files(@NotNull String name1, @NotNull Object data1, throw new IllegalArgumentException("Provided arguments must be pairs for (String, Data). Expected String and found " + (name == null ? null : name.getClass().getName())); files[j] = convertAttachment((String) name, data); } - return new WebhookMessage(null, null, null, null, false, files, AllowedMentions.all(), 0); + return new WebhookMessage(null, null, null, null, null, false, files, AllowedMentions.all(), 0); } /** @@ -344,6 +347,15 @@ public RequestBody getBody() { } else { payload.put("embeds", new JSONArray()); } + if (components != null && !components.isEmpty()) { + final JSONArray array = new JSONArray(); + for (LayoutComponent component : components) { + array.put(component); + } + payload.put("components", array); + } else { + payload.put("components", new JSONArray()); + } if (avatarUrl != null) payload.put("avatar_url", avatarUrl); if (username != null) @@ -363,6 +375,7 @@ public RequestBody getBody() { } return builder.addFormDataPart("payload_json", json).build(); } + System.out.println("json.toString() = " + json.toString()); return RequestBody.create(IOUtil.JSON, json); } diff --git a/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java b/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java index ae09591..000eb45 100644 --- a/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java +++ b/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java @@ -17,6 +17,7 @@ package club.minnced.discord.webhook.send; import club.minnced.discord.webhook.MessageFlags; +import club.minnced.discord.webhook.send.component.LayoutComponent; import discord4j.core.spec.MessageCreateSpec; import discord4j.core.spec.MessageEditSpec; import discord4j.discordjson.json.AllowedMentionsData; @@ -47,6 +48,7 @@ public class WebhookMessageBuilder { protected final StringBuilder content = new StringBuilder(); protected final List embeds = new LinkedList<>(); + protected final List components = new ArrayList<>(); protected final MessageAttachment[] files = new MessageAttachment[WebhookMessage.MAX_FILES]; protected AllowedMentions allowedMentions = AllowedMentions.all(); protected String username, avatarUrl; @@ -60,7 +62,7 @@ public class WebhookMessageBuilder { * @return True, if this builder is empty */ public boolean isEmpty() { - return content.length() == 0 && embeds.isEmpty() && getFileAmount() == 0; + return content.length() == 0 && embeds.isEmpty() && components.isEmpty() && getFileAmount() == 0; } /** @@ -182,6 +184,55 @@ public WebhookMessageBuilder addEmbeds(@NotNull Collection 5) + throw new IllegalStateException("Cannot have more than 5 action rows in a message"); + for (LayoutComponent component : components) { + Objects.requireNonNull(component, "Component"); + this.components.add(component); + } + return this; + } + + /** + * Adds the provided embeds to the builder + * + * @param components + * The layout components to add + * + * @return This builder for chaining convenience + * + * @throws java.lang.NullPointerException + * If provided with null + * @throws java.lang.IllegalStateException + * If more than {@value LayoutComponent#MAX_COMPONENTS} are added + */ + @NotNull + public WebhookMessageBuilder addComponents(Collection components) { + Objects.requireNonNull(components, "Components"); + if (this.components.size() + components.size() > 5) + throw new IllegalStateException("Cannot have more than 5 action rows in a message"); + for (LayoutComponent component : components) { + Objects.requireNonNull(component, "Component"); + this.components.add(component); + } + return this; + } /** * Configures the content for this builder * @@ -408,7 +459,7 @@ public WebhookMessageBuilder addFile(@NotNull String name, @NotNull InputStream public WebhookMessage build() { if (isEmpty()) throw new IllegalStateException("Cannot build an empty message!"); - return new WebhookMessage(username, avatarUrl, content.toString(), embeds, isTTS, + return new WebhookMessage(username, avatarUrl, content.toString(), embeds, components, isTTS, fileIndex == 0 ? null : Arrays.copyOf(files, fileIndex), allowedMentions, flags); } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java new file mode 100644 index 0000000..ce09be5 --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java @@ -0,0 +1,9 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.Nullable; + +public interface ActionComponent extends Component { + + @Nullable String getCustomID(); + +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java new file mode 100644 index 0000000..38f699a --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java @@ -0,0 +1,51 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ActionRow implements LayoutComponent { + + private final List components; + + public ActionRow(@NotNull List components) { + int buttonCount = 0; + boolean hasSelectMenu = false; + for (ActionComponent component : components) { + if (component instanceof Button) buttonCount++; + if (component instanceof SelectMenu) hasSelectMenu = true; + } + if (hasSelectMenu && components.size() > 1) + throw new IllegalStateException("An action row containing a select menu cant have have more than 1 component"); + if (buttonCount > 5) + throw new IllegalStateException("An action row cant contain more than 5 buttons"); + this.components = components; + } + + public static ActionRow of(ActionComponent... components) { + if (components == null || components.length == 0) + return new ActionRow(new ArrayList<>()); + return new ActionRow(Arrays.asList(components)); + } + + @Override + public int getType() { + return 1; + } + + @Override + public List getComponents() { + return this.components; + } + + @Override + public String toJSONString() { + JSONObject json = new JSONObject(); + json.put("type", this.getType()); + json.put("components", this.components); + return json.toString(); + } +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/Button.java b/src/main/java/club/minnced/discord/webhook/send/component/Button.java new file mode 100644 index 0000000..8a4ea64 --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/Button.java @@ -0,0 +1,113 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.json.JSONPropertyIgnore; + +import java.net.URL; + +public class Button implements ActionComponent { + + private final ButtonStyle style; + private final String label, customID, url; + private final boolean disabled; + private PartialEmoji emoji; + + public Button(@NotNull ButtonStyle style, @NotNull String label, @Nullable String customID, @Nullable String url, boolean disabled) { + this.style = style; + this.label = label; + this.customID = customID; + this.url = url; + this.disabled = disabled; + } + + public static Button primary(@NotNull String customID, @NotNull String label) { + return new Button(ButtonStyle.PRIMARY, label, customID, null, false); + } + + public static Button success(@NotNull String customID, @NotNull String label) { + return new Button(ButtonStyle.SUCCESS, label, customID, null, false); + } + + public static Button secondary(@NotNull String customID, @NotNull String label) { + return new Button(ButtonStyle.SECONDARY, label, customID, null, false); + } + + public static Button destructive(@NotNull String customID, @NotNull String label) { + return new Button(ButtonStyle.DANGER, label, customID, null, false); + } + + public static Button link(@NotNull URL url, @NotNull String label) { + return new Button(ButtonStyle.LINK, label, null, url.toString(), false); + } + + public Button withEmoji(@NotNull PartialEmoji emoji) { + this.emoji = emoji; + return this; + } + + @NotNull + @JSONPropertyIgnore + public ButtonStyle getStyle() { + return style; + } + + @NotNull + @JSONPropertyIgnore + public String getLabel() { + return label; + } + + @Nullable + @JSONPropertyIgnore + @Override + public String getCustomID() { + return customID; + } + + @Nullable + @JSONPropertyIgnore + public String getUrl() { + return url; + } + + @JSONPropertyIgnore + public boolean isDisabled() { + return disabled; + } + + @Override + public String toJSONString() { + JSONObject json = new JSONObject(); + json.put("type", this.getType()); + json.put("style", this.style.VALUE); + json.put("label", this.label); + if (this.customID != null) + json.put("custom_id", this.customID); + if (this.url != null) + json.put("url", this.url); + json.put("disabled", this.disabled); + json.put("emoji", this.emoji); + return json.toString(); + } + + @Override + public int getType() { + return 2; + } + + public enum ButtonStyle { + PRIMARY(1), + SECONDARY(2), + SUCCESS(3), + DANGER(4), + LINK(5); + + ButtonStyle(int value) { + this.VALUE = value; + } + + public final int VALUE; + } +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/Component.java b/src/main/java/club/minnced/discord/webhook/send/component/Component.java new file mode 100644 index 0000000..a8e6216 --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/Component.java @@ -0,0 +1,9 @@ +package club.minnced.discord.webhook.send.component; + +import org.json.JSONString; + +public interface Component extends JSONString { + + int getType(); + +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java b/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java new file mode 100644 index 0000000..2622cad --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java @@ -0,0 +1,11 @@ +package club.minnced.discord.webhook.send.component; + +import java.util.List; + +public interface LayoutComponent extends Component { + + int MAX_COMPONENTS = 5; + + List getComponents(); + +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java b/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java new file mode 100644 index 0000000..fae81e2 --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java @@ -0,0 +1,47 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; +import org.json.JSONPropertyIgnore; +import org.json.JSONString; + +public class PartialEmoji implements JSONString { + + private final String name; + private final long id; + private final boolean animated; + + public PartialEmoji(@NotNull String name, @NotNull String id, boolean animated) { + this(name, Long.parseLong(id), animated); + } + + public PartialEmoji(@NotNull String name, long id, boolean animated) { + this.name = name; + this.id = id; + this.animated = animated; + } + + @JSONPropertyIgnore + public String getName() { + return name; + } + + @JSONPropertyIgnore + public long getId() { + return id; + } + + @JSONPropertyIgnore + public boolean isAnimated() { + return animated; + } + + @Override + public String toJSONString() { + JSONObject json = new JSONObject(); + json.put("id", this.id); + json.put("name", this.name); + json.put("animated", this.animated); + return json.toString(); + } +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java b/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java new file mode 100644 index 0000000..4d57696 --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java @@ -0,0 +1,64 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class SelectMenu implements ActionComponent{ + + private final String customID; + private final List options; + private final String placeholder; + private final int minValues; + private final int maxValues; + private final boolean disabled; + + public SelectMenu(@NotNull String customID, @NotNull Collection options, @Nullable String placeholder, int minValues, int maxValues, boolean disabled) { + this.customID = customID; + this.options = new ArrayList<>(options); + this.placeholder = placeholder; + this.minValues = minValues; + this.maxValues = maxValues; + this.disabled = disabled; + } + + public static SelectMenu of(@NotNull String customID, @NotNull SelectOption... options) { + return new SelectMenu(customID, Arrays.asList(options), null, 1, 1, false); + } + + public static SelectMenu of(@NotNull String customID, @Nullable String placeholder, @NotNull SelectOption... options) { + return new SelectMenu(customID, Arrays.asList(options), placeholder, 1, 1, false); + + } + + @Nullable + @Override + public String getCustomID() { + return customID; + } + + @Override + public int getType() { + return 3; + } + + @Override + public String toJSONString() { + JSONObject json = new JSONObject(); + json.put("type", this.getType()); + json.put("custom_id", this.customID); + json.put("options", this.options); + if (placeholder != null) + json.put("placeholder", this.placeholder); + json.put("min_values", this.minValues); + json.put("max_values", this.maxValues); + json.put("disabled", this.disabled); + return json.toString(); + } + +} diff --git a/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java b/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java new file mode 100644 index 0000000..3c2844c --- /dev/null +++ b/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java @@ -0,0 +1,74 @@ +package club.minnced.discord.webhook.send.component; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.json.JSONPropertyIgnore; +import org.json.JSONString; + +public class SelectOption implements JSONString { + + private final String label; + private final String value; + private final String description; + private PartialEmoji emoji; + private final boolean def; + + public SelectOption(@NotNull String label, @NotNull String value, @Nullable String description, boolean def) { + this.label = label; + this.value = value; + this.description = description; + this.def = def; + } + + public static SelectOption of(@NotNull String label, @NotNull String value) { + return new SelectOption(label, value, null, false); + } + + public SelectOption withEmoji(PartialEmoji emoji) { + this.emoji = emoji; + return this; + } + + @NotNull + @JSONPropertyIgnore + public String getLabel() { + return label; + } + + @NotNull + @JSONPropertyIgnore + public String getValue() { + return value; + } + + @Nullable + @JSONPropertyIgnore + public String getDescription() { + return description; + } + + @Nullable + @JSONPropertyIgnore + public PartialEmoji getEmoji() { + return emoji; + } + + @JSONPropertyIgnore + public boolean isDef() { + return def; + } + + @Override + public String toJSONString() { + JSONObject json = new JSONObject(); + json.put("label", this.label); + json.put("value", this.value); + if (this.description != null) + json.put("description", this.description); + if (this.emoji != null) + json.put("emoji", this.emoji); + json.put("default", this.def); + return json.toString(); + } +} From f5f68c33c1c3c02257fa95e53a252e3cfa6e3915 Mon Sep 17 00:00:00 2001 From: RGoyalmc <76780190+RGoyalmc@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:14:07 +0530 Subject: [PATCH 2/4] added java docs --- .../send/component/ActionComponent.java | 7 +++ .../webhook/send/component/ActionRow.java | 18 +++++++ .../webhook/send/component/Button.java | 53 +++++++++++++++++++ .../webhook/send/component/Component.java | 7 +++ .../send/component/LayoutComponent.java | 6 +++ .../webhook/send/component/PartialEmoji.java | 12 +++++ .../webhook/send/component/SelectMenu.java | 13 +++++ .../webhook/send/component/SelectOption.java | 22 ++++++++ 8 files changed, 138 insertions(+) diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java index ce09be5..79714fa 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java @@ -2,8 +2,15 @@ import org.jetbrains.annotations.Nullable; +/** + * Components that can be inserted inside layout components (buttons & select menus) + */ public interface ActionComponent extends Component { + /** + * The dev-defined id of the component + * @return dev-defined id of the component. Nullable for link style buttons + */ @Nullable String getCustomID(); } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java index 38f699a..1a808d5 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java @@ -11,6 +11,15 @@ public class ActionRow implements LayoutComponent { private final List components; + /** + * Creates an action row with the given list of components + * @param components The components to be added in action row + * + * @throws IllegalStateException + * If a select menu is added with any other component + * @throws IllegalStateException + * If more than 5 buttons are added in the same action row + */ public ActionRow(@NotNull List components) { int buttonCount = 0; boolean hasSelectMenu = false; @@ -25,6 +34,15 @@ public ActionRow(@NotNull List components) { this.components = components; } + /** + * Creates an action row with the given components + * @param components The components to be added in action row + * + * @throws IllegalStateException + * If a select menu is added with any other component + * @throws IllegalStateException + * If more than 5 buttons are added in the same action row + */ public static ActionRow of(ActionComponent... components) { if (components == null || components.length == 0) return new ActionRow(new ArrayList<>()); diff --git a/src/main/java/club/minnced/discord/webhook/send/component/Button.java b/src/main/java/club/minnced/discord/webhook/send/component/Button.java index 8a4ea64..d332ba3 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/Button.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/Button.java @@ -22,43 +22,87 @@ public Button(@NotNull ButtonStyle style, @NotNull String label, @Nullable Strin this.disabled = disabled; } + /** + * A primary style button with the provided id and label + * @param customID dev-defined id + * @param label text on button + * @return A primary style button with the provided id and label + */ public static Button primary(@NotNull String customID, @NotNull String label) { return new Button(ButtonStyle.PRIMARY, label, customID, null, false); } + /** + * A success style button with the provided id and label + * @param customID dev-defined id + * @param label text on button + * @return A success style button with the provided id and label + */ public static Button success(@NotNull String customID, @NotNull String label) { return new Button(ButtonStyle.SUCCESS, label, customID, null, false); } + /** + * A secondary style button with the provided id and label + * @param customID dev-defined id + * @param label text on button + * @return A secondary style button with the provided id and label + */ public static Button secondary(@NotNull String customID, @NotNull String label) { return new Button(ButtonStyle.SECONDARY, label, customID, null, false); } + /** + * A destructive style button with the provided id and label + * @param customID dev-defined id + * @param label text on button + * @return A destructive style button with the provided id and label + */ public static Button destructive(@NotNull String customID, @NotNull String label) { return new Button(ButtonStyle.DANGER, label, customID, null, false); } + /** + * A link style button with the provided id and label + * @param url The URL to link + * @param label text on button + * @return A link style button with the provided link and label + */ public static Button link(@NotNull URL url, @NotNull String label) { return new Button(ButtonStyle.LINK, label, null, url.toString(), false); } + /** + * Adds an emoji to the button. Replaces it if it already exists + * @param emoji the emoji to add + * @return this instance of button + */ public Button withEmoji(@NotNull PartialEmoji emoji) { this.emoji = emoji; return this; } + /** + * @return The style of the button + */ @NotNull @JSONPropertyIgnore public ButtonStyle getStyle() { return style; } + /** + * @return The label/button text of the button + */ @NotNull @JSONPropertyIgnore public String getLabel() { return label; } + /** + * @return The dev-defined id of the button + */ @Nullable @JSONPropertyIgnore @Override @@ -66,12 +110,18 @@ public String getCustomID() { return customID; } + /** + * @return The url the button links to + */ @Nullable @JSONPropertyIgnore public String getUrl() { return url; } + /** + * @return If the button is disabled + */ @JSONPropertyIgnore public boolean isDisabled() { return disabled; @@ -108,6 +158,9 @@ public enum ButtonStyle { this.VALUE = value; } + /** + * Discord defined value for the button styles + */ public final int VALUE; } } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/Component.java b/src/main/java/club/minnced/discord/webhook/send/component/Component.java index a8e6216..9a8ea99 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/Component.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/Component.java @@ -4,6 +4,13 @@ public interface Component extends JSONString { + /** + * The type of the component + *

(1 for action row) + *

(2 for button) + *

(3 for select menu) + * @return The type of the component + */ int getType(); } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java b/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java index 2622cad..2fc6dc6 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/LayoutComponent.java @@ -4,8 +4,14 @@ public interface LayoutComponent extends Component { + /** + * Maximum allowed layout components (action rows) in a message + */ int MAX_COMPONENTS = 5; + /** + * @return The action components (buttons and select menus) + */ List getComponents(); } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java b/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java index fae81e2..b8feef5 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/PartialEmoji.java @@ -11,10 +11,22 @@ public class PartialEmoji implements JSONString { private final long id; private final boolean animated; + /** + * Emoji with only necessary features required by webhooks + * @param name the name of the emoji + * @param id the id of the emoji + * @param animated if the emoji is animated + */ public PartialEmoji(@NotNull String name, @NotNull String id, boolean animated) { this(name, Long.parseLong(id), animated); } + /** + * Emoji with only necessary features required by webhooks + * @param name the name of the emoji + * @param id the id of the emoji + * @param animated if the emoji is animated + */ public PartialEmoji(@NotNull String name, long id, boolean animated) { this.name = name; this.id = id; diff --git a/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java b/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java index 4d57696..6c9665a 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/SelectMenu.java @@ -27,10 +27,23 @@ public SelectMenu(@NotNull String customID, @NotNull Collection op this.disabled = disabled; } + /** + * A select menu with the defined id and options + * @param customID the dev-defined id + * @param options the options in the dropdown menu + * @return A select menu with the defined id and options + */ public static SelectMenu of(@NotNull String customID, @NotNull SelectOption... options) { return new SelectMenu(customID, Arrays.asList(options), null, 1, 1, false); } + /** + * A select menu with the defined id, placeholder and options + * @param customID the dev-defined id + * @param placeholder the placeholder text if nothing is selected + * @param options the options in the dropdown menu + * @return A select menu with the defined id, placeholder and options + */ public static SelectMenu of(@NotNull String customID, @Nullable String placeholder, @NotNull SelectOption... options) { return new SelectMenu(customID, Arrays.asList(options), placeholder, 1, 1, false); diff --git a/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java b/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java index 3c2844c..29c376b 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/SelectOption.java @@ -21,10 +21,32 @@ public SelectOption(@NotNull String label, @NotNull String value, @Nullable Stri this.def = def; } + /** + * A SelectOption with the provided label and value + * @param label The label for the option + * @param value The dev-defined value for the option + * @return A SelectOption with the provided label and value + */ public static SelectOption of(@NotNull String label, @NotNull String value) { return new SelectOption(label, value, null, false); } + /** + * A SelectOption with the provided label, value and description + * @param label The label for the option + * @param value The dev-defined value for the option + * @param description The description for the option + * @return A SelectOption with the provided label and value and description + */ + public static SelectOption of(@NotNull String label, @NotNull String value, String description) { + return new SelectOption(label, value, description, false); + } + + /** + * Adds an emoji to the option. Replaces it if it already exists + * @param emoji the emoji to add + * @return this instance of option + */ public SelectOption withEmoji(PartialEmoji emoji) { this.emoji = emoji; return this; From e0534286c438ec9fe0f7dfc98852698039aad9e6 Mon Sep 17 00:00:00 2001 From: RGoyalmc <76780190+RGoyalmc@users.noreply.github.com> Date: Sat, 22 Jan 2022 19:40:16 +0530 Subject: [PATCH 3/4] Improved javadocs Added missing annotations Added SingleEmojiContainer --- .../discord/webhook/send/WebhookMessage.java | 1 - .../webhook/send/WebhookMessageBuilder.java | 14 +- .../send/component/ActionComponent.java | 38 +++- .../webhook/send/component/ActionRow.java | 89 ++++++--- .../webhook/send/component/Button.java | 176 ++++++++++++------ .../webhook/send/component/Component.java | 48 ++++- .../send/component/LayoutComponent.java | 40 +++- .../webhook/send/component/PartialEmoji.java | 80 ++++++-- .../webhook/send/component/SelectMenu.java | 40 +++- .../webhook/send/component/SelectOption.java | 72 ++++--- .../send/component/SingleEmojiContainer.java | 37 ++++ 11 files changed, 486 insertions(+), 149 deletions(-) create mode 100644 src/main/java/club/minnced/discord/webhook/send/component/SingleEmojiContainer.java diff --git a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java index 36323a7..e3612a5 100644 --- a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java +++ b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java @@ -375,7 +375,6 @@ public RequestBody getBody() { } return builder.addFormDataPart("payload_json", json).build(); } - System.out.println("json.toString() = " + json.toString()); return RequestBody.create(IOUtil.JSON, json); } diff --git a/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java b/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java index 000eb45..e30a46c 100644 --- a/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java +++ b/src/main/java/club/minnced/discord/webhook/send/WebhookMessageBuilder.java @@ -62,7 +62,7 @@ public class WebhookMessageBuilder { * @return True, if this builder is empty */ public boolean isEmpty() { - return content.length() == 0 && embeds.isEmpty() && components.isEmpty() && getFileAmount() == 0; + return content.length() == 0 && embeds.isEmpty() && getFileAmount() == 0; } /** @@ -198,10 +198,10 @@ public WebhookMessageBuilder addEmbeds(@NotNull Collection 5) - throw new IllegalStateException("Cannot have more than 5 action rows in a message"); + if (this.components.size() + components.length > LayoutComponent.MAX_COMPONENTS) + throw new IllegalStateException("Cannot have more than " + LayoutComponent.MAX_COMPONENTS + " component layouts in a message"); for (LayoutComponent component : components) { Objects.requireNonNull(component, "Component"); this.components.add(component); @@ -223,10 +223,10 @@ public WebhookMessageBuilder addComponents(LayoutComponent... components) { * If more than {@value LayoutComponent#MAX_COMPONENTS} are added */ @NotNull - public WebhookMessageBuilder addComponents(Collection components) { + public WebhookMessageBuilder addComponents(@NotNull Collection components) { Objects.requireNonNull(components, "Components"); - if (this.components.size() + components.size() > 5) - throw new IllegalStateException("Cannot have more than 5 action rows in a message"); + if (this.components.size() + components.size() > LayoutComponent.MAX_COMPONENTS) + throw new IllegalStateException("Cannot have more than " + LayoutComponent.MAX_COMPONENTS + " component layouts in a message"); for (LayoutComponent component : components) { Objects.requireNonNull(component, "Component"); this.components.add(component); diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java index 79714fa..7224e03 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionComponent.java @@ -1,16 +1,48 @@ +/* + * Copyright 2018-2020 Florian Spieß + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package club.minnced.discord.webhook.send.component; import org.jetbrains.annotations.Nullable; /** - * Components that can be inserted inside layout components (buttons & select menus) + * Interactive components that can be inserted inside a {@link LayoutComponent} + * + * @see LayoutComponent#addComponent(ActionComponent) */ public interface ActionComponent extends Component { /** - * The dev-defined id of the component - * @return dev-defined id of the component. Nullable for link style buttons + * The custom id of the component. + *
This can be used for handling interactions with this component. + * + * @return Custom id of the component, or null for link style buttons */ @Nullable String getCustomID(); + /** + * Changes the disabled status of button + * @param disabled + * use true to disable button + */ + void withDisabled(boolean disabled); + + /** + * @return true if the button is disabled + */ + boolean isDisabled(); + } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java index 1a808d5..f4854e2 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/ActionRow.java @@ -1,64 +1,95 @@ +/* + * Copyright 2018-2020 Florian Spieß + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package club.minnced.discord.webhook.send.component; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONObject; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; public class ActionRow implements LayoutComponent { private final List components; + private ActionRow(@NotNull List components) { + validate(components); + this.components = components; + } + /** * Creates an action row with the given list of components - * @param components The components to be added in action row * + * @param components + * The components to be added to the action row * @throws IllegalStateException * If a select menu is added with any other component * @throws IllegalStateException - * If more than 5 buttons are added in the same action row + * If more than {@value LayoutComponent#MAX_COMPONENTS} buttons are added in the same action row + * @return An action row containing provided components, or null if the collection is empty or null */ - public ActionRow(@NotNull List components) { - int buttonCount = 0; - boolean hasSelectMenu = false; - for (ActionComponent component : components) { - if (component instanceof Button) buttonCount++; - if (component instanceof SelectMenu) hasSelectMenu = true; - } - if (hasSelectMenu && components.size() > 1) - throw new IllegalStateException("An action row containing a select menu cant have have more than 1 component"); - if (buttonCount > 5) - throw new IllegalStateException("An action row cant contain more than 5 buttons"); - this.components = components; + @Nullable + public ActionRow of(@NotNull Collection components) { + if (components.size() == 0) return null; + return new ActionRow(new ArrayList<>(components)); } /** - * Creates an action row with the given components - * @param components The components to be added in action row + * Creates an action row with the given list of components * + * @param components + * The components to be added to the action row * @throws IllegalStateException * If a select menu is added with any other component * @throws IllegalStateException - * If more than 5 buttons are added in the same action row + * If more than {@value LayoutComponent#MAX_COMPONENTS} buttons are added in the same action row + * @return An action row containing provided components, or null if the provided array is empty or null */ - public static ActionRow of(ActionComponent... components) { - if (components == null || components.length == 0) - return new ActionRow(new ArrayList<>()); + @Nullable + public static ActionRow of(@NotNull ActionComponent... components) { + if (components == null || components.length == 0) return null; return new ActionRow(Arrays.asList(components)); } @Override - public int getType() { - return 1; + @NotNull + public Type getType() { + return Type.ACTION_ROW; } @Override + @NotNull public List getComponents() { return this.components; } + @Override + @NotNull + public LayoutComponent addComponent(ActionComponent component) { + List newList = new ArrayList<>(this.components); + newList.add(component); + validate(newList); + this.components.add(component); + return this; + } + @Override public String toJSONString() { JSONObject json = new JSONObject(); @@ -66,4 +97,18 @@ public String toJSONString() { json.put("components", this.components); return json.toString(); } + + private void validate(List components) { + int buttonCount = 0; + boolean hasSelectMenu = false; + for (ActionComponent component : components) { + if (component instanceof Button) buttonCount++; + else if (component instanceof SelectMenu) hasSelectMenu = true; + else throw new IllegalArgumentException("Provided component not an instance of Button or SelectMenu"); + } + if (hasSelectMenu && components.size() > 1) + throw new IllegalArgumentException("An action row containing a select menu cant have have more than 1 component"); + if (buttonCount > Button.MAX_BUTTONS) + throw new IllegalArgumentException("An action row cannot contain more than " + Button.MAX_BUTTONS + " buttons"); + } } diff --git a/src/main/java/club/minnced/discord/webhook/send/component/Button.java b/src/main/java/club/minnced/discord/webhook/send/component/Button.java index d332ba3..1be7b23 100644 --- a/src/main/java/club/minnced/discord/webhook/send/component/Button.java +++ b/src/main/java/club/minnced/discord/webhook/send/component/Button.java @@ -1,20 +1,43 @@ +/* + * Copyright 2018-2020 Florian Spieß + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package club.minnced.discord.webhook.send.component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; -import org.json.JSONPropertyIgnore; import java.net.URL; -public class Button implements ActionComponent { +/** + * Button components that can be placed inside a {@link LayoutComponent} + * + * @see LayoutComponent#addComponent(ActionComponent) + */ + +public class Button implements ActionComponent, SingleEmojiContainer