diff --git a/build.gradle b/build.gradle index 2404b2b..78c261a 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,14 @@ repositories { includeGroup("dev.architectury") } } + + // CurseForge Maven for Discord Chat Mod integration + maven { + url = "https://www.cursemaven.com" + content { + includeGroup("curse.maven") + } + } } base { @@ -138,6 +146,9 @@ dependencies { testAnnotationProcessor("org.projectlombok:lombok:1.18.42") implementation "dev.ftb.mods:ftb-teams-neoforge:${ftb_teams_version}" + + // Optional integration with Discord Chat Mod + compileOnly "curse.maven:discord-chat-connect-879306:latest" } // This block of code expands all declared replace properties in the specified resource targets. diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/com/heyyczer/conversation/chat/handlers/GlobalChatChannelHandler.java b/src/main/java/com/heyyczer/conversation/chat/handlers/GlobalChatChannelHandler.java index 39fd6e2..47f1b5d 100644 --- a/src/main/java/com/heyyczer/conversation/chat/handlers/GlobalChatChannelHandler.java +++ b/src/main/java/com/heyyczer/conversation/chat/handlers/GlobalChatChannelHandler.java @@ -1,6 +1,7 @@ package com.heyyczer.conversation.chat.handlers; import com.heyyczer.conversation.chat.ChatChannelHandler; +import com.heyyczer.conversation.integration.DiscordChatModIntegration; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -25,6 +26,11 @@ public void handleMessage(ServerPlayer sender, Component message) { .append(message.copy().withColor(ChatFormatting.GRAY.getColor())) ); } + + // Forward to Discord if Discord Chat Mod is present + if (DiscordChatModIntegration.isPresent()) { + DiscordChatModIntegration.sendToDiscord(sender, message); + } } } diff --git a/src/main/java/com/heyyczer/conversation/integration/DiscordChatModIntegration.java b/src/main/java/com/heyyczer/conversation/integration/DiscordChatModIntegration.java new file mode 100644 index 0000000..83aeb65 --- /dev/null +++ b/src/main/java/com/heyyczer/conversation/integration/DiscordChatModIntegration.java @@ -0,0 +1,147 @@ +package com.heyyczer.conversation.integration; + +import com.mojang.logging.LogUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import org.slf4j.Logger; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * Integration class for Discord Chat Mod (denisnumb/discord-chat-mod). + * This class uses reflection to interact with Discord Chat Mod's API, + * ensuring that ConversationMod works with or without Discord Chat Mod installed. + */ +public class DiscordChatModIntegration { + + private static final Logger LOGGER = LogUtils.getLogger(); + private static final String DISCORD_CHAT_MOD_CLASS = "com.denisnumb.discord_chat_mod.DiscordChatMod"; + + private static Boolean isPresent = null; + + // Reflected classes + private static Class discordChatModClass; + private static Class minecraftUtilsClass; + private static Class channelCategoryClass; + private static Class chatStyleUtilsClass; + private static Class discordMessageUtilsClass; + private static Class discordChatStyleProviderClass; + private static Class messageTypeClass; + private static Class discordChannelRegistryClass; + + // Reflected methods + private static Method processChatMessageMethod; + private static Method buildPlayerParametersMethod; + private static Method mergeMapsMethod; + private static Method getDiscordMessageComponentsMethod; + private static Method sendMessageFromPlayerMethod; + private static Method handleDiscordMethod; + private static Method getAllContextsMethod; + + // Reflected enum values + private static Object playerChatCategory; + private static Object chatMessageType; + private static Object chatWebhookMessageType; + + /** + * Checks if Discord Chat Mod is present and loaded. + * + * @return true if Discord Chat Mod is available, false otherwise + */ + public static boolean isPresent() { + if (isPresent != null) { + return isPresent; + } + + try { + discordChatModClass = Class.forName(DISCORD_CHAT_MOD_CLASS); + minecraftUtilsClass = Class.forName("com.denisnumb.discord_chat_mod.MinecraftUtils"); + channelCategoryClass = Class.forName("com.denisnumb.discord_chat_mod.discord.ChannelCategory"); + chatStyleUtilsClass = Class.forName("com.denisnumb.discord_chat_mod.chat_style.ChatStyleUtils"); + discordMessageUtilsClass = Class.forName("com.denisnumb.discord_chat_mod.discord.utils.DiscordMessageUtils"); + discordChatStyleProviderClass = Class.forName("com.denisnumb.discord_chat_mod.discord.chat_style.DiscordChatStyleProvider"); + messageTypeClass = Class.forName("com.denisnumb.discord_chat_mod.discord.chat_style.MessageType"); + discordChannelRegistryClass = Class.forName("com.denisnumb.discord_chat_mod.discord.DiscordChannelRegistry"); + + // Get methods + processChatMessageMethod = minecraftUtilsClass.getMethod("processChatMessage", Component.class, channelCategoryClass); + buildPlayerParametersMethod = chatStyleUtilsClass.getMethod("buildPlayerParameters", ServerPlayer.class, Component.class); + mergeMapsMethod = chatStyleUtilsClass.getMethod("mergeMaps", Map.class, Map.class); + getDiscordMessageComponentsMethod = discordChatStyleProviderClass.getMethod("getDiscordMessageComponents", messageTypeClass, Map.class); + sendMessageFromPlayerMethod = discordMessageUtilsClass.getMethod("sendMessageFromPlayer", ServerPlayer.class, String.class, String.class, channelCategoryClass, java.util.Collection.class); + handleDiscordMethod = discordMessageUtilsClass.getMethod("handleDiscord", Runnable.class); + getAllContextsMethod = discordChannelRegistryClass.getMethod("getAllContexts"); + + // Get enum values + playerChatCategory = channelCategoryClass.getField("PLAYER_CHAT").get(null); + chatMessageType = messageTypeClass.getField("CHAT").get(null); + chatWebhookMessageType = messageTypeClass.getField("CHAT_WEBHOOK").get(null); + + isPresent = true; + LOGGER.info("Discord Chat Mod integration enabled"); + return true; + } catch (ClassNotFoundException e) { + LOGGER.debug("Discord Chat Mod not found - integration disabled"); + isPresent = false; + return false; + } catch (Exception e) { + LOGGER.warn("Failed to initialize Discord Chat Mod integration", e); + isPresent = false; + return false; + } + } + + /** + * Sends a message to Discord through Discord Chat Mod. + * This method should only be called if isPresent() returns true. + * + * @param sender the player who sent the message + * @param message the message component to send + */ + public static void sendToDiscord(ServerPlayer sender, Component message) { + if (!isPresent()) { + return; + } + + try { + // Process the chat message + Component processedMessage = (Component) processChatMessageMethod.invoke(null, message, playerChatCategory); + + // Build player parameters + Map playerParams = (Map) buildPlayerParametersMethod.invoke(null, sender, processedMessage); + + // Get message components for CHAT type + Map chatComponents = (Map) getDiscordMessageComponentsMethod.invoke(null, chatMessageType, playerParams); + + // Get message components for CHAT_WEBHOOK type + Map webhookComponents = (Map) getDiscordMessageComponentsMethod.invoke(null, chatWebhookMessageType, playerParams); + + // Merge the maps + Map mergedParams = new HashMap<>(playerParams); + Map finalParams = (Map) mergeMapsMethod.invoke(null, mergedParams, chatComponents); + finalParams = (Map) mergeMapsMethod.invoke(null, finalParams, webhookComponents); + + // Get all channel contexts + Object allContexts = getAllContextsMethod.invoke(null); + + // Extract content and username from the final parameters + String content = finalParams.getOrDefault("content", processedMessage.getString()); + String username = finalParams.getOrDefault("username", sender.getName().getString()); + + // Send the message asynchronously + handleDiscordMethod.invoke(null, (Runnable) () -> { + try { + sendMessageFromPlayerMethod.invoke(null, sender, content, username, playerChatCategory, allContexts); + } catch (Exception e) { + LOGGER.error("Failed to send message to Discord", e); + } + }); + + LOGGER.debug("Message sent to Discord from {}: {}", sender.getName().getString(), processedMessage.getString()); + } catch (Exception e) { + LOGGER.error("Failed to send message to Discord", e); + } + } +} diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml index 063ed27..f310318 100644 --- a/src/main/templates/META-INF/neoforge.mods.toml +++ b/src/main/templates/META-INF/neoforge.mods.toml @@ -73,6 +73,14 @@ versionRange = "${minecraft_version_range}" ordering = "NONE" side = "SERVER" +# Optional integration with Discord Chat Mod +[[dependencies."${mod_id}"]] +modId = "discord_chat_mod" +type = "optional" +versionRange = "[1.0,)" +ordering = "AFTER" +side = "SERVER" + # Features are specific properties of the game environment, that you may want to declare you require. This example declares # that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't # stop your mod loading on the server for example.