diff --git a/src/main/java/com/seailz/discordjar/DiscordJar.java b/src/main/java/com/seailz/discordjar/DiscordJar.java index 10ff9979..36f921cd 100644 --- a/src/main/java/com/seailz/discordjar/DiscordJar.java +++ b/src/main/java/com/seailz/discordjar/DiscordJar.java @@ -33,6 +33,7 @@ import com.seailz.discordjar.model.emoji.sticker.StickerPack; import com.seailz.discordjar.model.guild.Guild; import com.seailz.discordjar.model.guild.Member; +import com.seailz.discordjar.model.interaction.InteractionContextType; import com.seailz.discordjar.model.invite.Invite; import com.seailz.discordjar.model.invite.internal.InviteImpl; import com.seailz.discordjar.model.monetization.SKU; @@ -1067,6 +1068,7 @@ public void registerCommands(boolean push, boolean overwrite, CommandListener... Permission[] defaultMemberPermissions = (ann instanceof SlashCommandInfo) ? ((SlashCommandInfo) ann).defaultMemberPermissions() : ((ContextCommandInfo) ann).defaultMemberPermissions(); boolean canUseInDms = (ann instanceof SlashCommandInfo) ? ((SlashCommandInfo) ann).canUseInDms() : ((ContextCommandInfo) ann).canUseInDms(); boolean nsfw = (ann instanceof SlashCommandInfo) ? ((SlashCommandInfo) ann).nsfw() : ((ContextCommandInfo) ann).nsfw(); + InteractionContextType[] contextTypes = (ann instanceof ContextCommandInfo) ? ((ContextCommandInfo) ann).contexts() : ((SlashCommandInfo) ann).contexts(); if (overwrite) list.add(new Command( name, listener.getType(), @@ -1076,7 +1078,8 @@ public void registerCommands(boolean push, boolean overwrite, CommandListener... descriptionLocales, defaultMemberPermissions, canUseInDms, - nsfw + nsfw, + Arrays.stream(contextTypes).toList() )); else { new Thread(() -> { @@ -1090,7 +1093,8 @@ public void registerCommands(boolean push, boolean overwrite, CommandListener... descriptionLocales, defaultMemberPermissions, canUseInDms, - nsfw + nsfw, + Arrays.stream(contextTypes).toList() ) ); }, "djar--command-register").start(); diff --git a/src/main/java/com/seailz/discordjar/command/Command.java b/src/main/java/com/seailz/discordjar/command/Command.java index f8ecbfb4..76d6efad 100644 --- a/src/main/java/com/seailz/discordjar/command/Command.java +++ b/src/main/java/com/seailz/discordjar/command/Command.java @@ -2,6 +2,7 @@ import com.seailz.discordjar.command.annotation.Locale; import com.seailz.discordjar.core.Compilerable; +import com.seailz.discordjar.model.interaction.InteractionContextType; import com.seailz.discordjar.utils.flag.BitwiseUtil; import com.seailz.discordjar.utils.permission.Permission; import org.json.JSONArray; @@ -30,8 +31,10 @@ public record Command( Locale[] nameLocalizations, Locale[] descriptionLocalizations, Permission[] defaultMemberPermissions, + @Deprecated boolean canUseInDms, - boolean nsfw + boolean nsfw, + List contexts ) implements Compilerable { @Override public JSONObject compile() { @@ -75,6 +78,10 @@ public JSONObject compile() { obj.put("dm_permission", canUseInDms); if (nsfw) obj.put("nsfw", true); + JSONArray contextsJson = new JSONArray(); + contexts.forEach((context) -> contextsJson.put(context.getCode())); + obj.put("contexts", contextsJson); + return obj; } @@ -88,6 +95,7 @@ public static Command decompile(JSONObject obj) { Permission[] defaultMemberPermissions = new Permission[0]; boolean canUseInDms = true; boolean nsfw = false; + List contexts = new ArrayList<>(); if (obj.has("name_localizations") && !obj.isNull("name_localizations")) { JSONObject nameLocalesJson = obj.getJSONObject("name_localizations"); @@ -124,6 +132,12 @@ public static Command decompile(JSONObject obj) { } } + if (obj.has("contexts") && obj.get("contexts") != JSONObject.NULL) { + for (Object v : obj.getJSONArray("contexts")) { + contexts.add(InteractionContextType.fromCode((int) v)); + } + } + return new Command( name, type, @@ -133,7 +147,8 @@ public static Command decompile(JSONObject obj) { descriptionLocales.entrySet().stream().map((entry) -> newLocale(entry.getKey(), entry.getValue())).toArray(Locale[]::new), defaultMemberPermissions, canUseInDms, - nsfw + nsfw, + contexts ); } diff --git a/src/main/java/com/seailz/discordjar/command/annotation/ContextCommandInfo.java b/src/main/java/com/seailz/discordjar/command/annotation/ContextCommandInfo.java index ce37cf6c..af25b2c1 100644 --- a/src/main/java/com/seailz/discordjar/command/annotation/ContextCommandInfo.java +++ b/src/main/java/com/seailz/discordjar/command/annotation/ContextCommandInfo.java @@ -1,5 +1,6 @@ package com.seailz.discordjar.command.annotation; +import com.seailz.discordjar.model.interaction.InteractionContextType; import com.seailz.discordjar.utils.permission.Permission; import java.lang.annotation.ElementType; @@ -29,7 +30,16 @@ Permission[] defaultMemberPermissions() default {}; + /** + * @deprecated Use {@link #contexts()} instead. + */ + @Deprecated boolean canUseInDms() default true; boolean nsfw() default false; + + /** + * Contexts where this command can be used. Default is all contexts. + */ + InteractionContextType[] contexts() default {InteractionContextType.BOT_DM, InteractionContextType.GUILD, InteractionContextType.PRIVATE_CHANNEL}; } diff --git a/src/main/java/com/seailz/discordjar/command/annotation/SlashCommandInfo.java b/src/main/java/com/seailz/discordjar/command/annotation/SlashCommandInfo.java index de4ed35b..dbf02831 100644 --- a/src/main/java/com/seailz/discordjar/command/annotation/SlashCommandInfo.java +++ b/src/main/java/com/seailz/discordjar/command/annotation/SlashCommandInfo.java @@ -1,6 +1,7 @@ package com.seailz.discordjar.command.annotation; import com.seailz.discordjar.command.listeners.slash.SlashCommandListener; +import com.seailz.discordjar.model.interaction.InteractionContextType; import com.seailz.discordjar.utils.permission.Permission; import java.lang.annotation.ElementType; @@ -37,9 +38,18 @@ Permission[] defaultMemberPermissions() default {}; + /** + * @deprecated Use {@link #contexts()} instead. + */ + @Deprecated boolean canUseInDms() default true; boolean nsfw() default false; + /** + * Contexts where this command can be used. Default is all contexts. + */ + InteractionContextType[] contexts() default {InteractionContextType.BOT_DM, InteractionContextType.GUILD, InteractionContextType.PRIVATE_CHANNEL}; + } diff --git a/src/main/java/com/seailz/discordjar/model/application/Application.java b/src/main/java/com/seailz/discordjar/model/application/Application.java index 7e95cb2e..f5aa8c7c 100644 --- a/src/main/java/com/seailz/discordjar/model/application/Application.java +++ b/src/main/java/com/seailz/discordjar/model/application/Application.java @@ -3,6 +3,7 @@ import com.seailz.discordjar.DiscordJar; import com.seailz.discordjar.core.Compilerable; import com.seailz.discordjar.model.guild.Guild; +import com.seailz.discordjar.model.interaction.IntegrationType; import com.seailz.discordjar.model.scopes.InstallParams; import com.seailz.discordjar.model.team.Team; import com.seailz.discordjar.model.user.User; @@ -78,7 +79,7 @@ public record Application( InstallParams installParams, String roleConnectionsVerificationUrl, int approximateGuildCount, - HashMap integrationTypes, + HashMap integrationTypes, DiscordJar discordJar ) implements Compilerable, Snowflake { @@ -86,7 +87,7 @@ public record Application( public JSONObject compile() { JSONObject integrationTypes = new JSONObject(); this.integrationTypes.forEach((key, val) -> { - integrationTypes.put(String.valueOf(key.code), val.compile()); + integrationTypes.put(String.valueOf(key.getCode()), val.compile()); }); return new JSONObject() .put("id", id) @@ -142,7 +143,7 @@ public static Application decompile(JSONObject obj, DiscordJar discordJar) { InstallParams installParams; String roleConnectionsVerificationUrl; int approximateGuildCount; - HashMap integrationTypesConfiguration = null; + HashMap integrationTypesConfiguration = null; try { id = obj.getString("id"); @@ -293,7 +294,7 @@ public static Application decompile(JSONObject obj, DiscordJar discordJar) { integrationTypesConfiguration = new HashMap<>(); JSONObject integrationTypesConfig = obj.getJSONObject("integration_types_config"); for (String code : integrationTypesConfig.keySet()) { - integrationTypesConfiguration.put(IntegrationTypes.getByCode(Integer.parseInt(code)), IntegrationTypeConfiguration.decompile(integrationTypesConfig.getJSONObject(code))); + integrationTypesConfiguration.put(IntegrationType.fromCode(Integer.parseInt(code)), IntegrationTypeConfiguration.decompile(integrationTypesConfig.getJSONObject(code))); } } @@ -442,32 +443,6 @@ public int id() { } } - - public enum IntegrationTypes { - - GUILD_INSTALL(0), - USER_INSTALL(1), - UNKNOWN(-1) - ; - - private final int code; - - IntegrationTypes(int code) { - this.code = code; - } - - public int getCode() { - return code; - } - - public static IntegrationTypes getByCode(int code) { - for (IntegrationTypes value : values()) { - if (value.getCode() == code) return value; - } - return UNKNOWN; - } - } - public record IntegrationTypeConfiguration( InstallParams installParams ) implements Compilerable { diff --git a/src/main/java/com/seailz/discordjar/model/interaction/IntegrationType.java b/src/main/java/com/seailz/discordjar/model/interaction/IntegrationType.java new file mode 100644 index 00000000..351b3a76 --- /dev/null +++ b/src/main/java/com/seailz/discordjar/model/interaction/IntegrationType.java @@ -0,0 +1,25 @@ +package com.seailz.discordjar.model.interaction; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum IntegrationType { + + GUILD_INSTALL(0), + USER_INSTALL(1), + UNKNOWN(-1); + + private final int code; + + public static IntegrationType fromCode(int code) { + for (IntegrationType type : values()) { + if (type.code == code) { + return type; + } + } + return UNKNOWN; + } + +} diff --git a/src/main/java/com/seailz/discordjar/model/interaction/Interaction.java b/src/main/java/com/seailz/discordjar/model/interaction/Interaction.java index fb3de459..81a39e9f 100644 --- a/src/main/java/com/seailz/discordjar/model/interaction/Interaction.java +++ b/src/main/java/com/seailz/discordjar/model/interaction/Interaction.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; /** @@ -67,8 +68,10 @@ public class Interaction implements Compilerable { private final DiscordJar djar; @Deprecated private final String channelId; + private final InteractionContextType context; + private final HashMap authorizingIntegrationOwners; - public Interaction(String id, Application application, InteractionType type, InteractionData data, String guild, Channel channel, JSONObject member, User user, String token, int version, Message message, String appPermissions, String locale, String guildLocale, List entitlements, String raw, String channelId, DiscordJar discordJar) { + public Interaction(String id, Application application, InteractionType type, InteractionData data, String guild, Channel channel, JSONObject member, User user, String token, int version, Message message, String appPermissions, String locale, String guildLocale, List entitlements, String raw, String channelId, InteractionContextType context, HashMap authorizingIntegrationOwners, DiscordJar discordJar) { this.id = id; this.application = application; this.type = type; @@ -92,6 +95,8 @@ public Interaction(String id, Application application, InteractionType type, Int this.channelId = channelId; this.djar = discordJar; this.entitlements = entitlements; + this.context = context; + this.authorizingIntegrationOwners = authorizingIntegrationOwners; } @Deprecated @@ -169,6 +174,13 @@ public String raw() { public List entitlements() { return entitlements; } + public InteractionContextType context() { + return context; + } + + public HashMap authorizingIntegrationOwners() { + return authorizingIntegrationOwners; + } @Override public JSONObject compile() { @@ -196,6 +208,11 @@ public JSONObject compile() { } } + JSONObject authIntOwners = new JSONObject(); + this.authorizingIntegrationOwners.forEach((key, val) -> { + authIntOwners.put(String.valueOf(key.getCode()), val); + }); + return new JSONObject() .put("id", id) .put("application", application.id()) @@ -211,7 +228,9 @@ public JSONObject compile() { .put("locale", locale) .put("guild_locale", guildLocale) .put("channel_id", channelId) - .put("entitlements", entitlements); + .put("entitlements", entitlements) + .put("context", context.getCode()) + .put("authorizing_integration_owners", authIntOwners); } @NotNull @@ -231,6 +250,7 @@ public static Interaction decompile(JSONObject json, DiscordJar discordJar) thro String locale = json.has("locale") ? json.getString("locale") : null; String guildLocale = json.has("guild_locale") ? json.getString("guild_locale") : null; String channelId = json.has("channel_id") ? json.getString("channel_id") : null; + InteractionContextType context = json.has("context") ? InteractionContextType.fromCode(json.getInt("context")) : null; JSONArray entitlements = json.has("entitlements") ? json.getJSONArray("entitlements") : null; List entitlementList = new ArrayList<>(); @@ -240,7 +260,17 @@ public static Interaction decompile(JSONObject json, DiscordJar discordJar) thro } } - return new Interaction(id, application, type, data, guildId, channel, json.has("member") ? json.getJSONObject("member") : null, user, token, version, message, appPermissions, locale, guildLocale, entitlementList, json.toString(), channelId, discordJar); + HashMap authIntegrationOwners = null; + + if (json.has("authorizing_integration_owners")) { + authIntegrationOwners = new HashMap<>(); + JSONObject authIntegrationOwnersJson = json.getJSONObject("authorizing_integration_owners"); + for (String code : authIntegrationOwnersJson.keySet()) { + authIntegrationOwners.put(IntegrationType.fromCode(Integer.parseInt(code)), authIntegrationOwnersJson.getString(code)); + } + } + + return new Interaction(id, application, type, data, guildId, channel, json.has("member") ? json.getJSONObject("member") : null, user, token, version, message, appPermissions, locale, guildLocale, entitlementList, json.toString(), channelId, context, authIntegrationOwners, discordJar); } diff --git a/src/main/java/com/seailz/discordjar/model/interaction/InteractionContextType.java b/src/main/java/com/seailz/discordjar/model/interaction/InteractionContextType.java new file mode 100644 index 00000000..8e2427c9 --- /dev/null +++ b/src/main/java/com/seailz/discordjar/model/interaction/InteractionContextType.java @@ -0,0 +1,26 @@ +package com.seailz.discordjar.model.interaction; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum InteractionContextType { + + GUILD(0), + BOT_DM(1), + PRIVATE_CHANNEL(2), + UNKNOWN(-1); + + private final int code; + + public static InteractionContextType fromCode(int code) { + for (InteractionContextType type : values()) { + if (type.code == code) { + return type; + } + } + return UNKNOWN; + } + +} diff --git a/src/main/java/com/seailz/discordjar/model/message/Message.java b/src/main/java/com/seailz/discordjar/model/message/Message.java index 3a735a4d..5e64bc8c 100644 --- a/src/main/java/com/seailz/discordjar/model/message/Message.java +++ b/src/main/java/com/seailz/discordjar/model/message/Message.java @@ -89,7 +89,8 @@ public record Message( MessageFlag[] flags, // the message associated with the message_reference Message referencedMessage, - // sent if the message is a response to an Interaction + // sent if the message is a response to an Interaction - Deprecated, use interactionMetadata instead + @Deprecated Interaction interaction, // the thread that was started from this message, includes thread member object Thread thread, @@ -99,6 +100,7 @@ public record Message( int position, // data of the role subscription purchase or renewal that prompted this ROLE_SUBSCRIPTION_PURCHASE message RoleSubscriptionData roleSubscriptionData, + MessageInteractionMetadataObject interactionMetadata, DiscordJar discordJar ) implements Compilerable, Resolvable, Snowflake { @@ -134,6 +136,7 @@ public static Message decompile(JSONObject obj, DiscordJar discordJar) { List stickerItems = new ArrayList<>(); int position = 0; RoleSubscriptionData roleSubscriptionData = null; + MessageInteractionMetadataObject interactionMetadata = null; try { id = obj.getString("id"); @@ -339,7 +342,11 @@ public static Message decompile(JSONObject obj, DiscordJar discordJar) { if (obj.has("position")) position = obj.getInt("position"); if (obj.has("role_subscription_data")) roleSubscriptionData = RoleSubscriptionData.decompile(obj.getJSONObject("role_subscription_data")); - return new Message(id, channelId, author, content, timestamp, editedTimestamp, tts, mentionEveryone, mentions, mentionRoles, mentionChannels, attachments, embeds, reactions, nonce, pinned, webhookId, type, activity, application, applicationId, messageReference, flags, referencedMessage, interaction, thread, components, stickerItems, position, roleSubscriptionData, discordJar); + if (obj.has("interaction_metadata")) { + interactionMetadata = MessageInteractionMetadataObject.decompile(obj.getJSONObject("interaction_metadata")); + } + + return new Message(id, channelId, author, content, timestamp, editedTimestamp, tts, mentionEveryone, mentions, mentionRoles, mentionChannels, attachments, embeds, reactions, nonce, pinned, webhookId, type, activity, application, applicationId, messageReference, flags, referencedMessage, interaction, thread, components, stickerItems, position, roleSubscriptionData, interactionMetadata, discordJar); } @Override @@ -425,7 +432,11 @@ public JSONObject compile() { .put("referenced_message", referencedMessage == null ? JSONObject.NULL : referencedMessage.compile()) .put("interaction", interaction == null ? JSONObject.NULL : interaction.compile()) .put("thread", thread == null ? JSONObject.NULL : thread.compile()) - .put("components", componentsArray); + .put("components", componentsArray) + .put("sticker_items", stickerItems) + .put("position", position) + .put("role_subscription_data", roleSubscriptionData == null ? JSONObject.NULL : roleSubscriptionData.compile()) + .put("interaction_metadata", interactionMetadata == null ? JSONObject.NULL : interactionMetadata.compile()); } public void delete() { diff --git a/src/main/java/com/seailz/discordjar/model/message/MessageInteractionMetadataObject.java b/src/main/java/com/seailz/discordjar/model/message/MessageInteractionMetadataObject.java new file mode 100644 index 00000000..a3cf4efe --- /dev/null +++ b/src/main/java/com/seailz/discordjar/model/message/MessageInteractionMetadataObject.java @@ -0,0 +1,48 @@ +package com.seailz.discordjar.model.message; + +import com.seailz.discordjar.core.Compilerable; +import com.seailz.discordjar.model.interaction.IntegrationType; +import com.seailz.discordjar.model.interaction.InteractionType; +import org.json.JSONObject; + +import java.util.HashMap; + +public record MessageInteractionMetadataObject( + String id, + InteractionType type, + String userId, + HashMap authorizingIntegrationOwners, + String originalResponseMessageId, + MessageInteractionMetadataObject triggeringInteractionMetadata +) implements Compilerable { + @Override + public JSONObject compile() { + JSONObject object = new JSONObject(); + object.put("id", id); + object.put("type", type.getCode()); + object.put("user_id", userId); + object.put("authorizing_integration_owners", authorizingIntegrationOwners); + object.put("original_response_message_id", originalResponseMessageId); + object.put("triggering_interaction_metadata", triggeringInteractionMetadata.compile()); + return object; + } + + public static MessageInteractionMetadataObject decompile(JSONObject object) { + + HashMap authorizingIntegrationOwners = new HashMap<>(); + + if (object.has("authorizing_integration_owners")) + object.getJSONObject("authorizing_integration_owners").toMap() + .forEach((key, value) -> authorizingIntegrationOwners.put(IntegrationType.fromCode(Integer.parseInt(key)), (String) value)); + + + return new MessageInteractionMetadataObject( + object.has("id") ? object.getString("id") : null, + object.has("type") ? InteractionType.getType(object.getInt("type")) : null, + object.has("user_id") ? object.getString("user_id") : null, + authorizingIntegrationOwners, + object.has("original_response_message_id") ? object.getString("original_response_message_id") : null, + object.has("triggering_interaction_metadata") ? decompile(object.getJSONObject("triggering_interaction_metadata")) : null + ); + } +}